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.
@@ -1,12 +1,27 @@
1
1
  module Celluloid
2
2
  module IO
3
3
  # UDPSockets with combined blocking and evented support
4
- class UDPSocket
4
+ class UDPSocket < Socket
5
5
  extend Forwardable
6
- def_delegators :@socket, :addr, :bind, :connect, :send, :recvfrom_nonblock, :close, :closed?
6
+ def_delegators :to_io, :bind, :connect, :send, :recvfrom_nonblock
7
7
 
8
- def initialize(address_family = ::Socket::AF_INET)
9
- @socket = ::UDPSocket.new(address_family)
8
+ # @overload initialize(address_family)
9
+ # Opens a new udp socket using address_family.
10
+ # @param address_family [Numeric]
11
+ #
12
+ # @overload initialize(socket)
13
+ # Wraps an already existing udp socket.
14
+ # @param socket [::UDPSocket]
15
+ def initialize(*args)
16
+ if args.first.kind_of? ::BasicSocket
17
+ # socket
18
+ socket = args.first
19
+ fail ArgumentError, "wrong number of arguments (#{args.size} for 1)" if args.size != 1
20
+ fail ArgumentError, "wrong kind of socket (#{socket.class} for UDPSocket)" unless socket.kind_of? ::UDPSocket
21
+ super(socket)
22
+ else
23
+ super(::UDPSocket.new(*args))
24
+ end
10
25
  end
11
26
 
12
27
  # Wait until the socket is readable
@@ -16,13 +31,14 @@ module Celluloid
16
31
  # MSG_ options. The first element of the results, mesg, is the data
17
32
  # received. The second element, sender_addrinfo, contains
18
33
  # protocol-specific address information of the sender.
19
- def recvfrom(maxlen, flags = nil)
34
+ def recvfrom(maxlen, flags = 0)
20
35
  begin
21
- if @socket.respond_to? :recvfrom_nonblock
22
- @socket.recvfrom_nonblock(maxlen, flags)
36
+ socket = to_io
37
+ if socket.respond_to? :recvfrom_nonblock
38
+ socket.recvfrom_nonblock(maxlen, flags)
23
39
  else
24
40
  # FIXME: hax for JRuby
25
- @socket.recvfrom(maxlen, flags)
41
+ socket.recvfrom(maxlen, flags)
26
42
  end
27
43
  rescue ::IO::WaitReadable
28
44
  wait_readable
@@ -30,7 +46,6 @@ module Celluloid
30
46
  end
31
47
  end
32
48
 
33
- def to_io; @socket; end
34
49
  end
35
50
  end
36
51
  end
@@ -3,40 +3,48 @@ require 'socket'
3
3
  module Celluloid
4
4
  module IO
5
5
  # UNIXServer with combined blocking and evented support
6
- class UNIXServer
6
+ class UNIXServer < Socket
7
7
  extend Forwardable
8
- def_delegators :@server, :listen, :sysaccept, :close, :closed?
8
+ def_delegators :to_io, :listen, :sysaccept
9
9
 
10
10
  def self.open(socket_path)
11
11
  self.new(socket_path)
12
12
  end
13
13
 
14
- def initialize(socket_path)
15
- begin
16
- @server = ::UNIXServer.new(socket_path)
17
- rescue => ex
18
- # Translate the EADDRINUSE jRuby exception.
19
- raise unless RUBY_PLATFORM == 'java'
20
- if ex.class.name == "IOError" && # Won't agree to .is_a?(IOError)
21
- ex.message.include?("in use")
22
- raise Errno::EADDRINUSE.new(ex.message)
14
+ # @overload initialize(socket_path)
15
+ # @param socket_path [String]
16
+ #
17
+ # @overload initialize(socket)
18
+ # @param socket [::UNIXServer]
19
+ def initialize(socket)
20
+ if socket.kind_of? ::BasicSocket
21
+ # socket
22
+ fail ArgumentError, "wrong kind of socket (#{socket.class} for UNIXServer)" unless socket.kind_of? ::UNIXServer
23
+ super(socket)
24
+ else
25
+ begin
26
+ super(::UNIXServer.new(socket))
27
+ rescue => ex
28
+ # Translate the EADDRINUSE jRuby exception.
29
+ raise unless RUBY_PLATFORM == 'java'
30
+ if ex.class.name == "IOError" && # Won't agree to .is_a?(IOError)
31
+ ex.message.include?("in use")
32
+ raise Errno::EADDRINUSE.new(ex.message)
33
+ end
34
+ raise
23
35
  end
