celluloid-io 0.14.1 → 0.15.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/CHANGES.md +6 -4
- data/Gemfile +1 -1
- data/README.md +1 -1
- data/benchmarks/actor.rb +1 -1
- data/celluloid-io.gemspec +2 -2
- data/lib/celluloid/io/dns_resolver.rb +47 -42
- data/lib/celluloid/io/ssl_socket.rb +1 -1
- data/lib/celluloid/io/tcp_server.rb +3 -3
- data/lib/celluloid/io/tcp_socket.rb +2 -2
- data/lib/celluloid/io/unix_socket.rb +1 -1
- data/lib/celluloid/io/version.rb +1 -1
- data/spec/celluloid/io/dns_resolver_spec.rb +25 -18
- data/spec/celluloid/io/ssl_socket_spec.rb +8 -0
- data/spec/celluloid/io/tcp_server_spec.rb +4 -0
- data/spec/celluloid/io/tcp_socket_spec.rb +27 -1
- data/spec/celluloid/io/unix_server_spec.rb +3 -3
- data/spec/celluloid/io/unix_socket_spec.rb +26 -0
- data/spec/spec_helper.rb +11 -9
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ba7ea0fac5d91976c3b8a556686e8d4b778fc7b
|
4
|
+
data.tar.gz: 65297d64467f098af4097564c9b266b83d273b09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65b04ba6086bf29c11e8b12faf2d7f674660be68b67c77161d4658b7248885602f7cb3dff056af97355ba9e3ab9a3d32e9879e65468f344a3cff19bc65856a99
|
7
|
+
data.tar.gz: af01de9578c5408dea445db8ff076d75e75bc3ddca754c4c07895f486c9fed15bd10beab6f3808f2d291be38994e3d6e3af389ed88a7eb813cb8339b26c94840
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
0.
|
2
|
-
|
3
|
-
*
|
4
|
-
|
1
|
+
0.15.0.pre (2013-08-21)
|
2
|
+
-----------------------
|
3
|
+
* Improved DNS resolver with less NIH and more Ruby stdlib goodness
|
4
|
+
* Better match Ruby stdlib TCPServer API
|
5
|
+
* Add missing #setsockopt method on Celluloid::IO::TCPServer
|
6
|
+
* Add missing #peeraddr method on Celluloid::IO::SSLSocket
|
5
7
|
|
6
8
|
0.14.0 (2013-05-07)
|
7
9
|
-------------------
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -30,7 +30,7 @@ library just to take advantage of Celluloid::IO's event loop and you can
|
|
30
30
|
freely switch between evented and blocking IO even over the lifetime of a
|
31
31
|
single connection.
|
32
32
|
|
33
|
-
Celluloid::IO uses the [nio4r gem](https://github.com/
|
33
|
+
Celluloid::IO uses the [nio4r gem](https://github.com/celluloid/nio4r)
|
34
34
|
to monitor IO objects, which provides cross-platform and cross-Ruby
|
35
35
|
implementation access to high-performance system calls such as epoll
|
36
36
|
and kqueue.
|
data/benchmarks/actor.rb
CHANGED
data/celluloid-io.gemspec
CHANGED
@@ -15,8 +15,8 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Celluloid::IO::VERSION
|
17
17
|
|
18
|
-
gem.add_dependency 'celluloid', '>= 0.
|
19
|
-
gem.add_dependency 'nio4r', '>= 0.
|
18
|
+
gem.add_dependency 'celluloid', '>= 0.15.0.pre'
|
19
|
+
gem.add_dependency 'nio4r', '>= 0.5.0'
|
20
20
|
|
21
21
|
gem.add_development_dependency 'rake'
|
22
22
|
gem.add_development_dependency 'rspec'
|
@@ -5,36 +5,22 @@ module Celluloid
|
|
5
5
|
# Asynchronous DNS resolver using Celluloid::IO::UDPSocket
|
6
6
|
class DNSResolver
|
7
7
|
RESOLV_CONF = '/etc/resolv.conf'
|
8
|
-
HOSTS = '/etc/hosts'
|
9
8
|
DNS_PORT = 53
|
10
|
-
|
9
|
+
|
11
10
|
@mutex = Mutex.new
|
12
11
|
@identifier = 1
|
13
|
-
|
12
|
+
|
14
13
|
def self.generate_id
|
15
14
|
@mutex.synchronize { @identifier = (@identifier + 1) & 0xFFFF }
|
16
15
|
end
|
17
|
-
|
16
|
+
|
18
17
|
def self.nameservers(config = RESOLV_CONF)
|
19
18
|
File.read(config).scan(/^\s*nameserver\s+([0-9.:]+)/).flatten
|
20
19
|
end
|
21
20
|
|
22
|
-
# FIXME: Y U NO Resolv::Hosts?
|
23
|
-
def self.hosts(hostfile = HOSTS)
|
24
|
-
hosts = {}
|
25
|
-
File.open(hostfile) do |f|
|
26
|
-
f.each_line do |host_entry|
|
27
|
-
entries = host_entry.gsub(/#.*$/, '').gsub(/\s+/, ' ').split(' ')
|
28
|
-
addr = entries.shift
|
29
|
-
entries.each { |e| hosts[e] ||= addr }
|
30
|
-
end
|
31
|
-
end
|
32
|
-
hosts
|
33
|
-
end
|
34
|
-
|
35
21
|
def initialize
|
36
|
-
@nameservers
|
37
|
-
|
22
|
+
@nameservers = self.class.nameservers
|
23
|
+
|
38
24
|
# TODO: fall back on other nameservers if the first one is unavailable
|
39
25
|
@server = @nameservers.first
|
40
26
|
|
@@ -42,42 +28,61 @@ module Celluloid
|
|
42
28
|
# Celluloid::IO::UDPSocket
|
43
29
|
@socket = UDPSocket.new
|
44
30
|
end
|
45
|
-
|
46
|
-
def resolve(hostname)
|
47
|
-
host = @hosts[hostname]
|
48
|
-
if host
|
49
|
-
begin
|
50
|
-
return Resolv::IPv4.create(host)
|
51
|
-
rescue ArgumentError
|
52
|
-
end
|
53
31
|
|
54
|
-
|
55
|
-
|
56
|
-
|
32
|
+
def resolve(hostname)
|
33
|
+
if host = resolve_hostname(hostname)
|
34
|
+
unless ip_address = resolve_host(host)
|
35
|
+
raise Resolv::ResolvError, "invalid entry in hosts file: #{host}"
|
57
36
|
end
|
58
|
-
|
59
|
-
raise Resolv::ResolvError, "invalid entry in hosts file: #{host}"
|
37
|
+
return ip_address
|
60
38
|
end
|
61
|
-
|
62
|
-
query =
|
63
|
-
query.id = self.class.generate_id
|
64
|
-
query.rd = 1
|
65
|
-
query.add_question hostname, Resolv::DNS::Resource::IN::A
|
66
|
-
|
39
|
+
|
40
|
+
query = build_query(hostname)
|
67
41
|
@socket.send query.encode, 0, @server, DNS_PORT
|
68
42
|
data, _ = @socket.recvfrom(512)
|
69
43
|
response = Resolv::DNS::Message.decode(data)
|
70
|
-
|
44
|
+
|
71
45
|
addrs = []
|
72
46
|
# The answer might include IN::CNAME entries so filters them out
|
73
47
|
# to include IN::A & IN::AAAA entries only.
|
74
|
-
response.each_answer { |name, ttl, value| addrs <<
|
75
|
-
|
76
|
-
|
48
|
+
response.each_answer { |name, ttl, value| addrs << value.address if value.respond_to?(:address) }
|
49
|
+
|
77
50
|
return if addrs.empty?
|
78
51
|
return addrs.first if addrs.size == 1
|
79
52
|
addrs
|
80
53
|
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def resolve_hostname(hostname)
|
58
|
+
# Resolv::Hosts#getaddresses pushes onto a stack
|
59
|
+
# so since we want the first occurance, simply
|
60
|
+
# pop off the stack.
|
61
|
+
resolv.getaddresses(hostname).pop rescue nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def resolv
|
65
|
+
@resolv ||= Resolv::Hosts.new
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_query(hostname)
|
69
|
+
Resolv::DNS::Message.new.tap do |query|
|
70
|
+
query.id = self.class.generate_id
|
71
|
+
query.rd = 1
|
72
|
+
query.add_question hostname, Resolv::DNS::Resource::IN::A
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def resolve_host(host)
|
77
|
+
resolve_ip(Resolv::IPv4, host) || resolve_ip(Resolv::IPv6, host)
|
78
|
+
end
|
79
|
+
|
80
|
+
def resolve_ip(klass, host)
|
81
|
+
begin
|
82
|
+
klass.create(host)
|
83
|
+
rescue ArgumentError
|
84
|
+
end
|
85
|
+
end
|
81
86
|
end
|
82
87
|
end
|
83
88
|
end
|
@@ -7,7 +7,7 @@ module Celluloid
|
|
7
7
|
extend Forwardable
|
8
8
|
|
9
9
|
def_delegators :@socket, :read_nonblock, :write_nonblock, :close, :closed?,
|
10
|
-
:cert, :cipher, :client_ca, :peer_cert, :peer_cert_chain, :verify_result
|
10
|
+
:cert, :cipher, :client_ca, :peer_cert, :peer_cert_chain, :verify_result, :peeraddr
|
11
11
|
|
12
12
|
def initialize(io, ctx = OpenSSL::SSL::SSLContext.new)
|
13
13
|
super()
|
@@ -5,10 +5,10 @@ module Celluloid
|
|
5
5
|
# TCPServer with combined blocking and evented support
|
6
6
|
class TCPServer
|
7
7
|
extend Forwardable
|
8
|
-
def_delegators :@server, :listen, :sysaccept, :close, :closed?, :addr
|
8
|
+
def_delegators :@server, :listen, :sysaccept, :close, :closed?, :addr, :setsockopt
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@server = ::TCPServer.new(
|
10
|
+
def initialize(hostname_or_port, port = nil)
|
11
|
+
@server = ::TCPServer.new(hostname_or_port, port)
|
12
12
|
end
|
13
13
|
|
14
14
|
def accept
|
@@ -37,7 +37,7 @@ module Celluloid
|
|
37
37
|
# local end to establish the connection.
|
38
38
|
def initialize(remote_host, remote_port = nil, local_host = nil, local_port = nil)
|
39
39
|
super()
|
40
|
-
|
40
|
+
|
41
41
|
# Allow users to pass in a Ruby TCPSocket directly
|
42
42
|
if remote_host.is_a? ::TCPSocket
|
43
43
|
@addr = nil
|
@@ -65,7 +65,7 @@ module Celluloid
|
|
65
65
|
unless @addr
|
66
66
|
addrs = Array(DNSResolver.new.resolve(remote_host))
|
67
67
|
raise Resolv::ResolvError, "DNS result has no information for #{remote_host}" if addrs.empty?
|
68
|
-
|
68
|
+
|
69
69
|
# Pseudorandom round-robin DNS support :/
|
70
70
|
@addr = addrs[rand(addrs.size)]
|
71
71
|
end
|
data/lib/celluloid/io/version.rb
CHANGED
@@ -1,26 +1,33 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Celluloid::IO::DNSResolver do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
describe '#resolve' do
|
5
|
+
it 'resolves hostnames' do
|
6
|
+
resolver = Celluloid::IO::DNSResolver.new
|
7
|
+
resolver.resolve('localhost').should eq Resolv::IPv4.create("127.0.0.1")
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
if results.is_a?(Array)
|
13
|
-
results.all? {|i| i.is_a?(Resolv::IPv4) }.should be_true
|
14
|
-
else
|
15
|
-
results.is_a?(Resolv::IPv4).should be_true
|
10
|
+
it "resolves domain names" do
|
11
|
+
resolver = Celluloid::IO::DNSResolver.new
|
12
|
+
resolver.resolve("celluloid.io").should == Resolv::IPv4.create("207.97.227.245")
|
16
13
|
end
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
results.
|
22
|
-
|
23
|
-
|
14
|
+
|
15
|
+
it "resolves CNAME responses" do
|
16
|
+
resolver = Celluloid::IO::DNSResolver.new
|
17
|
+
results = resolver.resolve("www.google.com")
|
18
|
+
if results.is_a?(Array)
|
19
|
+
results.all? {|i| i.should be_an_instance_of(Resolv::IPv4) }
|
20
|
+
else
|
21
|
+
results.should be_an_instance_of(Resolv::IPv4)
|
22
|
+
end
|
23
|
+
# www.yahoo.com will be resolved randomly whether multiple or
|
24
|
+
# single entry.
|
25
|
+
results = resolver.resolve("www.yahoo.com")
|
26
|
+
if results.is_a?(Array)
|
27
|
+
results.all? {|i| i.should be_an_instance_of(Resolv::IPv4) }
|
28
|
+
else
|
29
|
+
results.should be_an_instance_of(Resolv::IPv4)
|
30
|
+
end
|
24
31
|
end
|
25
32
|
end
|
26
33
|
end
|
@@ -59,6 +59,14 @@ describe Celluloid::IO::SSLSocket do
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
+
context "duck typing ::SSLSocket" do
|
63
|
+
it "responds to #peeraddr" do
|
64
|
+
with_ssl_sockets do |ssl_client, ssl_peer|
|
65
|
+
expect{ ssl_client.peeraddr }.to_not raise_error
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
62
70
|
context "inside Celluloid::IO" do
|
63
71
|
it "connects to SSL servers over TCP" do
|
64
72
|
with_ssl_sockets do |ssl_client, ssl_peer|
|
@@ -4,6 +4,10 @@ describe Celluloid::IO::TCPServer do
|
|
4
4
|
describe "#accept" do
|
5
5
|
let(:payload) { 'ohai' }
|
6
6
|
|
7
|
+
it "can be initialized without a host" do
|
8
|
+
expect{ server = Celluloid::IO::TCPServer.new(2000); server.close }.to_not raise_error
|
9
|
+
end
|
10
|
+
|
7
11
|
context "inside Celluloid::IO" do
|
8
12
|
it "should be evented" do
|
9
13
|
with_tcp_server do |subject|
|
@@ -79,6 +79,32 @@ describe Celluloid::IO::TCPSocket do
|
|
79
79
|
}.to raise_error(Errno::ECONNREFUSED)
|
80
80
|
end
|
81
81
|
|
82
|
+
context 'eof?' do
|
83
|
+
it "blocks actor then returns by close" do
|
84
|
+
with_connected_sockets do |subject, peer|
|
85
|
+
started_at = Time.now
|
86
|
+
Thread.new{ sleep 0.5; peer.close; }
|
87
|
+
within_io_actor { subject.eof? }
|
88
|
+
(Time.now - started_at).should > 0.5
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it "blocks until gets the next byte" do
|
93
|
+
with_connected_sockets do |subject, peer|
|
94
|
+
peer << 0x00
|
95
|
+
peer.flush
|
96
|
+
expect {
|
97
|
+
within_io_actor {
|
98
|
+
subject.read(1)
|
99
|
+
Celluloid.timeout(0.5) {
|
100
|
+
subject.eof?.should be_false
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}.to raise_error(Celluloid::Task::TimeoutError)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
82
108
|
context "readpartial" do
|
83
109
|
it "raises EOFError when reading from a closed socket" do
|
84
110
|
with_connected_sockets do |subject, peer|
|
@@ -90,7 +116,7 @@ describe Celluloid::IO::TCPSocket do
|
|
90
116
|
end
|
91
117
|
|
92
118
|
it "raises IOError when active sockets are closed across threads" do
|
93
|
-
|
119
|
+
pending "not implemented"
|
94
120
|
|
95
121
|
with_connected_sockets do |subject, peer|
|
96
122
|
actor = ExampleActor.new
|
@@ -26,7 +26,7 @@ describe Celluloid::IO::UNIXServer do
|
|
26
26
|
peer.read(payload.size).should eq payload
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
it "raises if server already up" do
|
31
31
|
with_unix_server do |subject|
|
32
32
|
within_io_actor do
|
@@ -55,7 +55,7 @@ describe Celluloid::IO::UNIXServer do
|
|
55
55
|
peer.read(payload.size).should eq payload
|
56
56
|
end
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
it "raises if server already up" do
|
60
60
|
with_unix_server do |subject|
|
61
61
|
expect {
|
@@ -63,7 +63,7 @@ describe Celluloid::IO::UNIXServer do
|
|
63
63
|
}.to raise_error(Errno::EADDRINUSE)
|
64
64
|
end
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
@@ -92,6 +92,32 @@ describe Celluloid::IO::UNIXSocket do
|
|
92
92
|
}.to raise_error(EOFError)
|
93
93
|
end
|
94
94
|
end
|
95
|
+
|
96
|
+
context 'eof?' do
|
97
|
+
it "blocks actor then returns by close" do
|
98
|
+
with_connected_sockets do |subject, peer|
|
99
|
+
started_at = Time.now
|
100
|
+
Thread.new{ sleep 0.5; peer.close; }
|
101
|
+
within_io_actor { subject.eof? }
|
102
|
+
(Time.now - started_at).should > 0.5
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it "blocks until gets the next byte" do
|
107
|
+
with_connected_sockets do |subject, peer|
|
108
|
+
peer << 0x00
|
109
|
+
peer.flush
|
110
|
+
expect {
|
111
|
+
within_io_actor {
|
112
|
+
subject.read(1)
|
113
|
+
Celluloid.timeout(0.5) {
|
114
|
+
subject.eof?.should be_false
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}.to raise_error(Celluloid::Task::TimeoutError)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
95
121
|
end
|
96
122
|
|
97
123
|
context "outside Celluloid::IO" do
|
data/spec/spec_helper.rb
CHANGED
@@ -6,21 +6,23 @@ require 'coveralls'
|
|
6
6
|
Coveralls.wear!
|
7
7
|
|
8
8
|
logfile = File.open(File.expand_path("../../log/test.log", __FILE__), 'a')
|
9
|
-
|
9
|
+
logfile.sync = true
|
10
|
+
|
11
|
+
logger = Celluloid.logger = Logger.new(logfile)
|
12
|
+
|
13
|
+
Celluloid.shutdown_timeout = 1
|
10
14
|
|
11
15
|
RSpec.configure do |config|
|
16
|
+
config.filter_run :focus => true
|
17
|
+
config.run_all_when_everything_filtered = true
|
18
|
+
|
12
19
|
config.before do
|
20
|
+
Celluloid.logger = logger
|
13
21
|
Celluloid.shutdown
|
22
|
+
|
14
23
|
Celluloid.boot
|
15
|
-
FileUtils.rm("/tmp/cell_sock") if File.exist?("/tmp/cell_sock")
|
16
|
-
end
|
17
|
-
end
|
18
24
|
|
19
|
-
|
20
|
-
module Celluloid
|
21
|
-
class << self
|
22
|
-
undef :shutdown
|
23
|
-
def shutdown; end # hax: noop!
|
25
|
+
FileUtils.rm("/tmp/cell_sock") if File.exist?("/tmp/cell_sock")
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: celluloid-io
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.15.0.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Arcieri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: celluloid
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.15.0.pre
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.15.0.pre
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: nio4r
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.5.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
40
|
+
version: 0.5.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -177,9 +177,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
177
177
|
version: '0'
|
178
178
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
179
|
requirements:
|
180
|
-
- - '
|
180
|
+
- - '>'
|
181
181
|
- !ruby/object:Gem::Version
|
182
|
-
version:
|
182
|
+
version: 1.3.1
|
183
183
|
requirements: []
|
184
184
|
rubyforge_project:
|
185
185
|
rubygems_version: 2.0.3
|