celluloid-io 0.17.2 → 0.17.3
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 +1 -0
- data/CHANGES.md +35 -0
- data/lib/celluloid/io.rb +1 -0
- data/lib/celluloid/io/reactor.rb +1 -1
- data/lib/celluloid/io/socket.rb +80 -0
- data/lib/celluloid/io/ssl_server.rb +1 -4
- data/lib/celluloid/io/ssl_socket.rb +6 -13
- data/lib/celluloid/io/stream.rb +4 -3
- data/lib/celluloid/io/tcp_server.rb +30 -15
- data/lib/celluloid/io/tcp_socket.rb +66 -44
- data/lib/celluloid/io/udp_socket.rb +24 -9
- data/lib/celluloid/io/unix_server.rb +25 -17
- data/lib/celluloid/io/unix_socket.rb +6 -14
- data/lib/celluloid/io/version.rb +1 -1
- data/spec/celluloid/io/actor_spec.rb +54 -1
- data/spec/celluloid/io/socket_spec.rb +173 -0
- data/spec/celluloid/io/tcp_server_spec.rb +27 -0
- data/spec/celluloid/io/tcp_socket_spec.rb +14 -0
- data/spec/celluloid/io/unix_socket_spec.rb +31 -2
- metadata +80 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 868ae36a7ec2c6a1f488023e9a2358af5e66cdfd
|
4
|
+
data.tar.gz: 90e7851f8bfbe6dc3c47b3d450bb674b2db407c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1346986683c4491cfbda904e9f399bd1e6c5c6f4b0ac416003f61b7b9c55b8f26c4f07237ed8088e68030acc60a2f117c2b1523d2a46fa44d050eb5a0fec5077
|
7
|
+
data.tar.gz: c5d8b43d6a89f49a5751c3c43dbee6bc0cb6b3c21a205350170163182ebda93542eca67076cdc725387358d0ae91a125bf162bb866df99547e9631082e723f0b
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,35 @@
|
|
1
|
+
0.17.3 (2016-01-18)
|
2
|
+
-----
|
3
|
+
* [#163](https://github.com/celluloid/celluloid-io/pull/163)
|
4
|
+
Support Ruby 2.3.0.
|
5
|
+
|
6
|
+
* [#162](https://github.com/celluloid/celluloid-io/pull/162)
|
7
|
+
Fix broken specs.
|
8
|
+
|
9
|
+
* [#160](https://github.com/celluloid/celluloid-io/pull/160)
|
10
|
+
Use a common super class for all socket wrappers.
|
11
|
+
([@hannesg])
|
12
|
+
|
13
|
+
* [#159](https://github.com/celluloid/celluloid-io/pull/159)
|
14
|
+
UNIXSocket: don't delegate #readline and #puts.
|
15
|
+
([@hannesg])
|
16
|
+
|
17
|
+
* [#158](https://github.com/celluloid/celluloid-io/pull/158)
|
18
|
+
Use unix sockets in unix spec instead of tcp sockets.
|
19
|
+
([@hannesg])
|
20
|
+
|
21
|
+
* [#157](https://github.com/celluloid/celluloid-io/pull/157)
|
22
|
+
Stream#close is not called in subclasses.
|
23
|
+
([@hannesg])
|
24
|
+
|
25
|
+
* [#155](https://github.com/celluloid/celluloid-io/pull/155)
|
26
|
+
Only close Selector it not already closed.
|
27
|
+
|
28
|
+
* [#98](https://github.com/celluloid/celluloid-io/pull/98)
|
29
|
+
Added spec for writing later to a socket within a request/response cycle
|
30
|
+
using the timer primitives.
|
31
|
+
([@TiagoCardoso1983])
|
32
|
+
|
1
33
|
0.17.2 (2015-09-30)
|
2
34
|
-----
|
3
35
|
* Revamped test suite, using shared RSpec configuration layer provided by Celluloid itself.
|
@@ -84,3 +116,6 @@
|
|
84
116
|
0.7.0
|
85
117
|
-----
|
86
118
|
* Initial release forked from Celluloid
|
119
|
+
|
120
|
+
[@TiagoCardoso1983]: https://github.com/TiagoCardoso1983
|
121
|
+
[@hannesg]: https://github.com/hannesg
|
data/lib/celluloid/io.rb
CHANGED
data/lib/celluloid/io/reactor.rb
CHANGED
@@ -0,0 +1,80 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module IO
|
3
|
+
# Base class for all classes that wrap a ruby socket.
|
4
|
+
# @abstract
|
5
|
+
class Socket
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@socket, :close, :close_read, :close_write, :closed?
|
9
|
+
def_delegators :@socket, :read_nonblock, :write_nonblock
|
10
|
+
def_delegators :@socket, :addr, :getsockopt, :setsockopt, :getsockname, :fcntl
|
11
|
+
|
12
|
+
# @param socket [BasicSocket, OpenSSL::SSL::SSLSocket]
|
13
|
+
def initialize(socket)
|
14
|
+
case socket
|
15
|
+
when ::BasicSocket, OpenSSL::SSL::SSLSocket
|
16
|
+
@socket = socket
|
17
|
+
else
|
18
|
+
raise ArgumentError, "expected a socket, got #{socket.inspect}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the wrapped socket.
|
23
|
+
# @return [BasicSocket, OpenSSL::SSL::SSLSocket]
|
24
|
+
def to_io
|
25
|
+
@socket
|
26
|
+
end
|
27
|
+
|
28
|
+
# Compatibility
|
29
|
+
Constants = ::Socket::Constants
|
30
|
+
include Constants
|
31
|
+
|
32
|
+
# Celluloid::IO:Socket.new behaves like Socket.new for compatibility.
|
33
|
+
# This is is not problematic since Celluloid::IO::Socket is abstract.
|
34
|
+
# To instantiate a socket use one of its subclasses.
|
35
|
+
def self.new(*args)
|
36
|
+
if self == Celluloid::IO::Socket
|
37
|
+
return ::Socket.new(*args)
|
38
|
+
else
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Tries to convert the given ruby socket into a subclass of GenericSocket.
|
44
|
+
# @param socket
|
45
|
+
# @return [SSLSocket, TCPServer, TCPSocket, UDPSocket, UNIXServer, UNIXSocket]
|
46
|
+
# @return [nil] if the socket can't be converted
|
47
|
+
def self.try_convert(socket, convert_io = true)
|
48
|
+
case socket
|
49
|
+
when Celluloid::IO::Socket, Celluloid::IO::SSLServer
|
50
|
+
socket
|
51
|
+
when ::TCPServer
|
52
|
+
TCPServer.new(socket)
|
53
|
+
when ::TCPSocket
|
54
|
+
TCPSocket.new(socket)
|
55
|
+
when ::UDPSocket
|
56
|
+
UDPSocket.new(socket)
|
57
|
+
when ::UNIXServer
|
58
|
+
UNIXServer.new(socket)
|
59
|
+
when ::UNIXSocket
|
60
|
+
UNIXSocket.new(socket)
|
61
|
+
when OpenSSL::SSL::SSLServer
|
62
|
+
SSLServer.new(socket.to_io, socket.instance_variable_get(:@ctx))
|
63
|
+
when OpenSSL::SSL::SSLSocket
|
64
|
+
SSLSocket.new(socket)
|
65
|
+
else
|
66
|
+
if convert_io
|
67
|
+
return try_convert(IO.try_convert(socket), false)
|
68
|
+
end
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class << self
|
74
|
+
extend Forwardable
|
75
|
+
def_delegators '::Socket', *(::Socket.methods - self.methods - [:try_convert])
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -11,10 +11,7 @@ module Celluloid
|
|
11
11
|
attr_reader :tcp_server
|
12
12
|
|
13
13
|
def initialize(server, ctx)
|
14
|
-
|
15
|
-
server = Celluloid::IO::TCPServer.from_ruby_server(server)
|
16
|
-
end
|
17
|
-
@tcp_server = server
|
14
|
+
@tcp_server = Socket.try_convert(server)
|
18
15
|
@ctx = ctx
|
19
16
|
@start_immediately = true
|
20
17
|
end
|
@@ -6,11 +6,7 @@ module Celluloid
|
|
6
6
|
class SSLSocket < Stream
|
7
7
|
extend Forwardable
|
8
8
|
|
9
|
-
def_delegators
|
10
|
-
:read_nonblock,
|
11
|
-
:write_nonblock,
|
12
|
-
:close,
|
13
|
-
:closed?,
|
9
|
+
def_delegators :to_io,
|
14
10
|
:cert,
|
15
11
|
:cipher,
|
16
12
|
:client_ca,
|
@@ -22,14 +18,14 @@ module Celluloid
|
|
22
18
|
:sync_close=
|
23
19
|
|
24
20
|
def initialize(io, ctx = OpenSSL::SSL::SSLContext.new)
|
25
|
-
super()
|
26
21
|
@context = ctx
|
27
|
-
|
28
|
-
|
22
|
+
socket = OpenSSL::SSL::SSLSocket.new(::IO.try_convert(io), @context)
|
23
|
+
socket.sync_close = true if socket.respond_to?(:sync_close=)
|
24
|
+
super(socket)
|
29
25
|
end
|
30
26
|
|
31
27
|
def connect
|
32
|
-
|
28
|
+
to_io.connect_nonblock
|
33
29
|
self
|
34
30
|
rescue ::IO::WaitReadable
|
35
31
|
wait_readable
|
@@ -37,7 +33,7 @@ module Celluloid
|
|
37
33
|
end
|
38
34
|
|
39
35
|
def accept
|
40
|
-
|
36
|
+
to_io.accept_nonblock
|
41
37
|
self
|
42
38
|
rescue ::IO::WaitReadable
|
43
39
|
wait_readable
|
@@ -47,9 +43,6 @@ module Celluloid
|
|
47
43
|
retry
|
48
44
|
end
|
49
45
|
|
50
|
-
def to_io
|
51
|
-
@socket
|
52
|
-
end
|
53
46
|
end
|
54
47
|
end
|
55
48
|
end
|
data/lib/celluloid/io/stream.rb
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
module Celluloid
|
9
9
|
module IO
|
10
10
|
# Base class of all streams in Celluloid::IO
|
11
|
-
class Stream
|
11
|
+
class Stream < Socket
|
12
12
|
include Enumerable
|
13
13
|
|
14
14
|
# The "sync mode" of the stream
|
@@ -16,7 +16,8 @@ module Celluloid
|
|
16
16
|
# See IO#sync for full details.
|
17
17
|
attr_accessor :sync
|
18
18
|
|
19
|
-
def initialize
|
19
|
+
def initialize(socket)
|
20
|
+
super
|
20
21
|
@eof = false
|
21
22
|
@sync = true
|
22
23
|
@read_buffer = ''.force_encoding(Encoding::ASCII_8BIT)
|
@@ -309,7 +310,7 @@ module Celluloid
|
|
309
310
|
# Closes the stream and flushes any unwritten data.
|
310
311
|
def close
|
311
312
|
flush rescue nil
|
312
|
-
|
313
|
+
super
|
313
314
|
end
|
314
315
|
|
315
316
|
#######
|
@@ -3,36 +3,51 @@ require 'socket'
|
|
3
3
|
module Celluloid
|
4
4
|
module IO
|
5
5
|
# TCPServer with combined blocking and evented support
|
6
|
-
class TCPServer
|
6
|
+
class TCPServer < Socket
|
7
7
|
extend Forwardable
|
8
|
-
def_delegators
|
8
|
+
def_delegators :to_io, :listen, :sysaccept, :addr
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
# @overload initialize(port)
|
11
|
+
# Opens a tcp server on the given port.
|
12
|
+
# @param port [Numeric]
|
13
|
+
#
|
14
|
+
# @overload initialize(hostname, port)
|
15
|
+
# Opens a tcp server on the given port and interface.
|
16
|
+
# @param hostname [String]
|
17
|
+
# @param port [Numeric]
|
18
|
+
#
|
19
|
+
# @overload initialize(socket)
|
20
|
+
# Wraps an already existing tcp server instance.
|
21
|
+
# @param socket [::TCPServer]
|
22
|
+
def initialize(*args)
|
23
|
+
if args.first.kind_of? ::BasicSocket
|
24
|
+
# socket
|
25
|
+
socket = args.first
|
26
|
+
fail ArgumentError, "wrong number of arguments (#{args.size} for 1)" if args.size != 1
|
27
|
+
fail ArgumentError, "wrong kind of socket (#{socket.class} for TCPServer)" unless socket.kind_of? ::TCPServer
|
28
|
+
super(socket)
|
13
29
|
else
|
14
|
-
|
30
|
+
super(::TCPServer.new(*args))
|
15
31
|
end
|
16
32
|
end
|
17
33
|
|
34
|
+
# @return [TCPSocket]
|
18
35
|
def accept
|
19
|
-
Celluloid::IO.wait_readable(
|
36
|
+
Celluloid::IO.wait_readable(to_io)
|
20
37
|
accept_nonblock
|
21
38
|
end
|
22
39
|
|
40
|
+
# @return [TCPSocket]
|
23
41
|
def accept_nonblock
|
24
|
-
Celluloid::IO::TCPSocket.new(
|
25
|
-
end
|
26
|
-
|
27
|
-
def to_io
|
28
|
-
@server
|
42
|
+
Celluloid::IO::TCPSocket.new(to_io.accept_nonblock)
|
29
43
|
end
|
30
44
|
|
31
45
|
# Convert a Ruby TCPServer into a Celluloid::IO::TCPServer
|
46
|
+
# @deprecated Use .new instead.
|
32
47
|
def self.from_ruby_server(ruby_server)
|
33
|
-
|
34
|
-
|
35
|
-
|
48
|
+
warn "#from_ruby_server is deprecated please use .new instead"
|
49
|
+
|
50
|
+
self.new(ruby_server)
|
36
51
|
end
|
37
52
|
end
|
38
53
|
end
|
@@ -7,8 +7,7 @@ module Celluloid
|
|
7
7
|
class TCPSocket < Stream
|
8
8
|
extend Forwardable
|
9
9
|
|
10
|
-
def_delegators
|
11
|
-
def_delegators :@socket, :addr, :peeraddr, :setsockopt, :getsockname
|
10
|
+
def_delegators :to_io, :peeraddr
|
12
11
|
|
13
12
|
# Open a TCP socket, yielding it to the given block and closing it
|
14
13
|
# automatically when done (if a block is given)
|
@@ -25,91 +24,114 @@ module Celluloid
|
|
25
24
|
|
26
25
|
# Convert a Ruby TCPSocket into a Celluloid::IO::TCPSocket
|
27
26
|
# DEPRECATED: to be removed in a future release
|
27
|
+
# @deprecated Use {Celluloid::IO::TCPSocket#new} instead.
|
28
28
|
def self.from_ruby_socket(ruby_socket)
|
29
29
|
new(ruby_socket)
|
30
30
|
end
|
31
31
|
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
32
|
+
# @overload initialize(remote_host, remote_port = nil, local_host = nil, local_port = nil)
|
33
|
+
# Opens a TCP connection to remote_host on remote_port. If local_host
|
34
|
+
# and local_port are specified, then those parameters are used on the
|
35
|
+
# local end to establish the connection.
|
36
|
+
# @param remote_host [String, Resolv::IPv4, Resolv::IPv6]
|
37
|
+
# @param remote_port [Numeric]
|
38
|
+
# @param local_host [String]
|
39
|
+
# @param local_port [Numeric]
|
40
|
+
#
|
41
|
+
# @overload initialize(socket)
|
42
|
+
# Wraps an already existing tcp socket.
|
43
|
+
# @param socket [::TCPSocket]
|
44
|
+
#
|
45
|
+
def initialize(*args)
|
46
|
+
if args.first.kind_of? ::BasicSocket
|
47
|
+
# socket
|
48
|
+
socket = args.first
|
49
|
+
fail ArgumentError, "wrong number of arguments (#{args.size} for 1)" if args.size != 1
|
50
|
+
fail ArgumentError, "wrong kind of socket (#{socket.class} for TCPSocket)" unless socket.kind_of? ::TCPSocket
|
51
|
+
super(socket)
|
52
|
+
else
|
53
|
+
super(create_socket(*args))
|
45
54
|
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Receives a message
|
58
|
+
def recv(maxlen, flags = nil)
|
59
|
+
fail NotImplementedError, "flags not supported" if flags && !flags.zero?
|
60
|
+
readpartial(maxlen)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Send a message
|
64
|
+
def send(msg, flags, dest_sockaddr = nil)
|
65
|
+
fail NotImplementedError, "dest_sockaddr not supported" if dest_sockaddr
|
66
|
+
fail NotImplementedError, "flags not supported" unless flags.zero?
|
67
|
+
write(msg)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Resolv::IPv4, Resolv::IPv6]
|
71
|
+
def addr
|
72
|
+
socket = to_io
|
73
|
+
ra = socket.remote_address
|
74
|
+
if ra.ipv4?
|
75
|
+
return Resolv::IPv4.create(ra.ip_address)
|
76
|
+
elsif ra.ipv6?
|
77
|
+
return Resolv::IPv6.create(ra.ip_address)
|
78
|
+
else
|
79
|
+
raise ArgumentError, "not an ip socket: #{socket.inspect}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
private
|
46
83
|
|
84
|
+
def create_socket(remote_host, remote_port = nil, local_host = nil, local_port = nil)
|
47
85
|
# Is it an IPv4 address?
|
48
86
|
begin
|
49
|
-
|
87
|
+
addr = Resolv::IPv4.create(remote_host)
|
50
88
|
rescue ArgumentError
|
51
89
|
end
|
52
90
|
|
53
91
|
# Guess it's not IPv4! Is it IPv6?
|
54
|
-
unless
|
92
|
+
unless addr
|
55
93
|
begin
|
56
|
-
|
94
|
+
addr = Resolv::IPv6.create(remote_host)
|
57
95
|
rescue ArgumentError
|
58
96
|
end
|
59
97
|
end
|
60
98
|
|
61
99
|
# Guess it's not an IP address, so let's try DNS
|
62
|
-
unless
|
100
|
+
unless addr
|
63
101
|
addrs = Array(DNSResolver.new.resolve(remote_host))
|
64
102
|
fail Resolv::ResolvError, "DNS result has no information for #{remote_host}" if addrs.empty?
|
65
103
|
|
66
104
|
# Pseudorandom round-robin DNS support :/
|
67
|
-
|
105
|
+
addr = addrs[rand(addrs.size)]
|
68
106
|
end
|
69
107
|
|
70
|
-
case
|
108
|
+
case addr
|
71
109
|
when Resolv::IPv4
|
72
110
|
family = Socket::AF_INET
|
73
111
|
when Resolv::IPv6
|
74
112
|
family = Socket::AF_INET6
|
75
|
-
else fail ArgumentError, "unsupported address class: #{
|
113
|
+
else fail ArgumentError, "unsupported address class: #{addr.class}"
|
76
114
|
end
|
77
115
|
|
78
|
-
|
79
|
-
|
116
|
+
socket = Socket.new(family, Socket::SOCK_STREAM, 0)
|
117
|
+
socket.bind Addrinfo.tcp(local_host, local_port) if local_host
|
80
118
|
|
81
119
|
begin
|
82
|
-
|
120
|
+
socket.connect_nonblock Socket.sockaddr_in(remote_port, addr.to_s)
|
83
121
|
rescue Errno::EINPROGRESS, Errno::EALREADY
|
84
122
|
# JRuby raises EINPROGRESS, MRI raises EALREADY
|
85
|
-
|
86
|
-
wait_writable
|
123
|
+
Celluloid::IO.wait_writable(socket)
|
87
124
|
|
88
125
|
# HAX: for some reason we need to finish_connect ourselves on JRuby
|
89
126
|
# This logic is unnecessary but JRuby still throws Errno::EINPROGRESS
|
90
127
|
# if we retry the non-blocking connect instead of just finishing it
|
91
|
-
retry unless RUBY_PLATFORM == "java" &&
|
128
|
+
retry unless RUBY_PLATFORM == "java" && socket.to_channel.finish_connect
|
92
129
|
rescue Errno::EISCONN
|
93
130
|
# We're now connected! Yay exceptions for flow control
|
94
131
|
# NOTE: This is the approach the Ruby stdlib docs suggest ;_;
|
95
132
|
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def to_io
|
99
|
-
@socket
|
100
|
-
end
|
101
|
-
|
102
|
-
# Receives a message
|
103
|
-
def recv(maxlen, flags = nil)
|
104
|
-
fail NotImplementedError, "flags not supported" if flags && !flags.zero?
|
105
|
-
readpartial(maxlen)
|
106
|
-
end
|
107
133
|
|
108
|
-
|
109
|
-
def send(msg, flags, dest_sockaddr = nil)
|
110
|
-
fail NotImplementedError, "dest_sockaddr not supported" if dest_sockaddr
|
111
|
-
fail NotImplementedError, "flags not supported" unless flags.zero?
|
112
|
-
write(msg)
|
134
|
+
return socket
|
113
135
|
end
|
114
136
|
end
|
115
137
|
end
|