xconn 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +29 -0
  4. data/CHANGELOG.md +5 -0
  5. data/LICENSE +22 -0
  6. data/Makefile +9 -0
  7. data/README.md +2 -0
  8. data/Rakefile +14 -0
  9. data/bin/xconn +79 -0
  10. data/docs/index.md +7 -0
  11. data/docs/installation.md +11 -0
  12. data/example/sample/example.rb +23 -0
  13. data/example/sample/included_app.rb +12 -0
  14. data/lib/tasks/tasks.rake +24 -0
  15. data/lib/wamp/app.rb +39 -0
  16. data/lib/wamp/connection/session.rb +35 -0
  17. data/lib/wamp/connection/stream.rb +68 -0
  18. data/lib/wamp/connection/websocket_client.rb +137 -0
  19. data/lib/wamp/connection/websocket_connection.rb +52 -0
  20. data/lib/wamp/manager/base.rb +41 -0
  21. data/lib/wamp/manager/connection.rb +32 -0
  22. data/lib/wamp/manager/event/abort.rb +21 -0
  23. data/lib/wamp/manager/event/authenticate.rb +14 -0
  24. data/lib/wamp/manager/event/base.rb +41 -0
  25. data/lib/wamp/manager/event/call.rb +40 -0
  26. data/lib/wamp/manager/event/challenge.rb +26 -0
  27. data/lib/wamp/manager/event/error.rb +30 -0
  28. data/lib/wamp/manager/event/event.rb +20 -0
  29. data/lib/wamp/manager/event/goodbye.rb +18 -0
  30. data/lib/wamp/manager/event/hello.rb +38 -0
  31. data/lib/wamp/manager/event/invocation.rb +24 -0
  32. data/lib/wamp/manager/event/publish.rb +22 -0
  33. data/lib/wamp/manager/event/published.rb +13 -0
  34. data/lib/wamp/manager/event/register.rb +30 -0
  35. data/lib/wamp/manager/event/registered.rb +37 -0
  36. data/lib/wamp/manager/event/result.rb +13 -0
  37. data/lib/wamp/manager/event/subscribe.rb +39 -0
  38. data/lib/wamp/manager/event/subscribed.rb +26 -0
  39. data/lib/wamp/manager/event/unregister.rb +19 -0
  40. data/lib/wamp/manager/event/unregistered.rb +13 -0
  41. data/lib/wamp/manager/event/unsubscribe.rb +29 -0
  42. data/lib/wamp/manager/event/unsubscribed.rb +13 -0
  43. data/lib/wamp/manager/event/welcome.rb +21 -0
  44. data/lib/wamp/manager/event/yield.rb +13 -0
  45. data/lib/wamp/manager/event.rb +60 -0
  46. data/lib/wamp/manager/hello_event.rb +263 -0
  47. data/lib/wamp/manager/session.rb +91 -0
  48. data/lib/wamp/manager/subscription.rb +38 -0
  49. data/lib/wamp/manager.rb +12 -0
  50. data/lib/wamp/message_handler/api.rb +67 -0
  51. data/lib/wamp/message_handler/base.rb +65 -0
  52. data/lib/wamp/message_handler/call.rb +14 -0
  53. data/lib/wamp/message_handler/challenge.rb +12 -0
  54. data/lib/wamp/message_handler/error.rb +18 -0
  55. data/lib/wamp/message_handler/event.rb +18 -0
  56. data/lib/wamp/message_handler/goodbye.rb +19 -0
  57. data/lib/wamp/message_handler/hello.rb +18 -0
  58. data/lib/wamp/message_handler/invocation.rb +35 -0
  59. data/lib/wamp/message_handler/publish.rb +14 -0
  60. data/lib/wamp/message_handler/published.rb +18 -0
  61. data/lib/wamp/message_handler/register.rb +14 -0
  62. data/lib/wamp/message_handler/registered.rb +30 -0
  63. data/lib/wamp/message_handler/result.rb +17 -0
  64. data/lib/wamp/message_handler/subscribe.rb +14 -0
  65. data/lib/wamp/message_handler/subscribed.rb +30 -0
  66. data/lib/wamp/message_handler/unregister.rb +14 -0
  67. data/lib/wamp/message_handler/unregistered.rb +32 -0
  68. data/lib/wamp/message_handler/unsubscribe.rb +14 -0
  69. data/lib/wamp/message_handler/unsubscribed.rb +33 -0
  70. data/lib/wamp/message_handler/welcome.rb +12 -0
  71. data/lib/wamp/message_handler.rb +75 -0
  72. data/lib/wamp/router/base.rb +45 -0
  73. data/lib/wamp/router/client.rb +55 -0
  74. data/lib/wamp/router/connection.rb +150 -0
  75. data/lib/wamp/router/realm.rb +92 -0
  76. data/lib/wamp/router/server.rb +83 -0
  77. data/lib/wamp/router.rb +12 -0
  78. data/lib/wamp/serializer/cbor.rb +18 -0
  79. data/lib/wamp/serializer/json.rb +18 -0
  80. data/lib/wamp/serializer/message_pack.rb +18 -0
  81. data/lib/wamp/serializer.rb +5 -0
  82. data/lib/wamp/type/error.rb +24 -0
  83. data/lib/wamp/type/event.rb +16 -0
  84. data/lib/wamp/type/invocation.rb +16 -0
  85. data/lib/wamp/type/publication.rb +14 -0
  86. data/lib/wamp/type/registration.rb +14 -0
  87. data/lib/wamp/type/result.rb +16 -0
  88. data/lib/wamp/type/subscription.rb +14 -0
  89. data/lib/wamp/type/success.rb +8 -0
  90. data/lib/wamp/type.rb +16 -0
  91. data/lib/wamp/version.rb +5 -0
  92. data/lib/wamp.rb +15 -0
  93. data/mkdocs.yml +67 -0
  94. data/sig/wamp/serializer/json.rbs +10 -0
  95. data/sig/wamp.rbs +11 -0
  96. data/wamp.gemspec +40 -0
  97. metadata +223 -0
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wamp
4
+ module MessageHandler
5
+ # Published confirmation message
6
+ class Published < Base
7
+ def handle
8
+ validate_received_message
9
+
10
+ deliver_response(response)
11
+ end
12
+
13
+ def response
14
+ Type::Publication.new(publication_id: message.publication_id)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wamp
4
+ module MessageHandler
5
+ # publish event to subscriber
6
+ class Register < Base
7
+ def send_message(handler, &callback)
8
+ store[store_key] = { handler: handler, callback: callback, procedure: message.procedure }
9
+
10
+ send_serialized message
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wamp
4
+ module MessageHandler
5
+ # Registered callback
6
+ class Registered < Base
7
+ def handle
8
+ validate_received_message
9
+
10
+ store[alt_store_key] = { handler: stored_data.fetch(:handler), procedure: stored_data.fetch(:procedure) }
11
+ store_procedure
12
+
13
+ deliver_response(response)
14
+ end
15
+
16
+ def response
17
+ Type::Registration.new(registration_id: message.registration_id)
18
+ end
19
+
20
+ def alt_store_key
21
+ "registration_#{message.registration_id}"
22
+ end
23
+
24
+ def store_procedure
25
+ procedure = stored_data.fetch(:procedure)
26
+ store[procedure] = message.registration_id
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wamp
4
+ module MessageHandler
5
+ # Result
6
+ class Result < Base
7
+ def handle
8
+ validate_received_message
9
+ stored_data.fetch(:handler).call(response)
10
+ end
11
+
12
+ def response
13
+ Type::Result.new(args: message.args, kwargs: message.kwargs, details: message.details)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wamp
4
+ module MessageHandler
5
+ # Request subscription
6
+ class Subscribe < Base
7
+ def send_message(handler, &callback)
8
+ store[store_key] = { handler: handler, callback: callback, topic: message.topic }
9
+
10
+ send_serialized message
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wamp
4
+ module MessageHandler
5
+ # Receive subscribed
6
+ class Subscribed < Base
7
+ def handle
8
+ validate_received_message
9
+
10
+ store[alt_store_key] = { handler: stored_data.fetch(:handler), topic: stored_data.fetch(:topic) }
11
+ store_topic
12
+
13
+ deliver_response(response)
14
+ end
15
+
16
+ def response
17
+ Type::Subscription.new(subscription_id: message.subscription_id)
18
+ end
19
+
20
+ def alt_store_key
21
+ "subscription_#{message.subscription_id}"
22
+ end
23
+
24
+ def store_topic
25
+ topic = stored_data.fetch(:topic)
26
+ store[topic] = message.subscription_id
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wamp
4
+ module MessageHandler
5
+ # send unregister message
6
+ class Unregister < Base
7
+ def send_message(&callback)
8
+ store[store_key] = { callback: callback, registration_id: message.registration_id }
9
+
10
+ send_serialized message
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wamp
4
+ module MessageHandler
5
+ # callback for unregister message
6
+ class Unregistered < Base
7
+ def handle
8
+ validate_received_message
9
+
10
+ delete_procedure store.delete(alt_store_key)
11
+
12
+ deliver_response(response)
13
+ end
14
+
15
+ def response
16
+ Type::Success.new
17
+ end
18
+
19
+ def alt_store_key
20
+ "registration_#{registration_id}"
21
+ end
22
+
23
+ def delete_procedure(data)
24
+ store.delete data.fetch(:procedure)
25
+ end
26
+
27
+ def registration_id
28
+ stored_data.fetch(:registration_id)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wamp
4
+ module MessageHandler
5
+ # Request subscription
6
+ class Unsubscribe < Base
7
+ def send_message(&callback)
8
+ store[store_key] = { callback: callback, subscription_id: message.subscription_id }
9
+
10
+ send_serialized message
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wamp
4
+ module MessageHandler
5
+ # Receive unsubscribed
6
+ class Unsubscribed < Base
7
+ def handle
8
+ validate_received_message
9
+ delete_topic store.delete(alt_store_key)
10
+
11
+ deliver_response(response)
12
+ end
13
+
14
+ def response
15
+ Type::Success.new
16
+ end
17
+
18
+ private
19
+
20
+ def alt_store_key
21
+ "subscription_#{subscription_id}"
22
+ end
23
+
24
+ def delete_topic(data)
25
+ store.delete data.fetch(:topic)
26
+ end
27
+
28
+ def subscription_id
29
+ stored_data.fetch(:subscription_id)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wamp
4
+ module MessageHandler
5
+ # Welcome
6
+ class Welcome < Base
7
+ def handle
8
+ connection.executor&.call(connection.api)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "message_handler/base"
4
+
5
+ require_relative "message_handler/hello"
6
+ require_relative "message_handler/welcome"
7
+ require_relative "message_handler/challenge"
8
+ require_relative "message_handler/goodbye"
9
+
10
+ require_relative "message_handler/subscribe"
11
+ require_relative "message_handler/subscribed"
12
+ require_relative "message_handler/unsubscribe"
13
+ require_relative "message_handler/unsubscribed"
14
+
15
+ require_relative "message_handler/publish"
16
+ require_relative "message_handler/published"
17
+ require_relative "message_handler/event"
18
+
19
+ require_relative "message_handler/call"
20
+ require_relative "message_handler/result"
21
+
22
+ require_relative "message_handler/register"
23
+ require_relative "message_handler/registered"
24
+ require_relative "message_handler/unregister"
25
+ require_relative "message_handler/unregistered"
26
+
27
+ require_relative "message_handler/invocation"
28
+
29
+ require_relative "message_handler/error"
30
+
31
+ require_relative "message_handler/api"
32
+
33
+ module Wamp
34
+ # routes messages
35
+ module MessageHandler
36
+ # instantiate correct handler
37
+ module ClassMethods
38
+ def resolve(data, connection)
39
+ # return handle_when_not_joined(data, connection) unless connection.joiner.joined?
40
+
41
+ message = connection.joiner.serializer.deserialize(data)
42
+ klass_name = demodulize(message.class.name)
43
+ klass = constantize("Wamp::MessageHandler::#{klass_name}")
44
+ klass.new(message, connection)
45
+ end
46
+
47
+ def handle_when_not_joined(data, connection)
48
+ authenticate = connection.joiner.receive(data) # maybe welcome message then state should be joined
49
+ connection.transmit authenticate unless connection.joiner.joined?
50
+ connection.executor.call(connection.api) if connection.joiner.joined?
51
+ Struct.new(:handle).new
52
+ end
53
+
54
+ def from(message, connection)
55
+ klass_name = demodulize(message.class.name)
56
+ klass = constantize("Wamp::MessageHandler::#{klass_name}")
57
+ klass.new(message, connection)
58
+ end
59
+
60
+ def demodulize(path)
61
+ path = path.to_s
62
+ if i = path.rindex("::") # rubocop:disable Lint/AssignmentInCondition
63
+ path[(i + 2), path.length]
64
+ else
65
+ path
66
+ end
67
+ end
68
+
69
+ def constantize(camel_cased_word)
70
+ Object.const_get(camel_cased_word)
71
+ end
72
+ end
73
+ extend ClassMethods
74
+ end
75
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "realm"
4
+
5
+ module Wamp
6
+ module Router
7
+ # Router
8
+ class Base
9
+ attr_reader :realms
10
+
11
+ def initialize
12
+ @realms = {}
13
+ end
14
+
15
+ def add_realm(name)
16
+ realms[name] = Realm.new(name)
17
+ end
18
+
19
+ def remove_realm(name)
20
+ realms.delete(name)
21
+ end
22
+
23
+ def attach_client(client)
24
+ error_message = "cannot attach client to non-existent realm #{client.realm}"
25
+ raise Wampproto::ValueError, error_message unless realms.include?(client.realm)
26
+
27
+ realms[client.realm].attach_client(client)
28
+ end
29
+
30
+ def detach_client(client)
31
+ error_message = "cannot attach client to non-existent realm #{client.realm}"
32
+ raise Wampproto::ValueError, error_message unless realms.include?(client.realm)
33
+
34
+ realms[client.realm].detach_client(client)
35
+ end
36
+
37
+ def receive_message(client, message)
38
+ error_message = "cannot attach client to non-existent realm #{client.realm}"
39
+ raise Wampproto::ValueError, error_message unless realms.include?(client.realm)
40
+
41
+ realms[client.realm].receive_message(client.session_id, message)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "realm"
4
+
5
+ module Wamp
6
+ module Router
7
+ # Auth
8
+ class Authenticator
9
+ def self.authenticate(request)
10
+ Wampproto::Acceptor::Response.new(request.authid, "role", "secret")
11
+ end
12
+ end
13
+
14
+ # Server Session
15
+ class Client
16
+ attr_accessor :router, :connection
17
+ attr_reader :acceptor, :serializer
18
+
19
+ def initialize(serializer = Wampproto::Serializer::JSON, authenticator = Authenticator)
20
+ @serializer = serializer
21
+ @acceptor = Wampproto::Acceptor.new(serializer, authenticator)
22
+ end
23
+
24
+ def realm
25
+ acceptor.session_details&.realm
26
+ end
27
+
28
+ def send_message(message)
29
+ transmit(message)
30
+ end
31
+
32
+ def session_id
33
+ acceptor.session_details&.session_id
34
+ end
35
+
36
+ def transmit(data)
37
+ case data
38
+ when Wampproto::Message::Base
39
+ connection.on_message serializer.serialize(data)
40
+ else
41
+ connection.on_message data
42
+ end
43
+ end
44
+
45
+ def on_message(data)
46
+ unless acceptor.accepted?
47
+ msg, is_welcome = acceptor.receive(data)
48
+ transmit msg
49
+ router.attach_client(self) if is_welcome
50
+ end
51
+ router.receive_message(self, serializer.deserialize(data))
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "websocket/driver"
4
+
5
+ # Testing
6
+ class Authenticator
7
+ def self.authenticate(request)
8
+ Wampproto::Acceptor::Response.new(request.authid, "role")
9
+ end
10
+ end
11
+
12
+ module Wamp
13
+ module Router
14
+ # TOP Level Doc
15
+ class Connection < Client # rubocop:disable Metrics/ClassLength
16
+ include WebSocket::Driver::EventEmitter
17
+ CONNECTING = 0
18
+ OPEN = 1
19
+ CLOSING = 2
20
+ CLOSED = 3
21
+
22
+ attr_reader :socket, :session, :acceptor
23
+
24
+ def initialize(socket, &cleanup)
25
+ super()
26
+ @cleanup = cleanup
27
+ @socket = socket
28
+ @driver = WebSocket::Driver.server(self)
29
+ @driver.on(:open) { on_open(_1) }
30
+ @driver.on(:message) { on_message(_1.data) }
31
+ @driver.on(:close) { |evt| begin_close(evt.reason, evt.code) }
32
+ @driver.on(:connect) { on_connect }
33
+ @ready_state = CONNECTING
34
+ end
35
+
36
+ def on_connect
37
+ @driver.start if WebSocket::Driver.websocket?(@driver.env)
38
+ choose_serializer_from @driver.env["HTTP_SEC_WEBSOCKET_PROTOCOL"]
39
+ @acceptor = Wampproto::Acceptor.new(serializer, Authenticator)
40
+ end
41
+
42
+ def connection
43
+ self
44
+ end
45
+
46
+ def begin_close(reason, code)
47
+ return if @ready_state == CLOSED
48
+
49
+ @ready_state = CLOSING
50
+ @close_params = [reason, code]
51
+
52
+ @cleanup&.call(self)
53
+ finalize_close
54
+ end
55
+
56
+ def finalize_close
57
+ return if @ready_state == CLOSED
58
+
59
+ @ready_state = CLOSED
60
+ @driver.close
61
+ socket.close
62
+ end
63
+
64
+ def listen(&block) # rubocop:disable Metrics/MethodLength
65
+ return unless [CONNECTING, OPEN].include?(@ready_state)
66
+
67
+ begin
68
+ data = socket.read_nonblock(4096, exception: false)
69
+ case data
70
+ when :wait_readable
71
+ # do nothing
72
+ when nil
73
+ block&.call
74
+ @driver.close
75
+ else
76
+ receive_data(data)
77
+ end
78
+ rescue StandardError => e
79
+ puts e.message
80
+ puts e.backtrace
81
+ begin
82
+ block&.call
83
+ @driver.close
84
+ rescue StandardError
85
+ # Errno::ECONNRESET
86
+ puts "Failed to handle: Errno::ECONNRESET"
87
+ end
88
+ end
89
+ end
90
+
91
+ # triggers on_message
92
+ def receive_data(data)
93
+ return unless [OPEN, CONNECTING].include?(@ready_state)
94
+
95
+ @driver.parse(data)
96
+ end
97
+
98
+ # called when @driver.text is invoked
99
+ def write(data)
100
+ socket.write(data)
101
+ end
102
+
103
+ def transmit(message)
104
+ # return false if @ready_state > OPEN
105
+
106
+ case message
107
+ when Wampproto::Message::Base then transmit(serializer.serialize(message))
108
+ when Numeric then @driver.text(message.to_s)
109
+ when String then @driver.text(message)
110
+ when Array then @driver.binary(message)
111
+ else false
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ def on_open(_evt)
118
+ return unless @ready_state == CONNECTING
119
+
120
+ @ready_state = OPEN
121
+ end
122
+
123
+ def on_close(message)
124
+ return if @ready_state == CLOSED
125
+
126
+ @ready_state = CLOSED
127
+ socket.close
128
+ @driver.close(message.code, message.reason)
129
+ end
130
+
131
+ def close(_code, _reason)
132
+ @driver.close
133
+ end
134
+
135
+ attr_reader :serializer
136
+
137
+ def choose_serializer_from(protocols)
138
+ @serializer = if protocols.include?("wamp.2.msgpack")
139
+ Wampproto::Serializer::Msgpack
140
+ elsif protocols.include?("wamp.2.cbor")
141
+ Wampproto::Serializer::Cbor
142
+ elsif protocols.include?("wamp.2.json")
143
+ Wampproto::Serializer::JSON
144
+ else
145
+ close
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wamp
4
+ module Router
5
+ # Realm
6
+ class Realm
7
+ attr_reader :broker, :dealer, :name, :clients
8
+
9
+ DEALER_MESSAGES = [
10
+ Wampproto::Message::Call,
11
+ Wampproto::Message::Yield,
12
+ Wampproto::Message::Register,
13
+ Wampproto::Message::Unregister
14
+ ].freeze
15
+
16
+ BROKER_MESSAGES = [
17
+ Wampproto::Message::Publish,
18
+ Wampproto::Message::Subscribe,
19
+ Wampproto::Message::Unsubscribe
20
+ ].freeze
21
+
22
+ GOODBYE_MESSAGE = Wampproto::Message::Goodbye
23
+
24
+ def initialize(name)
25
+ @name = name
26
+ @broker = Wampproto::Broker.new(id_gen)
27
+ @dealer = Wampproto::Dealer.new(id_gen)
28
+ @clients = {}
29
+ end
30
+
31
+ def attach_client(client)
32
+ session_id = client.session_id
33
+ session_details = client.acceptor.session_details
34
+
35
+ clients[session_id] = client
36
+ broker.add_session(session_details)
37
+ dealer.add_session(session_details)
38
+ end
39
+
40
+ def detach_client(client)
41
+ remove_client(client.session_id)
42
+ end
43
+
44
+ def clear
45
+ clients.each { |client| remove_client(client.session_id) }
46
+ end
47
+
48
+ def receive_message(session_id, message)
49
+ case message
50
+ when *DEALER_MESSAGES then handle_dealer(session_id, message)
51
+ when *BROKER_MESSAGES then handle_broker(session_id, message)
52
+ when GOODBYE_MESSAGE then handle_goodbye(session_id, message)
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def handle_dealer(session_id, message)
59
+ send_message dealer.receive_message(session_id, message)
60
+ end
61
+
62
+ def handle_broker(session_id, message)
63
+ send_message broker.receive_message(session_id, message)
64
+ end
65
+
66
+ def handle_goodbye(session_id, _message)
67
+ goodbye = Wampproto::Message::Goodbye.new({}, "wamp.close.goodbye_and_out")
68
+ send_message Wampproto::MessageWithRecipient.new(goodbye, session_id)
69
+ end
70
+
71
+ def send_message(message_with_receipient)
72
+ Array(message_with_receipient).each do |object|
73
+ client = clients[object.recipient]
74
+
75
+ next unless client
76
+
77
+ client.send_message object.message
78
+ end
79
+ end
80
+
81
+ def id_gen
82
+ @id_gen ||= Wampproto::IdGenerator.new
83
+ end
84
+
85
+ def remove_client(session_id)
86
+ broker.remove_session(session_id)
87
+ dealer.remove_session(session_id)
88
+ clients.delete(session_id)
89
+ end
90
+ end
91
+ end
92
+ end