ione-rpc 1.0.0.pre0 → 1.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ione/rpc/client.rb +6 -3
- data/lib/ione/rpc/client_peer.rb +13 -4
- data/lib/ione/rpc/codec.rb +12 -3
- data/lib/ione/rpc/peer.rb +2 -1
- data/lib/ione/rpc/version.rb +1 -1
- data/lib/ione/rpc.rb +1 -0
- data/spec/ione/rpc/client_peer_spec.rb +33 -1
- data/spec/ione/rpc/client_spec.rb +24 -4
- data/spec/ione/rpc/codec_spec.rb +30 -17
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51144d6e83a35056bbefda51115605b74141c2a1
|
4
|
+
data.tar.gz: 0d983eee0b3c1810a6c8a3a1a311bf4d7e4b8941
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 652a513b927f5c485e960e2211e4a687ba64034285fa631dbcfee72a91ab19f4bcbbaa09b95875b8deeba3efe2b3b3f77bf8674de1536225f9d0d042b7e06f61
|
7
|
+
data.tar.gz: 500e33e208a693abf1ce8495d4c8e6250672eb16832bd17ca8e9195def804a41338d2484d431bd13c8bbf0263164bd1c3dca4f3a1f95a3df6a8dc1a41419cc90
|
data/lib/ione/rpc/client.rb
CHANGED
@@ -141,15 +141,18 @@ module Ione
|
|
141
141
|
# @param [Object] request the request to send.
|
142
142
|
# @param [Object] connection the connection to send the request on. This
|
143
143
|
# parameter is internal and should only be used from {#initialize_connection}.
|
144
|
+
# @param [Object] timeout the maximum time in seconds to wait for a response
|
145
|
+
# before failing the returned future with a {Ione::Rpc::TimeoutError}.
|
146
|
+
# There is no timeout by default.
|
144
147
|
# @return [Ione::Future<Object>] a future that resolves to the response
|
145
148
|
# from the server, or fails because there was an error while processing
|
146
149
|
# the request (this is not the same thing as the server sending an
|
147
150
|
# error response – that is protocol specific and up to the implementation
|
148
151
|
# to handle), or when there was no connection open.
|
149
|
-
def send_request(request, connection=nil)
|
152
|
+
def send_request(request, connection=nil, timeout=nil)
|
150
153
|
connection = connection || @lock.synchronize { choose_connection(@connections, request) }
|
151
154
|
if connection
|
152
|
-
f = connection.send_message(request)
|
155
|
+
f = connection.send_message(request, timeout)
|
153
156
|
f = f.fallback do |error|
|
154
157
|
if error.is_a?(Io::ConnectionClosedError)
|
155
158
|
@logger.warn('Request failed because the connection closed, retrying') if @logger
|
@@ -261,7 +264,7 @@ module Ione
|
|
261
264
|
end
|
262
265
|
|
263
266
|
def create_connection(raw_connection)
|
264
|
-
Ione::Rpc::ClientPeer.new(raw_connection, @codec, @max_channels)
|
267
|
+
Ione::Rpc::ClientPeer.new(raw_connection, @codec, @io_reactor, @max_channels)
|
265
268
|
end
|
266
269
|
|
267
270
|
def handle_connected(connection)
|
data/lib/ione/rpc/client_peer.rb
CHANGED
@@ -7,14 +7,15 @@ module Ione
|
|
7
7
|
module Rpc
|
8
8
|
# @private
|
9
9
|
class ClientPeer < Peer
|
10
|
-
def initialize(connection, codec, max_channels)
|
11
|
-
|
10
|
+
def initialize(connection, codec, scheduler, max_channels)
|
11
|
+
raise ArgumentError, 'More than 2**15 channels is not supported' if max_channels > 2**15
|
12
|
+
super(connection, codec, scheduler)
|
12
13
|
@lock = Mutex.new
|
13
14
|
@channels = [nil] * max_channels
|
14
15
|
@queue = []
|
15
16
|
end
|
16
17
|
|
17
|
-
def send_message(request)
|
18
|
+
def send_message(request, timeout=nil)
|
18
19
|
promise = Ione::Promise.new
|
19
20
|
channel = @lock.synchronize do
|
20
21
|
take_channel(promise)
|
@@ -26,6 +27,14 @@ module Ione
|
|
26
27
|
@queue << [request, promise]
|
27
28
|
end
|
28
29
|
end
|
30
|
+
if timeout
|
31
|
+
@scheduler.schedule_timer(timeout).on_value do
|
32
|
+
unless promise.future.completed?
|
33
|
+
error = Rpc::TimeoutError.new('No response received within %ss' % timeout.to_s)
|
34
|
+
promise.fail(error)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
29
38
|
promise.future
|
30
39
|
end
|
31
40
|
|
@@ -37,7 +46,7 @@ module Ione
|
|
37
46
|
@channels[channel] = nil
|
38
47
|
promise
|
39
48
|
end
|
40
|
-
if promise
|
49
|
+
if promise && !promise.future.completed?
|
41
50
|
promise.fulfill(response)
|
42
51
|
end
|
43
52
|
flush_queue
|
data/lib/ione/rpc/codec.rb
CHANGED
@@ -28,7 +28,7 @@ module Ione
|
|
28
28
|
# @return [String] an encoded frame with the message and channel
|
29
29
|
def encode(message, channel)
|
30
30
|
data = encode_message(message)
|
31
|
-
[
|
31
|
+
[2, 0, channel, data.bytesize, data.to_s].pack(FRAME_V2_FORMAT)
|
32
32
|
end
|
33
33
|
|
34
34
|
# Decodes a frame, piece by piece if necessary.
|
@@ -106,7 +106,11 @@ module Ione
|
|
106
106
|
def read_header
|
107
107
|
n = @buffer.read_short
|
108
108
|
@version = n >> 8
|
109
|
-
@
|
109
|
+
if @version == 1
|
110
|
+
@channel = n & 0xff
|
111
|
+
else
|
112
|
+
@channel = @buffer.read_short
|
113
|
+
end
|
110
114
|
@length = @buffer.read_int
|
111
115
|
end
|
112
116
|
|
@@ -115,13 +119,18 @@ module Ione
|
|
115
119
|
end
|
116
120
|
|
117
121
|
def header_ready?
|
118
|
-
@length.nil? && @buffer.size >=
|
122
|
+
@length.nil? && @buffer.size >= 8
|
119
123
|
end
|
120
124
|
|
121
125
|
def body_ready?
|
122
126
|
@length && @buffer.size >= @length
|
123
127
|
end
|
124
128
|
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
FRAME_V1_FORMAT = 'ccNa*'.freeze
|
133
|
+
FRAME_V2_FORMAT = 'ccnNa*'.freeze
|
125
134
|
end
|
126
135
|
|
127
136
|
# A codec that works with encoders like JSON, MessagePack, YAML and others
|
data/lib/ione/rpc/peer.rb
CHANGED
@@ -6,13 +6,14 @@ module Ione
|
|
6
6
|
class Peer
|
7
7
|
attr_reader :host, :port
|
8
8
|
|
9
|
-
def initialize(connection, codec)
|
9
|
+
def initialize(connection, codec, scheduler=nil)
|
10
10
|
@connection = connection
|
11
11
|
@connection.on_data(&method(:handle_data))
|
12
12
|
@connection.on_closed(&method(:handle_closed))
|
13
13
|
@host = @connection.host
|
14
14
|
@port = @connection.port
|
15
15
|
@codec = codec
|
16
|
+
@scheduler = scheduler
|
16
17
|
@buffer = Ione::ByteBuffer.new
|
17
18
|
@closed_promise = Promise.new
|
18
19
|
@current_message = nil
|
data/lib/ione/rpc/version.rb
CHANGED
data/lib/ione/rpc.rb
CHANGED
@@ -8,7 +8,7 @@ module Ione
|
|
8
8
|
module Rpc
|
9
9
|
describe ClientPeer do
|
10
10
|
let! :peer do
|
11
|
-
RpcSpec::TestClientPeer.new(connection, codec, max_channels)
|
11
|
+
RpcSpec::TestClientPeer.new(connection, codec, scheduler, max_channels)
|
12
12
|
end
|
13
13
|
|
14
14
|
let :connection do
|
@@ -19,6 +19,10 @@ module Ione
|
|
19
19
|
double(:codec)
|
20
20
|
end
|
21
21
|
|
22
|
+
let :scheduler do
|
23
|
+
double(:scheduler)
|
24
|
+
end
|
25
|
+
|
22
26
|
let :max_channels do
|
23
27
|
16
|
24
28
|
end
|
@@ -39,6 +43,15 @@ module Ione
|
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
46
|
+
before do
|
47
|
+
timer_promises = []
|
48
|
+
scheduler.stub(:schedule_timer) do |timeout|
|
49
|
+
timer_promises << Promise.new
|
50
|
+
timer_promises.last.future
|
51
|
+
end
|
52
|
+
scheduler.stub(:timer_promises).and_return(timer_promises)
|
53
|
+
end
|
54
|
+
|
42
55
|
include_examples 'peers'
|
43
56
|
|
44
57
|
context 'when the connection closes' do
|
@@ -51,6 +64,12 @@ module Ione
|
|
51
64
|
end
|
52
65
|
end
|
53
66
|
|
67
|
+
describe '#initialize' do
|
68
|
+
it 'raises ArgumentError when the specified max channels is more than the protocol handles' do
|
69
|
+
expect { RpcSpec::TestClientPeer.new(connection, codec, scheduler, 2**24) }.to raise_error(ArgumentError)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
54
73
|
describe '#send_message' do
|
55
74
|
it 'encodes and sends a request frame' do
|
56
75
|
peer.send_message('hello')
|
@@ -85,6 +104,19 @@ module Ione
|
|
85
104
|
connection.data_listener.call('bar@000')
|
86
105
|
f.value.payload.should == 'bar'
|
87
106
|
end
|
107
|
+
|
108
|
+
it 'fails the request when the timeout passes before the response is received' do
|
109
|
+
f = peer.send_message('foo', 2)
|
110
|
+
scheduler.timer_promises.first.fulfill
|
111
|
+
expect { f.value }.to raise_error(Rpc::TimeoutError)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'does not fail the request when the response is received before the timeout passes' do
|
115
|
+
f = peer.send_message('foo', 2)
|
116
|
+
connection.data_listener.call('bar@000')
|
117
|
+
scheduler.timer_promises.first.fulfill
|
118
|
+
expect { f.value }.to_not raise_error
|
119
|
+
end
|
88
120
|
end
|
89
121
|
end
|
90
122
|
end
|
@@ -16,8 +16,14 @@ module Ione
|
|
16
16
|
|
17
17
|
let :io_reactor do
|
18
18
|
running = [false]
|
19
|
+
timer_promises = []
|
19
20
|
r = double(:io_reactor)
|
20
21
|
r.stub(:running?) { running[0] }
|
22
|
+
r.stub(:schedule_timer) do |timeout|
|
23
|
+
timer_promises << Promise.new
|
24
|
+
timer_promises.last.future
|
25
|
+
end
|
26
|
+
r.stub(:timer_promises).and_return(timer_promises)
|
21
27
|
r.stub(:start) do
|
22
28
|
running[0] = true
|
23
29
|
Future.resolved(r)
|
@@ -197,7 +203,7 @@ module Ione
|
|
197
203
|
before do
|
198
204
|
client.start.value
|
199
205
|
client.created_connections.each do |connection|
|
200
|
-
connection.stub(:send_message).with('PING').and_return(Future.resolved('PONG'))
|
206
|
+
connection.stub(:send_message).with('PING', nil).and_return(Future.resolved('PONG'))
|
201
207
|
end
|
202
208
|
end
|
203
209
|
|
@@ -225,6 +231,20 @@ module Ione
|
|
225
231
|
end
|
226
232
|
end
|
227
233
|
|
234
|
+
context 'with a timeout' do
|
235
|
+
it 'passes the timeout to the connection' do
|
236
|
+
client.start.value
|
237
|
+
timeouts = []
|
238
|
+
client.created_connections.each do |connection|
|
239
|
+
connection.stub(:send_message) do |rq, timeout|
|
240
|
+
timeouts << timeout
|
241
|
+
end
|
242
|
+
end
|
243
|
+
client.send_request('PING', nil, 2)
|
244
|
+
timeouts.should include(2)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
228
248
|
context 'when the client chooses the connection per request' do
|
229
249
|
it 'asks the client to choose a connection to send the request on' do
|
230
250
|
client.override_choose_connection do |connections, request|
|
@@ -415,7 +435,7 @@ module Ione
|
|
415
435
|
|
416
436
|
it 'logs when requests fail' do
|
417
437
|
client.start.value
|
418
|
-
client.created_connections.each { |connection| connection.stub(:send_message).with('PING').and_return(Future.failed(StandardError.new('BORK'))) }
|
438
|
+
client.created_connections.each { |connection| connection.stub(:send_message).with('PING', nil).and_return(Future.failed(StandardError.new('BORK'))) }
|
419
439
|
client.send_request('PING')
|
420
440
|
logger.should have_received(:warn).with(/request failed: BORK/i)
|
421
441
|
end
|
@@ -605,9 +625,9 @@ module ClientSpec
|
|
605
625
|
@raw_connection.port
|
606
626
|
end
|
607
627
|
|
608
|
-
def send_message(request)
|
628
|
+
def send_message(request, timeout=nil)
|
609
629
|
@requests << request
|
610
|
-
@peer_connection.send_message(request)
|
630
|
+
@peer_connection.send_message(request, timeout)
|
611
631
|
Ione::Future.resolved
|
612
632
|
end
|
613
633
|
end
|
data/spec/ione/rpc/codec_spec.rb
CHANGED
@@ -20,19 +20,19 @@ module Ione
|
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'encodes the version in the first byte' do
|
23
|
-
encoded_message[0, 1].unpack('c').should == [
|
23
|
+
encoded_message[0, 1].unpack('c').should == [2]
|
24
24
|
end
|
25
25
|
|
26
|
-
it 'encodes the channel in the
|
27
|
-
encoded_message[
|
26
|
+
it 'encodes the channel in the third and fourth byte' do
|
27
|
+
encoded_message[2, 2].unpack('n').should == [42]
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'encodes the length of the frame in the following four bytes' do
|
31
|
-
encoded_message[
|
31
|
+
encoded_message[4, 4].unpack('N').should == [22]
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'encodes the object as JSON' do
|
35
|
-
encoded_message[
|
35
|
+
encoded_message[8..-1].should == '{"foo":"bar","baz":42}'
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -99,21 +99,34 @@ module Ione
|
|
99
99
|
42
|
100
100
|
end
|
101
101
|
|
102
|
-
let :
|
102
|
+
let :v1_frame do
|
103
103
|
%(\x01\x2a\x00\x00\x00\x16{"foo":"bar","baz":42})
|
104
104
|
end
|
105
105
|
|
106
|
-
|
107
|
-
|
108
|
-
msg, ch, _ = codec.decode(Ione::ByteBuffer.new(frm), nil)
|
109
|
-
msg.should == message
|
110
|
-
ch.should == channel
|
106
|
+
let :v2_frame do
|
107
|
+
%(\x02\x00\x00\x2a\x00\x00\x00\x16{"foo":"bar","baz":42})
|
111
108
|
end
|
112
109
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
110
|
+
context 'with a v1 frame' do
|
111
|
+
it 'decodes a frame' do
|
112
|
+
msg, ch, _ = codec.decode(Ione::ByteBuffer.new(v1_frame), nil)
|
113
|
+
msg.should == message
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'with a v2 frame' do
|
118
|
+
it 'decoding a encoded frame returns the original message' do
|
119
|
+
frm = codec.encode(message, channel)
|
120
|
+
msg, ch, _ = codec.decode(Ione::ByteBuffer.new(frm), nil)
|
121
|
+
msg.should == message
|
122
|
+
ch.should == channel
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'encoding a message gives the original frame' do
|
126
|
+
msg, ch, _ = codec.decode(Ione::ByteBuffer.new(v2_frame), nil)
|
127
|
+
frm = codec.encode(msg, ch)
|
128
|
+
frm.should == v2_frame
|
129
|
+
end
|
117
130
|
end
|
118
131
|
end
|
119
132
|
end
|
@@ -125,13 +138,13 @@ module Ione
|
|
125
138
|
|
126
139
|
describe '#encode' do
|
127
140
|
it 'calls #dump on the delegate' do
|
128
|
-
codec.encode({'foo' => 'bar'}, 0)[
|
141
|
+
codec.encode({'foo' => 'bar'}, 0)[8..-1].should == '{"foo":"bar"}'
|
129
142
|
end
|
130
143
|
end
|
131
144
|
|
132
145
|
describe '#decode' do
|
133
146
|
it 'calls #load on the delegate' do
|
134
|
-
buffer = Ione::ByteBuffer.new(%(\
|
147
|
+
buffer = Ione::ByteBuffer.new(%(\x02\x00\x00\x00\x00\x00\x00\x0d{"foo":"bar"}))
|
135
148
|
message, _, _ = codec.decode(buffer, nil)
|
136
149
|
message.should eql('foo' => 'bar')
|
137
150
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ione-rpc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.pre1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Theo Hultberg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ione
|