celluloid-io 0.14.1 → 0.15.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
|