actioncable 5.2.6.3 → 6.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +55 -39
- data/MIT-LICENSE +1 -1
- data/README.md +3 -546
- data/app/assets/javascripts/action_cable.js +574 -0
- data/lib/action_cable/channel/base.rb +10 -4
- data/lib/action_cable/channel/broadcasting.rb +18 -8
- data/lib/action_cable/channel/naming.rb +1 -1
- data/lib/action_cable/channel/streams.rb +30 -4
- data/lib/action_cable/channel/test_case.rb +310 -0
- data/lib/action_cable/channel.rb +1 -0
- data/lib/action_cable/connection/authorization.rb +1 -1
- data/lib/action_cable/connection/base.rb +13 -7
- data/lib/action_cable/connection/message_buffer.rb +1 -4
- data/lib/action_cable/connection/stream.rb +4 -2
- data/lib/action_cable/connection/subscriptions.rb +2 -5
- data/lib/action_cable/connection/test_case.rb +234 -0
- data/lib/action_cable/connection/web_socket.rb +1 -3
- data/lib/action_cable/connection.rb +1 -0
- data/lib/action_cable/engine.rb +1 -1
- data/lib/action_cable/gem_version.rb +4 -4
- data/lib/action_cable/helpers/action_cable_helper.rb +3 -3
- data/lib/action_cable/remote_connections.rb +1 -1
- data/lib/action_cable/server/base.rb +9 -4
- data/lib/action_cable/server/broadcasting.rb +1 -1
- data/lib/action_cable/server/worker.rb +6 -8
- data/lib/action_cable/server.rb +0 -1
- data/lib/action_cable/subscription_adapter/base.rb +4 -0
- data/lib/action_cable/subscription_adapter/postgresql.rb +28 -9
- data/lib/action_cable/subscription_adapter/redis.rb +4 -2
- data/lib/action_cable/subscription_adapter/test.rb +40 -0
- data/lib/action_cable/subscription_adapter.rb +1 -0
- data/lib/action_cable/test_case.rb +11 -0
- data/lib/action_cable/test_helper.rb +133 -0
- data/lib/action_cable.rb +15 -7
- data/lib/rails/generators/channel/USAGE +5 -6
- data/lib/rails/generators/channel/channel_generator.rb +6 -3
- data/lib/rails/generators/channel/templates/{assets → javascript}/channel.js.tt +6 -4
- data/lib/rails/generators/channel/templates/javascript/consumer.js.tt +6 -0
- data/lib/rails/generators/channel/templates/javascript/index.js.tt +5 -0
- data/lib/rails/generators/test_unit/channel_generator.rb +20 -0
- data/lib/rails/generators/test_unit/templates/channel_test.rb.tt +8 -0
- metadata +41 -16
- data/lib/assets/compiled/action_cable.js +0 -601
- data/lib/rails/generators/channel/templates/assets/cable.js.tt +0 -13
- data/lib/rails/generators/channel/templates/assets/channel.coffee.tt +0 -14
|
@@ -25,7 +25,7 @@ module ActionCable
|
|
|
25
25
|
#
|
|
26
26
|
# An example broadcasting for this channel looks like so:
|
|
27
27
|
#
|
|
28
|
-
# ActionCable.server.broadcast "comments_for_45", author: 'DHH', content: 'Rails is just swell'
|
|
28
|
+
# ActionCable.server.broadcast "comments_for_45", { author: 'DHH', content: 'Rails is just swell' }
|
|
29
29
|
#
|
|
30
30
|
# If you have a stream that is related to a model, then the broadcasting used can be generated from the model and channel.
|
|
31
31
|
# The following example would subscribe to a broadcasting like <tt>comments:Z2lkOi8vVGVzdEFwcC9Qb3N0LzE</tt>.
|
|
@@ -82,7 +82,7 @@ module ActionCable
|
|
|
82
82
|
# Build a stream handler by wrapping the user-provided callback with
|
|
83
83
|
# a decoder or defaulting to a JSON-decoding retransmitter.
|
|
84
84
|
handler = worker_pool_stream_handler(broadcasting, callback || block, coder: coder)
|
|
85
|
-
streams
|
|
85
|
+
streams[broadcasting] = handler
|
|
86
86
|
|
|
87
87
|
connection.server.event_loop.post do
|
|
88
88
|
pubsub.subscribe(broadcasting, handler, lambda do
|
|
@@ -99,7 +99,21 @@ module ActionCable
|
|
|
99
99
|
# Pass <tt>coder: ActiveSupport::JSON</tt> to decode messages as JSON before passing to the callback.
|
|
100
100
|
# Defaults to <tt>coder: nil</tt> which does no decoding, passes raw messages.
|
|
101
101
|
def stream_for(model, callback = nil, coder: nil, &block)
|
|
102
|
-
stream_from(broadcasting_for(
|
|
102
|
+
stream_from(broadcasting_for(model), callback || block, coder: coder)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Unsubscribes streams from the named <tt>broadcasting</tt>.
|
|
106
|
+
def stop_stream_from(broadcasting)
|
|
107
|
+
callback = streams.delete(broadcasting)
|
|
108
|
+
if callback
|
|
109
|
+
pubsub.unsubscribe(broadcasting, callback)
|
|
110
|
+
logger.info "#{self.class.name} stopped streaming from #{broadcasting}"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Unsubscribes streams for the <tt>model</tt>.
|
|
115
|
+
def stop_stream_for(model)
|
|
116
|
+
stop_stream_from(broadcasting_for(model))
|
|
103
117
|
end
|
|
104
118
|
|
|
105
119
|
# Unsubscribes all streams associated with this channel from the pubsub queue.
|
|
@@ -110,11 +124,23 @@ module ActionCable
|
|
|
110
124
|
end.clear
|
|
111
125
|
end
|
|
112
126
|
|
|
127
|
+
# Calls stream_for if record is present, otherwise calls reject.
|
|
128
|
+
# This method is intended to be called when you're looking
|
|
129
|
+
# for a record based on a parameter, if its found it will start
|
|
130
|
+
# streaming. If the record is nil then it will reject the connection.
|
|
131
|
+
def stream_or_reject_for(record)
|
|
132
|
+
if record
|
|
133
|
+
stream_for record
|
|
134
|
+
else
|
|
135
|
+
reject
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
113
139
|
private
|
|
114
140
|
delegate :pubsub, to: :connection
|
|
115
141
|
|
|
116
142
|
def streams
|
|
117
|
-
@_streams ||=
|
|
143
|
+
@_streams ||= {}
|
|
118
144
|
end
|
|
119
145
|
|
|
120
146
|
# Always wrap the outermost handler to invoke the user handler on the
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support"
|
|
4
|
+
require "active_support/test_case"
|
|
5
|
+
require "active_support/core_ext/hash/indifferent_access"
|
|
6
|
+
require "json"
|
|
7
|
+
|
|
8
|
+
module ActionCable
|
|
9
|
+
module Channel
|
|
10
|
+
class NonInferrableChannelError < ::StandardError
|
|
11
|
+
def initialize(name)
|
|
12
|
+
super "Unable to determine the channel to test from #{name}. " +
|
|
13
|
+
"You'll need to specify it using `tests YourChannel` in your " +
|
|
14
|
+
"test case definition."
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Stub +stream_from+ to track streams for the channel.
|
|
19
|
+
# Add public aliases for +subscription_confirmation_sent?+ and
|
|
20
|
+
# +subscription_rejected?+.
|
|
21
|
+
module ChannelStub
|
|
22
|
+
def confirmed?
|
|
23
|
+
subscription_confirmation_sent?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def rejected?
|
|
27
|
+
subscription_rejected?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def stream_from(broadcasting, *)
|
|
31
|
+
streams << broadcasting
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def stop_all_streams
|
|
35
|
+
@_streams = []
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def streams
|
|
39
|
+
@_streams ||= []
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Make periodic timers no-op
|
|
43
|
+
def start_periodic_timers; end
|
|
44
|
+
alias stop_periodic_timers start_periodic_timers
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class ConnectionStub
|
|
48
|
+
attr_reader :transmissions, :identifiers, :subscriptions, :logger
|
|
49
|
+
|
|
50
|
+
def initialize(identifiers = {})
|
|
51
|
+
@transmissions = []
|
|
52
|
+
|
|
53
|
+
identifiers.each do |identifier, val|
|
|
54
|
+
define_singleton_method(identifier) { val }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
@subscriptions = ActionCable::Connection::Subscriptions.new(self)
|
|
58
|
+
@identifiers = identifiers.keys
|
|
59
|
+
@logger = ActiveSupport::TaggedLogging.new ActiveSupport::Logger.new(StringIO.new)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def transmit(cable_message)
|
|
63
|
+
transmissions << cable_message.with_indifferent_access
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Superclass for Action Cable channel functional tests.
|
|
68
|
+
#
|
|
69
|
+
# == Basic example
|
|
70
|
+
#
|
|
71
|
+
# Functional tests are written as follows:
|
|
72
|
+
# 1. First, one uses the +subscribe+ method to simulate subscription creation.
|
|
73
|
+
# 2. Then, one asserts whether the current state is as expected. "State" can be anything:
|
|
74
|
+
# transmitted messages, subscribed streams, etc.
|
|
75
|
+
#
|
|
76
|
+
# For example:
|
|
77
|
+
#
|
|
78
|
+
# class ChatChannelTest < ActionCable::Channel::TestCase
|
|
79
|
+
# def test_subscribed_with_room_number
|
|
80
|
+
# # Simulate a subscription creation
|
|
81
|
+
# subscribe room_number: 1
|
|
82
|
+
#
|
|
83
|
+
# # Asserts that the subscription was successfully created
|
|
84
|
+
# assert subscription.confirmed?
|
|
85
|
+
#
|
|
86
|
+
# # Asserts that the channel subscribes connection to a stream
|
|
87
|
+
# assert_has_stream "chat_1"
|
|
88
|
+
#
|
|
89
|
+
# # Asserts that the channel subscribes connection to a specific
|
|
90
|
+
# # stream created for a model
|
|
91
|
+
# assert_has_stream_for Room.find(1)
|
|
92
|
+
# end
|
|
93
|
+
#
|
|
94
|
+
# def test_does_not_stream_with_incorrect_room_number
|
|
95
|
+
# subscribe room_number: -1
|
|
96
|
+
#
|
|
97
|
+
# # Asserts that not streams was started
|
|
98
|
+
# assert_no_streams
|
|
99
|
+
# end
|
|
100
|
+
#
|
|
101
|
+
# def test_does_not_subscribe_without_room_number
|
|
102
|
+
# subscribe
|
|
103
|
+
#
|
|
104
|
+
# # Asserts that the subscription was rejected
|
|
105
|
+
# assert subscription.rejected?
|
|
106
|
+
# end
|
|
107
|
+
# end
|
|
108
|
+
#
|
|
109
|
+
# You can also perform actions:
|
|
110
|
+
# def test_perform_speak
|
|
111
|
+
# subscribe room_number: 1
|
|
112
|
+
#
|
|
113
|
+
# perform :speak, message: "Hello, Rails!"
|
|
114
|
+
#
|
|
115
|
+
# assert_equal "Hello, Rails!", transmissions.last["text"]
|
|
116
|
+
# end
|
|
117
|
+
#
|
|
118
|
+
# == Special methods
|
|
119
|
+
#
|
|
120
|
+
# ActionCable::Channel::TestCase will also automatically provide the following instance
|
|
121
|
+
# methods for use in the tests:
|
|
122
|
+
#
|
|
123
|
+
# <b>connection</b>::
|
|
124
|
+
# An ActionCable::Channel::ConnectionStub, representing the current HTTP connection.
|
|
125
|
+
# <b>subscription</b>::
|
|
126
|
+
# An instance of the current channel, created when you call +subscribe+.
|
|
127
|
+
# <b>transmissions</b>::
|
|
128
|
+
# A list of all messages that have been transmitted into the channel.
|
|
129
|
+
#
|
|
130
|
+
#
|
|
131
|
+
# == Channel is automatically inferred
|
|
132
|
+
#
|
|
133
|
+
# ActionCable::Channel::TestCase will automatically infer the channel under test
|
|
134
|
+
# from the test class name. If the channel cannot be inferred from the test
|
|
135
|
+
# class name, you can explicitly set it with +tests+.
|
|
136
|
+
#
|
|
137
|
+
# class SpecialEdgeCaseChannelTest < ActionCable::Channel::TestCase
|
|
138
|
+
# tests SpecialChannel
|
|
139
|
+
# end
|
|
140
|
+
#
|
|
141
|
+
# == Specifying connection identifiers
|
|
142
|
+
#
|
|
143
|
+
# You need to set up your connection manually to provide values for the identifiers.
|
|
144
|
+
# To do this just use:
|
|
145
|
+
#
|
|
146
|
+
# stub_connection(user: users(:john))
|
|
147
|
+
#
|
|
148
|
+
# == Testing broadcasting
|
|
149
|
+
#
|
|
150
|
+
# ActionCable::Channel::TestCase enhances ActionCable::TestHelper assertions (e.g.
|
|
151
|
+
# +assert_broadcasts+) to handle broadcasting to models:
|
|
152
|
+
#
|
|
153
|
+
#
|
|
154
|
+
# # in your channel
|
|
155
|
+
# def speak(data)
|
|
156
|
+
# broadcast_to room, text: data["message"]
|
|
157
|
+
# end
|
|
158
|
+
#
|
|
159
|
+
# def test_speak
|
|
160
|
+
# subscribe room_id: rooms(:chat).id
|
|
161
|
+
#
|
|
162
|
+
# assert_broadcast_on(rooms(:chat), text: "Hello, Rails!") do
|
|
163
|
+
# perform :speak, message: "Hello, Rails!"
|
|
164
|
+
# end
|
|
165
|
+
# end
|
|
166
|
+
class TestCase < ActiveSupport::TestCase
|
|
167
|
+
module Behavior
|
|
168
|
+
extend ActiveSupport::Concern
|
|
169
|
+
|
|
170
|
+
include ActiveSupport::Testing::ConstantLookup
|
|
171
|
+
include ActionCable::TestHelper
|
|
172
|
+
|
|
173
|
+
CHANNEL_IDENTIFIER = "test_stub"
|
|
174
|
+
|
|
175
|
+
included do
|
|
176
|
+
class_attribute :_channel_class
|
|
177
|
+
|
|
178
|
+
attr_reader :connection, :subscription
|
|
179
|
+
|
|
180
|
+
ActiveSupport.run_load_hooks(:action_cable_channel_test_case, self)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
module ClassMethods
|
|
184
|
+
def tests(channel)
|
|
185
|
+
case channel
|
|
186
|
+
when String, Symbol
|
|
187
|
+
self._channel_class = channel.to_s.camelize.constantize
|
|
188
|
+
when Module
|
|
189
|
+
self._channel_class = channel
|
|
190
|
+
else
|
|
191
|
+
raise NonInferrableChannelError.new(channel)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def channel_class
|
|
196
|
+
if channel = self._channel_class
|
|
197
|
+
channel
|
|
198
|
+
else
|
|
199
|
+
tests determine_default_channel(name)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def determine_default_channel(name)
|
|
204
|
+
channel = determine_constant_from_test_name(name) do |constant|
|
|
205
|
+
Class === constant && constant < ActionCable::Channel::Base
|
|
206
|
+
end
|
|
207
|
+
raise NonInferrableChannelError.new(name) if channel.nil?
|
|
208
|
+
channel
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Set up test connection with the specified identifiers:
|
|
213
|
+
#
|
|
214
|
+
# class ApplicationCable < ActionCable::Connection::Base
|
|
215
|
+
# identified_by :user, :token
|
|
216
|
+
# end
|
|
217
|
+
#
|
|
218
|
+
# stub_connection(user: users[:john], token: 'my-secret-token')
|
|
219
|
+
def stub_connection(identifiers = {})
|
|
220
|
+
@connection = ConnectionStub.new(identifiers)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Subscribe to the channel under test. Optionally pass subscription parameters as a Hash.
|
|
224
|
+
def subscribe(params = {})
|
|
225
|
+
@connection ||= stub_connection
|
|
226
|
+
@subscription = self.class.channel_class.new(connection, CHANNEL_IDENTIFIER, params.with_indifferent_access)
|
|
227
|
+
@subscription.singleton_class.include(ChannelStub)
|
|
228
|
+
@subscription.subscribe_to_channel
|
|
229
|
+
@subscription
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Unsubscribe the subscription under test.
|
|
233
|
+
def unsubscribe
|
|
234
|
+
check_subscribed!
|
|
235
|
+
subscription.unsubscribe_from_channel
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Perform action on a channel.
|
|
239
|
+
#
|
|
240
|
+
# NOTE: Must be subscribed.
|
|
241
|
+
def perform(action, data = {})
|
|
242
|
+
check_subscribed!
|
|
243
|
+
subscription.perform_action(data.stringify_keys.merge("action" => action.to_s))
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Returns messages transmitted into channel
|
|
247
|
+
def transmissions
|
|
248
|
+
# Return only directly sent message (via #transmit)
|
|
249
|
+
connection.transmissions.map { |data| data["message"] }.compact
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Enhance TestHelper assertions to handle non-String
|
|
253
|
+
# broadcastings
|
|
254
|
+
def assert_broadcasts(stream_or_object, *args)
|
|
255
|
+
super(broadcasting_for(stream_or_object), *args)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def assert_broadcast_on(stream_or_object, *args)
|
|
259
|
+
super(broadcasting_for(stream_or_object), *args)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Asserts that no streams have been started.
|
|
263
|
+
#
|
|
264
|
+
# def test_assert_no_started_stream
|
|
265
|
+
# subscribe
|
|
266
|
+
# assert_no_streams
|
|
267
|
+
# end
|
|
268
|
+
#
|
|
269
|
+
def assert_no_streams
|
|
270
|
+
assert subscription.streams.empty?, "No streams started was expected, but #{subscription.streams.count} found"
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Asserts that the specified stream has been started.
|
|
274
|
+
#
|
|
275
|
+
# def test_assert_started_stream
|
|
276
|
+
# subscribe
|
|
277
|
+
# assert_has_stream 'messages'
|
|
278
|
+
# end
|
|
279
|
+
#
|
|
280
|
+
def assert_has_stream(stream)
|
|
281
|
+
assert subscription.streams.include?(stream), "Stream #{stream} has not been started"
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Asserts that the specified stream for a model has started.
|
|
285
|
+
#
|
|
286
|
+
# def test_assert_started_stream_for
|
|
287
|
+
# subscribe id: 42
|
|
288
|
+
# assert_has_stream_for User.find(42)
|
|
289
|
+
# end
|
|
290
|
+
#
|
|
291
|
+
def assert_has_stream_for(object)
|
|
292
|
+
assert_has_stream(broadcasting_for(object))
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
private
|
|
296
|
+
def check_subscribed!
|
|
297
|
+
raise "Must be subscribed!" if subscription.nil? || subscription.rejected?
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def broadcasting_for(stream_or_object)
|
|
301
|
+
return stream_or_object if stream_or_object.is_a?(String)
|
|
302
|
+
|
|
303
|
+
self.class.channel_class.broadcasting_for(stream_or_object)
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
include Behavior
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
data/lib/action_cable/channel.rb
CHANGED
|
@@ -5,7 +5,7 @@ module ActionCable
|
|
|
5
5
|
module Authorization
|
|
6
6
|
class UnauthorizedError < StandardError; end
|
|
7
7
|
|
|
8
|
-
# Closes the
|
|
8
|
+
# Closes the WebSocket connection if it is open and returns a 404 "File not Found" response.
|
|
9
9
|
def reject_unauthorized_connection
|
|
10
10
|
logger.error "An unauthorized connection attempt was rejected"
|
|
11
11
|
raise UnauthorizedError
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "action_dispatch"
|
|
4
|
+
require "active_support/rescuable"
|
|
4
5
|
|
|
5
6
|
module ActionCable
|
|
6
7
|
module Connection
|
|
@@ -46,6 +47,7 @@ module ActionCable
|
|
|
46
47
|
include Identification
|
|
47
48
|
include InternalChannel
|
|
48
49
|
include Authorization
|
|
50
|
+
include ActiveSupport::Rescuable
|
|
49
51
|
|
|
50
52
|
attr_reader :server, :env, :subscriptions, :logger, :worker_pool, :protocol
|
|
51
53
|
delegate :event_loop, :pubsub, to: :server
|
|
@@ -95,7 +97,12 @@ module ActionCable
|
|
|
95
97
|
end
|
|
96
98
|
|
|
97
99
|
# Close the WebSocket connection.
|
|
98
|
-
def close
|
|
100
|
+
def close(reason: nil, reconnect: true)
|
|
101
|
+
transmit(
|
|
102
|
+
type: ActionCable::INTERNAL[:message_types][:disconnect],
|
|
103
|
+
reason: reason,
|
|
104
|
+
reconnect: reconnect
|
|
105
|
+
)
|
|
99
106
|
websocket.close
|
|
100
107
|
end
|
|
101
108
|
|
|
@@ -136,13 +143,10 @@ module ActionCable
|
|
|
136
143
|
send_async :handle_close
|
|
137
144
|
end
|
|
138
145
|
|
|
139
|
-
|
|
140
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
|
141
|
-
protected
|
|
146
|
+
private
|
|
142
147
|
attr_reader :websocket
|
|
143
148
|
attr_reader :message_buffer
|
|
144
149
|
|
|
145
|
-
private
|
|
146
150
|
# The request that initiated the WebSocket connection is available here. This gives access to the environment, cookies, etc.
|
|
147
151
|
def request # :doc:
|
|
148
152
|
@request ||= begin
|
|
@@ -173,7 +177,7 @@ module ActionCable
|
|
|
173
177
|
message_buffer.process!
|
|
174
178
|
server.add_connection(self)
|
|
175
179
|
rescue ActionCable::Connection::Authorization::UnauthorizedError
|
|
176
|
-
|
|
180
|
+
close(reason: ActionCable::INTERNAL[:disconnect_reasons][:unauthorized], reconnect: false) if websocket.alive?
|
|
177
181
|
end
|
|
178
182
|
|
|
179
183
|
def handle_close
|
|
@@ -214,7 +218,7 @@ module ActionCable
|
|
|
214
218
|
end
|
|
215
219
|
|
|
216
220
|
def respond_to_invalid_request
|
|
217
|
-
close if websocket.alive?
|
|
221
|
+
close(reason: ActionCable::INTERNAL[:disconnect_reasons][:invalid_request]) if websocket.alive?
|
|
218
222
|
|
|
219
223
|
logger.error invalid_request_message
|
|
220
224
|
logger.info finished_request_message
|
|
@@ -258,3 +262,5 @@ module ActionCable
|
|
|
258
262
|
end
|
|
259
263
|
end
|
|
260
264
|
end
|
|
265
|
+
|
|
266
|
+
ActiveSupport.run_load_hooks(:action_cable_connection, ActionCable::Connection::Base)
|
|
@@ -30,13 +30,10 @@ module ActionCable
|
|
|
30
30
|
receive_buffered_messages
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
|
35
|
-
protected
|
|
33
|
+
private
|
|
36
34
|
attr_reader :connection
|
|
37
35
|
attr_reader :buffered_messages
|
|
38
36
|
|
|
39
|
-
private
|
|
40
37
|
def valid?(message)
|
|
41
38
|
message.is_a?(String)
|
|
42
39
|
end
|
|
@@ -98,8 +98,10 @@ module ActionCable
|
|
|
98
98
|
def hijack_rack_socket
|
|
99
99
|
return unless @socket_object.env["rack.hijack"]
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
@rack_hijack_io = @socket_object.env["rack.
|
|
101
|
+
# This should return the underlying io according to the SPEC:
|
|
102
|
+
@rack_hijack_io = @socket_object.env["rack.hijack"].call
|
|
103
|
+
# Retain existing behaviour if required:
|
|
104
|
+
@rack_hijack_io ||= @socket_object.env["rack.hijack_io"]
|
|
103
105
|
|
|
104
106
|
@event_loop.attach(@rack_hijack_io, self)
|
|
105
107
|
end
|
|
@@ -21,6 +21,7 @@ module ActionCable
|
|
|
21
21
|
logger.error "Received unrecognized command in #{data.inspect}"
|
|
22
22
|
end
|
|
23
23
|
rescue Exception => e
|
|
24
|
+
@connection.rescue_with_handler(e)
|
|
24
25
|
logger.error "Could not execute command from (#{data.inspect}) [#{e.class} - #{e.message}]: #{e.backtrace.first(5).join(" | ")}"
|
|
25
26
|
end
|
|
26
27
|
|
|
@@ -63,12 +64,8 @@ module ActionCable
|
|
|
63
64
|
subscriptions.each { |id, channel| remove_subscription(channel) }
|
|
64
65
|
end
|
|
65
66
|
|
|
66
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
|
67
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
|
68
|
-
protected
|
|
69
|
-
attr_reader :connection, :subscriptions
|
|
70
|
-
|
|
71
67
|
private
|
|
68
|
+
attr_reader :connection, :subscriptions
|
|
72
69
|
delegate :logger, to: :connection
|
|
73
70
|
|
|
74
71
|
def find(data)
|