ducts-client 0.2.0 → 0.4.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4c3b4b409de5782a0a17ff6be86f8a2a567508f7c6ac3c0baf6f4c0808301c2
4
- data.tar.gz: 628e413f6fe4399fb51c288850a36503b62e9ce810114bbf93d6cea8b9c479f9
3
+ metadata.gz: 74c85c4fbbb4e225d647251a22a526545a3f4921b88921fb6fb779705b1fe4f1
4
+ data.tar.gz: f5396e8363fb5c31159be810b0631d45bfaca068143f136beb8a52e82f54ba93
5
5
  SHA512:
6
- metadata.gz: caa94df4e3324917d66e1186dd158c8acf964eb5325f485e158ed5b1eee597d57ff5faef95dde812fe7a1d40a758a077f2cd8852c2f784657c9e9562861eced3
7
- data.tar.gz: 06111104f2f3a82de9586f57551de285f810a06b54034642638822087b3044edfc3db69cb5d89be420a0272a95da5c8aed53939f571f5febdb8d26b18f79e001
6
+ metadata.gz: 2627825a0174c77e7070ae62a57bd6a74563fc35fb2882bc53fcee11434cb3341145c4735fc2deb7c2fc88bb5f624a776bff7e3110d0ca273b3fc93784882fb3
7
+ data.tar.gz: 040e53690914465287f0f59e275f9711454c46994cef7aaa68c9687c9e2d87e6d53bb31500aff28fa73aee1315581738dab025bc80ac06519838b24bdd72217e
data/lib/ducts/client.rb CHANGED
@@ -6,6 +6,7 @@ require 'timeout'
6
6
  require 'uri'
7
7
  require 'net/http'
8
8
  require 'json'
9
+ require 'stringio'
9
10
  require 'faye/websocket'
10
11
  require 'faye/websocket/api/event'
11
12
  require 'msgpack'
@@ -56,6 +57,8 @@ module Ducts
56
57
  end
57
58
  end
58
59
 
60
+ DuctEventHandlerReturnValue = Struct.new(:source_rid, :source_eid, :source_data)
61
+
59
62
  class ConnectionEventListener < DuctEventListener
60
63
  attr_writer :onopen, :onclose, :onerror, :onmessage
61
64
  def onopen(event)
@@ -118,7 +121,10 @@ module Ducts
118
121
 
119
122
  @_last_rid = nil
120
123
  @_ws = nil
121
- @_waiting_completion = Hash.new
124
+ @_waiting_message_completion = Hash.new
125
+ @_waiting_closed_completion = Array.new
126
+ @_loop_queues = Hash.new
127
+ @_divided_buffers = Hash.new
122
128
  end
123
129
 
124
130
  def next_rid
@@ -130,29 +136,13 @@ module Ducts
130
136
  next_id
131
137
  end
132
138
 
133
- def open(wsd_url, uuid = nil, **params)
134
- _open(wsd_url, uuid, **params)
135
- end
136
-
137
- def reconnect
138
- _reconnect
139
- end
140
-
141
- def event_send(rid, eid, data)
142
- _send(rid, eid, data)
143
- end
144
-
145
- def event_call(eid, data)
146
- _call(eid, data)
147
- end
148
-
149
- def close
150
- _close
151
- end
152
-
153
139
  def set_event_handler(event_id, &handler)
154
140
  @_event_handler ||= {}
155
- @_event_handler[event_id] = handler
141
+ if handler
142
+ @_event_handler.store(event_id, handler)
143
+ else
144
+ @_event_handler.delete(event_id)
145
+ end
156
146
  end
157
147
 
158
148
  def state
@@ -163,16 +153,16 @@ module Ducts
163
153
  end
164
154
  end
165
155
 
166
- private
167
- def _reconnect(wsd)
168
- if wsd
169
- @WSD = wsd
170
- @EVENT = @WSD['EVENT']
156
+ def reconnect
157
+ completion = EM::Completion.new
158
+ if @_ws
159
+ completion.succeed(self)
160
+ return completion;
171
161
  end
172
- return if @_ws
173
162
  Faye::WebSocket::Client.new(@WSD['websocket_url_reconnect']).tap do |ws|
174
163
  ws.on(:open) do |event|
175
164
  @_ws = ws
165
+ completion.succeed(self)
176
166
  connection_event = DuctConnectionEvent.new('onopen', event)
177
167
  _onreconnect(connection_event)
178
168
  @connection_listener.onopen(connection_event)
@@ -192,10 +182,15 @@ module Ducts
192
182
  @connection_listener.onerror(connection_event)
193
183
  end
194
184
  end
185
+ completion
195
186
  end
196
187
 
197
- def _open(wsd_url, uuid, **params)
198
- return if @_ws
188
+ def open(wsd_url, uuid = nil, **params)
189
+ completion = EM::Completion.new
190
+ if @_ws
191
+ completion.succeed(self)
192
+ return completion;
193
+ end
199
194
  begin
200
195
  query = '?uuid=' + (uuid || 'dummy') + params.map{ |k, v| "&#{k}=#{v}" }.join
