actioncable 7.0.8.4 → 7.1.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +74 -122
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +4 -4
  5. data/app/assets/javascripts/action_cable.js +27 -6
  6. data/app/assets/javascripts/actioncable.esm.js +27 -6
  7. data/app/assets/javascripts/actioncable.js +27 -6
  8. data/lib/action_cable/channel/base.rb +17 -5
  9. data/lib/action_cable/channel/broadcasting.rb +3 -1
  10. data/lib/action_cable/channel/callbacks.rb +37 -0
  11. data/lib/action_cable/channel/naming.rb +4 -2
  12. data/lib/action_cable/channel/streams.rb +2 -0
  13. data/lib/action_cable/channel/test_case.rb +6 -1
  14. data/lib/action_cable/connection/authorization.rb +1 -1
  15. data/lib/action_cable/connection/base.rb +18 -5
  16. data/lib/action_cable/connection/callbacks.rb +55 -0
  17. data/lib/action_cable/connection/internal_channel.rb +3 -1
  18. data/lib/action_cable/connection/stream.rb +1 -3
  19. data/lib/action_cable/connection/stream_event_loop.rb +0 -1
  20. data/lib/action_cable/connection/subscriptions.rb +2 -0
  21. data/lib/action_cable/connection/tagged_logger_proxy.rb +6 -4
  22. data/lib/action_cable/connection/test_case.rb +3 -1
  23. data/lib/action_cable/connection/web_socket.rb +2 -0
  24. data/lib/action_cable/deprecator.rb +7 -0
  25. data/lib/action_cable/engine.rb +13 -5
  26. data/lib/action_cable/gem_version.rb +3 -3
  27. data/lib/action_cable/remote_connections.rb +11 -2
  28. data/lib/action_cable/server/base.rb +3 -0
  29. data/lib/action_cable/server/broadcasting.rb +2 -0
  30. data/lib/action_cable/server/configuration.rb +12 -2
  31. data/lib/action_cable/server/connections.rb +3 -1
  32. data/lib/action_cable/server/worker.rb +0 -1
  33. data/lib/action_cable/subscription_adapter/async.rb +0 -2
  34. data/lib/action_cable/subscription_adapter/postgresql.rb +0 -1
  35. data/lib/action_cable/subscription_adapter/redis.rb +3 -6
  36. data/lib/action_cable/subscription_adapter/test.rb +3 -5
  37. data/lib/action_cable/test_helper.rb +53 -22
  38. data/lib/action_cable/version.rb +1 -1
  39. data/lib/action_cable.rb +24 -12
  40. data/lib/rails/generators/channel/USAGE +14 -8
  41. data/lib/rails/generators/channel/channel_generator.rb +21 -7
  42. metadata +24 -12
  43. data/lib/action_cable/channel.rb +0 -17
  44. data/lib/action_cable/connection.rb +0 -22
  45. data/lib/action_cable/server.rb +0 -16
  46. data/lib/action_cable/subscription_adapter.rb +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 628ed5f32ba25ffd8af55381b7f6e2da738e2c858859de0bf6c90571450c2e34
4
- data.tar.gz: d1c1215140b31a12f7b7a60550a87646ee216a46c83ad01376ece3c31d1877af
3
+ metadata.gz: 91461ab9b310ec6a5388b520f81bacf9721248f6347680329790781c1a00ce07
4
+ data.tar.gz: 2657b14b90a52395b6b00e60bfb4219dfa17f34f1b1d2edefd24590e083da62d
5
5
  SHA512:
6
- metadata.gz: 053fb484287d6aa12426d34c8885ff23fcbe559ac559bf610713bed175c61976ee345e5df3e5bf1ca02d62c487bf40ae26626a10d2c7d1a5c42a5eeed5c56aec
7
- data.tar.gz: eac6757ed3c340311f712cfd29ea3448f666d43335c682fd83846f6244422083ca67b12b8b6ceb86c3d77bf9ca7fedc3747f655ca066388c4c59a7d79538c67c
6
+ metadata.gz: 7464c663a970b03531455c5907b347eea7eb89e705681085bf71ab55cdd916855c33a9e23214a9809a9015fa290d450d84a8586166866203a65aa13082614a23
7
+ data.tar.gz: 688d77d5c13b7447bd790514e6a0abceb01aa6406a417b644de7fc32ffbbc94ef26866c8426a8676a72d584bf6ed3ba1649840d199f5083abb2f4996a61c4ad7
data/CHANGELOG.md CHANGED
@@ -1,199 +1,151 @@
1
- ## Rails 7.0.8.4 (June 04, 2024) ##
1
+ ## Rails 7.1.3.4 (June 04, 2024) ##
2
2
 