24
- raise
25
36
  end
26
37
  end
27
38
 
28
39
  def accept
29
- Celluloid::IO.wait_readable(@server)
40
+ Celluloid::IO.wait_readable(to_io)
30
41
  accept_nonblock
31
42
  end
32
43
 
33
44
  def accept_nonblock
34
- Celluloid::IO::UNIXSocket.new(@server.accept_nonblock)
45
+ Celluloid::IO::UNIXSocket.new(to_io.accept_nonblock)
35
46
  end
36
47
 
37
- def to_io
38
- @server
39
- end
40
48
  end
41
49
  end
42
50
  end
@@ -4,10 +4,6 @@ module Celluloid
4
4
  module IO
5
5
  # UNIXSocket with combined blocking and evented support
6
6
  class UNIXSocket < Stream
7
- extend Forwardable
8
-
9
- def_delegators :@socket, :read_nonblock, :write_nonblock, :close, :closed?, :readline, :puts, :addr
10
-
11
7
  # Open a UNIX connection.
12
8
  def self.open(socket_path, &block)
13
9
  new(socket_path, &block)
@@ -15,31 +11,27 @@ module Celluloid
15
11
 
16
12
  # Convert a Ruby UNIXSocket into a Celluloid::IO::UNIXSocket
17
13
  # DEPRECATED: to be removed in a future release
14
+ # @deprecated use .new instead
18
15
  def self.from_ruby_socket(ruby_socket)
19
16
  new(ruby_socket)
20
17
  end
21
18
 
22
19
  # Open a UNIX connection.
23
20
  def initialize(socket_path, &block)
24
- super()
25
-
26
21
  # Allow users to pass in a Ruby UNIXSocket directly
27
22
  if socket_path.is_a? ::UNIXSocket
28
- @socket = socket_path
23
+ super(socket_path)
29
24
  return
30
25
  end
31
26
 
32
27
  # FIXME: not doing non-blocking connect
33
- @socket = if block
34
- ::UNIXSocket.open(socket_path, &block)
35
- else
36
- ::UNIXSocket.new(socket_path)
28
+ if block
29
+ super ::UNIXSocket.open(socket_path, &block)
30
+ else
31
+ super ::UNIXSocket.new(socket_path)
37
32
  end
38
33
  end
39
34
 
40
- def to_io
41
- @socket
42
- end
43
35
  end
44
36
  end
45
37
  end
@@ -1,5 +1,5 @@
1
1
  module Celluloid
2
2
  module IO
3
- VERSION = "0.17.2"
3
+ VERSION = "0.17.3"
4
4
  end
5
5
  end
@@ -1,5 +1,58 @@
1
1
  require "spec_helper"
2
2
 
3
3
  RSpec.describe Celluloid::IO, library: :IO do
4
- it_behaves_like "a Celluloid Actor", Celluloid::IO
4
+ it_behaves_like "a Celluloid Actor", Celluloid::IO do
5
+ let(:example_port) { assign_port }
6
+
7
+ context :timeouts do
8
+ let :sleeping_actor_class do
9
+ Class.new do
10
+ include Celluloid::IO
11
+ def initialize(addr, port)
12
+ @server = Celluloid::IO::TCPServer.new(addr, port)
13
+ async { @server.accept ; sleep 10 }
14
+ end
15
+ end
16
+ end
17
+ let :foo_actor_class do
18
+ Class.new do
19
+ include Celluloid::IO
20
+ def initialize(addr, port)
21
+ @sock = Celluloid::IO::TCPSocket.new(addr, port)
22
+ end
23
+
24
+ # returns true if the operation timedout
25
+ def timedout_read(duration)
26
+ begin
27
+ timeout(duration) do
28
+ @sock.wait_readable
29
+ end
30
+ rescue Celluloid::TaskTimeout
31
+ return true
32
+ end
33
+ false
34
+ end
35
+
36
+ # returns true if it cannot write (socket is already registered)
37
+ def failed_write
38
+ begin
39
+ @sock.wait_readable
40
+ rescue ArgumentError # IO Selector Exception
41
+ return true
42
+ end
43
+ false
44
+ end
45
+ end
46
+ end
47
+
48
+ it "frees up the socket when a timeout error occurs" do
49
+ a1 = sleeping_actor_class.new(example_addr, example_port)
50
+ a2 = foo_actor_class.new(example_addr, example_port)
51
+
52
+ expect(a2.timedout_read(1)).to eq true # this ensures that the socket timeouted trying to read
53
+ skip "not implemented"
54
+ expect(a2.failed_write).to eq false # this ensures that the socket isn't usable anymore
55
+ end
56
+ end
57
+ end
5
58
  end
