Skip to content

Commit 539bbfb

Browse files
committed
Merge pull request #4 from eacaps/mattheworiordan-node-js-support
Mattheworiordan node js support, pass a websocket library as an option
2 parents bc79ac1 + a765681 commit 539bbfb

File tree

7 files changed

+81
-45
lines changed

7 files changed

+81
-45
lines changed

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,26 @@ MyChannel.js
5555
}
5656
}
5757

58-
Actioncable is good stuff, even if it is in Ruby.
58+
Actioncable is good stuff, even if it is in Ruby.
59+
60+
## Connecting from Node.js
61+
62+
`es6-actioncable` will work under Node.js, however you will need to bear the following in mind:
63+
64+
* You will need to supply your own websocket library, 2 out of 2 developers recommend: https://www.npmjs.com/package/websocket.
65+
* Your ActionCable Rails server must be bound to a specific IP or `0.0.0.0`, but not localhost. This can be done as follows `rails server -b 0.0.0.0`. See https://twitter.com/mattheworiordan/status/713350750483693568 for an explanation of the issue.
66+
* You will need to pass the origin to the WebSocket library as Rails will by default reject requests with an invalid origin. See example below:
67+
68+
```javascript
69+
const consumer = Cable.createConsumer('ws://0.0.0.0:3000/cable', { createWebsocket: () => {
70+
var w3cwebsocket = require('websocket').w3cwebsocket;
71+
let webSocket = new w3cwebsocket(
72+
'ws://0.0.0.0:3000/cable',
73+
protocols,
74+
'http://0.0.0.0:3000',
75+
headers,
76+
extraRequestOptions
77+
);
78+
return webSocket;
79+
} });
80+
```

dist/actioncable/Cable.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ var _cableConsumer2 = _interopRequireDefault(_cableConsumer);
1212