3
3
  * No changes.
4
4
 
5
5
 
6
- ## Rails 7.0.8.3 (May 17, 2024) ##
6
+ ## Rails 7.1.3.3 (May 16, 2024) ##
7
7
 
8
8
  * No changes.
9
9
 
10
10
 
11
- ## Rails 7.0.8.2 (May 16, 2024) ##
11
+ ## Rails 7.1.3.2 (February 21, 2024) ##
12
12
 
13
13
  * No changes.
14
14
 
15
15
 
16
- ## Rails 7.0.8.1 (February 21, 2024) ##
16
+ ## Rails 7.1.3.1 (February 21, 2024) ##
17
17
 
18
18
  * No changes.
19
19
 
20
20
 
21
- ## Rails 7.0.8 (September 09, 2023) ##
21
+ ## Rails 7.1.3 (January 16, 2024) ##
22
22
 
23
23
  * No changes.
24
24
 
25
25
 
26
- ## Rails 7.0.7.2 (August 22, 2023) ##
26
+ ## Rails 7.1.2 (November 10, 2023) ##
27
27
 
28
28
  * No changes.
29
29
 
30
30
 
31
- ## Rails 7.0.7.1 (August 22, 2023) ##
31
+ ## Rails 7.1.1 (October 11, 2023) ##
32
32
 
33
33
  * No changes.
34
34
 
35
35
 
36
- ## Rails 7.0.7 (August 09, 2023) ##
36
+ ## Rails 7.1.0 (October 05, 2023) ##
37
37
 
38
38
  * No changes.
39
39
 
40
40
 
41
- ## Rails 7.0.6 (June 29, 2023) ##
42
-
43
- * Fix Action Cable Redis configuration with sentinels.
44
-
45
- *Dmitriy Ivliev*
46
-
47
-
48
- ## Rails 7.0.5.1 (June 26, 2023) ##
41
+ ## Rails 7.1.0.rc2 (October 01, 2023) ##
49
42
 
50
43
  * No changes.
51
44
 
52
45
 
53
- ## Rails 7.0.5 (May 24, 2023) ##
54
-
55
- * Restore Action Cable Redis pub/sub listener on connection failure.
56
-
57
- *Vladimir Dementyev*
58
-
59
-
60
- ## Rails 7.0.4.3 (March 13, 2023) ##
46
+ ## Rails 7.1.0.rc1 (September 27, 2023) ##
61
47
 
62
48
  * No changes.
63
49
 
64
50
 
65
- ## Rails 7.0.4.2 (January 24, 2023) ##
51
+ ## Rails 7.1.0.beta1 (September 13, 2023) ##
66
52
 
67
- * No changes.
53
+ * Add a `@server` instance variable referencing the `ActionCable.server`
54
+ singleton to `ActionCable::Channel::ConnectionStub`
68
55
 
56
+ This lets us delegate the `pubsub` and `config` method calls
57
+ to the server. This fixes `NoMethodError` errors when testing
58
+ channel logic that call `pubsub` (e.g. `stop_stream_for`).
69
59
 
70
- ## Rails 7.0.4.1 (January 17, 2023) ##
60
+ *Julian Foo*
71
61
 
72
- * No changes.
62
+ * Added `health_check_path` and `health_check_application` config to
63
+ mount a given health check rack app on a given path.
64
+ Useful when mounting Action Cable standalone.
73
65
 
66
+ *Joé Dupuis*
74
67
 
75
- ## Rails 7.0.4 (September 09, 2022) ##
68
+ * Introduce the `capture_broadcasts` test helper.
76
69
 
77
- * The Redis adapter is now compatible with redis-rb 5.0
70
+ Returns all messages broadcast in a block.
78
71
 
