wamp_client 0.0.1
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 +7 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +464 -0
- data/Rakefile +3 -0
- data/circle.yml +3 -0
- data/lib/wamp_client/auth.rb +18 -0
- data/lib/wamp_client/check.rb +57 -0
- data/lib/wamp_client/connection.rb +194 -0
- data/lib/wamp_client/message.rb +1283 -0
- data/lib/wamp_client/serializer.rb +40 -0
- data/lib/wamp_client/session.rb +776 -0
- data/lib/wamp_client/transport.rb +129 -0
- data/lib/wamp_client/version.rb +3 -0
- data/lib/wamp_client.rb +7 -0
- data/scripts/gen_message.rb +537 -0
- data/spec/auth_spec.rb +18 -0
- data/spec/check_spec.rb +197 -0
- data/spec/message_spec.rb +1478 -0
- data/spec/session_spec.rb +1004 -0
- data/spec/spec_helper.rb +43 -0
- data/tasks/rspec.rake +3 -0
- data/wamp_client.gemspec +29 -0
- metadata +170 -0
@@ -0,0 +1,776 @@
|
|
1
|
+
require 'wamp_client/transport'
|
2
|
+
require 'wamp_client/message'
|
3
|
+
require 'wamp_client/check'
|
4
|
+
require 'wamp_client/version'
|
5
|
+
|
6
|
+
module WampClient
|
7
|
+
|
8
|
+
WAMP_FEATURES = {
|
9
|
+
caller: {
|
10
|
+
features: {
|
11
|
+
caller_identification: true,
|
12
|
+
##call_timeout: true,
|
13
|
+
##call_canceling: true,
|
14
|
+
progressive_call_results: true
|
15
|
+
}
|
16
|
+
},
|
17
|
+
callee: {
|
18
|
+
features: {
|
19
|
+
caller_identification: true,
|
20
|
+
##call_trustlevels: true,
|
21
|
+
pattern_based_registration: true,
|
22
|
+
shared_registration: true,
|
23
|
+
##call_timeout: true,
|
24
|
+
##call_canceling: true,
|
25
|
+
# progressive_call_results: true,
|
26
|
+
registration_revocation: true
|
27
|
+
}
|
28
|
+
},
|
29
|
+
publisher: {
|
30
|
+
features: {
|
31
|
+
publisher_identification: true,
|
32
|
+
subscriber_blackwhite_listing: true,
|
33
|
+
publisher_exclusion: true
|
34
|
+
}
|
35
|
+
},
|
36
|
+
subscriber: {
|
37
|
+
features: {
|
38
|
+
publisher_identification: true,
|
39
|
+
##publication_trustlevels: true,
|
40
|
+
pattern_based_subscription: true,
|
41
|
+
subscription_revocation: true
|
42
|
+
##event_history: true,
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
class CallResult
|
48
|
+
attr_accessor :args, :kwargs
|
49
|
+
|
50
|
+
def initialize(args=nil, kwargs=nil)
|
51
|
+
self.args = args
|
52
|
+
self.kwargs = kwargs
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Subscription
|
57
|
+
attr_accessor :topic, :handler, :options, :session, :id
|
58
|
+
|
59
|
+
def initialize(topic, handler, options, session, id)
|
60
|
+
self.topic = topic
|
61
|
+
self.handler = handler
|
62
|
+
self.options = options
|
63
|
+
self.session = session
|
64
|
+
self.id = id
|
65
|
+
end
|
66
|
+
|
67
|
+
def unsubscribe
|
68
|
+
self.session.unsubscribe(self)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
class Registration
|
74
|
+
attr_accessor :procedure, :handler, :options, :session, :id
|
75
|
+
|
76
|
+
def initialize(procedure, handler, options, session, id)
|
77
|
+
self.procedure = procedure
|
78
|
+
self.handler = handler
|
79
|
+
self.options = options
|
80
|
+
self.session = session
|
81
|
+
self.id = id
|
82
|
+
end
|
83
|
+
|
84
|
+
def unregister
|
85
|
+
self.session.unregister(self)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
class Session
|
92
|
+
include WampClient::Check
|
93
|
+
|
94
|
+
# on_join callback is called when the session joins the router. It has the following parameters
|
95
|
+
# @param details [Hash] Object containing information about the joined session
|
96
|
+
@on_join
|
97
|
+
def on_join(&on_join)
|
98
|
+
@on_join = on_join
|
99
|
+
end
|
100
|
+
|
101
|
+
# on_leave callback is called when the session leaves the router. It has the following attributes
|
102
|
+
# @param reason [String] The reason the session left the router
|
103
|
+
# @param details [Hash] Object containing information about the left session
|
104
|
+
@on_leave
|
105
|
+
def on_leave(&on_leave)
|
106
|
+
@on_leave = on_leave
|
107
|
+
end
|
108
|
+
|
109
|
+
# on_challenge callback is called when an authentication challenge is received from the router. It has the
|
110
|
+
# following attributes
|
111
|
+
# @param authmethod [String] The type of auth being requested
|
112
|
+
# @param extra [Hash] Hash containing additional information
|
113
|
+
# @return signature, extras
|
114
|
+
@on_challenge
|
115
|
+
def on_challenge(&on_challenge)
|
116
|
+
@on_challenge = on_challenge
|
117
|
+
end
|
118
|
+
|
119
|
+
attr_accessor :id, :realm, :transport, :verbose, :options
|
120
|
+
|
121
|
+
# Private attributes
|
122
|
+
attr_accessor :_goodbye_sent, :_requests, :_subscriptions, :_registrations
|
123
|
+
|
124
|
+
# Constructor
|
125
|
+
# @param transport [WampClient::Transport::Base] The transport that the session will use
|
126
|
+
# @param options [Hash] Hash containing different session options
|
127
|
+
# @option options [String] :authid The authentication ID
|
128
|
+
# @option options [Array] :authmethods Different auth methods that this client supports
|
129
|
+
def initialize(transport, options={})
|
130
|
+
|
131
|
+
# Parameters
|
132
|
+
self.id = nil
|
133
|
+
self.realm = nil
|
134
|
+
self.verbose = false
|
135
|
+
self.options = options || {}
|
136
|
+
|
137
|
+
# Outstanding Requests
|
138
|
+
self._requests = {
|
139
|
+
publish: {},
|
140
|
+
subscribe: {},
|
141
|
+
unsubscribe: {},
|
142
|
+
call: {},
|
143
|
+
register: {},
|
144
|
+
unregister: {}
|
145
|
+
}
|
146
|
+
|
147
|
+
# Init Subs and Regs in place
|
148
|
+
self._subscriptions = {}
|
149
|
+
self._registrations = {}
|
150
|
+
|
151
|
+
# Setup Transport
|
152
|
+
self.transport = transport
|
153
|
+
self.transport.on_message do |msg|
|
154
|
+
self._receive_message(msg)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Other parameters
|
158
|
+
self._goodbye_sent = false
|
159
|
+
|
160
|
+
# Setup session callbacks
|
161
|
+
@on_join = nil
|
162
|
+
@on_leave = nil
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns 'true' if the session is open
|
167
|
+
def is_open?
|
168
|
+
!self.id.nil?
|
169
|
+
end
|
170
|
+
|
171
|
+
# Joins the WAMP Router
|
172
|
+
# @param realm [String] The name of the realm
|
173
|
+
def join(realm)
|
174
|
+
if is_open?
|
175
|
+
raise RuntimeError, "Session must be closed to call 'join'"
|
176
|
+
end
|
177
|
+
|
178
|
+
self.class.check_uri('realm', realm)
|
179
|
+
|
180
|
+
self.realm = realm
|
181
|
+
|
182
|
+
details = {}
|
183
|
+
details[:roles] = WAMP_FEATURES
|
184
|
+
details[:agent] = "Ruby-WampClient-#{WampClient::VERSION}"
|
185
|
+
details[:authid] = self.options[:authid]
|
186
|
+
details[:authmethods] = self.options[:authmethods]
|
187
|
+
|
188
|
+
# Send Hello message
|
189
|
+
hello = WampClient::Message::Hello.new(realm, details)
|
190
|
+
self._send_message(hello)
|
191
|
+
end
|
192
|
+
|
193
|
+
# Leaves the WAMP Router
|
194
|
+
# @param reason [String] URI signalling the reason for leaving
|
195
|
+
def leave(reason='wamp.close.normal', message='user initiated')
|
196
|
+
unless is_open?
|
197
|
+
raise RuntimeError, "Session must be opened to call 'leave'"
|
198
|
+
end
|
199
|
+
|
200
|
+
self.class.check_uri('reason', reason, true)
|
201
|
+
self.class.check_string('message', message, true)
|
202
|
+
|
203
|
+
details = {}
|
204
|
+
details[:message] = message
|
205
|
+
|
206
|
+
# Send Goodbye message
|
207
|
+
goodbye = WampClient::Message::Goodbye.new(details, reason)
|
208
|
+
self._send_message(goodbye)
|
209
|
+
self._goodbye_sent = true
|
210
|
+
end
|
211
|
+
|
212
|
+
# Generates an ID according to the specification (Section 5.1.2)
|
213
|
+
def _generate_id
|
214
|
+
rand(0..9007199254740992)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Converts and error message to a hash
|
218
|
+
# @param msg [WampClient::Message::Error]
|
219
|
+
def _error_to_hash(msg)
|
220
|
+
{
|
221
|
+
error: msg.error,
|
222
|
+
args: msg.arguments,
|
223
|
+
kwargs: msg.argumentskw
|
224
|
+
}
|
225
|
+
end
|
226
|
+
|
227
|
+
# Sends a message
|
228
|
+
# @param msg [WampClient::Message::Base]
|
229
|
+
def _send_message(msg)
|
230
|
+
puts 'TX: ' + msg.to_s if self.verbose
|
231
|
+
self.transport.send_message(msg.payload)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Processes received messages
|
235
|
+
# @param msg [Array]
|
236
|
+
def _receive_message(msg)
|
237
|
+
|
238
|
+
message = WampClient::Message::Base.parse(msg)
|
239
|
+
|
240
|
+
puts 'RX: ' + message.to_s if self.verbose
|
241
|
+
|
242
|
+
# WAMP Session is not open
|
243
|
+
if self.id.nil?
|
244
|
+
|
245
|
+
# Parse the welcome message
|
246
|
+
if message.is_a? WampClient::Message::Welcome
|
247
|
+
self.id = message.session
|
248
|
+
@on_join.call(message.details) unless @on_join.nil?
|
249
|
+
elsif message.is_a? WampClient::Message::Challenge
|
250
|
+
|
251
|
+
if @on_challenge
|
252
|
+
signature, extra = @on_challenge.call(message.authmethod, message.extra)
|
253
|
+
else
|
254
|
+
signature = nil
|
255
|
+
extra = nil
|
256
|
+
end
|
257
|
+
|
258
|
+
signature ||= ''
|
259
|
+
extra ||= {}
|
260
|
+
|
261
|
+
authenticate = WampClient::Message::Authenticate.new(signature, extra)
|
262
|
+
self._send_message(authenticate)
|
263
|
+
|
264
|
+
elsif message.is_a? WampClient::Message::Abort
|
265
|
+
@on_leave.call(message.reason, message.details) unless @on_leave.nil?
|
266
|
+
end
|
267
|
+
|
268
|
+
# Wamp Session is open
|
269
|
+
else
|
270
|
+
|
271
|
+
# If goodbye, close the session
|
272
|
+
if message.is_a? WampClient::Message::Goodbye
|
273
|
+
|
274
|
+
# If we didn't send the goodbye, respond
|
275
|
+
unless self._goodbye_sent
|
276
|
+
goodbye = WampClient::Message::Goodbye.new({}, 'wamp.error.goodbye_and_out')
|
277
|
+
self._send_message(goodbye)
|
278
|
+
end
|
279
|
+
|
280
|
+
# Close out session
|
281
|
+
self.id = nil
|
282
|
+
self.realm = nil
|
283
|
+
self._goodbye_sent = false
|
284
|
+
@on_leave.call(message.reason, message.details) unless @on_leave.nil?
|
285
|
+
|
286
|
+
else
|
287
|
+
|
288
|
+
# Process Errors
|
289
|
+
if message.is_a? WampClient::Message::Error
|
290
|
+
if message.request_type == WampClient::Message::Types::SUBSCRIBE
|
291
|
+
self._process_SUBSCRIBE_error(message)
|
292
|
+
elsif message.request_type == WampClient::Message::Types::UNSUBSCRIBE
|
293
|
+
self._process_UNSUBSCRIBE_error(message)
|
294
|
+
elsif message.request_type == WampClient::Message::Types::PUBLISH
|
295
|
+
self._process_PUBLISH_error(message)
|
296
|
+
elsif message.request_type == WampClient::Message::Types::REGISTER
|
297
|
+
self._process_REGISTER_error(message)
|
298
|
+
elsif message.request_type == WampClient::Message::Types::UNREGISTER
|
299
|
+
self._process_UNREGISTER_error(message)
|
300
|
+
elsif message.request_type == WampClient::Message::Types::CALL
|
301
|
+
self._process_CALL_error(message)
|
302
|
+
else
|
303
|
+
# TODO: Some Error?? Not Implemented yet
|
304
|
+
end
|
305
|
+
|
306
|
+
# Process Messages
|
307
|
+
else
|
308
|
+
if message.is_a? WampClient::Message::Subscribed
|
309
|
+
self._process_SUBSCRIBED(message)
|
310
|
+
elsif message.is_a? WampClient::Message::Unsubscribed
|
311
|
+
self._process_UNSUBSCRIBED(message)
|
312
|
+
elsif message.is_a? WampClient::Message::Published
|
313
|
+
self._process_PUBLISHED(message)
|
314
|
+
elsif message.is_a? WampClient::Message::Event
|
315
|
+
self._process_EVENT(message)
|
316
|
+
elsif message.is_a? WampClient::Message::Registered
|
317
|
+
self._process_REGISTERED(message)
|
318
|
+
elsif message.is_a? WampClient::Message::Unregistered
|
319
|
+
self._process_UNREGISTERED(message)
|
320
|
+
elsif message.is_a? WampClient::Message::Invocation
|
321
|
+
self._process_INVOCATION(message)
|
322
|
+
elsif message.is_a? WampClient::Message::Result
|
323
|
+
self._process_RESULT(message)
|
324
|
+
else
|
325
|
+
# TODO: Some Error?? Not Implemented yet
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
|
334
|
+
#region Subscribe Logic
|
335
|
+
|
336
|
+
# Subscribes to a topic
|
337
|
+
# @param topic [String] The topic to subscribe to
|
338
|
+
# @param handler [lambda] The handler(args, kwargs, details) when an event is received
|
339
|
+
# @param options [Hash] The options for the subscription
|
340
|
+
# @param callback [block] The callback(subscription, error) called to signal if the subscription was a success or not
|
341
|
+
def subscribe(topic, handler, options={}, &callback)
|
342
|
+
unless is_open?
|
343
|
+
raise RuntimeError, "Session must be open to call 'subscribe'"
|
344
|
+
end
|
345
|
+
|
346
|
+
self.class.check_uri('topic', topic)
|
347
|
+
self.class.check_dict('options', options)
|
348
|
+
self.class.check_nil('handler', handler, false)
|
349
|
+
|
350
|
+
# Create a new subscribe request
|
351
|
+
request = self._generate_id
|
352
|
+
self._requests[:subscribe][request] = {t: topic, h: handler, o: options, c: callback}
|
353
|
+
|
354
|
+
# Send the message
|
355
|
+
subscribe = WampClient::Message::Subscribe.new(request, options, topic)
|
356
|
+
self._send_message(subscribe)
|
357
|
+
end
|
358
|
+
|
359
|
+
# Processes the response to a subscribe request
|
360
|
+
# @param msg [WampClient::Message::Subscribed] The response from the subscribe
|
361
|
+
def _process_SUBSCRIBED(msg)
|
362
|
+
|
363
|
+
# Remove the pending subscription, add it to the registered ones, and inform the caller
|
364
|
+
s = self._requests[:subscribe].delete(msg.subscribe_request)
|
365
|
+
if s
|
366
|
+
|
367
|
+
details = {}
|
368
|
+
details[:topic] = s[:t] unless details[:topic]
|
369
|
+
details[:type] = 'subscribe'
|
370
|
+
|
371
|
+
n_s = Subscription.new(s[:t], s[:h], s[:o], self, msg.subscription)
|
372
|
+
self._subscriptions[msg.subscription] = n_s
|
373
|
+
c = s[:c]
|
374
|
+
c.call(n_s, nil, details) if c
|
375
|
+
end
|
376
|
+
|
377
|
+
end
|
378
|
+
|
379
|
+
# Processes an error from a request
|
380
|
+
# @param msg [WampClient::Message::Error] The response from the subscribe
|
381
|
+
def _process_SUBSCRIBE_error(msg)
|
382
|
+
|
383
|
+
# Remove the pending subscription and inform the caller of the failure
|
384
|
+
s = self._requests[:subscribe].delete(msg.request_request)
|
385
|
+
if s
|
386
|
+
|
387
|
+
details = msg.details || {}
|
388
|
+
details[:topic] = s[:t] unless details[:topic]
|
389
|
+
details[:type] = 'subscribe'
|
390
|
+
|
391
|
+
c = s[:c]
|
392
|
+
c.call(nil, self._error_to_hash(msg), details) if c
|
393
|
+
end
|
394
|
+
|
395
|
+
end
|
396
|
+
|
397
|
+
# Processes and event from the broker
|
398
|
+
# @param msg [WampClient::Message::Event] An event that was published
|
399
|
+
def _process_EVENT(msg)
|
400
|
+
|
401
|
+
args = msg.publish_arguments || []
|
402
|
+
kwargs = msg.publish_argumentskw || {}
|
403
|
+
|
404
|
+
s = self._subscriptions[msg.subscribed_subscription]
|
405
|
+
if s
|
406
|
+
details = msg.details || {}
|
407
|
+
details[:publication] = msg.published_publication
|
408
|
+
|
409
|
+
h = s.handler
|
410
|
+
h.call(args, kwargs, details) if h
|
411
|
+
end
|
412
|
+
|
413
|
+
end
|
414
|
+
|
415
|
+
#endregion
|
416
|
+
|
417
|
+
#region Unsubscribe Logic
|
418
|
+
|
419
|
+
# Unsubscribes from a subscription
|
420
|
+
# @param subscription [Subscription] The subscription object from when the subscription was created
|
421
|
+
# @param callback [block] The callback(subscription, error, details) called to signal if the subscription was a success or not
|
422
|
+
def unsubscribe(subscription, &callback)
|
423
|
+
unless is_open?
|
424
|
+
raise RuntimeError, "Session must be open to call 'unsubscribe'"
|
425
|
+
end
|
426
|
+
|
427
|
+
self.class.check_nil('subscription', subscription, false)
|
428
|
+
|
429
|
+
# Create a new unsubscribe request
|
430
|
+
request = self._generate_id
|
431
|
+
self._requests[:unsubscribe][request] = { s: subscription, c: callback }
|
432
|
+
|
433
|
+
# Send the message
|
434
|
+
unsubscribe = WampClient::Message::Unsubscribe.new(request, subscription.id)
|
435
|
+
self._send_message(unsubscribe)
|
436
|
+
end
|
437
|
+
|
438
|
+
# Processes the response to a unsubscribe request
|
439
|
+
# @param msg [WampClient::Message::Unsubscribed] The response from the unsubscribe
|
440
|
+
def _process_UNSUBSCRIBED(msg)
|
441
|
+
|
442
|
+
# Remove the pending unsubscription, add it to the registered ones, and inform the caller
|
443
|
+
s = self._requests[:unsubscribe].delete(msg.unsubscribe_request)
|
444
|
+
if s
|
445
|
+
n_s = s[:s]
|
446
|
+
self._subscriptions.delete(n_s.id)
|
447
|
+
|
448
|
+
details = {}
|
449
|
+
details[:topic] = s[:s].topic
|
450
|
+
details[:type] = 'unsubscribe'
|
451
|
+
|
452
|
+
c = s[:c]
|
453
|
+
c.call(n_s, nil, details) if c
|
454
|
+
end
|
455
|
+
|
456
|
+
end
|
457
|
+
|
458
|
+
|
459
|
+
# Processes an error from a request
|
460
|
+
# @param msg [WampClient::Message::Error] The response from the subscribe
|
461
|
+
def _process_UNSUBSCRIBE_error(msg)
|
462
|
+
|
463
|
+
# Remove the pending subscription and inform the caller of the failure
|
464
|
+
s = self._requests[:unsubscribe].delete(msg.request_request)
|
465
|
+
if s
|
466
|
+
|
467
|
+
details = msg.details || {}
|
468
|
+
details[:topic] = s[:s].topic unless details[:topic]
|
469
|
+
details[:type] = 'unsubscribe'
|
470
|
+
|
471
|
+
c = s[:c]
|
472
|
+
c.call(nil, self._error_to_hash(msg), details) if c
|
473
|
+
end
|
474
|
+
|
475
|
+
end
|
476
|
+
|
477
|
+
#endregion
|
478
|
+
|
479
|
+
#region Publish Logic
|
480
|
+
|
481
|
+
# Publishes and event to a topic
|
482
|
+
# @param topic [String] The topic to publish the event to
|
483
|
+
# @param args [Array] The arguments
|
484
|
+
# @param kwargs [Hash] The keyword arguments
|
485
|
+
# @param options [Hash] The options for the publish
|
486
|
+
# @param callback [block] The callback(publish, error, details) called to signal if the publish was a success or not
|
487
|
+
def publish(topic, args=nil, kwargs=nil, options={}, &callback)
|
488
|
+
unless is_open?
|
489
|
+
raise RuntimeError, "Session must be open to call 'publish'"
|
490
|
+
end
|
491
|
+
|
492
|
+
self.class.check_uri('topic', topic)
|
493
|
+
self.class.check_dict('options', options)
|
494
|
+
self.class.check_list('args', args, true)
|
495
|
+
self.class.check_dict('kwargs', kwargs, true)
|
496
|
+
|
497
|
+
# Create a new publish request
|
498
|
+
request = self._generate_id
|
499
|
+
self._requests[:publish][request] = {t: topic, a: args, k: kwargs, o: options, c: callback} if options[:acknowledge]
|
500
|
+
|
501
|
+
# Send the message
|
502
|
+
publish = WampClient::Message::Publish.new(request, options, topic, args, kwargs)
|
503
|
+
self._send_message(publish)
|
504
|
+
end
|
505
|
+
|
506
|
+
# Processes the response to a publish request
|
507
|
+
# @param msg [WampClient::Message::Published] The response from the subscribe
|
508
|
+
def _process_PUBLISHED(msg)
|
509
|
+
|
510
|
+
# Remove the pending publish and alert the callback
|
511
|
+
p = self._requests[:publish].delete(msg.publish_request)
|
512
|
+
if p
|
513
|
+
|
514
|
+
details = {}
|
515
|
+
details[:topic] = p[:t]
|
516
|
+
details[:type] = 'publish'
|
517
|
+
details[:publication] = msg.publication
|
518
|
+
|
519
|
+
c = p[:c]
|
520
|
+
c.call(p, nil, details) if c
|
521
|
+
end
|
522
|
+
|
523
|
+
end
|
524
|
+
|
525
|
+
# Processes an error from a publish request
|
526
|
+
# @param msg [WampClient::Message::Error] The response from the subscribe
|
527
|
+
def _process_PUBLISH_error(msg)
|
528
|
+
|
529
|
+
# Remove the pending publish and inform the caller of the failure
|
530
|
+
s = self._requests[:publish].delete(msg.request_request)
|
531
|
+
if s
|
532
|
+
|
533
|
+
details = msg.details || {}
|
534
|
+
details[:topic] = s[:t] unless details[:topic]
|
535
|
+
details[:type] = 'publish'
|
536
|
+
|
537
|
+
c = s[:c]
|
538
|
+
c.call(nil, self._error_to_hash(msg), details) if c
|
539
|
+
end
|
540
|
+
|
541
|
+
end
|
542
|
+
|
543
|
+
#endregion
|
544
|
+
|
545
|
+
#region Register Logic
|
546
|
+
|
547
|
+
# Register to a procedure
|
548
|
+
# @param procedure [String] The procedure to register for
|
549
|
+
# @param handler [lambda] The handler(args, kwargs, details) when a invocation is received
|
550
|
+
# @param options [Hash] The options for the registration
|
551
|
+
# @param callback [block] The callback(registration, error, details) called to signal if the registration was a success or not
|
552
|
+
def register(procedure, handler, options={}, &callback)
|
553
|
+
unless is_open?
|
554
|
+
raise RuntimeError, "Session must be open to call 'register'"
|
555
|
+
end
|
556
|
+
|
557
|
+
self.class.check_uri('procedure', procedure)
|
558
|
+
self.class.check_dict('options', options)
|
559
|
+
self.class.check_nil('handler', handler, false)
|
560
|
+
|
561
|
+
# Create a new registration request
|
562
|
+
request = self._generate_id
|
563
|
+
self._requests[:register][request] = {p: procedure, h: handler, o: options, c: callback}
|
564
|
+
|
565
|
+
# Send the message
|
566
|
+
register = WampClient::Message::Register.new(request, options, procedure)
|
567
|
+
self._send_message(register)
|
568
|
+
end
|
569
|
+
|
570
|
+
# Processes the response to a register request
|
571
|
+
# @param msg [WampClient::Message::Registered] The response from the subscribe
|
572
|
+
def _process_REGISTERED(msg)
|
573
|
+
|
574
|
+
# Remove the pending subscription, add it to the registered ones, and inform the caller
|
575
|
+
r = self._requests[:register].delete(msg.register_request)
|
576
|
+
if r
|
577
|
+
n_r = Registration.new(r[:p], r[:h], r[:o], self, msg.registration)
|
578
|
+
self._registrations[msg.registration] = n_r
|
579
|
+
|
580
|
+
details = {}
|
581
|
+
details[:procedure] = r[:p]
|
582
|
+
details[:type] = 'register'
|
583
|
+
|
584
|
+
c = r[:c]
|
585
|
+
c.call(n_r, nil, details) if c
|
586
|
+
end
|
587
|
+
|
588
|
+
end
|
589
|
+
|
590
|
+
# Processes an error from a request
|
591
|
+
# @param msg [WampClient::Message::Error] The response from the register
|
592
|
+
def _process_REGISTER_error(msg)
|
593
|
+
|
594
|
+
# Remove the pending registration and inform the caller of the failure
|
595
|
+
r = self._requests[:register].delete(msg.request_request)
|
596
|
+
if r
|
597
|
+
|
598
|
+
details = msg.details || {}
|
599
|
+
details[:procedure] = r[:p] unless details[:procedure]
|
600
|
+
details[:type] = 'register'
|
601
|
+
|
602
|
+
c = r[:c]
|
603
|
+
c.call(nil, self._error_to_hash(msg), details) if c
|
604
|
+
end
|
605
|
+
|
606
|
+
end
|
607
|
+
|
608
|
+
# Processes and event from the broker
|
609
|
+
# @param msg [WampClient::Message::Invocation] An procedure that was called
|
610
|
+
def _process_INVOCATION(msg)
|
611
|
+
|
612
|
+
request = msg.request
|
613
|
+
args = msg.call_arguments || []
|
614
|
+
kwargs = msg.call_argumentskw || {}
|
615
|
+
|
616
|
+
details = msg.details || {}
|
617
|
+
details[:request] = request
|
618
|
+
|
619
|
+
r = self._registrations[msg.registered_registration]
|
620
|
+
if r
|
621
|
+
h = r.handler
|
622
|
+
if h
|
623
|
+
begin
|
624
|
+
value = h.call(args, kwargs, details)
|
625
|
+
|
626
|
+
if value.nil?
|
627
|
+
value = CallResult.new
|
628
|
+
elsif not value.is_a?(CallResult)
|
629
|
+
value = CallResult.new([value])
|
630
|
+
end
|
631
|
+
|
632
|
+
# TODO: Handle promise or future
|
633
|
+
|
634
|
+
yield_msg = WampClient::Message::Yield.new(request, {}, value.args, value.kwargs)
|
635
|
+
self._send_message(yield_msg)
|
636
|
+
rescue Exception => e
|
637
|
+
error = WampClient::Message::Error.new(WampClient::Message::Types::INVOCATION, request, {}, 'wamp.error.runtime', [e.to_s])
|
638
|
+
self._send_message(error)
|
639
|
+
end
|
640
|
+
end
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
#endregion
|
645
|
+
|
646
|
+
#region Unregister Logic
|
647
|
+
|
648
|
+
# Unregisters from a procedure
|
649
|
+
# @param registration [Registration] The registration object from when the registration was created
|
650
|
+
# @param callback [block] The callback(registration, error, details) called to signal if the unregistration was a success or not
|
651
|
+
def unregister(registration, &callback)
|
652
|
+
unless is_open?
|
653
|
+
raise RuntimeError, "Session must be open to call 'unregister'"
|
654
|
+
end
|
655
|
+
|
656
|
+
self.class.check_nil('registration', registration, false)
|
657
|
+
|
658
|
+
# Create a new unsubscribe request
|
659
|
+
request = self._generate_id
|
660
|
+
self._requests[:unregister][request] = { r: registration, c: callback }
|
661
|
+
|
662
|
+
# Send the message
|
663
|
+
unregister = WampClient::Message::Unregister.new(request, registration.id)
|
664
|
+
self._send_message(unregister)
|
665
|
+
end
|
666
|
+
|
667
|
+
# Processes the response to a unregister request
|
668
|
+
# @param msg [WampClient::Message::Unregistered] The response from the unsubscribe
|
669
|
+
def _process_UNREGISTERED(msg)
|
670
|
+
|
671
|
+
# Remove the pending unregistration, add it to the registered ones, and inform the caller
|
672
|
+
r = self._requests[:unregister].delete(msg.unregister_request)
|
673
|
+
if r
|
674
|
+
r_s = r[:r]
|
675
|
+
self._registrations.delete(r_s.id)
|
676
|
+
|
677
|
+
details = {}
|
678
|
+
details[:procedure] = r_s.procedure
|
679
|
+
details[:type] = 'unregister'
|
680
|
+
|
681
|
+
c = r[:c]
|
682
|
+
c.call(r_s, nil, details) if c
|
683
|
+
end
|
684
|
+
|
685
|
+
end
|
686
|
+
|
687
|
+
# Processes an error from a request
|
688
|
+
# @param msg [WampClient::Message::Error] The response from the subscribe
|
689
|
+
def _process_UNREGISTER_error(msg)
|
690
|
+
|
691
|
+
# Remove the pending subscription and inform the caller of the failure
|
692
|
+
r = self._requests[:unregister].delete(msg.request_request)
|
693
|
+
if r
|
694
|
+
|
695
|
+
details = msg.details || {}
|
696
|
+
details[:procedure] = r[:r].procedure unless details[:procedure]
|
697
|
+
details[:type] = 'unregister'
|
698
|
+
|
699
|
+
c = r[:c]
|
700
|
+
c.call(nil, self._error_to_hash(msg), details) if c
|
701
|
+
end
|
702
|
+
|
703
|
+
end
|
704
|
+
|
705
|
+
#endregion
|
706
|
+
|
707
|
+
#region Call Logic
|
708
|
+
|
709
|
+
# Publishes and event to a topic
|
710
|
+
# @param procedure [String] The procedure to invoke
|
711
|
+
# @param args [Array] The arguments
|
712
|
+
# @param kwargs [Hash] The keyword arguments
|
713
|
+
# @param options [Hash] The options for the call
|
714
|
+
# @param callback [block] The callback(result, error, details) called to signal if the call was a success or not
|
715
|
+
def call(procedure, args=nil, kwargs=nil, options={}, &callback)
|
716
|
+
unless is_open?
|
717
|
+
raise RuntimeError, "Session must be open to call 'call'"
|
718
|
+
end
|
719
|
+
|
720
|
+
self.class.check_uri('procedure', procedure)
|
721
|
+
self.class.check_dict('options', options)
|
722
|
+
self.class.check_list('args', args, true)
|
723
|
+
self.class.check_dict('kwargs', kwargs, true)
|
724
|
+
|
725
|
+
# Create a new call request
|
726
|
+
request = self._generate_id
|
727
|
+
self._requests[:call][request] = {p: procedure, a: args, k: kwargs, o: options, c: callback}
|
728
|
+
|
729
|
+
# Send the message
|
730
|
+
call = WampClient::Message::Call.new(request, options, procedure, args, kwargs)
|
731
|
+
self._send_message(call)
|
732
|
+
end
|
733
|
+
|
734
|
+
# Processes the response to a publish request
|
735
|
+
# @param msg [WampClient::Message::Result] The response from the call
|
736
|
+
def _process_RESULT(msg)
|
737
|
+
|
738
|
+
details = msg.details || {}
|
739
|
+
|
740
|
+
call = self._requests[:call][msg.call_request]
|
741
|
+
|
742
|
+
# Don't remove if progress is true and the options had receive_progress true
|
743
|
+
self._requests[:call].delete(msg.call_request) unless (details[:progress] and (call and call[:o][:receive_progress]))
|
744
|
+
|
745
|
+
if call
|
746
|
+
details[:procedure] = call[:p] unless details[:procedure]
|
747
|
+
details[:type] = 'call'
|
748
|
+
|
749
|
+
c = call[:c]
|
750
|
+
c.call(CallResult.new(msg.yield_arguments, msg.yield_argumentskw), nil, details) if c
|
751
|
+
end
|
752
|
+
|
753
|
+
end
|
754
|
+
|
755
|
+
# Processes an error from a call request
|
756
|
+
# @param msg [WampClient::Message::Error] The response from the call
|
757
|
+
def _process_CALL_error(msg)
|
758
|
+
|
759
|
+
# Remove the pending publish and inform the caller of the failure
|
760
|
+
call = self._requests[:call].delete(msg.request_request)
|
761
|
+
if call
|
762
|
+
|
763
|
+
details = msg.details || {}
|
764
|
+
details[:procedure] = call[:p] unless details[:procedure]
|
765
|
+
details[:type] = 'call'
|
766
|
+
|
767
|
+
c = call[:c]
|
768
|
+
c.call(nil, self._error_to_hash(msg), details) if c
|
769
|
+
end
|
770
|
+
|
771
|
+
end
|
772
|
+
|
773
|
+
#endregion
|
774
|
+
|
775
|
+
end
|
776
|
+
end
|