ione 1.2.0.pre1 → 1.2.0.pre2

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.
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+
4
+ module Ione
5
+ module Io
6
+ # @private
7
+ class SslAcceptor < Acceptor
8
+ def initialize(host, port, backlog, unblocker, reactor, ssl_context, socket_impl=nil, ssl_socket_impl=nil)
9
+ super(host, port, backlog, unblocker, reactor, socket_impl)
10
+ @ssl_context = ssl_context
11
+ @ssl_socket_impl = ssl_socket_impl
12
+ end
13
+
14
+ def read
15
+ client_socket, host, port = accept
16
+ connection = SslServerConnection.new(client_socket, host, port, @unblocker, @ssl_context, method(:notify_accept_listeners), @ssl_socket_impl)
17
+ @reactor.accept(connection)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,79 @@
1
+ # encoding: utf-8
2
+
3
+ require 'openssl'
4
+
5
+
6
+ module Ione
7
+ module Io
8
+ # @private
9
+ class SslConnection < BaseConnection
10
+ def initialize(host, port, io, unblocker, ssl_context=nil, socket_impl=OpenSSL::SSL::SSLSocket)
11
+ super(host, port, unblocker)
12
+ @socket_impl = socket_impl
13
+ @ssl_context = ssl_context
14
+ @raw_io = io
15
+ @connected_promise = Promise.new
16
+ on_closed(&method(:cleanup_on_close))
17
+ end
18
+
19
+ def connect
20
+ if @io.nil? && @ssl_context
21
+ @io = @socket_impl.new(@raw_io, @ssl_context)
22
+ elsif @io.nil?
23
+ @io = @socket_impl.new(@raw_io)
24
+ end
25
+ @io.connect_nonblock
26
+ @state = :connected
27
+ @connected_promise.fulfill(self)
28
+ @connected_promise.future
29
+ rescue IO::WaitReadable, IO::WaitWritable
30
+ # WaitReadable in JRuby, WaitWritable in MRI
31
+ @connected_promise.future
32
+ rescue => e
33
+ close(e)
34
+ @connected_promise.future
35
+ end
36
+
37
+ def to_io
38
+ @raw_io
39
+ end
40
+
41
+ if RUBY_ENGINE == 'jruby'
42
+ # @private
43
+ def read
44
+ while true
45
+ new_data = @io.read_nonblock(2**16)
46
+ @data_listener.call(new_data) if @data_listener
47
+ end
48
+ rescue IO::WaitReadable
49
+ # no more data available
50
+ rescue => e
51
+ close(e)
52
+ end
53
+ else
54
+ # @private
55
+ def read
56
+ read_size = 2**16
57
+ while read_size > 0
58
+ new_data = @io.read_nonblock(read_size)
59
+ @data_listener.call(new_data) if @data_listener
60
+ read_size = @io.pending
61
+ end
62
+ rescue => e
63
+ close(e)
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def cleanup_on_close(cause)
70
+ if cause && !cause.is_a?(IoError)
71
+ cause = ConnectionError.new(cause.message)
72
+ end
73
+ unless @connected_promise.future.completed?
74
+ @connected_promise.fail(cause)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+
3
+ module Ione
4
+ module Io
5
+ # @private
6
+ class SslServerConnection < ServerConnection
7
+ def initialize(socket, host, port, unblocker, ssl_context, accept_callback, ssl_socket_impl=nil)
8
+ super(socket, host, port, unblocker)
9
+ @ssl_context = ssl_context
10
+ @accept_callback = accept_callback
11
+ @ssl_socket_impl = ssl_socket_impl || OpenSSL::SSL::SSLSocket
12
+ @ssl_state = :accepting
13
+ end
14
+
15
+ # @private
16
+ def to_io
17
+ if @ssl_state == :established
18
+ @io.to_io
19
+ else
20
+ @io
21
+ end
22
+ end
23
+
24
+ def read
25
+ if @ssl_state == :accepting
26
+ begin
27
+ @ssl_io ||= @ssl_socket_impl.new(@io, @ssl_context)
28
+ @ssl_io.accept_nonblock
29
+ @io = @ssl_io
30
+ @ssl_state = :established
31
+ @accept_callback.call(self)
32
+ rescue IO::WaitReadable
33
+ # connection not ready yet
34
+ rescue => e
35
+ close(e)
36
+ end
37
+ else
38
+ super
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
data/lib/ione/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Ione
4
- VERSION = '1.2.0.pre1'.freeze
4
+ VERSION = '1.2.0.pre2'.freeze
5
5
  end
