ione-rpc 1.0.0.pre0 → 1.0.0.pre1
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/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
|