[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