@@ -0,0 +1,97 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ describe 'SSL' do
7
+ let :io_reactor do
8
+ Ione::Io::IoReactor.new
9
+ end
10
+
11
+ let :port do
12
+ 2**15 + rand(2**15)
13
+ end
14
+
15
+ let :ssl_key do
16
+ OpenSSL::PKey::RSA.new(2048)
17
+ end
18
+
19
+ let :ssl_cert do
20
+ cert = OpenSSL::X509::Certificate.new
21
+ cert.version = 2
22
+ cert.serial = 1
23
+ name = OpenSSL::X509::Name.new([['CN', 'localhost']])
24
+ cert.subject = name
25
+ cert.issuer = name
26
+ cert.not_before = Time.now
27
+ cert.not_after = Time.now + (365*24*60*60)
28
+ cert.public_key = ssl_key.public_key
29
+ cert.sign(ssl_key, OpenSSL::Digest::SHA1.new)
30
+ cert
31
+ end
32
+
33
+ let :ssl_context do
34
+ ctx = OpenSSL::SSL::SSLContext.new
35
+ ctx.cert = ssl_cert
36
+ ctx.key = ssl_key
37
+ ctx
38
+ end
39
+
40
+ let :server_received_data do
41
+ Ione::ByteBuffer.new
42
+ end
43
+
44
+ let :client_received_data do
45
+ Ione::ByteBuffer.new
46
+ end
47
+
48
+ def start_server
49
+ ssl_context = OpenSSL::SSL::SSLContext.new
50
+ ssl_context.key = OpenSSL::PKey::RSA.new(ssl_key)
51
+ ssl_context.cert = OpenSSL::X509::Certificate.new(ssl_cert)
52
+
53
+ f = io_reactor.start
54
+ f = f.flat_map do
55
+ io_reactor.bind(ENV['SERVER_HOST'], port, ssl: ssl_context) do |acceptor|
56
+ acceptor.on_accept do |connection|
57
+ connection.on_data do |data|
58
+ server_received_data << data
59
+ connection.write(data.reverse)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ f
65
+ end
66
+
67
+ it 'establishes an encrypted connection' do
68
+ ssl_context = OpenSSL::SSL::SSLContext.new
69
+ ssl_context.cert = OpenSSL::X509::Certificate.new(ssl_cert)
70
+
71
+ response_received = Ione::Promise.new
72
+ f = start_server
73
+ f = f.flat_map do
74
+ io_reactor.connect(ENV['SERVER_HOST'], port, ssl: ssl_context)
75
+ end
76
+ client = f.value
77
+ client.on_data do |data|
78
+ client_received_data << data
79
+ response_received.fulfill(data)
80
+ end
81
+ client.write('hello world')
82
+ response_received.future.value
83
+ server_received_data.to_s.should == 'hello world'
84
+ client_received_data.to_s.should == 'dlrow olleh'
85
+ end
86
+
87
+ it 'fails to send a message when not using encryption' do
88
+ f = start_server
89
+ f = f.flat_map do
90
+ io_reactor.connect(ENV['SERVER_HOST'], port)
91
+ end
92
+ client = f.value
93
+ client.write('hello world')
94
+ await { client.closed? }
95
+ client.should be_closed
96
+ end
97
+ end
@@ -211,15 +211,91 @@ module Ione
211
211
  f2.should equal(future)
212
212
  end
213
213
 
214
- it 'passes the value as the second parameter to the block' do
214
+ it 'passes the value as the first parameter to the block when it expects two arguments' do
215
215
  v1, v2 = nil, nil
216
- future.on_complete { |_, v| v1 = v }
217
- future.on_complete { |_, v| v2 = v }
216
+ future.on_complete { |v, _| v1 = v }
217
+ future.on_complete { |v, _| v2 = v }
218
218
  promise.fulfill('bar')
219
219
  v1.should == 'bar'
220
220
  v2.should == 'bar'
221
221
  end
222
222
 