201
196
  uri = URI.parse(wsd_url + query)
@@ -210,6 +205,7 @@ module Ducts
210
205
  Faye::WebSocket::Client.new(@WSD['websocket_url']).tap do |ws|
211
206
  ws.on(:open) do |event|
212
207
  @_ws = ws
208
+ completion.succeed(self)
213
209
  connection_event = DuctConnectionEvent.new('onopen', event)
214
210
  _onopen(connection_event)
215
211
  @connection_listener.onopen(connection_event)
@@ -225,6 +221,19 @@ module Ducts
225
221
  @connection_listener.onerror(connection_event)
226
222
  end
227
223
  ws.on(:close) do |event|
224
+ @_ws = nil
225
+ case event.code
226
+ when 1000
227
+ @_waiting_closed_completion.each do |waiting_closed_completion|
228
+ waiting_closed_completion.succeed(event)
229
+ end
230
+ else
231
+ p event.code
232
+ @_waiting_closed_completion.each do |waiting_closed_completion|
233
+ waiting_closed_completion.fail(event)
234
+ end
235
+ end
236
+ @_waiting_closed_completion.clear
228
237
  connection_event = DuctConnectionEvent.new('onclose', event)
229
238
  @connection_listener.onclose(connection_event)
230
239
  end
@@ -232,62 +241,70 @@ module Ducts
232
241
  rescue => error
233
242
  @connection_listener.onerror(DuctConnectionEvent.new('onerror', error))
234
243
  end
244
+ completion
235
245
  end
236
246
 
247
+ def event_send(rid, eid, data)
248
+ msgpack = [ rid, eid, data ].to_msgpack
249
+ @_ws.send(msgpack.chars.map(&:ord))
250
+ rid
251
+ end
252
+
253
+ def event_call(eid, data)
254
+ rid = next_rid
255
+ event_send(rid, eid, data)
256
+ warn Message.construct(Message::Level::WARNING, 'request ID is duplicated.') if @_waiting_message_completion.keys.include? rid
257
+ EM::Completion.new.tap { |completion| @_waiting_message_completion[rid] = completion }
258
+ end
259
+
260
+ def close
261
+ completion = EM::Completion.new
262
+ if @_ws
263
+ @_ws.close
264
+ @_waiting_closed_completion.push(completion)
265
+ else
266
+ completion.succeed(nil)
267
+ end
268
+ completion
269
+ end
270
+
271
+ private
237
272
  def _onopen(event)
238
273
  @_send_timestamp = Time.now.to_f
239
274
  @_time_offset = 0
240
275
  @_time_latency = 0
241
276
  @_time_count = 0
242
- set_event_handler(@EVENT['ALIVE_MONITORING']) do |rid, eid, data|
243
- client_received = Time.now.to_f
244
- server_sent, server_received = data
245
- client_sent = @_send_timestamp
246
- new_offset = ((server_received - client_sent) - (client_received - server_sent)) / 2
247
- new_latency = ((client_received - client_sent) - (server_sent - server_received)) / 2
248
- @_time_offset = (@_time_offset * @_time_count + new_offset) / (@_time_count + 1)
249
- @_time_latency = (@_time_latency * @_time_count + new_latency) / (@_time_count + 1)
250
- @_time_count += 1
251
- end
277
+ set_event_handler(@EVENT['ALIVE_MONITORING'], &method(:_alive_monitoring_handler))
278
+ set_event_handler(@EVENT['LOOP_RESPONSE_START'], &method(:_loop_response_handler))
279
+ set_event_handler(@EVENT['LOOP_RESPONSE_NEXT'], &method(:_loop_response_handler))
280
+ set_event_handler(@EVENT['LOOP_RESPONSE_END'], &method(:_loop_response_end_handler))
281
+ set_event_handler(@EVENT['DIVIDED_RESPONSE_APPEND'], &method(:_divided_response_append_handler))
282
+ set_event_handler(@EVENT['DIVIDED_RESPONSE_END'], &method(:_divided_response_end_handler))
252
283
  rid = next_rid
253
284
  eid = @EVENT['ALIVE_MONITORING']
254
285
  value = @_send_timestamp
255
- _send(rid, eid, value)
286
+ event_send(rid, eid, value)
256
287
  end
257
288
 
258
289
  def _onreconnect(connection_event)
259
290
  end
260
291
 
261
- def _send(rid, eid, data)
262
- msgpack = [ rid, eid, data ].to_msgpack
263
- @_ws.send(msgpack.chars.map(&:ord))
264
- rid
265
- end
266
-
267
- def _call(eid, data)
268
- rid = next_rid
269
- _send(rid, eid, data)
270
- raise if @_waiting_completion.keys.include? rid
271
- EM::Completion.new.tap { |completion| @_waiting_completion[rid] = completion }
272
- end
273
-
274
- def _close
275
- begin
276
- @_ws.close if @_ws
277
- ensure
278
- @_ws = nil
279
- end
280
- end
281
-
282
292
  def _onmessage(connection_event)