@@ -0,0 +1,173 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Celluloid::IO::Socket, library: :IO do
4
+ let(:logger) { Specs::FakeLogger.current }
5
+ let(:example_port) { assign_port }
6
+
7
+ context '.try_convert' do
8
+
9
+ subject{ described_class.try_convert(socket) }
10
+
11
+ after(:each) do
12
+ if subject.respond_to? :close
13
+ subject.close
14
+ else
15
+ socket.close if socket.respond_to? :close
16
+ end
17
+ end
18
+
19
+ context 'with a Celluloid Socket' do
20
+ let(:socket){ Celluloid::IO::UDPSocket.new }
21
+
22
+ it 'returns given socket' do
23
+ expect(subject).to be socket
24
+ end
25
+ end
26
+
27
+ context 'with a ::TCPServer' do
28
+ let(:socket){ ::TCPServer.new(example_port) }
29
+
30
+ it 'creates a Celluloid::IO::TCPServer' do
31
+ expect(subject).to be_a Celluloid::IO::TCPServer
32
+ end
33
+ end
34
+
35
+ context 'with a ::TCPSocket' do
36
+ let!(:server){
37
+ ::TCPServer.new example_addr, example_port
38
+ }
39
+ after(:each){
40
+ server.close
41
+ }
42
+
43
+ let(:socket){
44
+ ::TCPSocket.new example_addr, example_port
45
+ }
46
+
47
+ it 'creates a Celluloid::IO::TCPSocket' do
48
+ expect(subject).to be_a Celluloid::IO::TCPSocket
49
+ end
50
+ end
51
+
52
+ context 'with a ::UDPSocket' do
53
+ let(:socket){ ::UDPSocket.new }
54
+
55
+ it 'creates a Celluloid::IO::UDPServer' do
56
+ expect(subject).to be_a Celluloid::IO::UDPSocket
57
+ end
58
+ end
59
+
60
+ context 'with a ::UNIXServer' do
61
+ let(:socket){ ::UNIXServer.new(example_unix_sock) }
62
+
63
+ it 'creates a Celluloid::IO::UNIXServer' do
64
+ expect(subject).to be_a Celluloid::IO::UNIXServer
65
+ end
66
+ end
67
+
68
+ context 'with a ::UNIXSocket' do
69
+ let!(:server){
70
+ ::UNIXServer.new(example_unix_sock)
71
+ }
72
+ after(:each){
73
+ server.close
74
+ }
75
+
76
+ let(:socket){
77
+ ::UNIXSocket.new example_unix_sock
78
+ }
79
+
80
+ it 'creates a Celluloid::IO::UNIXSocket' do
81
+ expect(subject).to be_a Celluloid::IO::UNIXSocket
82
+ end
83
+ end
84
+
85
+ context 'with an OpenSSL::SSL::SSLServer' do
86
+ let(:socket){
87
+ OpenSSL::SSL::SSLServer.new(::TCPServer.new(example_addr, example_port), OpenSSL::SSL::SSLContext.new)
88
+ }
89
+
90
+ it 'creates a Celluloid::IO::SSLServer' do
91
+ expect(subject).to be_a Celluloid::IO::SSLServer
92
+ end
93
+ end
94
+
95
+ context 'with an OpenSSL::SSL::SSLSocket' do
96
+ let!(:server){
97
+ OpenSSL::SSL::SSLServer.new(::TCPServer.new(example_addr, example_port), OpenSSL::SSL::SSLContext.new)
98
+ }
99
+ after(:each){
100
+ server.close
101
+ }
102
+
103
+ let(:socket){
104
+ OpenSSL::SSL::SSLSocket.new(::TCPSocket.new(example_addr, example_port))
105
+ }
106
+
107
+ it 'creates a Celluloid::IO::SSLSocket' do
108
+ expect(subject).to be_a Celluloid::IO::SSLSocket
109
+ end
110
+ end
111
+
112
+ context 'with an object responding to #to_io' do
113
+ let(:real){
114
+ ::UDPSocket.new
115
+ }
116
+
117
+ let(:socket){
118
+ proxy = double(:socket)
119
+ allow(proxy).to receive(:to_io){ real }
120
+ allow(proxy).to receive(:close){ real.close }
121
+ proxy
122
+ }
123
+
124
+ it 'creates a celluloid socket' do
125
+ expect(subject).to be_a described_class
126
+ end
127
+
128
+ it 'uses the returned IO' do
129
+ expect(subject.to_io).to be socket.to_io
130
+ end
131
+ end
132
+
133
+ context 'with a simple object' do
134
+ let(:socket){ Object.new }
135
+
136
+ it 'returns nil' do
137
+ expect(subject).to be_nil
138
+ end
139
+ end
140
+ end
141
+
142
+ context 'compatibility with ::Socket' do
143
+
144
+ context '.new' do
145
+ it "creates basic sockets" do
146
+ socket = Celluloid::IO::Socket.new(Celluloid::IO::Socket::AF_INET, Celluloid::IO::Socket::SOCK_STREAM, 0)
147
+ expect(socket).to be_a ::Socket
148
+ socket.close
149
+ end
150
+ end
151
+
152
+ context '.pair' do
153
+ it "creates basic sockets" do
154
+ a,b = Celluloid::IO::Socket.pair( Celluloid::IO::Socket::AF_UNIX, Celluloid::IO::Socket::SOCK_DGRAM, 0)
155
+ expect(a).to be_a ::Socket
156
+ expect(b).to be_a ::Socket
157
+ a.close
158
+ b.close
159
+ end
160
+ end
161
+
162
+ context '.for_fd' do
163
+ it "creates basic sockets" do
164
+ socket = Celluloid::IO::Socket.new(Celluloid::IO::Socket::AF_INET, Celluloid::IO::Socket::SOCK_STREAM, 0)
165
+ copy = Celluloid::IO::Socket.for_fd(socket.fileno)
166
+ expect(copy).to be_a ::Socket
167
+ copy.close
168
+ end
169
+ end
170
+
171
+ end
172
+
173
+ end
@@ -28,6 +28,33 @@ RSpec.describe Celluloid::IO::TCPServer, library: :IO do
28
28
  end
29
29
  end
30
30
 
31
+ it "sends information to the client later" do
32
+ class LaterActor
33
+ include Celluloid::IO
34
+
35
+ def send_later(socket)
36
+ peer = socket.accept
37
+ after(0.4) { peer.write "1" }
38
+ after(0.4) { peer.write "2" }
39
+ peer
40
+ end
41
+ end
42
+ with_tcp_server(example_port) do |subject|
43
+ thread = Thread.new { TCPSocket.new(example_addr, example_port) }
44
+ actor = LaterActor.new
45
+ begin
46
+ peer = actor.send_later(subject)
47
+ client = thread.value
48
+ client.write payload
49
+ expect(peer.read(payload.size)).to eq payload # confirm the client read
50
+ Timeout::timeout(1) { expect(client.read(1)).to eq "1" }
51
+ Timeout::timeout(2) { expect(client.read(1)).to eq "2" }
52
+ ensure
53
+ actor.terminate if actor.alive?
54
+ end
55
+ end
56
+ end
57
+
31
58
  context "outside Celluloid::IO" do
32
59
  it "should be blocking" do
33
60
  with_tcp_server(example_port) do |subject|