223
+ it 'passes future as the third parameter to the block when it expects three arguments' do
224
+ f1, f2 = nil, nil
225
+ future.on_complete { |_, _, f| f1 = f }
226
+ future.on_complete { |_, _, f| f2 = f }
227
+ promise.fulfill('bar')
228
+ f1.should equal(future)
229
+ f2.should equal(future)
230
+ end
231
+
232
+ it 'passes the value, error and future to the block when it expects any number of arguments' do
233
+ value = 'bar'
234
+ error = StandardError.new('bork')
235
+ args1, args2 = nil, nil
236
+ p1 = Promise.new
237
+ p2 = Promise.new
238
+ p1.future.on_complete { |*a| args1 = a }
239
+ p2.future.on_complete { |*a| args2 = a }
240
+ p1.fulfill(value)
241
+ p2.fail(error)
242
+ args1[0].should equal(value)
243
+ args1[1].should be_nil
244
+ args1[2].should equal(p1.future)
245
+ args2[0].should be_nil
246
+ args2[1].should equal(error)
247
+ args2[2].should equal(p2.future)
248
+ end
249
+
250
+ it 'passes the value, error and future to the listener when the listener is a lambda' do
251
+ value = 'bar'
252
+ error = StandardError.new('bork')
253
+ args1, args2 = nil, nil
254
+ p1 = Promise.new
255
+ p2 = Promise.new
256
+ p1.future.on_complete(&lambda { |v, e, f| args1 = [v, e, f] })
257
+ p2.future.on_complete(&lambda { |v, e, f| args2 = [v, e, f] })
258
+ p1.fulfill(value)
259
+ p2.fail(error)
260
+ args1[0].should equal(value)
261
+ args1[1].should be_nil
262
+ args1[2].should equal(p1.future)
263
+ args2[0].should be_nil
264
+ args2[1].should equal(error)
265
+ args2[2].should equal(p2.future)
266
+ end
267
+
268
+ it 'ignores optional arguments for lambdas' do
269
+ value = 'bar'
270
+ error = StandardError.new('bork')
271
+ args1, args2 = nil, nil
272
+ p1 = Promise.new
273
+ p2 = Promise.new
274
+ p1.future.on_complete(&lambda { |v, e, f=nil| args1 = [v, e, f] })
275
+ p2.future.on_complete(&lambda { |v, e, f, x=1, y=2| args2 = [v, e, f, x, y] })
276
+ p1.fulfill(value)
277
+ p2.fail(error)
278
+ args1[0].should equal(value)
279
+ args1[1].should be_nil
280
+ args1[2].should be_nil
281
+ args2[0].should be_nil
282
+ args2[1].should equal(error)
283
+ args2[2].should equal(p2.future)
284
+ end
285
+
286
+ it 'does not handle listeners that are lambdas and have one optional argument' do
287
+ pending 'MRI 1.9.3 thinks these lambas have arity 1 and not -2' if RUBY_ENGINE == 'ruby' && RUBY_VERSION < '2.0.0'
288
+ called1, called2 = false, false
289
+ p1 = Promise.new
290
+ p2 = Promise.new
291
+ p1.future.on_complete(&lambda { |v, e=nil| called1 = true })
292
+ p2.future.on_complete(&lambda { |v, e=nil| called2 = true })
293
+ p1.fulfill('bar')
294
+ p2.fail(StandardError.new('bork'))
295
+ called1.should be_false
296
+ called2.should be_false
297
+ end
298
+
223
299
  it 'notifies all listeners when the promise fails' do
224
300
  c1, c2 = nil, nil
225
301
  future.on_complete { c1 = true }
@@ -229,10 +305,10 @@ module Ione
229
305
  c2.should be_true
230
306
  end
231
307
 
232
- it 'passes the error as the third parameter' do
308
+ it 'passes the error as the second parameter to the block when it expects two arguments' do
233
309
  e1, e2 = nil, nil
234
- future.on_complete { |_, _, e| e1 = e }
235
- future.on_complete { |_, _, e| e2 = e }
310
+ future.on_complete { |_, e| e1 = e }
311
+ future.on_complete { |_, e| e2 = e }
236
312
  future.fail(error)
237
313
  e1.should equal(error)
238
314
  e2.should equal(error)
@@ -257,7 +333,7 @@ module Ione
257
333
  it 'notifies listeners registered after the promise was fulfilled' do
258
334
  f, v, e = nil, nil, nil
259
335
  promise.fulfill('bar')
260
- future.on_complete { |ff, vv, ee| f = ff; v = vv; e = ee }
336
+ future.on_complete { |vv, ee, ff| v = vv; e = ee; f = ff }
261
337
  f.should equal(future)
262
338
  v.should == 'bar'
263
339
  e.should be_nil
@@ -266,7 +342,7 @@ module Ione
266
342
  it 'notifies listeners registered after the promise failed' do
