ably-pubsub-core 0.0.1.pre
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/lib/ably/agent.rb +8 -0
- data/lib/ably/auth.rb +791 -0
- data/lib/ably/exceptions.rb +169 -0
- data/lib/ably/logger.rb +119 -0
- data/lib/ably/models/auth_details.rb +45 -0
- data/lib/ably/models/channel_details.rb +69 -0
- data/lib/ably/models/channel_metrics.rb +107 -0
- data/lib/ably/models/channel_occupancy.rb +50 -0
- data/lib/ably/models/channel_options.rb +120 -0
- data/lib/ably/models/channel_state_change.rb +84 -0
- data/lib/ably/models/channel_status.rb +63 -0
- data/lib/ably/models/cipher_params.rb +137 -0
- data/lib/ably/models/connection_details.rb +149 -0
- data/lib/ably/models/connection_state_change.rb +83 -0
- data/lib/ably/models/delta_extras.rb +28 -0
- data/lib/ably/models/device_details.rb +126 -0
- data/lib/ably/models/device_push_details.rb +94 -0
- data/lib/ably/models/error_info.rb +104 -0
- data/lib/ably/models/http_paginated_response.rb +112 -0
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +235 -0
- data/lib/ably/models/message.rb +223 -0
- data/lib/ably/models/message_encoders/base.rb +135 -0
- data/lib/ably/models/message_encoders/base64.rb +40 -0
- data/lib/ably/models/message_encoders/cipher.rb +84 -0
- data/lib/ably/models/message_encoders/json.rb +34 -0
- data/lib/ably/models/message_encoders/utf8.rb +26 -0
- data/lib/ably/models/nil_logger.rb +20 -0
- data/lib/ably/models/paginated_result.rb +201 -0
- data/lib/ably/models/presence_message.rb +244 -0
- data/lib/ably/models/protocol_message.rb +288 -0
- data/lib/ably/models/push_channel_subscription.rb +114 -0
- data/lib/ably/models/stats.rb +242 -0
- data/lib/ably/models/stats_types.rb +107 -0
- data/lib/ably/models/token_details.rb +148 -0
- data/lib/ably/models/token_request.rb +180 -0
- data/lib/ably/modules/ably.rb +26 -0
- data/lib/ably/modules/async_wrapper.rb +69 -0
- data/lib/ably/modules/channels_collection.rb +105 -0
- data/lib/ably/modules/conversions.rb +153 -0
- data/lib/ably/modules/encodeable.rb +111 -0
- data/lib/ably/modules/enum.rb +230 -0
- data/lib/ably/modules/event_emitter.rb +200 -0
- data/lib/ably/modules/event_machine_helpers.rb +26 -0
- data/lib/ably/modules/exception_codes.rb +128 -0
- data/lib/ably/modules/http_helpers.rb +41 -0
- data/lib/ably/modules/message_emitter.rb +78 -0
- data/lib/ably/modules/message_pack.rb +14 -0
- data/lib/ably/modules/model_common.rb +84 -0
- data/lib/ably/modules/safe_deferrable.rb +83 -0
- data/lib/ably/modules/safe_yield.rb +41 -0
- data/lib/ably/modules/state_emitter.rb +181 -0
- data/lib/ably/modules/state_machine.rb +59 -0
- data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/ably/modules/uses_state_machine.rb +111 -0
- data/lib/ably/realtime/auth.rb +283 -0
- data/lib/ably/realtime/channel/channel_manager.rb +272 -0
- data/lib/ably/realtime/channel/channel_properties.rb +40 -0
- data/lib/ably/realtime/channel/channel_state_machine.rb +104 -0
- data/lib/ably/realtime/channel/publisher.rb +82 -0
- data/lib/ably/realtime/channel/push_channel.rb +69 -0
- data/lib/ably/realtime/channel.rb +440 -0
- data/lib/ably/realtime/channels.rb +71 -0
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +225 -0
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +87 -0
- data/lib/ably/realtime/client.rb +363 -0
- data/lib/ably/realtime/connection/connection_manager.rb +594 -0
- data/lib/ably/realtime/connection/connection_state_machine.rb +144 -0
- data/lib/ably/realtime/connection/websocket_transport.rb +287 -0
- data/lib/ably/realtime/connection.rb +738 -0
- data/lib/ably/realtime/models/nil_channel.rb +30 -0
- data/lib/ably/realtime/presence/members_map.rb +395 -0
- data/lib/ably/realtime/presence/presence_manager.rb +78 -0
- data/lib/ably/realtime/presence/presence_state_machine.rb +57 -0
- data/lib/ably/realtime/presence.rb +502 -0
- data/lib/ably/realtime/push/admin.rb +75 -0
- data/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
- data/lib/ably/realtime/push/device_registrations.rb +105 -0
- data/lib/ably/realtime/push.rb +25 -0
- data/lib/ably/realtime/recovery_key_context.rb +36 -0
- data/lib/ably/realtime.rb +60 -0
- data/lib/ably/rest/channel/push_channel.rb +59 -0
- data/lib/ably/rest/channel.rb +194 -0
- data/lib/ably/rest/channels.rb +47 -0
- data/lib/ably/rest/client.rb +727 -0
- data/lib/ably/rest/middleware/encoder.rb +49 -0
- data/lib/ably/rest/middleware/exceptions.rb +53 -0
- data/lib/ably/rest/middleware/external_exceptions.rb +24 -0
- data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +22 -0
- data/lib/ably/rest/middleware/logger.rb +58 -0
- data/lib/ably/rest/middleware/parse_json.rb +27 -0
- data/lib/ably/rest/middleware/parse_message_pack.rb +43 -0
- data/lib/ably/rest/presence.rb +113 -0
- data/lib/ably/rest/push/admin.rb +58 -0
- data/lib/ably/rest/push/channel_subscriptions.rb +121 -0
- data/lib/ably/rest/push/device_registrations.rb +114 -0
- data/lib/ably/rest/push.rb +25 -0
- data/lib/ably/rest.rb +40 -0
- data/lib/ably/util/ably_extensions.rb +29 -0
- data/lib/ably/util/crypto.rb +134 -0
- data/lib/ably/util/pub_sub.rb +45 -0
- data/lib/ably/util/safe_deferrable.rb +44 -0
- data/lib/ably/version.rb +7 -0
- data/lib/ably.rb +16 -0
- metadata +497 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
require 'ably/modules/state_machine'
|
|
2
|
+
|
|
3
|
+
module Ably::Realtime
|
|
4
|
+
class Connection
|
|
5
|
+
# Internal class to manage connection state, recovery and state transitions for {Ably::Realtime::Connection}
|
|
6
|
+
class ConnectionStateMachine
|
|
7
|
+
include Ably::Modules::StateMachine
|
|
8
|
+
|
|
9
|
+
# States supported by this StateMachine match #{Connection::STATE}s
|
|
10
|
+
# :initialized
|
|
11
|
+
# :connecting
|
|
12
|
+
# :connected
|
|
13
|
+
# :disconnected
|
|
14
|
+
# :suspended
|
|
15
|
+
# :closing
|
|
16
|
+
# :closed
|
|
17
|
+
# :failed
|
|
18
|
+
Connection::STATE.each_with_index do |state_enum, index|
|
|
19
|
+
state state_enum.to_sym, initial: index == 0
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
transition :from => :initialized, :to => [:connecting, :closing]
|
|
23
|
+
transition :from => :connecting, :to => [:connected, :failed, :closing, :disconnected, :suspended]
|
|
24
|
+
transition :from => :connected, :to => [:disconnected, :suspended, :closing, :failed]
|
|
25
|
+
transition :from => :disconnected, :to => [:connecting, :closing, :suspended, :failed]
|
|
26
|
+
transition :from => :suspended, :to => [:connecting, :closing, :failed]
|
|
27
|
+
transition :from => :closing, :to => [:closed]
|
|
28
|
+
transition :from => :closed, :to => [:connecting]
|
|
29
|
+
transition :from => :failed, :to => [:connecting]
|
|
30
|
+
|
|
31
|
+
after_transition do |connection, transition|
|
|
32
|
+
connection.synchronize_state_with_statemachine
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
after_transition(to: [:connecting], from: [:initialized, :closed, :failed]) do |connection|
|
|
36
|
+
connection.manager.setup_transport
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
after_transition(to: [:connecting], from: [:failed]) do |connection|
|
|
40
|
+
connection.manager.reintialize_failed_chanels
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
after_transition(to: [:connecting], from: [:disconnected, :suspended]) do |connection|
|
|
44
|
+
connection.manager.reconnect_transport
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
before_transition(to: [:connected]) do |connection, current_transition|
|
|
48
|
+
connection.manager.connected current_transition.metadata.protocol_message
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
after_transition(to: [:connected]) do |connection, current_transition|
|
|
52
|
+
error = current_transition.metadata.reason
|
|
53
|
+
if is_error_type?(error)
|
|
54
|
+
connection.logger.warn { "ConnectionManager: Connected with error - #{error.message}" }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
after_transition(to: [:disconnected, :suspended], from: [:connecting]) do |connection, current_transition|
|
|
59
|
+
err = error_from_state_change(current_transition)
|
|
60
|
+
connection.manager.respond_to_transport_disconnected_when_connecting err
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
after_transition(to: [:disconnected, :suspended], from: [:connected]) do |connection, current_transition|
|
|
64
|
+
err = error_from_state_change(current_transition)
|
|
65
|
+
connection.manager.respond_to_transport_disconnected_whilst_connected err
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
after_transition(to: [:suspended]) do |connection, current_transition|
|
|
69
|
+
err = error_from_state_change(current_transition)
|
|
70
|
+
connection.manager.suspend_active_channels err
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
after_transition(to: [:disconnected, :suspended]) do |connection|
|
|
74
|
+
connection.manager.destroy_transport # never reuse a transport if the connection has failed
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
before_transition(to: [:failed]) do |connection, current_transition|
|
|
78
|
+
err = error_from_state_change(current_transition)
|
|
79
|
+
connection.manager.fail err
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
after_transition(to: [:failed]) do |connection, current_transition|
|
|
83
|
+
err = error_from_state_change(current_transition)
|
|
84
|
+
connection.manager.fail_active_channels err
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# RTN7C - If a connection enters the SUSPENDED, CLOSED or FAILED state...
|
|
88
|
+
# the client should consider the delivery of those messages as failed
|
|
89
|
+
after_transition(to: [:suspended, :closed, :failed]) do |connection, current_transition|
|
|
90
|
+
err = error_from_state_change(current_transition)
|
|
91
|
+
connection.manager.nack_messages_on_all_channels err
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
after_transition(to: [:closing], from: [:initialized, :disconnected, :suspended]) do |connection|
|
|
95
|
+
connection.manager.force_close_connection
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
after_transition(to: [:closing], from: [:connecting, :connected]) do |connection|
|
|
99
|
+
connection.manager.close_connection
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
before_transition(to: [:closed], from: [:closing]) do |connection|
|
|
103
|
+
connection.manager.destroy_transport
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
after_transition(to: [:closed]) do |connection|
|
|
107
|
+
connection.manager.detach_active_channels
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Transitions responsible for updating connection#error_reason
|
|
111
|
+
before_transition(to: [:disconnected, :suspended, :failed]) do |connection, current_transition|
|
|
112
|
+
err = error_from_state_change(current_transition)
|
|
113
|
+
connection.set_failed_connection_error_reason err
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
before_transition(to: [:connected, :closed]) do |connection, current_transition|
|
|
117
|
+
err = error_from_state_change(current_transition)
|
|
118
|
+
if err
|
|
119
|
+
connection.set_failed_connection_error_reason err
|
|
120
|
+
else
|
|
121
|
+
# Connected & Closed are "healthy" final states so reset the error reason
|
|
122
|
+
connection.clear_error_reason
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def self.error_from_state_change(current_transition)
|
|
127
|
+
# ConnectionStateChange object is always passed in current_transition metadata object
|
|
128
|
+
connection_state_change = current_transition.metadata
|
|
129
|
+
# Reason attribute contains errors
|
|
130
|
+
err = connection_state_change && connection_state_change.reason
|
|
131
|
+
err if is_error_type?(err)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
private
|
|
135
|
+
def connection
|
|
136
|
+
object
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def logger
|
|
140
|
+
connection.logger
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
|
|
3
|
+
module Ably::Realtime
|
|
4
|
+
class Connection
|
|
5
|
+
# EventMachine WebSocket transport
|
|
6
|
+
# @api private
|
|
7
|
+
class WebsocketTransport < EventMachine::Connection
|
|
8
|
+
include Ably::Modules::EventEmitter
|
|
9
|
+
extend Ably::Modules::Enum
|
|
10
|
+
|
|
11
|
+
# Valid WebSocket connection states
|
|
12
|
+
STATE = ruby_enum('STATE',
|
|
13
|
+
:initialized,
|
|
14
|
+
:connecting,
|
|
15
|
+
:connected,
|
|
16
|
+
:disconnecting,
|
|
17
|
+
:disconnected
|
|
18
|
+
)
|
|
19
|
+
include Ably::Modules::StateEmitter
|
|
20
|
+
|
|
21
|
+
attr_reader :host
|
|
22
|
+
|
|
23
|
+
def initialize(connection, url)
|
|
24
|
+
@connection = connection
|
|
25
|
+
@state = STATE.Initialized
|
|
26
|
+
@url = url
|
|
27
|
+
@host = URI.parse(url).hostname
|
|
28
|
+
|
|
29
|
+
setup_event_handlers
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Disconnect the socket transport connection and write all pending text.
|
|
33
|
+
# If Disconnected state is not automatically emitted, it will be emitted automatically
|
|
34
|
+
#
|
|
35
|
+
# @return [void]
|
|
36
|
+
#
|
|
37
|
+
# @api public
|
|
38
|
+
def disconnect
|
|
39
|
+
close_connection_after_writing
|
|
40
|
+
change_state STATE.Disconnecting
|
|
41
|
+
create_timer(2) do
|
|
42
|
+
# if connection is not disconnected within 2s, set state as disconnected
|
|
43
|
+
change_state STATE.Disconnected unless disconnected?
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Network connection has been established
|
|
48
|
+
# Required {http://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Connection EventMachine::Connection} interface
|
|
49
|
+
def post_init
|
|
50
|
+
clear_timer
|
|
51
|
+
change_state STATE.Connecting
|
|
52
|
+
setup_driver
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Remote TCP connection attempt completes successfully
|
|
56
|
+
# Required {http://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Connection EventMachine::Connection} interface
|
|
57
|
+
def connection_completed
|
|
58
|
+
change_state STATE.Connected
|
|
59
|
+
start_tls(tls_opts) if client.use_tls?
|
|
60
|
+
driver.start
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Called by the event loop whenever data has been received by the network connection.
|
|
64
|
+
# Simply pass onto the WebSocket driver to process and determine content boundaries.
|
|
65
|
+
# Required {http://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Connection EventMachine::Connection} interface
|
|
66
|
+
def receive_data(data)
|
|
67
|
+
driver.parse(data)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Called whenever a connection (either a server or client connection) is closed
|
|
71
|
+
# Required {http://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Connection EventMachine::Connection} interface
|
|
72
|
+
def unbind
|
|
73
|
+
change_state STATE.Disconnected, reason_closed || 'Websocket connection closed unexpectedly'
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# URL end point including initialization configuration
|
|
77
|
+
# {http://www.rubydoc.info/gems/websocket-driver/0.3.5/WebSocket/Driver WebSocket::Driver} interface
|
|
78
|
+
def url
|
|
79
|
+
@url
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# {http://www.rubydoc.info/gems/websocket-driver/0.3.5/WebSocket/Driver WebSocket::Driver} interface
|
|
83
|
+
def write(data)
|
|
84
|
+
send_data(data)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# TLS verification support, original implementation by Mislav Marohnić:
|
|
88
|
+
#
|
|
89
|
+
# https://github.com/lostisland/faraday/commit/63cf47c95b573539f047c729bd9ad67560bc83ff
|
|
90
|
+
def ssl_verify_peer(cert_string)
|
|
91
|
+
cert = nil
|
|
92
|
+
begin
|
|
93
|
+
cert = OpenSSL::X509::Certificate.new(cert_string)
|
|
94
|
+
rescue OpenSSL::X509::CertificateError => e
|
|
95
|
+
disconnect_with_reason "Websocket host '#{host}' returned an invalid TLS certificate: #{e.message}"
|
|
96
|
+
return false
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
@last_seen_cert = cert
|
|
100
|
+
|
|
101
|
+
if certificate_store.verify(@last_seen_cert)
|
|
102
|
+
begin
|
|
103
|
+
certificate_store.add_cert(@last_seen_cert)
|
|
104
|
+
rescue OpenSSL::X509::StoreError => e
|
|
105
|
+
unless e.message == 'cert already in hash table'
|
|
106
|
+
disconnect_with_reason "Websocket host '#{host}' returned an invalid TLS certificate: #{e.message}"
|
|
107
|
+
return false
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
true
|
|
111
|
+
else
|
|
112
|
+
disconnect_with_reason "Websocket host '#{host}' returned an invalid TLS certificate"
|
|
113
|
+
false
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def ssl_handshake_completed
|
|
118
|
+
unless OpenSSL::SSL.verify_certificate_identity(@last_seen_cert, host)
|
|
119
|
+
disconnect_with_reason "Websocket host '#{host}' returned an invalid TLS certificate"
|
|
120
|
+
false
|
|
121
|
+
else
|
|
122
|
+
true
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def certificate_store
|
|
127
|
+
@certificate_store ||= OpenSSL::X509::Store.new.tap do |store|
|
|
128
|
+
store.set_default_paths
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# True if socket connection is ready to be released
|
|
133
|
+
# i.e. it is not currently connecting or connected
|
|
134
|
+
def ready_for_release?
|
|
135
|
+
!connecting? && !connected?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# @return [Ably::Util::PubSub] Websocket Transport internal incoming protocol message bus
|
|
139
|
+
#
|
|
140
|
+
# @api private
|
|
141
|
+
def __incoming_protocol_msgbus__
|
|
142
|
+
@__incoming_protocol_msgbus__ ||= create_pub_sub_message_bus
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# @return [Ably::Util::PubSub] Websocket Transport internal outgoing protocol message bus
|
|
146
|
+
#
|
|
147
|
+
# @api private
|
|
148
|
+
def __outgoing_protocol_msgbus__
|
|
149
|
+
@__outgoing_protocol_msgbus__ ||= create_pub_sub_message_bus
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
private
|
|
153
|
+
def driver
|
|
154
|
+
@driver
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def connection
|
|
158
|
+
@connection
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def disconnect_with_reason(reason)
|
|
162
|
+
client.logger.error { "WebsocketTransport: Disconnecting due to error: #{reason}" }
|
|
163
|
+
@reason_closed = reason
|
|
164
|
+
disconnect
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def reason_closed
|
|
168
|
+
@reason_closed
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Send object down the WebSocket driver connection as a serialized string/byte array based on protocol
|
|
172
|
+
# @param [Object] object to serialize and send to the WebSocket driver
|
|
173
|
+
def send_object(object)
|
|
174
|
+
case client.protocol
|
|
175
|
+
when :json
|
|
176
|
+
driver.text(object.to_json)
|
|
177
|
+
when :msgpack
|
|
178
|
+
driver.binary(object.to_msgpack.unpack('C*'))
|
|
179
|
+
else
|
|
180
|
+
client.logger.fatal { "WebsocketTransport: Unsupported protocol '#{client.protocol}' for serialization, object cannot be serialized and sent to Ably over this WebSocket" }
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def setup_event_handlers
|
|
185
|
+
__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
|
|
186
|
+
send_object protocol_message
|
|
187
|
+
client.logger.debug { "WebsocketTransport: Prot msg sent =>: #{protocol_message.action} #{protocol_message}" }
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def clear_timer
|
|
192
|
+
if defined?(@timer) && @timer
|
|
193
|
+
@timer.cancel
|
|
194
|
+
@timer = nil
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def create_timer(period)
|
|
199
|
+
@timer = EventMachine::Timer.new(period) do
|
|
200
|
+
yield
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def setup_driver
|
|
205
|
+
@driver = WebSocket::Driver.client(self)
|
|
206
|
+
|
|
207
|
+
driver.on("open") do
|
|
208
|
+
logger.debug { "WebsocketTransport: socket opened to #{url}, waiting for Connected protocol message" }
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
driver.on("message") do |event|
|
|
212
|
+
event_data = parse_event_data(event.data).freeze
|
|
213
|
+
protocol_message = Ably::Models::ProtocolMessage.new(event_data, logger: logger)
|
|
214
|
+
action_name = Ably::Models::ProtocolMessage::ACTION[event_data['action']] rescue event_data['action']
|
|
215
|
+
logger.debug { "WebsocketTransport: Prot msg recv <=: #{action_name} - #{event_data}" }
|
|
216
|
+
|
|
217
|
+
if protocol_message.invalid?
|
|
218
|
+
error = Ably::Exceptions::ProtocolError.new("Invalid Protocol Message received: #{event_data}\nConnection moving to the failed state as the protocol is invalid and unsupported", 400, Ably::Exceptions::Codes::PROTOCOL_ERROR)
|
|
219
|
+
logger.fatal { "WebsocketTransport: #{error.message}" }
|
|
220
|
+
failed_protocol_message = Ably::Models::ProtocolMessage.new(
|
|
221
|
+
action: Ably::Models::ProtocolMessage::ACTION.Error,
|
|
222
|
+
error: error.as_json,
|
|
223
|
+
logger: logger
|
|
224
|
+
)
|
|
225
|
+
__incoming_protocol_msgbus__.publish :protocol_message, failed_protocol_message
|
|
226
|
+
else
|
|
227
|
+
__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
driver.on("ping") do
|
|
232
|
+
__incoming_protocol_msgbus__.publish :protocol_message, Ably::Models::ProtocolMessage.new(action: Ably::Models::ProtocolMessage::ACTION.Heartbeat, source: :websocket)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
driver.on("error") do |error|
|
|
236
|
+
logger.error { "WebsocketTransport: Protocol Error on transports - #{error.message}" }
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
@reason_closed = nil
|
|
240
|
+
driver.on("closed") do |event|
|
|
241
|
+
@reason_closed = "#{event.code}: #{event.reason}"
|
|
242
|
+
logger.warn { "WebsocketTransport: Driver reported transport as closed - #{reason_closed}" }
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def client
|
|
247
|
+
connection.client
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Used to log transport messages
|
|
251
|
+
def logger
|
|
252
|
+
connection.logger
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def parse_event_data(data)
|
|
256
|
+
case client.protocol
|
|
257
|
+
when :json
|
|
258
|
+
JSON.parse(data)
|
|
259
|
+
when :msgpack
|
|
260
|
+
MessagePack.unpack(data)
|
|
261
|
+
else
|
|
262
|
+
client.logger.fatal { "WebsocketTransport: Unsupported Protocol Message format #{client.protocol}" }
|
|
263
|
+
data
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def create_pub_sub_message_bus
|
|
268
|
+
Ably::Util::PubSub.new(
|
|
269
|
+
coerce_into: lambda do |event|
|
|
270
|
+
raise KeyError, "Expected :protocol_message, :#{event} is disallowed" unless event == :protocol_message
|
|
271
|
+
:protocol_message
|
|
272
|
+
end
|
|
273
|
+
)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# TLS options to pass to EventMachine::Connection#start_tls
|
|
277
|
+
#
|
|
278
|
+
# See https://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Connection#start_tls-instance_method
|
|
279
|
+
def tls_opts
|
|
280
|
+
{
|
|
281
|
+
sni_hostname: host,
|
|
282
|
+
verify_peer: true,
|
|
283
|
+
}
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|