wamp_client 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 72e6d6b94bf4b5f244885438e6c122c952d5d084
4
+ data.tar.gz: 0366993e7d42a1e3673210af8c8b4a05b63206e9
5
+ SHA512:
6
+ metadata.gz: 0de90b5fc4cdf71d2e76b587a232530b47fb8e0ebd961c5a77299f130714c575ac36061a5c0bdeb75de5a535d9711a07523b79360d1d1c777738f0314f70179a
7
+ data.tar.gz: bf65fac58f124abb76d0c4518067f28f2046a29ffcb1d7fb0aede645654ae9d445ffeafded1c3d94aa759e316f9fd2738f38c72f8a2c3b4d2550a6ff21f6a4e8
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ .idea
16
+ *.tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in wamp_client.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Eric Chapman
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,464 @@
1
+ # WampClient
2
+
3
+ [![Circle CI](https://circleci.com/gh/ericchapman/ruby_wamp_client/tree/master.svg?&style=shield&circle-token=92813c17f9c9510c4c644e41683e7ba2572e0b2a)](https://circleci.com/gh/ericchapman/ruby_wamp_client/tree/master)
4
+ [![Codecov](https://img.shields.io/codecov/c/github/ericchapman/ruby_wamp_client/master.svg)](https://codecov.io/github/ericchapman/ruby_wamp_client)
5
+
6
+ Client for talking to a WAMP Router. This is defined at
7
+
8
+ https://tools.ietf.org/html/draft-oberstet-hybi-tavendo-wamp-02
9
+
10
+ ## Revision History
11
+
12
+ - v0.0.1:
13
+ - Initial Release
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'wamp_client'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install wamp_client
30
+
31
+ ## Usage
32
+
33
+ ### Connection
34
+ The connection object is used to instantiate and maintain a WAMP session as well as the underlying transport. A user
35
+ creates a connection and then operates on the session once the session has been established.
36
+
37
+ Note that once "connection.open" is called, the library will automatically attempt to reconnect if the connection
38
+ closes for any reason. Calling "connection.close" will stop the reconnect logic as well as close the connection if it
39
+ is open
40
+
41
+ #### Creating a connection
42
+ A connection can be created as follows
43
+
44
+ ```ruby
45
+ require 'wamp_client'
46
+
47
+ options = {
48
+ uri: 'ws://127.0.0.1:8080/ws',
49
+ realm: 'realm1'
50
+ }
51
+ connection = WampClient::Connection.new(options)
52
+
53
+ connection.on_join do |session, details|
54
+ puts "Session Open"
55
+
56
+ # Register for something
57
+ def add(args, kwargs, details)
58
+ args[0] + args[1]
59
+ end
60
+ session.register('com.example.procedure', method(:add)) do |registration, error, details|
61
+
62
+ # Call It
63
+ session.call('com.example.procedure', [3,4]) do |result, error, details|
64
+ if result
65
+ puts result.args[0] # => 7
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+
72
+ end
73
+
74
+ connection.open
75
+ ```
76
+
77
+ #### Closing a connection
78
+ A connection is closed by simply calling "close"
79
+
80
+ ```ruby
81
+ connection.close
82
+ ```
83
+
84
+ Note that the connection will still call "on_leave" and "on_disconnect" as it closes the session and the transport
85
+
86
+ #### Callbacks
87
+ A session has the following callbacks
88
+
89
+ **on_connect** - Called when the transport is opened
90
+ ```ruby
91
+ connection.on_connect do
92
+
93
+ end
94
+ ```
95
+
96
+ **on_join** - Called when the session is established
97
+ ```ruby
98
+ connection.on_join do |session, details|
99
+
100
+ end
101
+ ```
102
+
103
+ **on_leave** - Called when the session is terminated
104
+ ```ruby
105
+ connection.on_leave do |reason, details|
106
+
107
+ end
108
+ ```
109
+
110
+ **on_disconnect** - Called when the connection is terminated
111
+ ```ruby
112
+ connection.on_disconnect do |reason|
113
+
114
+ end
115
+ ```
116
+
117
+ **oon_challenge** - Called when an authentication challenge is created
118
+ ```ruby
119
+ connection.on_challenge do |authmethod, extra|
120
+
121
+ end
122
+ ```
123
+
124
+ ### Authentication
125
+ The library supports authentication. Here is how to perform the different methods
126
+
127
+ #### WAMPCRA
128
+ To perform WAMP CRA, do the following
129
+
130
+ ```ruby
131
+ require 'wamp_client'
132
+
133
+ options = {
134
+ uri: 'ws://127.0.0.1:8080/ws',
135
+ realm: 'realm1',
136
+ authid: 'joe',
137
+ authmethods: ['wampcra']
138
+ }
139
+ connection = WampClient::Connection.new(options)
140
+
141
+ connection.on_challenge do |authmethod, extra|
142
+ puts 'Challenge'
143
+ if authmethod == 'wampcra'
144
+ WampClient::Auth::Cra.sign('secret', extra[:challenge])
145
+ else
146
+ raise RuntimeError, "Unsupported auth method #{authmethod}"
147
+ end
148
+ end
149
+
150
+ connection.on_join do |session, details|
151
+ puts "Session Open"
152
+ end
153
+
154
+ connection.open
155
+ ```
156
+
157
+ ### Handlers and Callbacks
158
+ This library makes extensive use of "blocks", "lambdas", "procs", and method pointers for any returned values because
159
+ all communication is performed asynchronously. The library defines two types of methods
160
+
161
+ - handlers - Can be called **AT ANY TIME**. These can be blocks, lambdas, procs, or method pointers
162
+ - callbacks - Only invoked in response to specific call. These are only blocks
163
+
164
+ Note that all callbacks can be set to nil, handlers however cannot since the user is explicitly setting them up.
165
+
166
+ #### Handlers
167
+ All handlers are called with the following parameters
168
+
169
+ - args [Array] - Array of arguments
170
+ - kwargs [Hash] - Hash of key/value arguments
171
+ - details [Hash] - Hash containing some details about the call
172
+
173
+ Some examples of this are shown below
174
+
175
+ **lambda**
176
+
177
+ ```ruby
178
+ handler = lambda do |args, kwargs, details|
179
+ # TODO: Do Something!!
180
+ end
181
+ session.subscribe('com.example.topic', handler)
182
+ ```
183
+
184
+ **method**
185
+
186
+ ```ruby
187
+ def handler(args, kwargs, details)
188
+ # TODO: Do Something!!
189
+ end
190
+ session.subscribe('com.example.topic', method(:handler))
191
+ ```
192
+
193
+ #### Callbacks
194
+ All callbacks are called with the following parameters
195
+
196
+ - result [Object] - Some object with the result information (depends on the call)
197
+ - error [Hash] - Hash containing "error", "args", and "kwargs" if an error occurred
198
+ - details [Hash] - Hash containing some details about the call
199
+
200
+ An example of this is shown below
201
+
202
+ ```ruby
203
+ session.call('com.example.procedure') do |result, error, details|
204
+ # TODO: Do something
205
+ end
206
+ ```
207
+
208
+ ### Topic Subscriptions and Publications
209
+
210
+ #### Subscribe
211
+ This method subscribes to a topic. The prototype for the method is
212
+
213
+ ```ruby
214
+ subscribe(topic, handler, options={}, &callback)
215
+ ```
216
+
217
+ where the parameters are defined as
218
+
219
+ - topic [String] - The topic to subscribe to
220
+ - handler [lambda] - The handler(args, kwargs, details) when an event is received
221
+ - options [Hash] - The options for the subscription
222
+ - callback [block] - The callback(subscription, error, details) called to signal if the subscription was a success or not
223
+
224
+ To subscribe, do the following
225
+
226
+ ```ruby
227
+ handler = lambda do |args, kwargs, details|
228
+ # TODO: Do something
229
+ end
230
+
231
+ session.subscribe('com.example.topic', handler)
232
+ ```
233
+
234
+ If you would like confirmation of the success of the subscription, do the following
235
+
236
+ ```ruby
237
+ handler = lambda do |args, kwargs, details|
238
+ # TODO: Do something
239
+ end
240
+
241
+ session.subscribe('com.example.topic', handler) do |subscription, error, details|
242
+ # TODO: Do something
243
+ end
244
+ ```
245
+
246
+ Options are
247
+
248
+ - match [String] - "exact", "prefix", or "wildcard"
249
+
250
+ #### Unsubscribe
251
+ This method unsubscribes from a topic. The prototype for the method is as follows
252
+
253
+ ```ruby
254
+ def unsubscribe(subscription, &callback)
255
+ ```
256
+
257
+ where the parameters are defined as
258
+
259
+ - subscription [Subscription] - The subscription object from when the subscription was created
260
+ - callback [block] - The callback(subscription, error, details) called to signal if the unsubscription was a success or not
261
+
262
+ To unsubscribe, do the following
263
+
264
+ ```ruby
265
+ handler = lambda do |args, kwargs, details|
266
+ # TODO: Do something
267
+ end
268
+
269
+ session.subscribe('com.example.topic', handler) do |subscription, error, details|
270
+ @subscription = subscription
271
+ end
272
+
273
+ # At some later time...
274
+
275
+ session.unsubscribe(@subscription)
276
+
277
+ # or ...
278
+
279
+ @subscription.unsubscribe
280
+
281
+ ```
282
+
283
+ #### Publish
284
+ This method publishes an event to all of the subscribers. The prototype for the method is
285
+
286
+ ```ruby
287
+ publish(topic, args=nil, kwargs=nil, options={}, &callback)
288
+ ```
289
+
290
+ where the parameters are defined as
291
+
292
+ - topic [String] - The topic to publish the event to
293
+ - args [Array] - The arguments
294
+ - kwargs [Hash] - The keyword arguments
295
+ - options [Hash] - The options for the subscription
296
+ - callback [block] - The callback(publish, error, details) is called to signal if the publish was a success or not
297
+
298
+ To publish, do the following
299
+
300
+ ```ruby
301
+ session.publish('com.example.topic', [15], {param: value})
302
+ ```
303
+
304
+ If you would like confirmation, do the following
305
+
306
+ ```ruby
307
+ session.publish('com.example.topic', [15], {param: value}, {acknowledge: true}, callback) do |publish, error, details|
308
+ # TODO: Do something
309
+ end
310
+ ```
311
+
312
+ Options are
313
+
314
+ - acknowledge [Boolean] - set to "true" if you want the Broker to acknowledge if the Publish was successful or not
315
+ - disclose_me [Boolean] - "true" if the publisher would like the subscribers to know his identity
316
+ - exclude [Array[Integer]] - Array of session IDs to exclude
317
+ - exclude_authid [Array[String]] - Array of auth IDs to exclude
318
+ - exclude_authrole [Array[String]] - Array of auth roles to exclude
319
+ - eligible [Array[Integer]] - Array of session IDs to include
320
+ - eligible_authid [Array[String]] - Array of auth IDs to include
321
+ - eligible_authrole [Array[String]] - Array of auth roles to include
322
+ - exclude_me [Boolean] - set to "false" if you would like yourself to receive an event that you fired
323
+
324
+ ### Procedure Registrations and Calls
325
+
326
+ #### Register
327
+ This method registers to a procedure. The prototype for the method is
328
+
329
+ ```ruby
330
+ register(procedure, handler, options={}, &callback)
331
+ ```
332
+
333
+ where the parameters are defined as
334
+
335
+ - procedure [String] - The procedure to register for
336
+ - handler [lambda] - The handler(args, kwargs, details) when a invocation is received
337
+ - options [Hash] - The options for the registration
338
+ - callback [block] - The callback(registration, error, details) called to signal if the registration was a success or not
339
+
340
+ To register, do the following
341
+
342
+ ```ruby
343
+ handler = lambda do |args, kwargs, details|
344
+ # TODO: Do something
345
+ end
346
+
347
+ session.register('com.example.procedure', handler)
348
+ ```
349
+
350
+ If you would like confirmation of the success of the registration, do the following
351
+
352
+ ```ruby
353
+ handler = lambda do |args, kwargs, details|
354
+ # TODO: Do something
355
+ end
356
+
357
+ session.register('com.example.procedure', handler, {}, callback) do |registration, error, details|
358
+ # TODO: Do something
359
+ end
360
+ ```
361
+
362
+ Options are
363
+
364
+ - match [String] - "exact", "prefix", or "wildcard"
365
+ - invoke [String] - "single", "roundrobin", "random", "first", "last"
366
+
367
+ #### Unregister
368
+ This method unregisters from a procedure. The prototype for the method is as follows
369
+
370
+ ```ruby
371
+ def unregister(registration, &callback)
372
+ ```
373
+
374
+ where the parameters are defined as
375
+
376
+ - registration [Registration] - The registration object from when the registration was created
377
+ - callback [lambda] - The callback(registration, error, details) called to signal if the unregistration was a success
378
+ or not
379
+
380
+ To unregister, do the following
381
+
382
+ ```ruby
383
+ handler = lambda do |args, kwargs, details|
384
+ # TODO: Do something
385
+ end
386
+
387
+ session.register('com.example.procedure', handler, {}) do |registration, error, details|
388
+ @registration = registration
389
+ end
390
+
391
+ # At some later time...
392
+
393
+ session.unregister(@registration)
394
+
395
+ # or ...
396
+
397
+ @registration.unregister
398
+
399
+ ```
400
+
401
+ #### Call
402
+ This method calls a procedure. The prototype for the method is
403
+
404
+ ```ruby
405
+ call(procedure, args=nil, kwargs=nil, options={}, &callback)
406
+ ```
407
+
408
+ where the parameters are defined as
409
+
410
+ - procedure [String] - The procedure to invoke
411
+ - args [Array] - The arguments
412
+ - kwargs [Hash] - The keyword arguments
413
+ - options [Hash] - The options for the call
414
+ - callback [block] - The callback(result, error, details) called to signal if the call was a success or not
415
+
416
+ To call, do the following
417
+
418
+ ```ruby
419
+ session.call('com.example.procedure', [15], {param: value}, {}) do |result, error, details|
420
+ # TODO: Do something
421
+ args = result.args
422
+ kwargs = result.kwargs
423
+ end
424
+ ```
425
+
426
+ Options are
427
+
428
+ - receive_progress [Boolean] - "true" if you support results being able to be sent progressively
429
+ - disclose_me [Boolean] - "true" if the caller would like the callee to know the identity
430
+
431
+ ## Contributing
432
+
433
+ 1. Fork it ( https://github.com/ericchapman/ruby_wamp_client )
434
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
435
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
436
+ 4. Push to the branch (`git push origin my-new-feature`)
437
+ 5. Create a new Pull Request
438
+
439
+ ### TODOs
440
+
441
+ - progressive_call_results (callee)
442
+ - call_timeout
443
+ - call_canceling
444
+
445
+ ### Testing
446
+
447
+ The unit tests are run as follows
448
+
449
+ $ bundle exec rake spec
450
+
451
+ ### Scripts
452
+
453
+ #### Message
454
+
455
+ The *lib/wamp_client/message.rb* file and the *spec/message_spec.rb* file are autogenerated using the script
456
+ *scripts/gen_message.rb*. This is done as follows
457
+
458
+ $ cd scripts
459
+ $ ./gen_message.rb
460
+ $ mv message.rb.tmp ../lib/wamp_client/message.rb
461
+ $ mv message_spec.rb.tmp ../spec/message_spec.rb
462
+
463
+ As I was writing the code for the messages I caught myself cutting and pasting allot and decided these would be
464
+ better suited to be autogenerated.
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ Dir.glob('tasks/**/*.rake').each(&method(:import))
data/circle.yml ADDED
@@ -0,0 +1,3 @@
1
+ test:
2
+ post:
3
+ - bundle exec rake spec
@@ -0,0 +1,18 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module WampClient
5
+ module Auth
6
+ module Cra
7
+
8
+ # Generates the signature from the challenge
9
+ # @param key [String]
10
+ # @param challenge [String]
11
+ def self.sign(key, challenge)
12
+ hash = OpenSSL::HMAC.digest('sha256', key, challenge)
13
+ Base64.encode64(hash).gsub(/\n/,'')
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,57 @@
1
+ module WampClient
2
+ module Check
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ def check_equal(name, expected, value)
11
+ raise ArgumentError, "The '#{name}' argument must have the value '#{expected}'. Instead the value was '#{value}'" unless value == expected
12
+ end
13
+
14
+ def check_gte(name, expected, value)
15
+ raise ArgumentError, "The '#{name}' argument must be greater than or equal to '#{expected}'. Instead the value was '#{value}'" unless value >= expected
16
+ end
17
+
18
+ def check_nil(name, param, nil_allowed)
19
+ raise ArgumentError, "The '#{name}' argument cannot be nil" if param.nil? and not nil_allowed
20
+ end
21
+
22
+ def check_int(name, param, nil_allowed=false)
23
+ check_nil(name, param, nil_allowed)
24
+ raise ArgumentError, "The '#{name}' argument must be an integer" unless param.nil? or param.is_a? Integer
25
+ end
26
+
27
+ def check_string(name, param, nil_allowed=false)
28
+ check_nil(name, param, nil_allowed)
29
+ raise ArgumentError, "The '#{name}' argument must be a string" unless param.nil? or param.is_a? String
30
+ end
31
+
32
+ def check_bool(name, param, nil_allowed=false)
33
+ check_nil(name, param, nil_allowed)
34
+ raise ArgumentError, "The '#{name}' argument must be a boolean" unless param.nil? or !!param == param
35
+ end
36
+
37
+ def check_dict(name, param, nil_allowed=false)
38
+ check_nil(name, param, nil_allowed)
39
+ raise ArgumentError, "The '#{name}' argument must be a hash" unless param.nil? or param.is_a? Hash
40
+ end
41
+
42
+ def check_list(name, param, nil_allowed=false)
43
+ check_nil(name, param, nil_allowed)
44
+ raise ArgumentError, "The '#{name}' argument must be an array" unless param.nil? or param.is_a? Array
45
+ end
46
+
47
+ def check_uri(name, param, nil_allowed=false)
48
+ check_string(name, param, nil_allowed)
49
+ end
50
+
51
+ def check_id(name, param, nil_allowed=false)
52
+ check_int(name, param, nil_allowed)
53
+ end
54
+ end
55
+
56
+ end
57
+ end