iproto 0.3.3 → 0.3.6
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.
- data/iproto.gemspec +2 -2
- data/lib/iproto/connection_api.rb +3 -0
- data/lib/iproto/core-ext.rb +1 -0
- data/lib/iproto/em.rb +74 -41
- data/lib/iproto/tcp_socket.rb +52 -18
- data/lib/iproto.rb +6 -5
- metadata +2 -2
data/iproto.gemspec
CHANGED
data/lib/iproto/core-ext.rb
CHANGED
data/lib/iproto/em.rb
CHANGED
@@ -3,7 +3,7 @@ require 'fiber'
|
|
3
3
|
require 'iproto/core-ext'
|
4
4
|
|
5
5
|
module IProto
|
6
|
-
|
6
|
+
class EMConnection < ::EM::Connection
|
7
7
|
module FixedLengthProtocol
|
8
8
|
def post_init
|
9
9
|
raise "you should set @_needed_size" unless @_needed_size
|
@@ -28,36 +28,50 @@ module IProto
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
class Connection < ::EM::Connection
|
32
31
|
include IProto::ConnectionAPI
|
33
32
|
include FixedLengthProtocol
|
34
|
-
|
33
|
+
|
34
|
+
attr_reader :host, :port
|
35
35
|
|
36
36
|
def initialize(host, port, reconnect = true)
|
37
37
|
@host = host
|
38
38
|
@port = port
|
39
|
+
@reconnect_timeout = Numeric === reconnect ? reconnect : DEFAULT_RECONNECT
|
39
40
|
@should_reconnect = !!reconnect
|
40
41
|
@reconnect_timer = nil
|
41
|
-
@connected =
|
42
|
+
@connected = :init_waiting
|
42
43
|
@waiting_requests = {}
|
43
44
|
@waiting_for_connect = []
|
44
45
|
init_protocol
|
46
|
+
@shutdown_hook = false
|
47
|
+
shutdown_hook
|
48
|
+
end
|
49
|
+
|
50
|
+
def connected?
|
51
|
+
@connected == true
|
52
|
+
end
|
53
|
+
|
54
|
+
def could_be_connected?
|
55
|
+
@connected && (@connected != :force || ::EM.reactor_running?)
|
45
56
|
end
|
46
57
|
|
47
58
|
def shutdown_hook
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
59
|
+
unless @shutdown_hook
|
60
|
+
::EM.add_shutdown_hook {
|
61
|
+
@connected = @should_reconnect ? :force : false
|
62
|
+
if Integer === @reconnect_timer
|
63
|
+
::EM.cancel_timer @reconnect_timer
|
64
|
+
end
|
65
|
+
@reconnect_timer = nil
|
66
|
+
@shutdown_hook = false
|
67
|
+
}
|
68
|
+
@shutdown_hook = true
|
69
|
+
end
|
55
70
|
end
|
56
71
|
|
57
72
|
def connection_completed
|
58
73
|
@reconnect_timer = nil
|
59
74
|
@connected = true
|
60
|
-
shutdown_hook
|
61
75
|
_perform_waiting_for_connect(true)
|
62
76
|
end
|
63
77
|
|
@@ -70,15 +84,19 @@ module IProto
|
|
70
84
|
def receive_chunk(chunk)
|
71
85
|
if @_state == :receive_header
|
72
86
|
@type, body_size, @request_id = chunk.unpack(PACK)
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
do_response(request, chunk)
|
87
|
+
if body_size > 0
|
88
|
+
@_needed_size = body_size
|
89
|
+
@_state = :receive_body
|
90
|
+
return
|
91
|
+
else
|
92
|
+
chunk = ''
|
93
|
+
end
|
81
94
|
end
|
95
|
+
request = @waiting_requests.delete @request_id
|
96
|
+
raise IProto::UnexpectedResponse.new("For request id #{@request_id}") unless request
|
97
|
+
@_needed_size = HEADER_SIZE
|
98
|
+
@_state = :receive_header
|
99
|
+
do_response(request, chunk)
|
82
100
|
end
|
83
101
|
|
84
102
|
def do_response(request, data)
|
@@ -86,9 +104,11 @@ module IProto
|
|
86
104
|
end
|
87
105
|
|
88
106
|
def _setup_reconnect_timer(timeout)
|
89
|
-
if @reconnect_timer.nil?
|
107
|
+
if @reconnect_timer.nil?
|
90
108
|
@reconnect_timer = :waiting
|
91
|
-
|
109
|
+
shutdown_hook
|
110
|
+
if timeout == 0
|
111
|
+
@connected = :waiting
|
92
112
|
reconnect @host, @port
|
93
113
|
else
|
94
114
|
@reconnect_timer = ::EM.add_timer(timeout) do
|
@@ -99,22 +119,28 @@ module IProto
|
|
99
119
|
end
|
100
120
|
|
101
121
|
def _send_request(request_type, body, request)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
122
|
+
if @connected == true
|
123
|
+
_do_send_request(request_type, body, request)
|
124
|
+
elsif could_be_connected?
|
125
|
+
@waiting_for_connect << [request_type, body, request]
|
126
|
+
if @connected == :force
|
107
127
|
_setup_reconnect_timer(0)
|
108
128
|
end
|
129
|
+
elsif ::EM.reactor_running?
|
130
|
+
EM.next_tick{
|
131
|
+
do_response(request, IProto::Disconnected.new("connection is closed"))
|
132
|
+
}
|
109
133
|
else
|
110
|
-
|
134
|
+
do_response(request, IProto::Disconnected.new("connection is closed"))
|
111
135
|
end
|
112
136
|
end
|
113
137
|
|
114
138
|
def _perform_waiting_for_connect(real)
|
115
139
|
if real
|
116
140
|
@waiting_for_connect.each do |request_type, body, request|
|
141
|
+
::EM.next_tick{
|
117
142
|
_do_send_request(request_type, body, request)
|
143
|
+
}
|
118
144
|
end
|
119
145
|
else
|
120
146
|
i = -1
|
@@ -138,13 +164,15 @@ module IProto
|
|
138
164
|
|
139
165
|
def close_connection(*args)
|
140
166
|
@should_reconnect = nil
|
141
|
-
if @reconnect_timer
|
142
|
-
::EM.cancel_timer @reconnect_timer
|
143
|
-
@reconnect_timer = nil
|
167
|
+
if Integer === @reconnect_timer
|
168
|
+
::EM.cancel_timer @reconnect_timer
|
144
169
|
end
|
145
|
-
|
170
|
+
@reconnect_timer = nil
|
171
|
+
|
172
|
+
if @connected == true
|
146
173
|
super(*args)
|
147
174
|
end
|
175
|
+
@connected = false
|
148
176
|
discard_requests
|
149
177
|
end
|
150
178
|
|
@@ -158,16 +186,23 @@ module IProto
|
|
158
186
|
end
|
159
187
|
|
160
188
|
def unbind
|
189
|
+
prev_connected = @connected
|
190
|
+
@connected = false
|
161
191
|
discard_requests
|
192
|
+
@connected = prev_connected
|
193
|
+
|
162
194
|
case @should_reconnect
|
163
195
|
when true
|
164
|
-
@
|
165
|
-
|
196
|
+
@reconnect_timer = nil
|
197
|
+
unless @connected == :force
|
198
|
+
@connected = false
|
199
|
+
_setup_reconnect_timer(@reconnect_timeout)
|
200
|
+
end
|
166
201
|
when false
|
167
|
-
if @connected
|
168
|
-
raise IProto::Disconnected
|
169
|
-
else
|
202
|
+
if @connected == :init_waiting
|
170
203
|
raise IProto::CouldNotConnect
|
204
|
+
else
|
205
|
+
raise IProto::Disconnected
|
171
206
|
end
|
172
207
|
when nil
|
173
208
|
# do nothing cause we explicitely disconnected
|
@@ -175,7 +210,7 @@ module IProto
|
|
175
210
|
end
|
176
211
|
end
|
177
212
|
|
178
|
-
class
|
213
|
+
class EMFiberedConnection < EMConnection
|
179
214
|
def send_request(request_type, body)
|
180
215
|
_send_request(request_type, body, Fiber.current)
|
181
216
|
result = Fiber.yield
|
@@ -184,11 +219,9 @@ module IProto
|
|
184
219
|
end
|
185
220
|
end
|
186
221
|
|
187
|
-
class
|
222
|
+
class EMCallbackConnection < EMConnection
|
188
223
|
def send_request(request_type, body, cb = nil, &block)
|
189
224
|
_send_request(request_type, body, cb || block)
|
190
225
|
end
|
191
226
|
end
|
192
|
-
|
193
|
-
end
|
194
227
|
end
|
data/lib/iproto/tcp_socket.rb
CHANGED
@@ -4,41 +4,63 @@ module IProto
|
|
4
4
|
class TCPSocket
|
5
5
|
include IProto::ConnectionAPI
|
6
6
|
def initialize(host, port, reconnect = true)
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@reconnect =
|
7
|
+
@addr = [host, port]
|
8
|
+
@reconnect_timeout = Numeric === reconnect ? reconnect : DEFAULT_RECONNECT
|
9
|
+
@reconnect = !!reconnect
|
10
|
+
@socket = nil
|
11
|
+
@reconnect_time = Time.now - 1
|
12
|
+
@retry = true
|
10
13
|
end
|
11
14
|
|
12
15
|
def close
|
13
16
|
@reconnect = false
|
14
17
|
if @socket
|
15
18
|
@socket.close rescue nil
|
19
|
+
@socket = :disconnected
|
16
20
|
end
|
17
21
|
end
|
18
22
|
|
23
|
+
def connected?
|
24
|
+
@socket && @socket != :disconnected
|
25
|
+
end
|
26
|
+
|
27
|
+
def could_be_connected?
|
28
|
+
@socket ? @socket != :disconnected
|
29
|
+
: (@retry || @reconnect_time < Time.now)
|
30
|
+
end
|
31
|
+
|
19
32
|
def socket
|
20
|
-
|
33
|
+
if (sock = @socket)
|
34
|
+
sock != :disconnected ? sock : raise(Disconnected, "disconnected earlier")
|
35
|
+
else
|
36
|
+
sock = @socket = ::TCPSocket.new(*@addr)
|
37
|
+
@retry = true
|
38
|
+
end
|
39
|
+
sock
|
21
40
|
rescue Errno::ECONNREFUSED => e
|
41
|
+
@socket = :disconnected unless @reconnect
|
22
42
|
raise CouldNotConnect, e
|
23
43
|
end
|
24
44
|
|
45
|
+
class Retry < RuntimeError; end
|
46
|
+
|
25
47
|
# begin ConnectionAPI
|
26
48
|
def send_request(request_type, body)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
49
|
+
begin
|
50
|
+
request_id = next_request_id
|
51
|
+
r = socket.send ([request_type, body.bytesize, request_id].pack(PACK) << body), 0
|
52
|
+
response_size = recv_header request_id
|
53
|
+
recv_response response_size
|
54
|
+
rescue Errno::EPIPE, Retry => e
|
55
|
+
_raise_disconnected(e, !@retry)
|
56
|
+
@retry = false
|
57
|
+
retry
|
58
|
+
end
|
34
59
|
end
|
35
60
|
# end ConnectionAPI
|
36
61
|
|
37
62
|
def recv_header(request_id)
|
38
|
-
header = socket.read(
|
39
|
-
@socket = nil if @reconnect
|
40
|
-
raise Disconnected, 'disconnected while read'
|
41
|
-
end
|
63
|
+
header = socket.read(HEADER_SIZE) or _raise_disconnected('disconnected while read', @retry ? :retry : true)
|
42
64
|
type, response_size, recv_request_id = header.unpack(PACK)
|
43
65
|
unless request_id == recv_request_id
|
44
66
|
raise UnexpectedResponse.new("Waiting response for request_id #{request_id}, but received for #{recv_request_id}")
|
@@ -47,9 +69,21 @@ module IProto
|
|
47
69
|
end
|
48
70
|
|
49
71
|
def recv_response(response_size)
|
50
|
-
socket.read(response_size) or
|
51
|
-
|
52
|
-
|
72
|
+
socket.read(response_size) or _raise_disconnected('disconnected while read', 2)
|
73
|
+
end
|
74
|
+
|
75
|
+
def _raise_disconnected(message, _raise = true)
|
76
|
+
if @reconnect
|
77
|
+
@socket = nil
|
78
|
+
@reconnect_time = Time.now + @reconnect_timeout
|
79
|
+
else
|
80
|
+
@socket = :disconnected
|
81
|
+
end
|
82
|
+
case _raise
|
83
|
+
when true
|
84
|
+
raise Disconnected, message
|
85
|
+
when :retry
|
86
|
+
raise Retry
|
53
87
|
end
|
54
88
|
end
|
55
89
|
end
|
data/lib/iproto.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module IProto
|
2
|
-
VERSION = '0.3.
|
2
|
+
VERSION = '0.3.6'
|
3
3
|
class IProtoError < StandardError; end
|
4
|
-
class
|
4
|
+
class ConnectionError < IProtoError; end
|
5
|
+
class CouldNotConnect < ConnectionError; end
|
6
|
+
class Disconnected < ConnectionError; end
|
5
7
|
class UnexpectedResponse < IProtoError; end
|
6
|
-
class Disconnected < IProtoError; end
|
7
8
|
|
8
9
|
require 'iproto/connection_api'
|
9
10
|
|
@@ -14,10 +15,10 @@ module IProto
|
|
14
15
|
case type
|
15
16
|
when :em
|
16
17
|
require 'iproto/em'
|
17
|
-
::EM.connect host, port, IProto::
|
18
|
+
::EM.connect host, port, IProto::EMFiberedConnection, host, port, reconnect
|
18
19
|
when :em_callback
|
19
20
|
require 'iproto/em'
|
20
|
-
::EM.connect host, port, IProto::
|
21
|
+
::EM.connect host, port, IProto::EMCallbackConnection, host, port, reconnect
|
21
22
|
when :block
|
22
23
|
require 'iproto/tcp_socket'
|
23
24
|
IProto::TCPSocket.new(host, port, reconnect)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iproto
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-18 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Mail.Ru simple network protocol
|
15
15
|
email: ceo@prepor.ru
|