283
293
  begin
284
294
  rid, eid, data = %i(rid eid data).map{ |name| connection_event.source.public_send(name) }
285
295
  begin
286
296
  @catchall_event_handler.call(rid, eid, data)
287
297
  handle = @_event_handler[eid]
288
- handle = @uncaught_event_handler unless handle
289
- handle.call(rid, eid, data)
290
- completion = @_waiting_completion.delete(rid)
298
+ handle ||= @uncaught_event_handler
299
+ handle.call(rid, eid, data).tap do |ret|
300
+ if ret.instance_of? DuctEventHandlerReturnValue
301
+ rid = ret.source_rid
302
+ eid = ret.source_eid
303
+ data = ret.source_data
304
+ end
305
+ end
306
+ return if rid.nil?
307
+ completion = @_waiting_message_completion.delete(rid)
291
308
  if completion
292
309
  if eid > 0
293
310
  completion.succeed(data)
@@ -302,5 +319,63 @@ module Ducts
302
319
  @event_error_handler.call(-1, -1, nil, error)
303
320
  end
304
321
  end
322
+
323
+ def _alive_monitoring_handler(rid, eid, data)
324
+ client_received = Time.now.to_f
325
+ server_sent, server_received = data
326
+ client_sent = @_send_timestamp
327
+ new_offset = ((server_received - client_sent) - (client_received - server_sent)) / 2
328
+ new_latency = ((client_received - client_sent) - (server_sent - server_received)) / 2
329
+ @_time_offset = (@_time_offset * @_time_count + new_offset) / (@_time_count + 1)
330
+ @_time_latency = (@_time_latency * @_time_count + new_latency) / (@_time_count + 1)
331
+ @_time_count += 1
332
+ end
333
+
334
+ def _loop_response_handler(rid, eid, data)
335
+ warn Message.construct(Message::Level::WARNING, 'loop response has error.') if eid.negative?
336
+ source_eid = data[1]
337
+ source_data = data[2]
338
+ @catchall_event_handler.call(rid, source_eid, source_data)
339
+ handle = @_event_handler[source_eid.abs]
340
+ handle ||= @uncaught_event_handler
341
+ ret = handle.call(rid, source_eid, source_data)
342
+ handled_source_data = (ret.instance_of? DuctEventHandlerReturnValue)? ret.source_data : source_data
343
+ @_loop_queues[rid] ||= EM::Queue.new
344
+ queue = @_loop_queues[rid]
345
+ queue.push(handled_source_data) if handled_source_data
346
+ DuctEventHandlerReturnValue.new(rid, source_eid, queue)
347
+ end
348
+
349
+ def _loop_response_end_handler(rid, eid, data)
350
+ warn Message.construct(Message::Level::WARNING, 'loop response end has error.') if eid.negative?
351
+ source_eid = data[1]
352
+ source_data = data[2]
353
+ warn Message.construct(Message::Level::WARNING, "loop response end has data: #{source_data}.") if source_data
354
+ @catchall_event_handler.call(rid, source_eid, source_data)
355
+ handle = @_event_handler[source_eid.abs]
356
+ handle ||= @uncaught_event_handler
357
+ handle.call(rid, source_eid, source_data)
358
+ queue = @_loop_queues.delete(rid)
359
+ queue.push(nil)
360
+ DuctEventHandlerReturnValue.new(rid, source_eid, source_eid)
361
+ end
362
+
363
+ def _divided_response_append_handler(rid, eid, data)
364
+ @_divided_buffers[rid] ||= StringIO.new('', 'wb').set_encoding(Encoding::ASCII_8BIT)
365
+ divided_buffer = @_divided_buffers[rid]
366
+ divided_buffer.write(data)
367
+ DuctEventHandlerReturnValue.new(nil, eid, nil)
368
+ end
369
+
370
+ def _divided_response_end_handler(rid, eid, data)
371
+ divided_buffer = @_divided_buffers.delete(rid)
372
+ divided_buffer.write(data)
373
+ source_rid, source_eid, source_data = MessagePack.unpack(divided_buffer.string)
374
+ @catchall_event_handler.call(source_rid, source_eid, source_data)
375
+ handle = @_event_handler[source_eid]
376
+ handle ||= @uncaught_event_handler
377
+ handle.call(source_rid, source_eid, source_data)
378
+ DuctEventHandlerReturnValue.new(source_rid, source_eid, source_data)
379
+ end
305
380
  end
306
381
  end
@@ -4,8 +4,8 @@ module Ducts
4
4
  class Client
5
5
  module Version
6
6
  MAJOR = 0
7
- MINOR = 2
8
- PATCH = 0
7
+ MINOR = 4
8
+ PATCH = 5
9
9
  end
10
10
 
11
11
  VERSION = [ Version::MAJOR, Version::MINOR, Version::PATCH ].map(&:to_s).join('.')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ducts-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenshiro Ueda
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-05-21 00:00:00.000000000 Z
11
+ date: 2021-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faye-websocket