[DRE-commits] [ruby-celluloid-io] 01/03: Imported Upstream version 0.15.0

Miguel Landaeta nomadium at moszumanska.debian.org
Sat Jul 26 21:39:28 UTC 2014


This is an automated email from the git hooks/post-receive script.

nomadium pushed a commit to branch master
in repository ruby-celluloid-io.

commit 6a967bd2495cc33740377db0327a9ac5bc3af53a
Author: Miguel Landaeta <nomadium at debian.org>
Date:   Fri Jul 25 23:05:39 2014 -0300

    Imported Upstream version 0.15.0
---
 .coveralls.yml                         |   1 +
 .gitignore                             |  17 ++
 .rspec                                 |   4 +
 .travis.yml                            |  17 ++
 CHANGES.md                             |  56 +++++
 Gemfile                                |   5 +
 Guardfile                              |   5 +
 LICENSE.txt                            |  22 ++
 README.md                              |  97 ++++++++
 Rakefile                               |   6 +
 benchmarks/actor.rb                    |  60 +++++
 celluloid-io.gemspec                   |  27 +++
 checksums.yaml.gz                      | Bin 0 -> 269 bytes
 examples/echo_client.rb                |  25 ++
 examples/echo_server.rb                |  41 ++++
 examples/echo_unix_client.rb           |  29 +++
 examples/echo_unix_server.rb           |  46 ++++
 lib/celluloid/io.rb                    |  55 +++++
 lib/celluloid/io/dns_resolver.rb       |  88 ++++++++
 lib/celluloid/io/mailbox.rb            |  10 +
 lib/celluloid/io/reactor.rb            |  62 +++++
 lib/celluloid/io/ssl_server.rb         |  36 +++
 lib/celluloid/io/ssl_socket.rb         |  39 ++++
 lib/celluloid/io/stream.rb             | 401 +++++++++++++++++++++++++++++++++
 lib/celluloid/io/tcp_server.rb         |  35 +++
 lib/celluloid/io/tcp_socket.rb         | 117 ++++++++++
 lib/celluloid/io/udp_socket.rb         |  36 +++
 lib/celluloid/io/unix_server.rb        |  32 +++
 lib/celluloid/io/unix_socket.rb        |  45 ++++
 lib/celluloid/io/version.rb            |   5 +
 log/.gitignore                         |   1 +
 logo.png                               | Bin 0 -> 25743 bytes
 metadata.yml                           | 206 +++++++++++++++++
 spec/celluloid/io/actor_spec.rb        |   5 +
 spec/celluloid/io/dns_resolver_spec.rb |  33 +++
 spec/celluloid/io/mailbox_spec.rb      |   5 +
 spec/celluloid/io/ssl_server_spec.rb   |  91 ++++++++
 spec/celluloid/io/ssl_socket_spec.rb   | 219 ++++++++++++++++++
 spec/celluloid/io/tcp_server_spec.rb   |  51 +++++
 spec/celluloid/io/tcp_socket_spec.rb   | 195 ++++++++++++++++
 spec/celluloid/io/udp_socket_spec.rb   |  36 +++
 spec/celluloid/io/unix_server_spec.rb  |  70 ++++++
 spec/celluloid/io/unix_socket_spec.rb  | 166 ++++++++++++++
 spec/fixtures/client.crt               |  22 ++
 spec/fixtures/client.key               |  27 +++
 spec/fixtures/server.crt               |  22 ++
 spec/fixtures/server.key               |  27 +++
 spec/spec_helper.rb                    | 106 +++++++++
 tasks/benchmarks.task                  |  19 ++
 tasks/rspec.task                       |   7 +
 50 files changed, 2727 insertions(+)