1313
exports["default"] = {
1414
PING_IDENTIFIER: "_ping",
15-
createConsumer: function createConsumer(url) {
16-
return new _cableConsumer2["default"](url);
15+
createConsumer: function createConsumer(url, options) {
16+
return new _cableConsumer2["default"](url, options);
1717
},
1818
// eac added 20150908
1919
endConsumer: function endConsumer(consumer) {

dist/actioncable/cable/Connection.js

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
//# Encapsulate the cable connection held by the consumer. This is an internal class not intended for direct user manipulation.
22

3-
"use strict";
3+
'use strict';
44

5-
Object.defineProperty(exports, "__esModule", {
5+
Object.defineProperty(exports, '__esModule', {
66
value: true
77
});
88

9-
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
9+
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
1010

11-
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
11+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
1212

1313
var slice = [].slice;
1414
var indexOf = [].indexOf;
@@ -21,8 +21,11 @@ var Connection = (function () {
2121
var _this = this;
2222
this.events = {
2323
message: function message(event) {
24-
var identifier, message, ref;
25-
ref = JSON.parse(event.data), identifier = ref.identifier, message = ref.message;
24+
var identifier, message, ref, type;
25+
ref = JSON.parse(event.data), identifier = ref.identifier, message = ref.message, type = ref.type;
26+
if (['confirm_subscription', 'reject_subscription'].indexOf(type) >= 0) {
27+
return;
28+
}
2629
return _this.consumer.subscriptions.notify(identifier, "received", message);
2730
},
2831
open: function open() {
@@ -40,7 +43,7 @@ var Connection = (function () {
4043
}
4144

4245
_createClass(Connection, [{
43-
key: "send",
46+
key: 'send',
4447
value: function send(data) {
4548
if (this.isOpen()) {
4649
this.webSocket.send(JSON.stringify(data));
@@ -50,16 +53,20 @@ var Connection = (function () {
5053
}
5154
}
5255
}, {
53-
key: "open",
56+
key: 'open',
5457
value: function open() {
5558
if (this.isState("open", "connecting")) {
5659
return;
5760
}
58-
this.webSocket = new WebSocket(this.consumer.url);
61+
if (this.consumer.options.createWebsocket) {
62+
this.webSocket = this.consumer.options.createWebsocket();
63+
} else {
64+
this.webSocket = new WebSocket(this.consumer.url);
65+
}
5966
return this.installEventHandlers();
6067
}
6168
}, {
62-
key: "close",
69+
key: 'close',
6370
value: function close() {
6471
var ref;
6572
if (this.isState("closed", "closing")) {
@@ -68,7 +75,7 @@ var Connection = (function () {
6875
return (ref = this.webSocket) != null ? ref.close() : void 0;
6976
}
7077
}, {
71-
key: "reopen",
78+
key: 'reopen',
7279
value: function reopen() {
7380
if (this.isOpen()) {
7481
return this.closeSilently((function (_this) {
@@ -81,31 +88,28 @@ var Connection = (function () {
8188
}
8289
}
8390
}, {
84-
key: "isOpen",
91+
key: 'isOpen',
8592
value: function isOpen() {
8693
return this.isState("open");
8794
}
8895
}, {
89-
key: "isState",
96+
key: 'isState',
9097
value: function isState() {
9198
var ref, states;
9299
states = 1 <= arguments.length ? slice.call(arguments, 0) : [];
93100
return ref = this.getState(), indexOf.call(states, ref) >= 0;
94101
}
95102
}, {
96-
key: "getState",
103+
key: 'getState',
97104
value: function getState() {
98105
var ref, state, value;
99-
for (state in WebSocket) {
100-
value = WebSocket[state];
101-
if (value === ((ref = this.webSocket) != null ? ref.readyState : void 0)) {
102-
return state.toLowerCase();
103-
}
106+
var states = ['connecting', 'open', 'closing', 'closed'];
107+
if (this.webSocket) {
108+
return states[this.webSocket.readyState];
104109
}
105-
return null;
106110
}
107111
}, {
108-
key: "closeSilently",
112+
key: 'closeSilently',
109113
value: function closeSilently(callback) {
110114
if (callback == null) {
111115
callback = function () {};
@@ -120,7 +124,7 @@ var Connection = (function () {
120124
}
121125
}
122126
}, {
123-
key: "installEventHandlers",
127+
key: 'installEventHandlers',
124128
value: function installEventHandlers() {
125129
var eventName, results;
126130
results = [];
@@ -130,15 +134,15 @@ var Connection = (function () {
130134
return results;
131135
}
132136
}, {
133-
key: "installEventHandler",
137+
key: 'installEventHandler',
134138
value: function installEventHandler(eventName, handler) {
135139
if (handler == null) {
136140
handler = this.events[eventName].bind(this);
137141
}
138142
return this.webSocket.addEventListener(eventName, handler);
139143
}
140144
}, {
141-
key: "uninstallEventHandlers",
145+
key: 'uninstallEventHandlers',
142146
value: function uninstallEventHandlers() {
143147
var eventName, results;
144148
results = [];
@@ -148,7 +152,7 @@ var Connection = (function () {
148152
return results;
149153
}
150154
}, {
151-
key: "toJSON",
155+
key: 'toJSON',
152156
value: function toJSON() {
153157
return {
154158
state: this.getState()
@@ -159,5 +163,5 @@ var Connection = (function () {
159163
return Connection;
160164
})();
161165

162-
exports["default"] = Connection;
163-
module.exports = exports["default"];
166+
exports['default'] = Connection;
167+
module.exports = exports['default'];

dist/actioncable/cable/Consumer.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,15 @@ var _ConnectionMonitor = require('./ConnectionMonitor');
4141
var _ConnectionMonitor2 = _interopRequireDefault(_ConnectionMonitor);
4242

4343
var Consumer = (function () {
44-
function Consumer(url) {
44+
function Consumer(url, options) {
4545
_classCallCheck(this, Consumer);
4646

4747
this.url = url;
48+
if (!options) {
49+
options = {};
50+
}
51+
this.options = options;
52+
4853
this.subscriptions = new _Subscriptions2['default'](this);
4954
this.connection = new _Connection2['default'](this);
5055
this.connectionMonitor = new _ConnectionMonitor2['default'](this);

src/actioncable/Cable.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import Consumer from './cable/Consumer';
22

33
export default {
44
PING_IDENTIFIER: "_ping",
5-
createConsumer: (url) => {
6-
return new Consumer(url);
5+
createConsumer: (url, options) => {
6+
return new Consumer(url, options);
77
},
88
// eac added 20150908
99
endConsumer: (consumer) => {
1010
consumer.connection.close();
1111
consumer.connectionMonitor.stop();
1212
}
13-
};
13+
};

src/actioncable/cable/Connection.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ class Connection {
99
let _this = this;
1010
this.events = {
1111
message: function(event) {
12-
var identifier, message, ref;
13-
ref = JSON.parse(event.data), identifier = ref.identifier, message = ref.message;
12+
var identifier, message, ref, type;
13+
ref = JSON.parse(event.data), identifier = ref.identifier, message = ref.message, type = ref.type;
14+
if (['confirm_subscription', 'reject_subscription'].indexOf(type) >= 0) { return; }
1415
return _this.consumer.subscriptions.notify(identifier, "received", message);
1516
},
1617
open: function() {
@@ -40,7 +41,11 @@ class Connection {
4041
if (this.isState("open", "connecting")) {
4142
return;
4243
}
43-
this.webSocket = new WebSocket(this.consumer.url);
44+
if(this.consumer.options.createWebsocket) {
45+
this.webSocket = this.consumer.options.createWebsocket();
46+
} else {
47+
this.webSocket = new WebSocket(this.consumer.url);
48+
}
4449
return this.installEventHandlers();
4550
}
4651

@@ -76,13 +81,10 @@ class Connection {
7681

7782
getState() {
7883
var ref, state, value;
79-
for (state in WebSocket) {
80-
value = WebSocket[state];
81-
if (value === ((ref = this.webSocket) != null ? ref.readyState : void 0)) {
82-
return state.toLowerCase();
83-
}
84+
var states = ['connecting','open','closing','closed'];
85+
if (this.webSocket) {
86+
return states[this.webSocket.readyState];
8487
}
85-
return null;
8688
}
8789

8890
closeSilently(callback) {

src/actioncable/cable/Consumer.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ import Connection from './Connection';
1818
import ConnectionMonitor from './ConnectionMonitor';
1919

2020
class Consumer {
21-
constructor(url) {
21+
constructor(url, options) {
2222
this.url = url;
23+
if (!options) { options = {}; }
24+
this.options = options;
25+
2326
this.subscriptions = new Subscriptions(this);
2427
this.connection = new Connection(this);
2528
this.connectionMonitor = new ConnectionMonitor(this);
@@ -40,4 +43,4 @@ class Consumer {
4043
}
4144
}
4245

43-
export default Consumer;
46+
export default Consumer;

0 commit comments

Comments
 (0)