267
343
  f, v, e = nil, nil, nil
268
344
  promise.fail(StandardError.new('bork'))
269
- future.on_complete { |ff, vv, ee| f = ff; v = vv; e = ee }
345
+ future.on_complete { |vv, ee, ff| v = vv; e = ee; f = ff }
270
346
  f.should equal(future)
271
347
  v.should be_nil
272
348
  e.message.should == 'bork'
@@ -274,7 +350,7 @@ module Ione
274
350
 
275
351
  it 'notifies listeners registered after the promise failed' do
276
352
  promise.fail(error)
277
- expect { future.on_complete { |v| raise 'blurgh' } }.to_not raise_error
353
+ expect { future.on_complete { raise 'blurgh' } }.to_not raise_error
278
354
  end
279
355
 
280
356
  it 'returns nil' do
@@ -432,6 +508,12 @@ module Ione
432
508
  promise.fulfill(:hello)
433
509
  listeners.map(&:value).should == Array.new(10, :hello)
434
510
  end
511
+
512
+ it 'is aliased as #get' do
513
+ obj = 'hello world'
514
+ promise.fulfill(obj)
515
+ future.get.should equal(obj)
516
+ end
435
517
  end
436
518
 
437
519
  describe '#map' do
@@ -512,7 +594,7 @@ module Ione
512
594
 
513
595
  it 'accepts anything that implements #on_complete as a chained future' do
514
596
  fake_future = double(:fake_future)
515
- fake_future.stub(:on_complete) { |&listener| listener.call(nil, :foobar) }
597
+ fake_future.stub(:on_complete) { |&listener| listener.call(:foobar, nil) }
516
598
  p = Promise.new
517
599
  f = p.future.flat_map { fake_future }
518
600
  p.fulfill
@@ -531,13 +613,26 @@ module Ione
531
613
  end
532
614
 
533
615
  context 'when the block returns something that quacks like a future' do
534
- it 'works like #flat_map' do
535
- fake_future = double(:fake_future)
536
- fake_future.stub(:on_complete) { |&listener| listener.call(nil, :foobar) }
537
- p = Promise.new
538
- f = p.future.then { |v| fake_future }
539
- p.fulfill
540
- f.value.should == :foobar
616
+ context 'and yields a value from #on_complete' do
617
+ it 'works like #flat_map' do
618
+ fake_future = double(:fake_future)
619
+ fake_future.stub(:on_complete) { |&listener| listener.call(:foobar) }
620
+ p = Promise.new
621
+ f = p.future.then { |v| fake_future }
622
+ p.fulfill
623
+ f.value.should == :foobar
624
+ end
625
+ end
626
+
627
+ context 'and yields an error from #on_complete' do
628
+ it 'works like #flat_map' do
629
+ fake_future = double(:fake_future)
630
+ fake_future.stub(:on_complete) { |&listener| listener.call(nil, StandardError.new('bork')) }
631
+ p = Promise.new
632
+ f = p.future.then { |v| fake_future }
633
+ p.fulfill
634
+ expect { f.value }.to raise_error(StandardError, 'bork')
635
+ end
541
636
  end
542
637
  end
543
638
 
@@ -662,7 +757,7 @@ module Ione
662
757
 
663
758
  it 'accepts anything that implements #on_complete as a fallback future' do
664
759
  fake_future = double(:fake_future)
665
- fake_future.stub(:on_complete) { |&listener| listener.call(nil, 'foo') }
760
+ fake_future.stub(:on_complete) { |&listener| listener.call('foo', nil) }
666
761
  p = Promise.new
667
762
  f = p.future.fallback { fake_future }
668
763
  p.fail(error)
@@ -703,7 +798,7 @@ module Ione
703
798
 
704
799
  it 'accepts anything that implements #on_complete as futures' do
705
800
  fake_future = double(:fake_future)
706
- fake_future.stub(:on_complete) { |&listener| listener.call(nil, :foobar) }
801
+ fake_future.stub(:on_complete) { |&listener| listener.call(:foobar, nil) }
707
802
  future = Future.traverse([1, 2, 3]) { fake_future }
708
803
  future.value.should == [:foobar, :foobar, :foobar]
709
804
  end
@@ -785,9 +880,9 @@ module Ione
785
880
 
786
881
  it 'accepts anything that implements #on_complete as futures' do
787
882
  ff1, ff2, ff3 = double, double, double