diff --git a/.coveralls.yml b/.coveralls.yml
new file mode 100644
index 0000000..e1da6a3
--- /dev/null
+++ b/.coveralls.yml
@@ -0,0 +1 @@
+service-name: travis-pro
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d87d4be
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+*.gem
+*.rbc
+.bundle
+.config
+.yardoc
+Gemfile.lock
+InstalledFiles
+_yardoc
+coverage
+doc/
+lib/bundler/man
+pkg
+rdoc
+spec/reports
+test/tmp
+test/version_tmp
+tmp
diff --git a/.rspec b/.rspec
new file mode 100644
index 0000000..270de17
--- /dev/null
+++ b/.rspec
@@ -0,0 +1,4 @@
+--color 
+--format documentation 
+--backtrace
+--default_path spec
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..5ea492a
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,17 @@
+script: rake ci
+rvm:
+  - 1.9.3
+  - 2.0.0
+  - ruby-head
+  - jruby-19mode
+  - rbx-19mode
+  - jruby-head
+
+matrix:
+  allow_failures:
+    - rvm: ruby-head
+    - rvm: jruby-head
+    - rvm: rbx-19mode
+
+notifications:
+  irc: "irc.freenode.org#celluloid"
diff --git a/CHANGES.md b/CHANGES.md
new file mode 100644
index 0000000..7692261
--- /dev/null
+++ b/CHANGES.md
@@ -0,0 +1,56 @@
+0.15.0 (2013-09-04)
+-------------------
+* Improved DNS resolver with less NIH and more Ruby stdlib goodness
+* Better match Ruby stdlib TCPServer API
+* Add missing #send and #recv on Celluloid::IO::TCPSocket
+* Add missing #setsockopt method on Celluloid::IO::TCPServer
+* Add missing #peeraddr method on Celluloid::IO::SSLSocket
+
+0.14.0 (2013-05-07)
+-------------------
+* Add `close_read`/`close_write` delegates for rack-hijack support
+* Depend on EventedMailbox from core
+
+0.13.1
+------
+* Remove overhead for `wait_readable`/`wait_writable`
+
+0.13.0
+------
+* Support for many, many more IO methods, particularly line-oriented
+  methods like #gets, #readline, and #readlines
+* Initial SSL support via Celluloid::IO::SSLSocket and
+  Celluloid::IO::SSLServer
+* Concurrent writes between tasks of the same actor are now coordinated
+  using Celluloid::Conditions instead of signals
+* Celluloid 0.13 compatibility fixes
+
+0.12.0
+------
+* Tracking release for Celluloid 0.12.0
+
+0.11.0
+------
+* "Unofficial" SSL support (via nio4r 0.4.0)
+
+0.10.0
+------
+* Read/write operations are now atomic across tasks
+* True non-blocking connect support
+* Non-blocking DNS resolution support
+
+0.9.0
+-----
+* TCPServer, TCPSocket, and UDPSocket classes in Celluloid::IO namespace
+  with both evented and blocking I/O support
+* Celluloid::IO::Mailbox.new now takes a single parameter to specify an
+  alternative reactor (e.g. Celluloid::ZMQ::Reactor)
+
+0.8.0
+-----
+* Switch to nio4r-based reactor
+* Compatibility with Celluloid 0.8.0 API changes
+
+0.7.0
+-----
+* Initial release forked from Celluloid
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..52ce11f
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,5 @@
+source 'https://rubygems.org'
+gemspec
+
+gem 'coveralls', require: false
+gem 'celluloid', github: 'celluloid/celluloid', branch: 'master'
diff --git a/Guardfile b/Guardfile
new file mode 100644
index 0000000..1745569
--- /dev/null
+++ b/Guardfile
@@ -0,0 +1,5 @@
+guard 'rspec', :cli => '--format documentation' do
+  watch(%r{^spec/.+_spec\.rb$})
+  watch(%r{^lib/(.+)\.rb$})     { |m| "spec/#{m[1]}_spec.rb" }
+  watch('spec/spec_helper.rb')  { "spec/" }
+end
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..db37b74
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Tony Arcieri
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f5a853b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,97 @@
+![Celluloid::IO](https://github.com/celluloid/celluloid-io/raw/master/logo.png)
+================
+[![Gem Version](https://badge.fury.io/rb/celluloid-io.png)](http://rubygems.org/gems/celluloid-io)
+[![Build Status](https://secure.travis-ci.org/celluloid/celluloid-io.png?branch=master)](http://travis-ci.org/celluloid/celluloid-io)
+[![Code Climate](https://codeclimate.com/github/celluloid/celluloid-io.png)](https://codeclimate.com/github/celluloid/celluloid-io)
+[![Coverage Status](https://coveralls.io/repos/celluloid/celluloid-io/badge.png?branch=master)](https://coveralls.io/r/celluloid/celluloid-io)
+
+You don't have to choose between threaded and evented IO! Celluloid::IO
+provides an event-driven IO system for building fast, scalable network
+applications that integrates directly with the
+[Celluloid actor library](https://github.com/celluloid/celluloid), making it
+easy to combine both threaded and evented concepts. Celluloid::IO is ideal for
+servers which handle large numbers of mostly-idle connections, such as Websocket
+servers or chat/messaging systems.
+
+Celluloid::IO provides a different class of actor: one that's slightly slower
+and heavier than standard Celluloid actors, but one which contains a
+high-performance reactor just like EventMachine or Cool.io. This means
+Celluloid::IO actors have the power of both Celluloid actors and evented
+I/O loops. Unlike certain other evented I/O systems which limit you to a
+single event loop per process, Celluloid::IO lets you make as many actors as
+you want, system resources permitting.
+
+Rather than callbacks, Celluloid::IO exposes a synchronous API built on duck
+types of Ruby's own IO classes, such as TCPServer and TCPSocket. These classes
+work identically to their core Ruby counterparts, but in the scope of
+Celluloid::IO actors provide "evented" performance. Since they're drop-in
+replacements for the standard classes, there's no need to rewrite every
+library just to take advantage of Celluloid::IO's event loop and you can
+freely switch between evented and blocking IO even over the lifetime of a
+single connection.
+
+Celluloid::IO uses the [nio4r gem](https://github.com/celluloid/nio4r)
+to monitor IO objects, which provides cross-platform and cross-Ruby
+implementation access to high-performance system calls such as epoll
+and kqueue.
+
+Like Celluloid::IO? [Join the Celluloid Google Group](http://groups.google.com/group/celluloid-ruby)
+
+Documentation
+-------------
+
+[Please see the Celluloid::IO Wiki](https://github.com/celluloid/celluloid-io/wiki)
+for more detailed documentation and usage notes.
+
+[YARD documentation](http://rubydoc.info/github/celluloid/celluloid-io/frames)
+is also available
+
+Installation
+------------
+
+Add this line to your application's Gemfile:
+
+    gem 'celluloid-io'
+
+And then execute:
+
+    $ bundle
+
+Or install it yourself as:
+
+    $ gem install celluloid-io
+
+Inside of your Ruby program, require Celluloid::IO with:
+
+    require 'celluloid/io'
+
+Supported Platforms
+-------------------
+
+Celluloid::IO works on Ruby 1.9.3, 2.0.0, JRuby 1.6+, and Rubinius 2.0.
+
+JRuby or Rubinius are the preferred platforms as they support true thread-level
+parallelism when executing Ruby code, whereas MRI/YARV is constrained by a global
+interpreter lock (GIL) and can only execute one thread at a time.
+
+Celluloid::IO requires Ruby 1.9 mode on all interpreters.
+
+Contributing to Celluloid::IO
+-----------------------------
+
+* Fork this repository on github
+* Make your changes and send me a pull request
+* If I like them I'll merge them
+* If I've accepted a patch, feel free to ask for a commit bit!
+
+License
+-------
+
+Copyright (c) 2013 Tony Arcieri. Distributed under the MIT License. See
+LICENSE.txt for further details.
+
+Contains code originally from the RubySpec project also under the MIT License.
+Copyright (c) 2008 Engine Yard, Inc. All rights reserved.
+
+Contains code originally from the 'OpenSSL for Ruby 2' project released under
+the Ruby license. Copyright (C) 2001 GOTOU YUUZOU. All rights reserved.
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..f43f2b6
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,6 @@
+#!/usr/bin/env rake
+require 'bundler/gem_tasks'
+Dir["tasks/**/*.task"].each { |task| load task }
+
+task :default => :spec
+task :ci      => %w(spec benchmark)
diff --git a/benchmarks/actor.rb b/benchmarks/actor.rb
new file mode 100755
index 0000000..4a9a679
--- /dev/null
+++ b/benchmarks/actor.rb
@@ -0,0 +1,60 @@
+#!/usr/bin/env ruby
+
+require 'rubygems'
+require 'bundler/setup'
+require 'celluloid/io'
+require 'benchmark/ips'
+
+class ExampleActor
+  include Celluloid::IO
+
+  def initialize
+    @condition = Celluloid::Condition.new
+  end
+
+  def example_method; end
+
+  def finished
+    @condition.signal
+  end
+
+  def wait_until_finished
+    @condition.wait
+  end
+end
+
+example_actor = ExampleActor.new
+mailbox = Celluloid::IO::Mailbox.new
+
+latch_in, latch_out = Queue.new, Queue.new
+latch = Thread.new do
+  while true
+    n = latch_in.pop
+    for i in 0...n; mailbox.receive; end
+    latch_out << :done
+  end
+end
+
+Benchmark.ips do |ips|
+  ips.report("spawn")       { ExampleActor.new.terminate }
+
+  ips.report("calls")       { example_actor.example_method }
+
+  ips.report("async calls") do |n|
+    waiter = example_actor.future.wait_until_finished
+
+    for i in 1..n; example_actor.async.example_method; end
+    example_actor.async.finished
+
+    waiter.value
+  end
+
+# Deadlocking o_O
+=begin
+  ips.report("messages") do |n|
+    latch_in << n
+    for i in 0...n; mailbox << :message; end
+    latch_out.pop
+  end
+=end
+end
diff --git a/celluloid-io.gemspec b/celluloid-io.gemspec
new file mode 100644
index 0000000..874a8db
--- /dev/null
+++ b/celluloid-io.gemspec
@@ -0,0 +1,27 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path('../lib/celluloid/io/version', __FILE__)
+
+Gem::Specification.new do |gem|
+  gem.name          = "celluloid-io"
+  gem.version       = Celluloid::IO::VERSION
+  gem.license       = 'MIT'
+  gem.authors       = ["Tony Arcieri"]
+  gem.email         = ["tony.arcieri at gmail.com"]
+  gem.description   = "Evented IO for Celluloid actors"
+  gem.summary       = "Celluloid::IO allows you to monitor multiple IO objects within a Celluloid actor"
+  gem.homepage      = "http://github.com/celluloid/celluloid-io"
+
+  gem.executables   = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+  gem.files         = `git ls-files`.split("\n")
+  gem.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")
+  gem.require_paths = ["lib"]
+
+  gem.add_dependency 'celluloid', '>= 0.15.0'
+  gem.add_dependency 'nio4r',     '>= 0.5.0'
+
+  gem.add_development_dependency 'rake'
+  gem.add_development_dependency 'rspec'
+  gem.add_development_dependency 'benchmark_suite'
+  gem.add_development_dependency 'guard-rspec'
+  gem.add_development_dependency 'rb-fsevent', '~> 0.9.1' if RUBY_PLATFORM =~ /darwin/
+end
diff --git a/checksums.yaml.gz b/checksums.yaml.gz
new file mode 100644
index 0000000..53811b7
Binary files /dev/null and b/checksums.yaml.gz differ
diff --git a/examples/echo_client.rb b/examples/echo_client.rb
new file mode 100644
index 0000000..70bd600
--- /dev/null
+++ b/examples/echo_client.rb
@@ -0,0 +1,25 @@
+#!/usr/bin/env ruby
+
+require 'rubygems'
+require 'bundler/setup'
+require 'celluloid/io'
+
+class EchoClient
+  include Celluloid::IO
+
+  def initialize(host, port)
+    puts "*** Connecting to echo server on #{host}:#{port}"
+
+    # This is actually creating a Celluloid::IO::TCPSocket
+    @socket = TCPSocket.new(host, port)
+  end
+
+  def echo(s)
+    @socket.write(s)
+    @socket.readpartial(4096)
+  end
+
+end
+
+client = EchoClient.new("127.0.0.1", 1234)
+puts client.echo("TEST FOR ECHO")
diff --git a/examples/echo_server.rb b/examples/echo_server.rb
new file mode 100755
index 0000000..e40d053
--- /dev/null
+++ b/examples/echo_server.rb
@@ -0,0 +1,41 @@
+#!/usr/bin/env ruby
+#
+# Run this as: bundle exec examples/echo_server.rb
+
+require 'bundler/setup'
+require 'celluloid/io'
+
+class EchoServer
+  include Celluloid::IO
+  finalizer :finalize
+
+  def initialize(host, port)
+    puts "*** Starting echo server on #{host}:#{port}"
+
+    # Since we included Celluloid::IO, we're actually making a
+    # Celluloid::IO::TCPServer here
+    @server = TCPServer.new(host, port)
+    async.run
+  end
+
+  def finalize
+    @server.close if @server
+  end
+
+  def run
+    loop { async.handle_connection @server.accept }
+  end
+
+  def handle_connection(socket)
+    _, port, host = socket.peeraddr
+    puts "*** Received connection from #{host}:#{port}"
+    loop { socket.write socket.readpartial(4096) }
+  rescue EOFError
+    puts "*** #{host}:#{port} disconnected"
+    socket.close
+  end
+end
+
+supervisor = EchoServer.supervise("127.0.0.1", 1234)
+trap("INT") { supervisor.terminate; exit }
+sleep
diff --git a/examples/echo_unix_client.rb b/examples/echo_unix_client.rb
new file mode 100644
index 0000000..803d675
--- /dev/null
+++ b/examples/echo_unix_client.rb
@@ -0,0 +1,29 @@
+require 'bundler/setup'
+require 'celluloid/io'
+
+class EchoUNIXClient
+  include Celluloid::IO
+  finalizer :finalize
+
+  def initialize(socket_path)
+    puts "*** connecting to #{socket_path}"
+    @socket_path = socket_path
+    @socket = UNIXSocket.open(socket_path)
+  end
+
+  def echo(msg)
+    puts "*** send to server: '#{msg}'"
+    @socket.puts(msg)
+    data = @socket.readline.chomp
+    puts "*** server unswer '#{data}'"
+    data
+  end
+
+  def finalize
+    @socket.close if @socket
+  end
+
+end
+
+c = EchoUNIXClient.new("/tmp/sock_test")
+c.echo("DATA")
diff --git a/examples/echo_unix_server.rb b/examples/echo_unix_server.rb
new file mode 100644
index 0000000..1c7b028
--- /dev/null
+++ b/examples/echo_unix_server.rb
@@ -0,0 +1,46 @@
+require 'bundler/setup'
+require 'celluloid/io'
+
+class EchoUNIXServer
+  include Celluloid::IO
+  finalizer :finalize
+
+  attr_reader :socket_path, :server
+
+  def initialize(socket_path)
+    puts "*** start server #{socket_path}"
+    @socket_path = socket_path
+    @server = UNIXServer.open(socket_path)
+    async.run
+  end
+
+  def run
+    loop { async.handle_connection @server.accept }
+  end
+
+  def handle_connection(socket)
+    loop do
+      data = socket.readline
+      puts "*** gets data #{data}"
+      socket.write(data)
+    end
+
+  rescue EOFError
+    puts "*** disconnected"
+
+  ensure
+    socket.close
+  end
+
+  def finalize
+    if @server
+      @server.close
+      File.delete(@socket_path)
+    end
+  end
+
+end
+
+supervisor = EchoUNIXServer.supervise("/tmp/sock_test")
+trap("INT") { supervisor.terminate; exit }
+sleep
diff --git a/lib/celluloid/io.rb b/lib/celluloid/io.rb
new file mode 100644
index 0000000..6a5ae31
--- /dev/null
+++ b/lib/celluloid/io.rb
@@ -0,0 +1,55 @@
+require 'celluloid/io/version'
+
+require 'celluloid'
+require 'celluloid/io/dns_resolver'
+require 'celluloid/io/mailbox'
+require 'celluloid/io/reactor'
+require 'celluloid/io/stream'
+
+require 'celluloid/io/tcp_server'
+require 'celluloid/io/tcp_socket'
+require 'celluloid/io/udp_socket'
+require 'celluloid/io/unix_server'
+require 'celluloid/io/unix_socket'
+
+require 'celluloid/io/ssl_server'
+require 'celluloid/io/ssl_socket'
+
+module Celluloid
+  # Actors with evented IO support
+  module IO
+    def self.included(klass)
+      klass.send :include, Celluloid
+      klass.mailbox_class Celluloid::IO::Mailbox
+    end
+
+    def self.evented?
+      actor = Thread.current[:celluloid_actor]
+      actor && actor.mailbox.is_a?(Celluloid::IO::Mailbox)
+    end
+
+    def wait_readable(io)
+      io = io.to_io
+      if IO.evented?
+        mailbox = Thread.current[:celluloid_mailbox]
+        mailbox.reactor.wait_readable(io)
+      else
+        Kernel.select([io])
+      end
+      nil
+    end
+    module_function :wait_readable
+
+    def wait_writable(io)
+      io = io.to_io
+      if IO.evented?
+        mailbox = Thread.current[:celluloid_mailbox]
+        mailbox.reactor.wait_writable(io)
+      else
+        Kernel.select([], [io])
+      end
+      nil
+    end
+    module_function :wait_writable
+  end
+end
diff --git a/lib/celluloid/io/dns_resolver.rb b/lib/celluloid/io/dns_resolver.rb
new file mode 100644
index 0000000..8c9fa43
--- /dev/null
+++ b/lib/celluloid/io/dns_resolver.rb
@@ -0,0 +1,88 @@
+require 'resolv'
+
+module Celluloid
+  module IO
+    # Asynchronous DNS resolver using Celluloid::IO::UDPSocket
+    class DNSResolver
+      RESOLV_CONF = '/etc/resolv.conf'
+      DNS_PORT    = 53
+
+      @mutex = Mutex.new
+      @identifier = 1
+
+      def self.generate_id
+        @mutex.synchronize { @identifier = (@identifier + 1) & 0xFFFF }
+      end
+
+      def self.nameservers(config = RESOLV_CONF)
+        File.read(config).scan(/^\s*nameserver\s+([0-9.:]+)/).flatten
+      end
+
+      def initialize
+        @nameservers = self.class.nameservers
+
+        # TODO: fall back on other nameservers if the first one is unavailable
+        @server = @nameservers.first
+
+        # The non-blocking secret sauce is here, as this is actually a
+        # Celluloid::IO::UDPSocket
+        @socket = UDPSocket.new
+      end
+
+      def resolve(hostname)
+        if host = resolve_hostname(hostname)
+          unless ip_address = resolve_host(host)
+            raise Resolv::ResolvError, "invalid entry in hosts file: #{host}"
+          end
+          return ip_address
+        end
+
+        query = build_query(hostname)
+        @socket.send query.encode, 0, @server, DNS_PORT
+        data, _ = @socket.recvfrom(512)
+        response = Resolv::DNS::Message.decode(data)
+
+        addrs = []
+        # The answer might include IN::CNAME entries so filters them out
+        # to include IN::A & IN::AAAA entries only.
+        response.each_answer { |name, ttl, value| addrs << value.address if value.respond_to?(:address) }
+
+        return if addrs.empty?
+        return addrs.first if addrs.size == 1
+        addrs
+      end
+
+      private
+
+      def resolve_hostname(hostname)
+        # Resolv::Hosts#getaddresses pushes onto a stack
+        # so since we want the first occurance, simply
+        # pop off the stack.
+        resolv.getaddresses(hostname).pop rescue nil
+      end
+
+      def resolv
+        @resolv ||= Resolv::Hosts.new
+      end
+
+      def build_query(hostname)
+        Resolv::DNS::Message.new.tap do |query|
+          query.id = self.class.generate_id
+          query.rd = 1
+          query.add_question hostname, Resolv::DNS::Resource::IN::A
+        end
+      end
+
+      def resolve_host(host)
+        resolve_ip(Resolv::IPv4, host) || resolve_ip(Resolv::IPv6, host)
+      end
+
+      def resolve_ip(klass, host)
+        begin
+          klass.create(host)
+        rescue ArgumentError
+        end
+      end
+    end
+  end
+end
diff --git a/lib/celluloid/io/mailbox.rb b/lib/celluloid/io/mailbox.rb
new file mode 100644
index 0000000..1fe2cec
--- /dev/null
+++ b/lib/celluloid/io/mailbox.rb
@@ -0,0 +1,10 @@
+module Celluloid
+  module IO
+    # An alternative implementation of Celluloid::Mailbox using Reactor
+    class Mailbox < Celluloid::EventedMailbox
+      def initialize
+        super(Reactor)
+      end
+    end
+  end
+end
diff --git a/lib/celluloid/io/reactor.rb b/lib/celluloid/io/reactor.rb
new file mode 100644
index 0000000..9e20f15
--- /dev/null
+++ b/lib/celluloid/io/reactor.rb
@@ -0,0 +1,62 @@
+require 'nio'
+
+module Celluloid
+  module IO
+    # React to external I/O events. This is kinda sorta supposed to resemble the
+    # Reactor design pattern.
+    class Reactor
+      extend Forwardable
+
+      # Unblock the reactor (i.e. to signal it from another thread)
+      def_delegator :@selector, :wakeup
+      # Terminate the reactor
+      def_delegator :@selector, :close, :shutdown
+
+      def initialize
+        @selector = NIO::Selector.new
+      end
+
+      # Wait for the given IO object to become readable
+      def wait_readable(io)
+        wait io, :r
+      end
+
+      # Wait for the given IO object to become writable
+      def wait_writable(io)
+        wait io, :w
+      end
+
+      # Wait for the given IO operation to complete
+      def wait(io, set)
+        # zomg ugly type conversion :(
+        unless io.is_a?(::IO) or io.is_a?(OpenSSL::SSL::SSLSocket)
+          if io.respond_to? :to_io
+            io = io.to_io
+          elsif ::IO.respond_to? :try_convert
+            io = ::IO.try_convert(io)
+          end
+
+          raise TypeError, "can't convert #{io.class} into IO" unless io.is_a?(::IO)
+        end
+
+        monitor = @selector.register(io, set)
+        monitor.value = Task.current
+        Task.suspend :iowait
+      end
+
+      # Run the reactor, waiting for events or wakeup signal
+      def run_once(timeout = nil)
+        @selector.select(timeout) do |monitor|
+          task = monitor.value
+          monitor.close
+
+          if task.running?
+            task.resume
+          else
+            Logger.warn("reactor attempted to resume a dead task")
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/celluloid/io/ssl_server.rb b/lib/celluloid/io/ssl_server.rb
new file mode 100644
index 0000000..9d98b90
--- /dev/null
+++ b/lib/celluloid/io/ssl_server.rb
@@ -0,0 +1,36 @@
+require 'socket'
+
+module Celluloid
+  module IO
+    # SSLServer wraps a TCPServer to provide immediate SSL accept
+    class SSLServer
+      extend Forwardable
+      def_delegators :@tcp_server, :listen, :shutdown, :close, :closed?, :to_io
+
+      attr_accessor :start_immediately
+      attr_reader :tcp_server
+
+      def initialize(server, ctx)
+        if server.is_a?(::TCPServer)
+          server = Celluloid::IO::TCPServer.from_ruby_server(server)
+        end
+        @tcp_server = server
+        @ctx = ctx
+        @start_immediately = true
+      end
+
+      def accept
+        sock = @tcp_server.accept
+        begin
+          ssl = Celluloid::IO::SSLSocket.new(sock, @ctx)
+          ssl.accept if @start_immediately
+          ssl
+        rescue OpenSSL::SSL::SSLError
+          sock.close
+          raise
+        end
+      end
+    end
+  end
+end
+
diff --git a/lib/celluloid/io/ssl_socket.rb b/lib/celluloid/io/ssl_socket.rb
new file mode 100644
index 0000000..dce281c
--- /dev/null
+++ b/lib/celluloid/io/ssl_socket.rb
@@ -0,0 +1,39 @@
+require 'openssl'
+
+module Celluloid
+  module IO
+    # SSLSocket with Celluloid::IO support
+    class SSLSocket < Stream
+      extend Forwardable
+
+      def_delegators :@socket, :read_nonblock, :write_nonblock, :close, :closed?,
+        :cert, :cipher, :client_ca, :peer_cert, :peer_cert_chain, :verify_result, :peeraddr
+
+      def initialize(io, ctx = OpenSSL::SSL::SSLContext.new)
+        super()
+        @context = ctx
+        @socket = OpenSSL::SSL::SSLSocket.new(::IO.try_convert(io), @context)
+      end
+
+      def connect
+        @socket.connect_nonblock
+      rescue ::IO::WaitReadable
+        wait_readable
+        retry
+      end
+
+      def accept
+        @socket.accept_nonblock
+        self
+      rescue ::IO::WaitReadable
+        wait_readable
+        retry
+      rescue ::IO::WaitWritable
+        wait_writable
+        retry
+      end
+
+      def to_io; @socket; end
+    end
+  end
+end
diff --git a/lib/celluloid/io/stream.rb b/lib/celluloid/io/stream.rb
new file mode 100644
index 0000000..fcd699c
--- /dev/null
+++ b/lib/celluloid/io/stream.rb
@@ -0,0 +1,401 @@
+# Partially adapted from Ruby's OpenSSL::Buffering
+# Originally from the 'OpenSSL for Ruby 2' project
+# Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo at notwork.org>
+# All rights reserved.
+#
+# This program is licenced under the same licence as Ruby.
+
+module Celluloid
+  module IO
+    # Base class of all streams in Celluloid::IO
+    class Stream
+      include Enumerable
+
+      # The "sync mode" of the stream
+      #
+      # See IO#sync for full details.
+      attr_accessor :sync
+
+      # Default size to read from or write to the stream for buffer operations
+      BLOCK_SIZE = 1024*16
+
+      def initialize
+        @eof  = false
+        @sync = true # FIXME: hax
+        @read_buffer = ''.force_encoding(Encoding::ASCII_8BIT)
+        @write_buffer = ''.force_encoding(Encoding::ASCII_8BIT)
+
+        @read_latch  = Latch.new
+        @write_latch = Latch.new
+      end
+
+      # Wait until the current object is readable
+      def wait_readable; Celluloid::IO.wait_readable(self); end
+
+      # Wait until the current object is writable
+      def wait_writable; Celluloid::IO.wait_writable(self); end
+
+      # System read via the nonblocking subsystem
+      def sysread(length = nil, buffer = nil)
+        buffer ||= ''.force_encoding(Encoding::ASCII_8BIT)
+
+        @read_latch.synchronize do
+          begin
+            read_nonblock(length, buffer)
+          rescue ::IO::WaitReadable
+            wait_readable
+            retry
+          end
+        end
+
+        buffer
+      end
+
+      # System write via the nonblocking subsystem
+      def syswrite(string)
+        length = string.length
+        total_written = 0
+
+        remaining = string
+
+        @write_latch.synchronize do
+          while total_written < length
+            begin
+              written = write_nonblock(remaining)
+            rescue ::IO::WaitWritable
+              wait_writable
+              retry
+            rescue EOFError
+              return total_written
+            end
+
+            total_written += written
+
+            # FIXME: mutating the original buffer here. Seems bad.
+            remaining.slice!(0, written) if written < remaining.length
+          end
+        end
+
+        total_written
+      end
+
+      # Reads +size+ bytes from the stream.  If +buf+ is provided it must
+      # reference a string which will receive the data.
+      #
+      # See IO#read for full details.
+      def read(size=nil, buf=nil)
+        if size == 0
+          if buf
+            buf.clear
+            return buf
+          else
+            return ""
+          end
+        end
+
+        until @eof
+          break if size && size <= @read_buffer.size
+          fill_rbuff
+          break unless size
+        end
+
+        ret = consume_rbuff(size) || ""
+
+        if buf
+          buf.replace(ret)
+          ret = buf
+        end
+
+        (size && ret.empty?) ? nil : ret
+      end
+
+      # Reads at most +maxlen+ bytes from the stream.  If +buf+ is provided it
+      # must reference a string which will receive the data.
+      #
+      # See IO#readpartial for full details.
+      def readpartial(maxlen, buf=nil)
+        if maxlen == 0
+          if buf
+            buf.clear
+            return buf
+          else
+            return ""
+          end
+        end
+
+        if @read_buffer.empty?
+          begin
+            return sysread(maxlen, buf)
+          rescue Errno::EAGAIN
+            retry
+          end
+        end
+
+        ret = consume_rbuff(maxlen)
+
+        if buf
+          buf.replace(ret)
+          ret = buf
+        end
+
+        raise EOFError if ret.empty?
+        ret
+      end
+
+      # Reads the next "line+ from the stream.  Lines are separated by +eol+.  If
+      # +limit+ is provided the result will not be longer than the given number of
+      # bytes.
+      #
+      # +eol+ may be a String or Regexp.
+      #
+      # Unlike IO#gets the line read will not be assigned to +$_+.
+      #
+      # Unlike IO#gets the separator must be provided if a limit is provided.
+      def gets(eol=$/, limit=nil)
+        idx = @read_buffer.index(eol)
+
+        until @eof
+          break if idx
+          fill_rbuff
+          idx = @read_buffer.index(eol)
+        end
+
+        if eol.is_a?(Regexp)
+          size = idx ? idx+$&.size : nil
+        else
+          size = idx ? idx+eol.size : nil
+        end
+
+        if limit and limit >= 0
+          size = [size, limit].min
+        end
+
+        consume_rbuff(size)
+      end
+
+      # Executes the block for every line in the stream where lines are separated
+      # by +eol+.
+      #
+      # See also #gets
+      def each(eol=$/)
+        while line = self.gets(eol)
+          yield line
+        end
+      end
+      alias each_line each
+
+      # Reads lines from the stream which are separated by +eol+.
+      #
+      # See also #gets
+      def readlines(eol=$/)
+        ary = []
+
+        while line = self.gets(eol)
+          ary << line
+        end
+
+        ary
+      end
+
+      # Reads a line from the stream which is separated by +eol+.
+      #
+      # Raises EOFError if at end of file.
+      def readline(eol=$/)
+        raise EOFError if eof?
+        gets(eol)
+      end
+
+      # Reads one character from the stream.  Returns nil if called at end of
+      # file.
+      def getc
+        read(1)
+      end
+
+      # Calls the given block once for each byte in the stream.
+      def each_byte # :yields: byte
+        while c = getc
+          yield(c.ord)
+        end
+      end
+
+      # Reads a one-character string from the stream.  Raises an EOFError at end
+      # of file.
+      def readchar
+        raise EOFError if eof?
+        getc
+      end
+
+      # Pushes character +c+ back onto the stream such that a subsequent buffered
+      # character read will return it.
+      #
+      # Unlike IO#getc multiple bytes may be pushed back onto the stream.
+      #
+      # Has no effect on unbuffered reads (such as #sysread).
+      def ungetc(c)
+        @read_buffer[0,0] = c.chr
+      end
+
+      # Returns true if the stream is at file which means there is no more data to
+      # be read.
+      def eof?
+        fill_rbuff if !@eof && @read_buffer.empty?
+        @eof && @read_buffer.empty?
+      end
+      alias eof eof?
+
+      # Writes +s+ to the stream.  If the argument is not a string it will be
+      # converted using String#to_s.  Returns the number of bytes written.
+      def write(s)
+        do_write(s)
+        s.bytesize
+      end
+
+      # Writes +s+ to the stream.  +s+ will be converted to a String using
+      # String#to_s.
+      def << (s)
+        do_write(s)
+        self
+      end
+
+      # Writes +args+ to the stream along with a record separator.
+      #
+      # See IO#puts for full details.
+      def puts(*args)
+        s = ""
+        if args.empty?
+          s << "\n"
+        end
+
+        args.each do |arg|
+          s << arg.to_s
+          if $/ && /\n\z/ !~ s
+            s << "\n"
+          end
+        end
+
+        do_write(s)
+        nil
+      end
+
+      # Writes +args+ to the stream.
+      #
+      # See IO#print for full details.
+      def print(*args)
+        s = ""
+        args.each { |arg| s << arg.to_s }
+        do_write(s)
+        nil
+      end
+
+      # Formats and writes to the stream converting parameters under control of
+      # the format string.
+      #
+      # See Kernel#sprintf for format string details.
+      def printf(s, *args)
+        do_write(s % args)
+        nil
+      end
+
+      # Flushes buffered data to the stream.
+      def flush
+        osync = @sync
+        @sync = true
+        do_write ""
+        return self
+      ensure
+        @sync = osync
+      end
+
+      # Closes the stream and flushes any unwritten data.
+      def close
+        flush rescue nil
+        sysclose
+      end
+
+      #######
+      private
+      #######
+
+      # Fills the buffer from the underlying stream
+      def fill_rbuff
+        begin
+          @read_buffer << sysread(BLOCK_SIZE)
+        rescue Errno::EAGAIN
+          retry
+        rescue EOFError
+          @eof = true
+        end
+      end
+
+      # Consumes +size+ bytes from the buffer
+      def consume_rbuff(size=nil)
+        if @read_buffer.empty?
+          nil
+        else
+          size = @read_buffer.size unless size
+          ret = @read_buffer[0, size]
+          @read_buffer[0, size] = ""
+          ret
+        end
+      end
+
+      # Writes +s+ to the buffer.  When the buffer is full or #sync is true the
+      # buffer is flushed to the underlying stream.
+      def do_write(s)
+        @write_buffer << s
+        @write_buffer.force_encoding(Encoding::BINARY)
+        @sync ||= false
+
+        if @sync or @write_buffer.size > BLOCK_SIZE or idx = @write_buffer.rindex($/)
+          remain = idx ? idx + $/.size : @write_buffer.length
+          nwritten = 0
+
+          while remain > 0
+            str = @write_buffer[nwritten,remain]
+            begin
+              nwrote = syswrite(str)
+            rescue Errno::EAGAIN
+              retry
+            end
+            remain -= nwrote
+            nwritten += nwrote
+          end
+
+          @write_buffer[0,nwritten] = ""
+        end
+      end
+
+      # Perform an operation exclusively, uncontested by other tasks
+      class Latch
+        def initialize
+          @owner = nil
+          @waiters = 0
+          @condition = Celluloid::Condition.new
+        end
+
+        # Synchronize an operation across all tasks in the current actor
+        def synchronize
+          actor = Thread.current[:celluloid_actor]
+          return yield unless actor
+
+          if @owner || @waiters > 0
+            @waiters += 1
+            @condition.wait
+            @waiters -= 1
+          end
+
+          @owner = Task.current
+
+          begin
+            ret = yield
+          ensure
+            @owner = nil
+            @condition.signal if @waiters > 0
+          end
+
+          ret
+        end
+      end
+    end
+  end
+end
diff --git a/lib/celluloid/io/tcp_server.rb b/lib/celluloid/io/tcp_server.rb
new file mode 100644
index 0000000..307acf4
--- /dev/null
+++ b/lib/celluloid/io/tcp_server.rb
@@ -0,0 +1,35 @@
+require 'socket'
+
+module Celluloid
+  module IO
+    # TCPServer with combined blocking and evented support
+    class TCPServer
+      extend Forwardable
+      def_delegators :@server, :listen, :sysaccept, :close, :closed?, :addr, :setsockopt
+
+      def initialize(hostname_or_port, port = nil)
+        @server = ::TCPServer.new(hostname_or_port, port)
+      end
+
+      def accept
+        Celluloid::IO.wait_readable(@server)
+        accept_nonblock
+      end
+
+      def accept_nonblock
+        Celluloid::IO::TCPSocket.new(@server.accept_nonblock)
+      end
+
+      def to_io
+        @server
+      end
+
+      # Convert a Ruby TCPServer into a Celluloid::IO::TCPServer
+      def self.from_ruby_server(ruby_server)
+        server = allocate
+        server.instance_variable_set(:@server, ruby_server)
+        server
+      end
+    end
+  end
+end
diff --git a/lib/celluloid/io/tcp_socket.rb b/lib/celluloid/io/tcp_socket.rb
new file mode 100644
index 0000000..a348fdc
--- /dev/null
+++ b/lib/celluloid/io/tcp_socket.rb
@@ -0,0 +1,117 @@
+require 'socket'
+require 'resolv'
+
+module Celluloid
+  module IO
+    # TCPSocket with combined blocking and evented support
+    class TCPSocket < Stream
+      extend Forwardable
+
+      def_delegators :@socket, :read_nonblock, :write_nonblock, :close, :close_read, :close_write, :closed?
+      def_delegators :@socket, :addr, :peeraddr, :setsockopt
+
+      # Open a TCP socket, yielding it to the given block and closing it
+      # automatically when done (if a block is given)
+      def self.open(*args, &block)
+        sock = new(*args)
+
+        if block_given?
+          begin
+            yield sock
+          ensure
+            sock.close
+          end
+        end
+
+        sock
+      end
+
+      # Convert a Ruby TCPSocket into a Celluloid::IO::TCPSocket
+      # DEPRECATED: to be removed in a future release
+      def self.from_ruby_socket(ruby_socket)
+        new(ruby_socket)
+      end
+
+      # Opens a TCP connection to remote_host on remote_port. If local_host
+      # and local_port are specified, then those parameters are used on the
+      # local end to establish the connection.
+      def initialize(remote_host, remote_port = nil, local_host = nil, local_port = nil)
+        super()
+
+        # Allow users to pass in a Ruby TCPSocket directly
+        if remote_host.is_a? ::TCPSocket
+          @addr = nil
+          @socket = remote_host
+          return
+        elsif remote_port.nil?
+          raise ArgumentError, "wrong number of arguments (1 for 2)"
+        end
+
+        # Is it an IPv4 address?
+        begin
+          @addr = Resolv::IPv4.create(remote_host)
+        rescue ArgumentError
+        end
+
+        # Guess it's not IPv4! Is it IPv6?
+        unless @addr
+          begin
+            @addr = Resolv::IPv6.create(remote_host)
+          rescue ArgumentError
+          end
+        end
+
+        # Guess it's not an IP address, so let's try DNS
+        unless @addr
+          addrs = Array(DNSResolver.new.resolve(remote_host))
+          raise Resolv::ResolvError, "DNS result has no information for #{remote_host}" if addrs.empty?
+
+          # Pseudorandom round-robin DNS support :/
+          @addr = addrs[rand(addrs.size)]
+        end
+
+        case @addr
+        when Resolv::IPv4
+          family = Socket::AF_INET
+        when Resolv::IPv6
+          family = Socket::AF_INET6
+        else raise ArgumentError, "unsupported address class: #{@addr.class}"
+        end
+
+        @socket = Socket.new(family, Socket::SOCK_STREAM, 0)
+        @socket.bind Addrinfo.tcp(local_host, local_port) if local_host
+
+        begin
+          @socket.connect_nonblock Socket.sockaddr_in(remote_port, @addr.to_s)
+        rescue Errno::EINPROGRESS
+          wait_writable
+
+          # HAX: for some reason we need to finish_connect ourselves on JRuby
+          # This logic is unnecessary but JRuby still throws Errno::EINPROGRESS
+          # if we retry the non-blocking connect instead of just finishing it
+          retry unless defined?(JRUBY_VERSION) && @socket.to_channel.finish_connect
+        rescue Errno::EISCONN
+          # We're now connected! Yay exceptions for flow control
+          # NOTE: This is the approach the Ruby stdlib docs suggest ;_;
+        end
+      end
+
+      def to_io
+        @socket
+      end
+
+      # Receives a message
+      def recv(maxlen, flags = nil)
+        raise NotImplementedError, "flags not supported" if flags && !flags.zero?
+        readpartial(maxlen)
+      end
+
+      # Send a message
+      def send(msg, flags, dest_sockaddr = nil)
+        raise NotImplementedError, "dest_sockaddr not supported" if dest_sockaddr
+        raise NotImplementedError, "flags not supported" unless flags.zero?
+        write(msg)
+      end
+    end
+  end
+end
diff --git a/lib/celluloid/io/udp_socket.rb b/lib/celluloid/io/udp_socket.rb
new file mode 100644
index 0000000..37a82a7
--- /dev/null
+++ b/lib/celluloid/io/udp_socket.rb
@@ -0,0 +1,36 @@
+module Celluloid
+  module IO
+    # UDPSockets with combined blocking and evented support
+    class UDPSocket
+      extend Forwardable
+      def_delegators :@socket, :bind, :send, :recvfrom_nonblock, :close, :closed?
+
+      def initialize
+        @socket = ::UDPSocket.new
+      end
+
+      # Wait until the socket is readable
+      def wait_readable; Celluloid::IO.wait_readable(self); end
+
+      # Receives up to maxlen bytes from socket. flags is zero or more of the
+      # MSG_ options. The first element of the results, mesg, is the data
+      # received. The second element, sender_addrinfo, contains
+      # protocol-specific address information of the sender.
+      def recvfrom(maxlen, flags = nil)
+        begin
+          if @socket.respond_to? :recvfrom_nonblock
+            @socket.recvfrom_nonblock(maxlen, flags)
+          else
+            # FIXME: hax for JRuby
+            @socket.recvfrom(maxlen, flags)
+          end
+        rescue ::IO::WaitReadable
+          wait_readable
+          retry
+        end
+      end
+
+      def to_io; @socket; end
+    end
+  end
+end
diff --git a/lib/celluloid/io/unix_server.rb b/lib/celluloid/io/unix_server.rb
new file mode 100644
index 0000000..f658d0f
--- /dev/null
+++ b/lib/celluloid/io/unix_server.rb
@@ -0,0 +1,32 @@
+require 'socket'
+
+module Celluloid
+  module IO
+    # UNIXServer with combined blocking and evented support
+    class UNIXServer
+      extend Forwardable
+      def_delegators :@server, :listen, :sysaccept, :close, :closed?
+
+      def self.open(socket_path)
+        self.new(socket_path)
+      end
+
+      def initialize(socket_path)
+        @server = ::UNIXServer.new(socket_path)
+      end
+
+      def accept
+        Celluloid::IO.wait_readable(@server)
+        accept_nonblock
+      end
+
+      def accept_nonblock
+        Celluloid::IO::UNIXSocket.new(@server.accept_nonblock)
+      end
+
+      def to_io
+        @server
+      end
+    end
+  end
+end
diff --git a/lib/celluloid/io/unix_socket.rb b/lib/celluloid/io/unix_socket.rb
new file mode 100644
index 0000000..e5d4d50
--- /dev/null
+++ b/lib/celluloid/io/unix_socket.rb
@@ -0,0 +1,45 @@
+require 'socket'
+
+module Celluloid
+  module IO
+    # UNIXSocket with combined blocking and evented support
+    class UNIXSocket < Stream
+      extend Forwardable
+
+      def_delegators :@socket, :read_nonblock, :write_nonblock, :close, :closed?, :readline, :puts, :addr
+
+      # Open a UNIX connection.
+      def self.open(socket_path, &block)
+        self.new(socket_path, &block)
+      end
+
+      # Convert a Ruby UNIXSocket into a Celluloid::IO::UNIXSocket
+      # DEPRECATED: to be removed in a future release
+      def self.from_ruby_socket(ruby_socket)
+        new(ruby_socket)
+      end
+
+      # Open a UNIX connection.
+      def initialize(socket_path, &block)
+        super()
+
+        # Allow users to pass in a Ruby UNIXSocket directly
+        if socket_path.is_a? ::UNIXSocket
+          @socket = socket_path
+          return
+        end
+
+        # FIXME: not doing non-blocking connect
+        @socket = if block
+          ::UNIXSocket.open(socket_path, &block)
+        else
+          ::UNIXSocket.new(socket_path)
+        end
+      end
+
+      def to_io
+        @socket
+      end
+    end
+  end
+end
diff --git a/lib/celluloid/io/version.rb b/lib/celluloid/io/version.rb
new file mode 100644
index 0000000..abb7d75
--- /dev/null
+++ b/lib/celluloid/io/version.rb
@@ -0,0 +1,5 @@
+module Celluloid
+  module IO
+    VERSION = "0.15.0"
+  end
+end
diff --git a/log/.gitignore b/log/.gitignore
new file mode 100644
index 0000000..397b4a7
--- /dev/null
+++ b/log/.gitignore
@@ -0,0 +1 @@
+*.log
diff --git a/logo.png b/logo.png
new file mode 100644
index 0000000..b0a958c
Binary files /dev/null and b/logo.png differ
diff --git a/metadata.yml b/metadata.yml
new file mode 100644
index 0000000..3bfe6df
--- /dev/null
+++ b/metadata.yml
@@ -0,0 +1,206 @@
+--- !ruby/object:Gem::Specification
+name: celluloid-io
+version: !ruby/object:Gem::Version
+  version: 0.15.0
+platform: ruby
+authors:
+- Tony Arcieri
+autorequire: 
+bindir: bin
+cert_chain: []
+date: 2013-09-04 00:00:00.000000000 Z
+dependencies:
+- !ruby/object:Gem::Dependency
+  name: celluloid
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 0.15.0
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 0.15.0
+- !ruby/object:Gem::Dependency
+  name: nio4r
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 0.5.0
+  type: :runtime
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: 0.5.0
+- !ruby/object:Gem::Dependency
+  name: rake
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: rspec
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: benchmark_suite
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: guard-rspec
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - '>='
+      - !ruby/object:Gem::Version
+        version: '0'
+- !ruby/object:Gem::Dependency
+  name: rb-fsevent
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: 0.9.1
+  type: :development
+  prerelease: false
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: 0.9.1
+description: Evented IO for Celluloid actors
+email:
+- tony.arcieri at gmail.com
+executables: []
+extensions: []
+extra_rdoc_files: []
+files:
+- .coveralls.yml
+- .gitignore
+- .rspec
+- .travis.yml
+- CHANGES.md
+- Gemfile
+- Guardfile
+- LICENSE.txt
+- README.md
+- Rakefile
+- benchmarks/actor.rb
+- celluloid-io.gemspec
+- examples/echo_client.rb
+- examples/echo_server.rb
+- examples/echo_unix_client.rb
+- examples/echo_unix_server.rb
+- lib/celluloid/io.rb
+- lib/celluloid/io/dns_resolver.rb
+- lib/celluloid/io/mailbox.rb
+- lib/celluloid/io/reactor.rb
+- lib/celluloid/io/ssl_server.rb
+- lib/celluloid/io/ssl_socket.rb
+- lib/celluloid/io/stream.rb
+- lib/celluloid/io/tcp_server.rb
+- lib/celluloid/io/tcp_socket.rb
+- lib/celluloid/io/udp_socket.rb
+- lib/celluloid/io/unix_server.rb
+- lib/celluloid/io/unix_socket.rb
+- lib/celluloid/io/version.rb
+- log/.gitignore
+- logo.png
+- spec/celluloid/io/actor_spec.rb
+- spec/celluloid/io/dns_resolver_spec.rb
+- spec/celluloid/io/mailbox_spec.rb
+- spec/celluloid/io/ssl_server_spec.rb
+- spec/celluloid/io/ssl_socket_spec.rb
+- spec/celluloid/io/tcp_server_spec.rb
+- spec/celluloid/io/tcp_socket_spec.rb
+- spec/celluloid/io/udp_socket_spec.rb
+- spec/celluloid/io/unix_server_spec.rb
+- spec/celluloid/io/unix_socket_spec.rb
+- spec/fixtures/client.crt
+- spec/fixtures/client.key
+- spec/fixtures/server.crt
+- spec/fixtures/server.key
+- spec/spec_helper.rb
+- tasks/benchmarks.task
+- tasks/rspec.task
+homepage: http://github.com/celluloid/celluloid-io
+licenses:
+- MIT
+metadata: {}
+post_install_message: 
+rdoc_options: []
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+  requirements:
+  - - '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+required_rubygems_version: !ruby/object:Gem::Requirement
+  requirements:
+  - - '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+requirements: []
+rubyforge_project: 
+rubygems_version: 2.0.3
+signing_key: 
+specification_version: 4
+summary: Celluloid::IO allows you to monitor multiple IO objects within a Celluloid
+  actor
+test_files:
+- spec/celluloid/io/actor_spec.rb
+- spec/celluloid/io/dns_resolver_spec.rb
+- spec/celluloid/io/mailbox_spec.rb
+- spec/celluloid/io/ssl_server_spec.rb
+- spec/celluloid/io/ssl_socket_spec.rb
+- spec/celluloid/io/tcp_server_spec.rb
+- spec/celluloid/io/tcp_socket_spec.rb
+- spec/celluloid/io/udp_socket_spec.rb
+- spec/celluloid/io/unix_server_spec.rb
+- spec/celluloid/io/unix_socket_spec.rb
+- spec/fixtures/client.crt
+- spec/fixtures/client.key
+- spec/fixtures/server.crt
+- spec/fixtures/server.key
+- spec/spec_helper.rb
diff --git a/spec/celluloid/io/actor_spec.rb b/spec/celluloid/io/actor_spec.rb
new file mode 100644
index 0000000..e72364d
--- /dev/null
+++ b/spec/celluloid/io/actor_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe Celluloid::IO do
+  it_behaves_like "a Celluloid Actor", Celluloid::IO
+end
diff --git a/spec/celluloid/io/dns_resolver_spec.rb b/spec/celluloid/io/dns_resolver_spec.rb
new file mode 100644
index 0000000..036ab1d
--- /dev/null
+++ b/spec/celluloid/io/dns_resolver_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe Celluloid::IO::DNSResolver do
+  describe '#resolve' do
+    it 'resolves hostnames' do
+      resolver = Celluloid::IO::DNSResolver.new
+      resolver.resolve('localhost').should eq Resolv::IPv4.create("127.0.0.1")
+    end
+
+    it "resolves domain names" do
+      resolver = Celluloid::IO::DNSResolver.new
+      resolver.resolve("celluloid.io").should == Resolv::IPv4.create("207.97.227.245")
+    end
+
+    it "resolves CNAME responses" do
+      resolver = Celluloid::IO::DNSResolver.new
+      results = resolver.resolve("www.google.com")
+      if results.is_a?(Array)
+        results.all? {|i| i.should be_an_instance_of(Resolv::IPv4) }
+      else
+        results.should be_an_instance_of(Resolv::IPv4)
+      end
+      # www.yahoo.com will be resolved randomly whether multiple or
+      # single entry.
+      results = resolver.resolve("www.yahoo.com")
+      if results.is_a?(Array)
+        results.all? {|i| i.should be_an_instance_of(Resolv::IPv4) }
+      else
+        results.should be_an_instance_of(Resolv::IPv4)
+      end
+    end
+  end
+end
diff --git a/spec/celluloid/io/mailbox_spec.rb b/spec/celluloid/io/mailbox_spec.rb
new file mode 100644
index 0000000..7a6109e
--- /dev/null
+++ b/spec/celluloid/io/mailbox_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe Celluloid::IO::Mailbox do
+  it_behaves_like "a Celluloid Mailbox"
+end
diff --git a/spec/celluloid/io/ssl_server_spec.rb b/spec/celluloid/io/ssl_server_spec.rb
new file mode 100644
index 0000000..1a53f21
--- /dev/null
+++ b/spec/celluloid/io/ssl_server_spec.rb
@@ -0,0 +1,91 @@
+require 'spec_helper'
+
+describe Celluloid::IO::SSLServer do
+  let(:client_cert) { OpenSSL::X509::Certificate.new fixture_dir.join("client.crt").read }
+  let(:client_key)  { OpenSSL::PKey::RSA.new fixture_dir.join("client.key").read }
+  let(:client_context) do
+    OpenSSL::SSL::SSLContext.new.tap do |context|
+      context.cert = client_cert
+      context.key  = client_key
+    end
+  end
+
+  let(:server_cert) { OpenSSL::X509::Certificate.new fixture_dir.join("server.crt").read }
+  let(:server_key)  { OpenSSL::PKey::RSA.new fixture_dir.join("server.key").read }
+  let(:server_context) do
+    OpenSSL::SSL::SSLContext.new.tap do |context|
+      context.cert = server_cert
+      context.key  = server_key
+    end
+  end
+
+  describe "#accept" do
+    let(:payload) { 'ohai' }
+
+    context "inside Celluloid::IO" do
+      it "should be evented" do
+        with_ssl_server do |subject|
+          within_io_actor { Celluloid::IO.evented? }.should be_true
+        end
+      end
+
+      it "accepts a connection and returns a Celluloid::IO::SSLSocket" do
+        with_ssl_server do |subject|
+          thread = Thread.new do
+            raw = TCPSocket.new(example_addr, example_ssl_port)
+            OpenSSL::SSL::SSLSocket.new(raw, client_context).connect
+          end
+          peer = within_io_actor { subject.accept }
+          peer.should be_a Celluloid::IO::SSLSocket
+
+          client = thread.value
+          client.write payload
+          peer.read(payload.size).should eq payload
+        end
+      end
+    end
+
+    context "outside Celluloid::IO" do
+      it "should be blocking" do
+        with_ssl_server do |subject|
+          Celluloid::IO.should_not be_evented
+        end
+      end
+
+      it "accepts a connection and returns a Celluloid::IO::SSLSocket" do
+        with_ssl_server do |subject|
+          thread = Thread.new do
+            raw = TCPSocket.new(example_addr, example_ssl_port)
+            OpenSSL::SSL::SSLSocket.new(raw, client_context).connect
+          end
+          peer = subject.accept
+          peer.should be_a Celluloid::IO::SSLSocket
+
+          client = thread.value
+          client.write payload
+          peer.read(payload.size).should eq payload
+        end
+      end
+    end
+  end
+
+  describe "#initialize" do
+    it "should auto-wrap a raw ::TCPServer" do
+      raw_server = ::TCPServer.new(example_addr, example_ssl_port)
+      with_ssl_server(raw_server) do |ssl_server|
+        ssl_server.tcp_server.class.should == Celluloid::IO::TCPServer
+      end
+    end
+  end
+
+  def with_ssl_server(raw_server = nil)
+    raw_server ||= Celluloid::IO::TCPServer.new(example_addr, example_ssl_port)
+    server = Celluloid::IO::SSLServer.new(raw_server, server_context)
+    begin
+      yield server
+    ensure
+      server.close
+    end
+  end
+end
+
diff --git a/spec/celluloid/io/ssl_socket_spec.rb b/spec/celluloid/io/ssl_socket_spec.rb
new file mode 100644
index 0000000..86df511
--- /dev/null
+++ b/spec/celluloid/io/ssl_socket_spec.rb
@@ -0,0 +1,219 @@
+require 'spec_helper'
+require 'openssl'
+
+describe Celluloid::IO::SSLSocket do
+  let(:request)  { 'ping' }
+  let(:response) { 'pong' }
+
+  let(:client_cert) { OpenSSL::X509::Certificate.new fixture_dir.join("client.crt").read }
+  let(:client_key)  { OpenSSL::PKey::RSA.new fixture_dir.join("client.key").read }
+  let(:client_context) do
+    OpenSSL::SSL::SSLContext.new.tap do |context|
+      context.cert = client_cert
+      context.key  = client_key
+    end
+  end
+
+  let(:client) do
+    remaining_attempts = 3
+
+    begin
+      TCPSocket.new example_addr, example_ssl_port
+    rescue Errno::ECONNREFUSED
+      # HAX: sometimes this fails to connect? o_O
+      # This is quite likely due to the Thread.pass style spinlocks for startup
+      raise if remaining_attempts < 1
+      remaining_attempts -= 1
+
+      # Seems gimpy, but sleep and retry
+      sleep 0.1
+      retry
+    end
+  end
+
+  let(:ssl_client) { Celluloid::IO::SSLSocket.new client, client_context }
+
+  let(:server_cert) { OpenSSL::X509::Certificate.new fixture_dir.join("server.crt").read }
+  let(:server_key)  { OpenSSL::PKey::RSA.new fixture_dir.join("server.key").read }
+  let(:server_context) do
+    OpenSSL::SSL::SSLContext.new.tap do |context|
+      context.cert = server_cert
+      context.key  = server_key
+    end
+  end
+
+  let(:server)     { TCPServer.new example_addr, example_ssl_port }
+  let(:ssl_server) { OpenSSL::SSL::SSLServer.new server, server_context }
+  let(:server_thread) do
+    Thread.new { ssl_server.accept }.tap do |thread|
+      Thread.pass while thread.status && thread.status != "sleep"
+      thread.join unless thread.status
+    end
+  end
+
+  let(:celluloid_server) { Celluloid::IO::TCPServer.new example_addr, example_ssl_port }
+  let(:raw_server_thread) do
+    Thread.new { celluloid_server.accept }.tap do |thread|
+      Thread.pass while thread.status && thread.status != "sleep"
+      thread.join unless thread.status
+    end
+  end
+
+  context "duck typing ::SSLSocket" do
+    it "responds to #peeraddr" do
+      with_ssl_sockets do |ssl_client, ssl_peer|
+        expect{ ssl_client.peeraddr }.to_not raise_error
+      end
+    end
+  end
+
+  context "inside Celluloid::IO" do
+    it "connects to SSL servers over TCP" do
+      with_ssl_sockets do |ssl_client, ssl_peer|
+        within_io_actor do
+          ssl_peer << request
+          ssl_client.read(request.size).should eq(request)
+
+          ssl_client << response
+          ssl_peer.read(response.size).should eq(response)
+        end
+      end
+    end
+
+    it "starts SSL on a connected TCP socket" do
+      pending "JRuby support" if defined?(JRUBY_VERSION)
+      with_raw_sockets do |client, peer|
+        within_io_actor do
+          peer << request
+          client.read(request.size).should eq(request)
+
+          client << response
+          peer.read(response.size).should eq(response)
+
+          # now that we've written bytes, upgrade to SSL
+          client_thread = Thread.new { OpenSSL::SSL::SSLSocket.new(client).connect }
+          ssl_peer = Celluloid::IO::SSLSocket.new peer, server_context
+          ssl_peer.should eq(ssl_peer.accept)
+          ssl_client = client_thread.value
+
+          ssl_peer << request
+          ssl_client.read(request.size).should eq(request)
+
+          ssl_client << response
+          ssl_peer.read(response.size).should eq(response)
+        end
+      end
+    end
+  end
+
+  context "outside Celluloid::IO" do
+    it "connects to SSL servers over TCP" do
+      with_ssl_sockets do |ssl_client, ssl_peer|
+        ssl_peer << request
+        ssl_client.read(request.size).should eq(request)
+
+        ssl_client << response
+        ssl_peer.read(response.size).should eq(response)
+      end
+    end
+
+    it "starts SSL on a connected TCP socket" do
+      pending "JRuby support" if defined?(JRUBY_VERSION)
+      with_raw_sockets do |client, peer|
+        peer << request
+        client.read(request.size).should eq(request)
+
+        client << response
+        peer.read(response.size).should eq(response)
+
+        # now that we've written bytes, upgrade to SSL
+        client_thread = Thread.new { OpenSSL::SSL::SSLSocket.new(client).connect }
+        ssl_peer = Celluloid::IO::SSLSocket.new peer, server_context
+        ssl_peer.should eq(ssl_peer.accept)
+        ssl_client = client_thread.value
+
+        ssl_peer << request
+        ssl_client.read(request.size).should eq(request)
+
+        ssl_client << response
+        ssl_peer.read(response.size).should eq(response)
+      end
+    end
+  end
+
+  it "knows its cert" do
+    # FIXME: seems bad? o_O
+    pending "wtf is wrong with this on JRuby" if defined? JRUBY_VERSION
+    with_ssl_sockets do |ssl_client|
+      ssl_client.cert.to_der.should eq(client_cert.to_der)
+    end
+  end
+
+  it "knows its peer_cert" do
+    with_ssl_sockets do |ssl_client|
+      ssl_client.peer_cert.to_der.should eq(ssl_client.to_io.peer_cert.to_der)
+    end
+  end
+
+  it "knows its peer_cert_chain" do
+    with_ssl_sockets do |ssl_client|
+      ssl_client.peer_cert_chain.zip(ssl_client.to_io.peer_cert_chain).map do |c1, c2|
+        c1.to_der == c2.to_der
+      end.should be_all
+    end
+  end
+
+  it "knows its cipher" do
+    with_ssl_sockets do |ssl_client|
+      ssl_client.cipher.should eq(ssl_client.to_io.cipher)
+    end
+  end
+
+  it "knows its client_ca" do
+    # jruby-openssl does not implement this method
+    pending "jruby-openssl support" if defined? JRUBY_VERSION
+
+    with_ssl_sockets do |ssl_client|
+      ssl_client.client_ca.should eq(ssl_client.to_io.client_ca)
+    end
+  end
+
+  it "verifies peer certificates" do
+    # FIXME: JRuby seems to be giving the wrong result here o_O
+    pending "jruby-openssl support" if defined? JRUBY_VERSION
+
+    with_ssl_sockets do |ssl_client, ssl_peer|
+      ssl_client.verify_result.should eq(OpenSSL::X509::V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
+    end
+  end
+
+  def with_ssl_sockets
+    server_thread
+    ssl_client.connect
+
+    begin
+      ssl_peer = server_thread.value
+      yield ssl_client, ssl_peer
+    ensure
+      server_thread.join
+      ssl_server.close
+      ssl_client.close
+      ssl_peer.close
+    end
+  end
+
+  def with_raw_sockets
+    raw_server_thread
+    client
+
+    begin
+      peer = raw_server_thread.value
+      yield client, peer
+    ensure
+      raw_server_thread.join
+      celluloid_server.close
+      client.close
+      peer.close
+    end
+  end
+end
diff --git a/spec/celluloid/io/tcp_server_spec.rb b/spec/celluloid/io/tcp_server_spec.rb
new file mode 100644
index 0000000..e7577bd
--- /dev/null
+++ b/spec/celluloid/io/tcp_server_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe Celluloid::IO::TCPServer do
+  describe "#accept" do
+    let(:payload) { 'ohai' }
+
+    it "can be initialized without a host" do
+      expect{ server = Celluloid::IO::TCPServer.new(2000); server.close }.to_not raise_error
+    end
+
+    context "inside Celluloid::IO" do
+      it "should be evented" do
+        with_tcp_server do |subject|
+          within_io_actor { Celluloid::IO.evented? }.should be_true
+        end
+      end
+
+      it "accepts a connection and returns a Celluloid::IO::TCPSocket" do
+        with_tcp_server do |subject|
+          thread = Thread.new { TCPSocket.new(example_addr, example_port) }
+          peer = within_io_actor { subject.accept }
+          peer.should be_a Celluloid::IO::TCPSocket
+
+          client = thread.value
+          client.write payload
+          peer.read(payload.size).should eq payload
+        end
+      end
+
+      context "outside Celluloid::IO" do
+        it "should be blocking" do
+          with_tcp_server do |subject|
+            Celluloid::IO.should_not be_evented
+          end
+        end
+
+        it "accepts a connection and returns a Celluloid::IO::TCPSocket" do
+          with_tcp_server do |subject|
+            thread = Thread.new { TCPSocket.new(example_addr, example_port) }
+            peer   = subject.accept
+            peer.should be_a Celluloid::IO::TCPSocket
+
+            client = thread.value
+            client.write payload
+            peer.read(payload.size).should eq payload
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/spec/celluloid/io/tcp_socket_spec.rb b/spec/celluloid/io/tcp_socket_spec.rb
new file mode 100644
index 0000000..82f84e1
--- /dev/null
+++ b/spec/celluloid/io/tcp_socket_spec.rb
@@ -0,0 +1,195 @@
+require 'spec_helper'
+
+describe Celluloid::IO::TCPSocket do
+  let(:payload) { 'ohai' }
+
+  context "inside Celluloid::IO" do
+    it "connects to TCP servers" do
+      server = ::TCPServer.new example_addr, example_port
+      thread = Thread.new { server.accept }
+      socket = within_io_actor { Celluloid::IO::TCPSocket.new example_addr, example_port }
+      peer = thread.value
+
+      peer << payload
+      within_io_actor { socket.read(payload.size) }.should eq payload
+
+      server.close
+      socket.close
+      peer.close
+    end
+
+    it "should be evented" do
+      with_connected_sockets do |subject|
+        within_io_actor { Celluloid::IO.evented? }.should be_true
+      end
+    end
+
+    it "read complete payload when nil size is given to #read" do
+      with_connected_sockets do |subject, peer|
+        peer << payload
+        within_io_actor { subject.read(nil) }.should eq payload
+      end
+    end
+
+    it "read complete payload when no size is given to #read" do
+      with_connected_sockets do |subject, peer|
+        peer << payload
+        within_io_actor { subject.read }.should eq payload
+      end
+    end
+
+    it "reads data" do
+      with_connected_sockets do |subject, peer|
+        peer << payload
+        within_io_actor { subject.read(payload.size) }.should eq payload
+      end
+    end
+
+    it "reads data in binary encoding" do
+      with_connected_sockets do |subject, peer|
+        peer << payload
+        within_io_actor { subject.read(payload.size).encoding }.should eq Encoding::BINARY
+      end
+    end
+
+    it "reads partial data" do
+      with_connected_sockets do |subject, peer|
+        peer << payload * 2
+        within_io_actor { subject.readpartial(payload.size) }.should eq payload
+      end
+    end
+
+    it "reads partial data in binary encoding" do
+      with_connected_sockets do |subject, peer|
+        peer << payload * 2
+        within_io_actor { subject.readpartial(payload.size).encoding }.should eq Encoding::BINARY
+      end
+    end
+
+    it "writes data" do
+      with_connected_sockets do |subject, peer|
+        within_io_actor { subject << payload }
+        peer.read(payload.size).should eq payload
+      end
+    end
+
+    it "raises Errno::ECONNREFUSED when the connection is refused" do
+      expect {
+        within_io_actor { ::TCPSocket.new(example_addr, example_port) }
+      }.to raise_error(Errno::ECONNREFUSED)
+    end
+
+    context 'eof?' do
+      it "blocks actor then returns by close" do
+        with_connected_sockets do |subject, peer|
+          started_at = Time.now
+          Thread.new{ sleep 0.5; peer.close; }
+          within_io_actor { subject.eof? }
+          (Time.now - started_at).should > 0.5
+        end
+      end
+      
+      it "blocks until gets the next byte" do
+        with_connected_sockets do |subject, peer|
+          peer << 0x00
+          peer.flush
+          expect {
+            within_io_actor {
+              subject.read(1)
+              Celluloid.timeout(0.5) {
+                subject.eof?.should be_false
+              }
+            }
+          }.to raise_error(Celluloid::Task::TimeoutError)
+        end
+      end
+    end
+
+    context "readpartial" do
+      it "raises EOFError when reading from a closed socket" do
+        with_connected_sockets do |subject, peer|
+          peer.close
+          expect {
+            within_io_actor { subject.readpartial(payload.size) }
+          }.to raise_error(EOFError)
+        end
+      end
+
+      it "raises IOError when active sockets are closed across threads" do
+        pending "not implemented"
+
+        with_connected_sockets do |subject, peer|
+          actor = ExampleActor.new
+          begin
+            read_future = actor.future.wrap do
+              subject.readpartial(payload.size)
+            end
+            sleep 0.1
+            subject.close
+            expect { read_future.value 0.25 }.to raise_error(IOError)
+          ensure
+            actor.terminate if actor.alive?
+          end
+        end
+      end
+
+      it "raises IOError when partial reading from a socket the peer closed" do
+        pending "async block running on receiver"
+        with_connected_sockets do |subject, peer|
+          actor = ExampleActor.new
+          begin
+            actor.async.wrap { sleep 0.01; peer.close }
+            expect do
+              within_io_actor { subject.readpartial(payload.size) }
+            end.to raise_error(IOError)
+          ensure
+            actor.terminate if actor.alive?
+          end
+        end
+      end
+    end
+  end
+
+  context "outside Celluloid::IO" do
+    it "connects to TCP servers" do
+      server = ::TCPServer.new example_addr, example_port
+      thread = Thread.new { server.accept }
+      socket = Celluloid::IO::TCPSocket.new example_addr, example_port
+      peer = thread.value
+
+      peer << payload
+      socket.read(payload.size).should eq payload
+
+      server.close
+      socket.close
+      peer.close
+    end
+
+    it "should be blocking" do
+      with_connected_sockets do |subject|
+        Celluloid::IO.should_not be_evented
+      end
+    end
+
+    it "reads data" do
+      with_connected_sockets do |subject, peer|
+        peer << payload
+        subject.read(payload.size).should eq payload
+      end
+    end
+
+    it "reads partial data" do
+      with_connected_sockets do |subject, peer|
+        peer << payload * 2
+        subject.readpartial(payload.size).should eq payload
+      end
+    end
+
+    it "writes data" do
+      with_connected_sockets do |subject, peer|
+        subject << payload
+        peer.read(payload.size).should eq payload
+      end
+    end
+  end
+end
diff --git a/spec/celluloid/io/udp_socket_spec.rb b/spec/celluloid/io/udp_socket_spec.rb
new file mode 100644
index 0000000..d7d4769
--- /dev/null
+++ b/spec/celluloid/io/udp_socket_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe Celluloid::IO::UDPSocket do
+  let(:payload) { 'ohai' }
+  subject do
+    Celluloid::IO::UDPSocket.new.tap do |sock|
+      sock.bind example_addr, example_port
+    end
+  end
+
+  after { subject.close }
+
+  context "inside Celluloid::IO" do
+    it "should be evented" do
+      within_io_actor { Celluloid::IO.evented? }.should be_true
+    end
+
+    it "sends and receives packets" do
+      within_io_actor do
+        subject.send payload, 0, example_addr, example_port
+        subject.recvfrom(payload.size).first.should == payload
+      end
+    end
+  end
+
+  context "outside Celluloid::IO" do
+    it "should be blocking" do
+      Celluloid::IO.should_not be_evented
+    end
+
+    it "sends and receives packets" do
+      subject.send payload, 0, example_addr, example_port
+      subject.recvfrom(payload.size).first.should == payload
+    end
+  end
+end
diff --git a/spec/celluloid/io/unix_server_spec.rb b/spec/celluloid/io/unix_server_spec.rb
new file mode 100644
index 0000000..a0f9fdc
--- /dev/null
+++ b/spec/celluloid/io/unix_server_spec.rb
@@ -0,0 +1,70 @@
+require 'spec_helper'
+
+describe Celluloid::IO::UNIXServer do
+  describe "#accept" do
+    before do
+      pending "JRuby support" if defined?(JRUBY_VERSION)
+    end
+
+    let(:payload) { 'ohai' }
+
+    context "inside Celluloid::IO" do
+      it "should be evented" do
+        with_unix_server do |subject|
+          within_io_actor { Celluloid::IO.evented? }.should be_true
+        end
+      end
+
+      it "accepts a connection and returns a Celluloid::IO::UNIXSocket" do
+        with_unix_server do |subject|
+          thread = Thread.new { UNIXSocket.new(example_unix_sock) }
+          peer = within_io_actor { subject.accept }
+          peer.should be_a Celluloid::IO::UNIXSocket
+
+          client = thread.value
+          client.write payload
+          peer.read(payload.size).should eq payload
+        end
+      end
+
+      it "raises if server already up" do
+        with_unix_server do |subject|
+          within_io_actor do
+            expect {
+              Celluloid::IO::UNIXServer.open(example_unix_sock)
+            }.to raise_error(Errno::EADDRINUSE)
+          end
+        end
+      end
+
+      context "outside Celluloid::IO" do
+        it "should be blocking" do
+          with_unix_server do |subject|
+            Celluloid::IO.should_not be_evented
+          end
+        end
+
+        it "accepts a connection and returns a Celluloid::IO::UNIXSocket" do
+          with_unix_server do |subject|
+            thread = Thread.new { UNIXSocket.new(example_unix_sock) }
+            peer   = subject.accept
+            peer.should be_a Celluloid::IO::UNIXSocket
+
+            client = thread.value
+            client.write payload
+            peer.read(payload.size).should eq payload
+          end
+        end
+
+        it "raises if server already up" do
+          with_unix_server do |subject|
+            expect {
+              Celluloid::IO::UNIXServer.open(example_unix_sock)
+            }.to raise_error(Errno::EADDRINUSE)
+          end
+        end
+
+      end
+    end
+  end
+end
diff --git a/spec/celluloid/io/unix_socket_spec.rb b/spec/celluloid/io/unix_socket_spec.rb
new file mode 100644
index 0000000..132d18d
--- /dev/null
+++ b/spec/celluloid/io/unix_socket_spec.rb
@@ -0,0 +1,166 @@
+require 'spec_helper'
+
+describe Celluloid::IO::UNIXSocket do
+  before do
+    pending "JRuby support" if defined?(JRUBY_VERSION)
+  end
+
+  let(:payload) { 'ohai' }
+
+  context "inside Celluloid::IO" do
+    it "connects to UNIX servers" do
+      server = ::UNIXServer.open example_unix_sock
+      thread = Thread.new { server.accept }
+      socket = within_io_actor { Celluloid::IO::UNIXSocket.open example_unix_sock }
+      peer = thread.value
+
+      peer << payload
+      within_io_actor { socket.read(payload.size) }.should eq payload
+
+      server.close
+      socket.close
+      peer.close
+      File.delete(example_unix_sock)
+    end
+
+    it "should be evented" do
+      with_connected_unix_sockets do |subject|
+        within_io_actor { Celluloid::IO.evented? }.should be_true
+      end
+    end
+
+    it "read complete payload when nil size is given to #read" do
+      with_connected_unix_sockets do |subject, peer|
+        peer << payload
+        within_io_actor { subject.read(nil) }.should eq payload
+      end
+    end
+
+    it "read complete payload when no size is given to #read" do
+      with_connected_unix_sockets do |subject, peer|
+        peer << payload
+        within_io_actor { subject.read }.should eq payload
+      end
+    end
+
+    it "reads data" do
+      with_connected_unix_sockets do |subject, peer|
+        peer << payload
+        within_io_actor { subject.read(payload.size) }.should eq payload
+      end
+    end
+
+    it "reads data in binary encoding" do
+      with_connected_unix_sockets do |subject, peer|
+        peer << payload
+        within_io_actor { subject.read(payload.size).encoding }.should eq Encoding::BINARY
+      end
+    end
+
+    it "reads partial data" do
+      with_connected_unix_sockets do |subject, peer|
+        peer << payload * 2
+        within_io_actor { subject.readpartial(payload.size) }.should eq payload
+      end
+    end
+
+    it "reads partial data in binary encoding" do
+      with_connected_unix_sockets do |subject, peer|
+        peer << payload * 2
+        within_io_actor { subject.readpartial(payload.size).encoding }.should eq Encoding::BINARY
+      end
+    end
+
+    it "writes data" do
+      with_connected_unix_sockets do |subject, peer|
+        within_io_actor { subject << payload }
+        peer.read(payload.size).should eq payload
+      end
+    end
+
+    it "raises Errno::ENOENT when the connection is refused" do
+      expect {
+        within_io_actor { Celluloid::IO::UNIXSocket.open(example_unix_sock) }
+      }.to raise_error(Errno::ENOENT)
+    end
+
+    it "raises EOFError when partial reading from a closed socket" do
+      with_connected_unix_sockets do |subject, peer|
+        peer.close
+        expect {
+          within_io_actor { subject.readpartial(payload.size) }
+        }.to raise_error(EOFError)
+      end
+    end
+
+    context 'eof?' do
+      it "blocks actor then returns by close" do
+        with_connected_sockets do |subject, peer|
+          started_at = Time.now
+          Thread.new{ sleep 0.5; peer.close; }
+          within_io_actor { subject.eof? }
+          (Time.now - started_at).should > 0.5
+        end
+      end
+      
+      it "blocks until gets the next byte" do
+        with_connected_sockets do |subject, peer|
+          peer << 0x00
+          peer.flush
+          expect {
+            within_io_actor {
+              subject.read(1)
+              Celluloid.timeout(0.5) {
+                subject.eof?.should be_false
+              }
+            }
+          }.to raise_error(Celluloid::Task::TimeoutError)
+        end
+      end
+    end
+  end
+
+  context "outside Celluloid::IO" do
+    it "connects to UNIX servers" do
+      server = ::UNIXServer.new example_unix_sock
+      thread = Thread.new { server.accept }
+      socket = Celluloid::IO::UNIXSocket.open example_unix_sock
+      peer = thread.value
+
+      peer << payload
+      socket.read(payload.size).should eq payload
+
+      server.close
+      socket.close
+      peer.close
+      File.delete example_unix_sock
+    end
+
+    it "should be blocking" do
+      with_connected_unix_sockets do |subject|
+        Celluloid::IO.should_not be_evented
+      end
+    end
+
+    it "reads data" do
+      with_connected_unix_sockets do |subject, peer|
+        peer << payload
+        subject.read(payload.size).should eq payload
+      end
+    end
+
+    it "reads partial data" do
+      with_connected_unix_sockets do |subject, peer|
+        peer << payload * 2
+        subject.readpartial(payload.size).should eq payload
+      end
+    end
+
+    it "writes data" do
+      with_connected_unix_sockets do |subject, peer|
+        subject << payload
+        peer.read(payload.size).should eq payload
+      end
+    end
+  end
+end
diff --git a/spec/fixtures/client.crt b/spec/fixtures/client.crt
new file mode 100644
index 0000000..ffa2344
--- /dev/null
+++ b/spec/fixtures/client.crt
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDmjCCAoICCQDwZ8yE/0n4PjANBgkqhkiG9w0BAQUFADCBjjELMAkGA1UEBhMC
+VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28x
+EjAQBgNVBAoTCUNlbGx1bG9pZDEbMBkGA1UEAxMSY2xpZW50LmV4YW1wbGUuY29t
+MSEwHwYJKoZIhvcNAQkBFhJjbGllbnRAZXhhbXBsZS5jb20wHhcNMTIxMTI1MTkx
+NjI2WhcNMjIxMTIzMTkxNjI2WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh
+bGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoTCUNlbGx1
+bG9pZDEbMBkGA1UEAxMSY2xpZW50LmV4YW1wbGUuY29tMSEwHwYJKoZIhvcNAQkB
+FhJjbGllbnRAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDV6zzpkAIX4FijnytX84GgHb8hYdNJAn+9g57XGrqtAH6BlLoANFl4n/+y
+nEQwBqlrNnfstPrf7sPezytntSVyufSE+LGUGBJA/jyjQCMcEe8+4bfOC2ZhCpvn
+I2dKNKwsmM+DyWs/PVl7XEAZF2P4iQ8eGLVFjph+KA/D79cHkIeGt4FEA4xJWqKf
++Kjftxo1cBqLx2dUiucRL7tva3ingAqYSs/i82jKLGlj7fdRMytOx87Nhs35RWpu
+66l7hvpetx3t2wU2obKOzKhS4ycaZ2AptEDNXKaBTQ5lejSRxFBCpYQtqmkd0bMG
+/T5ZfXC45axj9a2rj8AKZct+mLCzAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAHzr
+b4VTktAi+8baGRJCXupt0Ari8ffoWhsYerELFLQF7K2sluxOqCsGEEF21e99fZxP
+lisLi0DIQ7cNlOGjRJ3xaydE74Fsry3xBNKoR8I7OMr9VFsrC54tc0x7NQ7bRHy6
+kCjSwKN4I2KWJjQ8yf8mIalmUKOmb/hirzna8io4CiDeJGZ1XNAQ9dl1RHRW442G
+GTu2ofAtU8TlzilZyclMY/lN7whw7sKP+pPr6fpAOJZsR64IzbBcWHHjJhx70XOx
+jnd5FB1oXnuupgPqEKmatSCytrue8GTkanB8VZ6+Zd/4XgTkie3UtCZW8R+NL/Lo
+us/+Ks3WIDyYdPSPnbE=
+-----END CERTIFICATE-----
diff --git a/spec/fixtures/client.key b/spec/fixtures/client.key
new file mode 100644
index 0000000..9a80e15
--- /dev/null
+++ b/spec/fixtures/client.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA1es86ZACF+BYo58rV/OBoB2/IWHTSQJ/vYOe1xq6rQB+gZS6
+ADRZeJ//spxEMAapazZ37LT63+7D3s8rZ7Ulcrn0hPixlBgSQP48o0AjHBHvPuG3
+zgtmYQqb5yNnSjSsLJjPg8lrPz1Ze1xAGRdj+IkPHhi1RY6YfigPw+/XB5CHhreB
+RAOMSVqin/io37caNXAai8dnVIrnES+7b2t4p4AKmErP4vNoyixpY+33UTMrTsfO
+zYbN+UVqbuupe4b6Xrcd7dsFNqGyjsyoUuMnGmdgKbRAzVymgU0OZXo0kcRQQqWE
+LappHdGzBv0+WX1wuOWsY/Wtq4/ACmXLfpiwswIDAQABAoIBAQCFDDcpaUj0ArP+
+qEuz+x6/MGEk6QwZV7WNcGSFkvlSCoGkJJV+9RBExvao5yo92JbcuNbj4Tg7uOwY
+EzAC45a0AVZEscz4t/P6emXKf2SW28y6hnbkbxCxAIEwxENE0vfXEP/YDplmjsit
+whWXxYWHGe/OHz33UhYkONR9YBmUeLrtloRNUV82XDSpn4d7toLKaZW2kOFl4nFR
+SQ3pDPk1hleG8AZcfnF2LwaPx1XjPwBnXY9FK2jyNupVghfCw/Sv91dbbVkkIG14
+A8WpZKAXjXXOcTroof5+NJSPXzYrRuvP8K6H2zGj7F/AsP4hiZqGkb4tel0yH5VM
+oLCUTHqhAoGBAPysxeoT1ytajQ55UV1yjsnQ3jF9YcWsZwPEJgMI+bt+QzAfqqZs
+Kuvg8Gi7ukbcc2pKwXv+ma9HLJq/pQbWlfxcMNulY0VCPY/ceaPen+EfCJTApVpY
+YFS25i/JnIp9IudpQBuLHz9Yy4f1W2VoeG/mFqOmUxiTx4LM87G6rdtDAoGBANi7
+5raiwDS+rD91l5FLq3kdvgSDgYk4hh7BBJNJt8vhJYInIev5eb/Z41X8PfqWa2ir
+9aanpMYhWDJxbdSQDd3/op6jtOZM7InLceAm2O29VY2+HW5ePpc21AHsqoZpFYEZ
+YP8xvbSjJzfkrYr4y+aAMXONVAi4afqG7x6GqCXRAoGBAPbzFWu1gHKKyZn/0Bn4
+wL1WOhM8a7Z6zSPNLSmCODGbMadzC6Ijzb9D1TNHZsOi6dpUvc2mBCZe9aU48N1C
+FMzUfZvuhJtIJkrYPLp/9tpbLlPUBMfL4Dprl4XVEf34V4i8QT+qNRwAeMukbXMr
+K6qRwkanZEd9B107WmG2Bf1pAoGACpld1g6tgabVe6D/kY52y0yGD2hy/Ef0Xyqn
+U6CmSWUwVWYehZDEwHoiYQEd8tRKWmsWb1kBeOMGkijz6xJEa1fmFwYAgca/RpnZ
+btHXiADbXzwt6kjXnMOEqLdvO3WGJLMeCDzhfyT/dP9M8V/rcNFSGcmOk4KZRDQ3
+G3IQZRECgYBqHqvxHeL087UHXE1tdAGFVfxOiYE1afksJbkV06VnO8XXr9sNSWwy
+YjXVY9k6U1BFo7jHrWr6TkeMkB45wyn/fasHKU7qsPt0joRFkXMCzwl376hto3Tg
+RGXQCA4RUQXkxaDctJ5TgcF7MhK7tAFd1aBVlxwGENLYUVL0ZPaMrw==
+-----END RSA PRIVATE KEY-----
diff --git a/spec/fixtures/server.crt b/spec/fixtures/server.crt
new file mode 100644
index 0000000..faefd88
--- /dev/null
+++ b/spec/fixtures/server.crt
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDmjCCAoICCQD+dJ16wNIKnzANBgkqhkiG9w0BAQUFADCBjjELMAkGA1UEBhMC
+VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28x
+EjAQBgNVBAoTCUNlbGx1bG9pZDEbMBkGA1UEAxMSc2VydmVyLmV4YW1wbGUuY29t
+MSEwHwYJKoZIhvcNAQkBFhJzZXJ2ZXJAZXhhbXBsZS5jb20wHhcNMTIxMTI1MTkx
+NjAwWhcNMjIxMTIzMTkxNjAwWjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh
+bGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoTCUNlbGx1
+bG9pZDEbMBkGA1UEAxMSc2VydmVyLmV4YW1wbGUuY29tMSEwHwYJKoZIhvcNAQkB
+FhJzZXJ2ZXJAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCpLs3Xg00RtoG4+SKaaNfw6Dve9d0Kkg0CNfU8AsxOgTIU1Qu8s+bzKkqe
+66NfCa6T8VPpk9VbIlF2ONdgY4o8muV1q+mS6j2HDAtWPiDjP+9YOwGf/DT3LhSb
+g8k+alL2cqe7B1XNUsNFEvQ+yQLlj9MWKb7nbYDM8aqdv46KGoDj9v9rfm4QiKwI
+B6u/KoQG22YF7sT4O44jU/u20xcm3rV1Elg0gC/UP/YqnuMPCZYcK/Z9vYGtDJ6G
+8OYDcPZSZBdkqlffhYBssSj3R7sTCqoh4ms08ukcGycbvUO+cWrPKnmySsGaCYtG
+kp7QsH1ec7QGA01hZL2yL8CuJMUbAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBABE4
+gYVSdC87NhpA49k0vcLLU7v7mU3a3no/vu1CIqQxzx8/xh26Qi3aGb1s9MgHpF2Z
+NiB1irXER2tyz/F3qCi8OCo7eNsndmDjj4GnkBjEPTtqRxH9imRWw4bJyqwqFHcu
+1kczCZa+2VFQFEL4ErGycPFKM59ppTcJ0IxbK7PCGzO75TRQoAl52+3Aob+oejPP
+qFbiqNlV1T3EKa5yLdvOC5sLrEcfm3iMxmOtNVzp9OBhjXfm8Q1zgYs4VyJXzLMK
+wf956w2YEbpTAAzNc53zly/Jhr4MnFsa9Mn1oYp9Rfjzw/qJtXw+a3PtEKrO4XNe
+TsKHsAkj8XvUrhliiNQ=
+-----END CERTIFICATE-----
diff --git a/spec/fixtures/server.key b/spec/fixtures/server.key
new file mode 100644
index 0000000..50c923e
--- /dev/null
+++ b/spec/fixtures/server.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAqS7N14NNEbaBuPkimmjX8Og73vXdCpINAjX1PALMToEyFNUL
+vLPm8ypKnuujXwmuk/FT6ZPVWyJRdjjXYGOKPJrldavpkuo9hwwLVj4g4z/vWDsB
+n/w09y4Um4PJPmpS9nKnuwdVzVLDRRL0PskC5Y/TFim+522AzPGqnb+OihqA4/b/
+a35uEIisCAervyqEBttmBe7E+DuOI1P7ttMXJt61dRJYNIAv1D/2Kp7jDwmWHCv2
+fb2BrQyehvDmA3D2UmQXZKpX34WAbLEo90e7EwqqIeJrNPLpHBsnG71DvnFqzyp5
+skrBmgmLRpKe0LB9XnO0BgNNYWS9si/AriTFGwIDAQABAoIBAGKRoV4p4rIqOiQy
+CuYZpY53T8KUToeFFk0ucMXY/33RqgMXKTJ1Ql50SmuS8GlDs9IALZqOBiWFth6B
++YHwHK84s+2+DmUJUnWnH8fMhM7CBknKfyTeBWHqGBmPS6WwvstVe8HtASGSUbCh
+3WnjJWvoQtzLz6z4UK2XM4ea/ooY+hlcw6DM+jZuTstzLFE/9BPoHueaW8UemjaH
+ZUXMKm3+I3iIjHszUUWM59bS9pOBn/YvIJbVE5wMIVCP2YXDCgfpV2Z4nDiAHppn
+fnha2TzHzlPMgwhBpz06r6G8X+A6gJl98TDSK41nIMyXqiZ2aoALL3UOssAMfUHr
+2y9CGdECgYEA27F1IyUW3JgqCeqZ7eCeT4emhAvyohzM5pzWI7C8ZosF14zFRpek
+TgmjdTGMQ1EZVVkyj85RyvMm3MkcKOHetc5g2jJg3EkxvAV+PMs7yjpqg3itEjC6
+vIhXLoXdq+FuruA2h4O0hi6yuf1FCQYtpNLTe49qetjsaWzwwowHwlMCgYEAxSRo
+k+AdpoNXblnIhd0EaKjGAsHFrC039o7JqQe/mKAiXaGiidIDk5Vt/ChT6Qa6fiLq
+cdysCn+tSCt/DdRrELZohc0ipuy5/agQmJgWoW7oay8ldzxHP9VevWo4UuqVudW9
+evhKe0a9uXCrSimvZ5PJk91lmBx92FBeP6Y+qRkCgYAXQsvPQ88O3kGdOSzBJgY9
+D3TPCGDRT1FWnYaC0uSvysp8jxgYKFgqNxUKhIuAWSbghYg397VrUqFrwRNtNLUa
+9NYGZE0jJdDRQpeiIjaba+H5N57DjUtISPtKHrxgxYatl2nOoWBM0Mb1sF5N3UyZ
+5gSkUYQJq8wkQXegcakkpwKBgEdvvgV3vMbN6SyvlB4NzL8wCTCOjtapPBI4A5Mg
+n6jqvgk3vPI8C9e62jP5WQ6jxYhXlqTT1fOn+F6ihFO6mWFg99ckUl4ygeMMt5bT
+5b9xtP7CAs2GJjtXUhFJIEfLgZ3pedPJjRPGupEr5qXlHQ5nWzAdlebczC1KUhy2
+XRZhAoGAGA3SAAF79PK3c3+bOOviXxDFuH5TCBmbtEpJ+/jCbwR6Z8fMRswZJ3Gc
+l8eNMsB+Kfif+806xAgLxsyhYuyvF6rE/V34GKjW22T1gwk6gY/rOgFn42jo8lwl
+HFbSB+IG0+Go0m+0QmyRh7SyPvDNtyYzBFFdl9U8JYYGM0Nfgd0=
+-----END RSA PRIVATE KEY-----
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..1e47ac7
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,106 @@
+require 'rubygems'
+require 'bundler/setup'
+require 'celluloid/io'
+require 'celluloid/rspec'
+require 'coveralls'
+Coveralls.wear!
+
+logfile = File.open(File.expand_path("../../log/test.log", __FILE__), 'a')
+logfile.sync = true
+
+logger = Celluloid.logger = Logger.new(logfile)
+
+Celluloid.shutdown_timeout = 1
+
+RSpec.configure do |config|
+  config.filter_run :focus => true
+  config.run_all_when_everything_filtered = true
+
+  config.before do
+    Celluloid.logger = logger
+    Celluloid.shutdown
+
+    Celluloid.boot
+
+    FileUtils.rm("/tmp/cell_sock") if File.exist?("/tmp/cell_sock")
+  end
+end
+
+class ExampleActor
+  include Celluloid::IO
+  execute_block_on_receiver :wrap
+
+  def wrap
+    yield
+  end
+end
+
+EXAMPLE_PORT = 12345
+
+def example_addr; '127.0.0.1'; end
+def example_port; EXAMPLE_PORT; end
+def example_unix_sock; '/tmp/cell_sock'; end
+def example_ssl_port; EXAMPLE_PORT + 1; end
+
+def fixture_dir; Pathname.new File.expand_path("../fixtures", __FILE__); end
+
+def within_io_actor(&block)
+  actor = ExampleActor.new
+  actor.wrap(&block)
+ensure
+  actor.terminate if actor.alive?
+end
+
+def with_tcp_server
+  server = Celluloid::IO::TCPServer.new(example_addr, example_port)
+  begin
+    yield server
+  ensure
+    server.close
+  end
+end
+
+def with_unix_server
+  server = Celluloid::IO::UNIXServer.open(example_unix_sock)
+  begin
+    yield server
+  ensure
+    server.close
+    File.delete(example_unix_sock)
+  end
+end
+
+def with_connected_sockets
+  with_tcp_server do |server|
+    # FIXME: client isn't actually a Celluloid::IO::TCPSocket yet
+    client = ::TCPSocket.new(example_addr, example_port)
+    peer = server.accept
+
+    begin
+      yield peer, client
+    ensure
+      begin
+        client.close
+        peer.close
+      rescue
+      end
+    end
+  end
+end
+
+def with_connected_unix_sockets
+  with_unix_server do |server|
+    client = Celluloid::IO::UNIXSocket.new(example_unix_sock)
+    peer = server.accept
+
+    begin
+      yield peer, client
+    ensure
+      begin
+        client.close
+        peer.close
+      rescue
+      end
+    end
+  end
+end
diff --git a/tasks/benchmarks.task b/tasks/benchmarks.task
new file mode 100644
index 0000000..df11368
--- /dev/null
+++ b/tasks/benchmarks.task
@@ -0,0 +1,19 @@
+require 'timeout'
+
+desc "Run Celluloid benchmarks"
+task :benchmark do
+  # Travis has an out-of-date version of rbx that rashes on the benchmarks
+  exit 0 if ENV['CI'] and RUBY_ENGINE == 'rbx'
+
+  begin
+    Timeout.timeout(120) do
+      glob = File.expand_path("../../benchmarks/*.rb", __FILE__)
+      Dir[glob].each { |benchmark| load benchmark }
+    end
+  rescue Exception, Timeout::Error => ex
+    puts "ERROR: Couldn't complete benchmark: #{ex.class}: #{ex}"
+    puts "  #{ex.backtrace.join("\n  ")}"
+
+    exit 1 unless ENV['CI'] # Hax for running benchmarks on Travis
+  end
+end
diff --git a/tasks/rspec.task b/tasks/rspec.task
new file mode 100644
index 0000000..a089676
--- /dev/null
+++ b/tasks/rspec.task
@@ -0,0 +1,7 @@
+require 'rspec/core/rake_task'
+
+RSpec::Core::RakeTask.new
+
+RSpec::Core::RakeTask.new(:rcov) do |task|
+  task.rcov = true
+end
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-celluloid-io.git



More information about the Pkg-ruby-extras-commits mailing list