79
- Compatibility with redis-rb 3.x was dropped.
80
-
81
- *Jean Boussier*
82
-
83
- * The Action Cable server is now mounted with `anchor: true`.
84
-
85
- This means that routes that also start with `/cable` will no longer clash with Action Cable.
72
+ ```ruby
73
+ messages = capture_broadcasts("test") do
74
+ ActionCable.server.broadcast "test", { message: "one" }
75
+ ActionCable.server.broadcast "test", { message: "two" }
76
+ end
77
+ assert_equal 2, messages.length
78
+ assert_equal({ "message" => "one" }, messages.first)
79
+ assert_equal({ "message" => "two" }, messages.last)
80
+ ```
86
81
 
87
82
  *Alex Ghiculescu*
88
83
 
84
+ * Display broadcasted messages on error message when using `assert_broadcast_on`
89
85
 
90
- ## Rails 7.0.3.1 (July 12, 2022) ##
91
-
92
- * No changes.
93
-
94
-
95
- ## Rails 7.0.3 (May 09, 2022) ##
96
-
97
- * No changes.
98
-
99
-
100
- ## Rails 7.0.2.4 (April 26, 2022) ##
101
-
102
- * No changes.
103
-
104
-
105
- ## Rails 7.0.2.3 (March 08, 2022) ##
106
-
107
- * No changes.
108
-
109
-
110
- ## Rails 7.0.2.2 (February 11, 2022) ##
86
+ *Stéphane Robino*
111
87
 
112
- * No changes.
113
-
114
-
115
- ## Rails 7.0.2.1 (February 11, 2022) ##
116
-
117
- * No changes.
118
-
119
-
120
- ## Rails 7.0.2 (February 08, 2022) ##
121
-
122
- * No changes.
88
+ * The Action Cable client now supports subprotocols to allow passing arbitrary data
89
+ to the server.
123
90
 
91
+ ```js
92
+ const consumer = ActionCable.createConsumer()
124
93
 
125
- ## Rails 7.0.1 (January 06, 2022) ##
126
-
127
- * No changes.
94
+ consumer.addSubProtocol('custom-protocol')
128
95
 
96
+ consumer.connect()
97
+ ```
129
98
 
130
- ## Rails 7.0.0 (December 15, 2021) ##
99
+ See also:
131
100
 
132
- * No changes.
101
+ * https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#subprotocols
133
102
 
103
+ *Guillaume Hain*
134
104
 
135
- ## Rails 7.0.0.rc3 (December 14, 2021) ##
136
-
137
- * No changes.
138
-
139
-
140
- ## Rails 7.0.0.rc2 (December 14, 2021) ##
141
-
142
- * No changes.
143
-
144
- ## Rails 7.0.0.rc1 (December 06, 2021) ##
145
-
146
- * The Action Cable client now ensures successful channel subscriptions:
147
-
148
- * The client maintains a set of pending subscriptions until either
149
- the server confirms the subscription or the channel is torn down.
150
- * Rectifies the race condition where an unsubscribe is rapidly followed
151
- by a subscribe (on the same channel identifier) and the requests are
152
- handled out of order by the ActionCable server, thereby ignoring the
153
- subscribe command.
154
-
155
- *Daniel Spinosa*
156
-
157
-
158
- ## Rails 7.0.0.alpha2 (September 15, 2021) ##
159
-
160
- * No changes.
105
+ * Redis pub/sub adapter now automatically reconnects when Redis connection is lost.
161
106
 
107
+ *Vladimir Dementyev*
162
108
 
163
- ## Rails 7.0.0.alpha1 (September 15, 2021) ##
109
+ * The `connected()` callback can now take a `{reconnected}` parameter to differentiate
110
+ connections from reconnections.
164
111
 
165
- * Compile ESM package that can be used directly in the browser as actioncable.esm.js.
112
+ ```js
113
+ import consumer from "./consumer"
166
114
 
167
- *DHH*
115
+ consumer.subscriptions.create("ExampleChannel", {
116
+ connected({reconnected}) {
117
+ if (reconnected) {
118
+ ...
119
+ } else {
120
+ ...
121
+ }
122
+ }
123
+ })
124
+ ```
168
125
 
169
- * Move action_cable.js to actioncable.js to match naming convention used for other Rails frameworks, and use JS console to communicate the deprecation.
126
+ *Mansa Keïta*
170
127
 