788
- ff1.stub(:on_complete) { |&listener| listener.call(nil, 1) }
789
- ff2.stub(:on_complete) { |&listener| listener.call(nil, 2) }
790
- ff3.stub(:on_complete) { |&listener| listener.call(nil, 3) }
883
+ ff1.stub(:on_complete) { |&listener| listener.call(1, nil) }
884
+ ff2.stub(:on_complete) { |&listener| listener.call(2, nil) }
885
+ ff3.stub(:on_complete) { |&listener| listener.call(3, nil) }
791
886
  future = Future.reduce([ff1, ff2, ff3], 0) { |sum, n| sum + n }
792
887
  future.value.should == 6
793
888
  end
@@ -907,9 +1002,9 @@ module Ione
907
1002
 
908
1003
  it 'accepts anything that implements #on_complete as futures' do
909
1004
  ff1, ff2, ff3 = double, double, double
910
- ff1.stub(:on_complete) { |&listener| listener.call(nil, 1) }
911
- ff2.stub(:on_complete) { |&listener| listener.call(nil, 2) }
912
- ff3.stub(:on_complete) { |&listener| listener.call(nil, 3) }
1005
+ ff1.stub(:on_complete) { |&listener| listener.call(1, nil) }
1006
+ ff2.stub(:on_complete) { |&listener| listener.call(2, nil) }
1007
+ ff3.stub(:on_complete) { |&listener| listener.call(3, nil) }
913
1008
  future = Future.all(ff1, ff2, ff3)
914
1009
  future.value.should == [1, 2, 3]
915
1010
  end
@@ -1006,8 +1101,8 @@ module Ione
1006
1101
 
1007
1102
  it 'accepts anything that implements #on_complete as futures' do
1008
1103
  ff1, ff2 = double, double
1009
- ff1.stub(:on_complete) { |&listener| listener.call(nil, 1) }
1010
- ff2.stub(:on_complete) { |&listener| listener.call(nil, 2) }
1104
+ ff1.stub(:on_complete) { |&listener| listener.call(1, nil) }
1105
+ ff2.stub(:on_complete) { |&listener| listener.call(2, nil) }
1011
1106
  future = Future.first(ff1, ff2)
1012
1107
  future.value.should == 1
1013
1108
  end
@@ -1040,11 +1135,21 @@ module Ione
1040
1135
 
1041
1136
  it 'calls its complete callbacks immediately' do
1042
1137
  f, v = nil, nil
1043
- future.on_complete { |ff, vv| f = ff; v = vv }
1138
+ future.on_complete { |vv, _, ff| f = ff; v = vv }
1044
1139
  f.should equal(future)
1045
1140
  v.should == 'hello world'
1046
1141
  end
1047
1142
 
1143
+ it 'calls its complete callbacks with the right arity' do
1144
+ f1, v, f2 = nil, nil, nil
1145
+ future.on_complete { |ff| f1 = ff }
1146
+ future.on_complete { |vv, ee| v = vv }
1147
+ future.on_complete { |vv, ee, ff| f2 = ff }
1148
+ f1.should equal(future)
1149
+ f2.should equal(future)
1150
+ v.should == 'hello world'
1151
+ end
1152
+
1048
1153
  it 'does not block on #value' do
1049
1154
  future.value.should == 'hello world'
1050
1155
  end
@@ -1081,15 +1186,25 @@ module Ione
1081
1186
 
1082
1187
  it 'calls its complete callbacks immediately' do
1083
1188
  f, e = nil, nil
1084
- future.on_complete { |ff, _, ee| f = ff; e = ee }
1189
+ future.on_complete { |_, ee, ff| f = ff; e = ee }
1085
1190
  f.should equal(future)
1086
1191
  e.message.should == 'bork'
1087
1192
  end
1088
1193
 
1194
+ it 'calls its complete callbacks with the right arity' do
1195
+ f1, e, f2 = nil, nil, nil
1196
+ future.on_complete { |ff| f1 = ff }
1197
+ future.on_complete { |vv, ee| e = ee }
1198
+ future.on_complete { |vv, ee, ff| f2 = ff }
1199
+ f1.should equal(future)
1200
+ f2.should equal(future)
1201
+ e.message.should == 'bork'
1202
+ end
1203
+
1089
1204
  it 'does not block on #value' do
1090
1205
  expect { future.value }.to raise_error('bork')
1091
1206
  end
1092
1207
  end
1093
1208
  end
1094
1209
  end
1095
- end
1210
+ end