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