171
- *DHH*
128
+ * The Redis adapter is now compatible with redis-rb 5.0
172
129
 
173
- * Stop transpiling the UMD package generated as actioncable.js and drop the IE11 testing that relied on that.
130
+ Compatibility with redis-rb 3.x was dropped.
174
131
 
175
- *DHH*
132
+ *Jean Boussier*
176
133
 
177
- * Truncate broadcast logging messages.
134
+ * The Action Cable server is now mounted with `anchor: true`.
178
135
 
179
- *J Smith*
136
+ This means that routes that also start with `/cable` will no longer clash with Action Cable.
180
137
 
181
- * OpenSSL constants are now used for Digest computations.
138
+ *Alex Ghiculescu*
182
139
 
183
- *Dirkjan Bussink*
140
+ * `ActionCable.server.remote_connections.where(...).disconnect` now sends `disconnect` message
141
+ before closing the connection with the reconnection strategy specified (defaults to `true`).
184
142
 
185
- * The Action Cable client now includes safeguards to prevent a "thundering
186
- herd" of client reconnects after server connectivity loss:
143
+ *Vladimir Dementyev*
187
144
 
188
- * The client will wait a random amount between 1x and 3x of the stale
189
- threshold after the server's last ping before making the first
190
- reconnection attempt.
191
- * Subsequent reconnection attempts now use exponential backoff instead of
192
- logarithmic backoff. To allow the delay between reconnection attempts to
193
- increase slowly at first, the default exponentiation base is < 2.
194
- * Random jitter is applied to each delay between reconnection attempts.
145
+ * Added command callbacks to `ActionCable::Connection::Base`.
195
146
 
196
- *Jonathan Hefner*
147
+ Now you can define `before_command`, `after_command`, and `around_command` to be invoked before, after or around any command received by a client respectively.
197
148
 
149
+ *Vladimir Dementyev*
198
150
 
