celluloid-io 0.17.2 → 0.17.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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|