199
- Please check [6-1-stable](https://github.com/rails/rails/blob/6-1-stable/actioncable/CHANGELOG.md) for previous changes.
151
+ Please check [7-0-stable](https://github.com/rails/rails/blob/7-0-stable/actioncable/CHANGELOG.md) for previous changes.
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015-2022 Basecamp, LLC
1
+ Copyright (c) 37signals LLC
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,13 +1,13 @@
1
- # Action Cable – Integrated WebSockets for Rails
1
+ # Action Cable – Integrated WebSockets for \Rails
2
2
 
3
- Action Cable seamlessly integrates WebSockets with the rest of your Rails application.
3
+ Action Cable seamlessly integrates WebSockets with the rest of your \Rails application.
4
4
  It allows for real-time features to be written in Ruby in the same style
5
- and form as the rest of your Rails application, while still being performant
5
+ and form as the rest of your \Rails application, while still being performant
6
6
  and scalable. It's a full-stack offering that provides both a client-side
7
7
  JavaScript framework and a server-side Ruby framework. You have access to your full
8
8
  domain model written with Active Record or your ORM of choice.
9
9
 
10
- You can read more about Action Cable in the [Action Cable Overview](https://edgeguides.rubyonrails.org/action_cable_overview.html) guide.
10
+ You can read more about Action Cable in the [Action Cable Overview](https://guides.rubyonrails.org/action_cable_overview.html) guide.
11
11
 
12
12
  ## Support
13
13
 
@@ -4,8 +4,8 @@
4
4
  })(this, (function(exports) {
5
5
  "use strict";
6
6
  var adapters = {
7
- logger: self.console,
8
- WebSocket: self.WebSocket
7
+ logger: typeof console !== "undefined" ? console : undefined,
8
+ WebSocket: typeof WebSocket !== "undefined" ? WebSocket : undefined
9
9
  };
10
10
  var logger = {
11
11
  log(...messages) {
@@ -121,7 +121,8 @@
121
121
  disconnect_reasons: {
122
122
  unauthorized: "unauthorized",
123
123
  invalid_request: "invalid_request",
124
- server_restart: "server_restart"
124
+ server_restart: "server_restart",
125
+ remote: "remote"
125
126
  },
126
127
  default_mount_path: "/cable",
127
128
  protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
@@ -150,11 +151,12 @@
150
151
  logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
151
152
  return false;
152
153
  } else {
153
- logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${protocols}`);
154
+ const socketProtocols = [ ...protocols, ...this.consumer.subprotocols || [] ];
155
+ logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
154
156
  if (this.webSocket) {
155
157
  this.uninstallEventHandlers();
156
158
  }
157
- this.webSocket = new adapters.WebSocket(this.consumer.url, protocols);
159
+ this.webSocket = new adapters.WebSocket(this.consumer.url, socketProtocols);
158
160
  this.installEventHandlers();
159
161
  this.monitor.start();
160
162
  return true;
@@ -196,6 +198,9 @@
196
198
  isActive() {
197
199
  return this.isState("open", "connecting");
198
200
  }
201
+ triedToReconnect() {
202
+ return this.monitor.reconnectAttempts > 0;
203
+ }
199
204
  isProtocolSupported() {
200
205
  return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
201
206
  }
@@ -233,6 +238,9 @@
233
238
  const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
234
239
  switch (type) {
235
240
  case message_types.welcome:
241
+ if (this.triedToReconnect()) {
242
+ this.reconnectAttempted = true;
243
+ }
236
244
  this.monitor.recordConnect();
237
245
  return this.subscriptions.reload();
238
246
 
@@ -247,7 +255,16 @@
247
255
 
248
256
  case message_types.confirmation:
249
257
  this.subscriptions.confirmSubscription(identifier);
250
- return this.subscriptions.notify(identifier, "connected");
258
+ if (this.reconnectAttempted) {
259
+ this.reconnectAttempted = false;
260
+ return this.subscriptions.notify(identifier, "connected", {
261
+ reconnected: true
262
+ });
263
+ } else {
264
+ return this.subscriptions.notify(identifier, "connected", {
265
+ reconnected: false
266
+ });
267
+ }
251
268
 
252
269
  case message_types.rejection:
253
270
  return this.subscriptions.reject(identifier);
@@ -427,6 +444,7 @@
427
444
  this._url = url;
428
445
  this.subscriptions = new Subscriptions(this);
429
446
  this.connection = new Connection(this);
447
+ this.subprotocols = [];
430
448
  }
431
449
  get url() {
432
450
  return createWebSocketURL(this._url);
@@ -447,6 +465,9 @@
447
465
  return this.connection.open();
448
466
  }
449
467
  }
468
+ addSubProtocol(subprotocol) {
469
+ this.subprotocols = [ ...this.subprotocols, subprotocol ];
470
+ }
450
471
  }
451
472
  function createWebSocketURL(url) {
452
473
  if (typeof url === "function") {
@@ -1,6 +1,6 @@
1
1
  var adapters = {
2
- logger: self.console,
3
- WebSocket: self.WebSocket
2
+ logger: typeof console !== "undefined" ? console : undefined,
3
+ WebSocket: typeof WebSocket !== "undefined" ? WebSocket : undefined
4
4
  };
5
5
 
6
6
  var logger = {
@@ -123,7 +123,8 @@ var INTERNAL = {
123
123
  disconnect_reasons: {
124
124
  unauthorized: "unauthorized",
125
125
  invalid_request: "invalid_request",
126
- server_restart: "server_restart"
126
+ server_restart: "server_restart",
127
+ remote: "remote"
127
128
  },
128
129
  default_mount_path: "/cable",
129
130
  protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
@@ -156,11 +157,12 @@ class Connection {
156
157
  logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
157
158
  return false;
158
159
  } else {
159
- logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${protocols}`);
160
+ const socketProtocols = [ ...protocols, ...this.consumer.subprotocols || [] ];
161
+ logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
160
162
  if (this.webSocket) {
161
163
  this.uninstallEventHandlers();
162
164
  }
163
- this.webSocket = new adapters.WebSocket(this.consumer.url, protocols);
165
+ this.webSocket = new adapters.WebSocket(this.consumer.url, socketProtocols);
164
166
  this.installEventHandlers();
165
167
  this.monitor.start();
166
168
  return true;
@@ -202,6 +204,9 @@ class Connection {
202
204
  isActive() {
203
205
  return this.isState("open", "connecting");
204
206
  }
207
+ triedToReconnect() {
208
+ return this.monitor.reconnectAttempts > 0;
209
+ }
205
210
  isProtocolSupported() {
206
211
  return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
207
212
  }
@@ -241,6 +246,9 @@ Connection.prototype.events = {
241
246
  const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
242
247
  switch (type) {
243
248
  case message_types.welcome:
249
+ if (this.triedToReconnect()) {
250
+ this.reconnectAttempted = true;
251
+ }
244
252
  this.monitor.recordConnect();
245
253
  return this.subscriptions.reload();
246
254
 
@@ -255,7 +263,16 @@ Connection.prototype.events = {
255
263
 
256
264
  case message_types.confirmation:
257
265
  this.subscriptions.confirmSubscription(identifier);
258
- return this.subscriptions.notify(identifier, "connected");
266
+ if (this.reconnectAttempted) {
267
+ this.reconnectAttempted = false;
268
+ return this.subscriptions.notify(identifier, "connected", {
269
+ reconnected: true
270
+ });
271
+ } else {
272
+ return this.subscriptions.notify(identifier, "connected", {
273
+ reconnected: false
274
+ });
275
+ }
259
276
 
260
277
  case message_types.rejection:
261
278
  return this.subscriptions.reject(identifier);
@@ -440,6 +457,7 @@ class Consumer {
440
457
  this._url = url;
441
458
  this.subscriptions = new Subscriptions(this);
442
459
  this.connection = new Connection(this);
460
+ this.subprotocols = [];
443
461
  }
444
462
  get url() {
445
463
  return createWebSocketURL(this._url);
@@ -460,6 +478,9 @@ class Consumer {
460
478
  return this.connection.open();
461
479
  }
462
480
  }
481
+ addSubProtocol(subprotocol) {
482
+ this.subprotocols = [ ...this.subprotocols, subprotocol ];
483
+ }
463
484
  }
464
485
 
465
486
  function createWebSocketURL(url) {
@@ -4,8 +4,8 @@
4
4
  })(this, (function(exports) {
5
5
  "use strict";
6
6
  var adapters = {
7
- logger: self.console,
8
- WebSocket: self.WebSocket
7
+ logger: typeof console !== "undefined" ? console : undefined,
8
+ WebSocket: typeof WebSocket !== "undefined" ? WebSocket : undefined
9
9
  };
10
10
  var logger = {
11
11
  log(...messages) {
@@ -121,7 +121,8 @@
121
121
  disconnect_reasons: {
122
122
  unauthorized: "unauthorized",
123
123
  invalid_request: "invalid_request",
124
- server_restart: "server_restart"
124
+ server_restart: "server_restart",
125
+ remote: "remote"
125
126
  },
126
127
  default_mount_path: "/cable",
127
128
  protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
@@ -150,11 +151,12 @@
150
151
  logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
151
152
  return false;
152
153
  } else {
153
- logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${protocols}`);
154
+ const socketProtocols = [ ...protocols, ...this.consumer.subprotocols || [] ];
155
+ logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
154
156
  if (this.webSocket) {
155
157
  this.uninstallEventHandlers();
156
158
  }
157
- this.webSocket = new adapters.WebSocket(this.consumer.url, protocols);
159
+ this.webSocket = new adapters.WebSocket(this.consumer.url, socketProtocols);
158
160
  this.installEventHandlers();
159
161
  this.monitor.start();
160
162
  return true;
@@ -196,6 +198,9 @@
196
198
  isActive() {
197
199
  return this.isState("open", "connecting");
198
200
  }
201
+ triedToReconnect() {
202
+ return this.monitor.reconnectAttempts > 0;
203
+ }
199
204
  isProtocolSupported() {
200
205
  return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
201
206
  }
@@ -233,6 +238,9 @@
233
238
  const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
234
239
  switch (type) {
235
240
  case message_types.welcome:
241
+ if (this.triedToReconnect()) {
242
+ this.reconnectAttempted = true;
243
+ }
236
244
  this.monitor.recordConnect();
237
245
  return this.subscriptions.reload();
238
246
 
@@ -247,7 +255,16 @@
247
255
 
248
256
  case message_types.confirmation:
249
257
  this.subscriptions.confirmSubscription(identifier);
250
- return this.subscriptions.notify(identifier, "connected");
258
+ if (this.reconnectAttempted) {
259
+ this.reconnectAttempted = false;
260
+ return this.subscriptions.notify(identifier, "connected", {
261
+ reconnected: true
262
+ });
263
+ } else {
264
+ return this.subscriptions.notify(identifier, "connected", {
265
+ reconnected: false
266
+ });
267
+ }
251
268
 
252
269
  case message_types.rejection:
253
270
  return this.subscriptions.reject(identifier);
@@ -427,6 +444,7 @@
427
444
  this._url = url;
428
445
  this.subscriptions = new Subscriptions(this);
429
446
  this.connection = new Connection(this);
447
+ this.subprotocols = [];
430
448
  }
431
449
  get url() {
432
450
  return createWebSocketURL(this._url);
@@ -447,6 +465,9 @@
447
465
  return this.connection.open();
448
466
  }
449
467
  }
468
+ addSubProtocol(subprotocol) {
469
+ this.subprotocols = [ ...this.subprotocols, subprotocol ];
470
+ }
450
471
  }
451
472
  function createWebSocketURL(url) {
452
473
  if (typeof url === "function") {
@@ -2,9 +2,12 @@
2
2
 
3
3
  require "set"
4
4
  require "active_support/rescuable"
5
+ require "active_support/parameter_filter"
5
6
 
6
7
  module ActionCable
7
8
  module Channel
9
+ # = Action Cable \Channel \Base
10
+ #
8
11
  # The channel provides the basic structure of grouping behavior into logical units when communicating over the WebSocket connection.
9
12
  # You can think of a channel like a form of controller, but one that's capable of pushing content to the subscriber in addition to simply
10
13
  # responding to the subscriber's direct requests.
@@ -209,9 +212,11 @@ module ActionCable
209
212
  # Transmit a hash of data to the subscriber. The hash will automatically be wrapped in a JSON envelope with
210
213
  # the proper channel identifier marked as the recipient.
211
214
  def transmit(data, via: nil) # :doc:
212
- status = "#{self.class.name} transmitting #{data.inspect.truncate(300)}"
213
- status += " (via #{via})" if via
214
- logger.debug(status)
215
+ logger.debug do
216
+ status = "#{self.class.name} transmitting #{data.inspect.truncate(300)}"
217
+ status += " (via #{via})" if via
218
+ status
219
+ end
215
220
 
216
221
  payload = { channel_class: self.class.name, data: data, via: via }
217
222
  ActiveSupport::Notifications.instrument("transmit.action_cable", payload) do
@@ -262,7 +267,7 @@ module ActionCable
262
267
  end
263
268
 
264
269
  def dispatch_action(action, data)
265
- logger.info action_signature(action, data)
270
+ logger.debug action_signature(action, data)
266
271
 
267
272
  if method(action).arity == 1
268
273
  public_send action, data
@@ -275,12 +280,19 @@ module ActionCable
275
280
 
276
281
  def action_signature(action, data)
277
282
  (+"#{self.class.name}##{action}").tap do |signature|
278
- if (arguments = data.except("action")).any?
283
+ arguments = data.except("action")
284
+
285
+ if arguments.any?
286
+ arguments = parameter_filter.filter(arguments)
279
287
  signature << "(#{arguments.inspect})"
280
288
  end
281
289
  end
282
290
  end
283
291
 
292
+ def parameter_filter
293
+ @parameter_filter ||= ActiveSupport::ParameterFilter.new(connection.config.filter_parameters)
294
+ end
295
+
284
296
  def transmit_subscription_confirmation
285
297
  unless subscription_confirmation_sent?
286
298
  logger.debug "#{self.class.name} is transmitting the subscription confirmation"
@@ -7,7 +7,9 @@ module ActionCable
7
7
  module Broadcasting
8
8
  extend ActiveSupport::Concern
9
9
 
10
- delegate :broadcasting_for, :broadcast_to, to: :class
10
+ included do
11
+ delegate :broadcasting_for, :broadcast_to, to: :class
12
+ end
11
13
 
12
14
  module ClassMethods
13
15
  # Broadcast a hash to a unique broadcasting for this <tt>model</tt> in this channel.