@depup/primus 8.0.9-depup.0

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.
Files changed (45) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +36 -0
  3. package/changes.json +30 -0
  4. package/dist/primus.js +3663 -0
  5. package/errors.js +51 -0
  6. package/index.js +1153 -0
  7. package/middleware/access-control.js +20 -0
  8. package/middleware/authorization.js +57 -0
  9. package/middleware/error.js +41 -0
  10. package/middleware/forwarded.js +18 -0
  11. package/middleware/no-cache.js +25 -0
  12. package/middleware/primus.js +47 -0
  13. package/middleware/spec.js +36 -0
  14. package/middleware/xss.js +28 -0
  15. package/package.json +140 -0
  16. package/parsers/binary.js +54 -0
  17. package/parsers/ejson.js +41 -0
  18. package/parsers/json.js +35 -0
  19. package/parsers/msgpack.js +55 -0
  20. package/parsers.json +12 -0
  21. package/primus.js +1158 -0
  22. package/spark.js +515 -0
  23. package/transformer.js +237 -0
  24. package/transformers/browserchannel/client.js +92 -0
  25. package/transformers/browserchannel/index.js +19 -0
  26. package/transformers/browserchannel/server.js +64 -0
  27. package/transformers/engine.io/README.md +34 -0
  28. package/transformers/engine.io/client.js +111 -0
  29. package/transformers/engine.io/index.js +15 -0
  30. package/transformers/engine.io/library.js +2273 -0
  31. package/transformers/engine.io/server.js +63 -0
  32. package/transformers/faye/client.js +103 -0
  33. package/transformers/faye/index.js +12 -0
  34. package/transformers/faye/server.js +85 -0
  35. package/transformers/sockjs/README.md +31 -0
  36. package/transformers/sockjs/client.js +86 -0
  37. package/transformers/sockjs/index.js +15 -0
  38. package/transformers/sockjs/library.js +4201 -0
  39. package/transformers/sockjs/server.js +108 -0
  40. package/transformers/uws/index.js +12 -0
  41. package/transformers/uws/server.js +131 -0
  42. package/transformers/websockets/client.js +112 -0
  43. package/transformers/websockets/index.js +12 -0
  44. package/transformers/websockets/server.js +77 -0
  45. package/transformers.json +26 -0
package/transformer.js ADDED
@@ -0,0 +1,237 @@
1
+ 'use strict';
2
+
3
+ var log = require('diagnostics')('primus:transformer')
4
+ , middlewareError = require('./middleware/error')
5
+ , url = require('url').parse
6
+ , Ultron = require('ultron')
7
+ , fuse = require('fusing');
8
+
9
+ function noop() {}
10
+
11
+ /**
12
+ * Transformer skeleton
13
+ *
14
+ * @constructor
15
+ * @param {Primus} primus Reference to the Primus instance.
16
+ * @api public
17
+ */
18
+ function Transformer(primus) {
19
+ this.fuse();
20
+
21
+ this.ultron = new Ultron(primus.server); // Handles listeners with ease.
22
+ this.Spark = primus.Spark; // Reference to the Spark constructor.
23
+ this.primus = primus; // Reference to the Primus instance.
24
+ this.service = null; // Stores the real-time service.
25
+
26
+ this.initialise();
27
+ }
28
+
29
+ fuse(Transformer, require('eventemitter3'));
30
+
31
+ //
32
+ // Simple logger shortcut.
33
+ //
34
+ Object.defineProperty(Transformer.prototype, 'logger', {
35
+ get: function logger() {
36
+ return {
37
+ error: this.primus.emits('log', 'error'), // Log error <line>.
38
+ warn: this.primus.emits('log', 'warn'), // Log warn <line>.
39
+ info: this.primus.emits('log', 'info'), // Log info <line>.
40
+ debug: this.primus.emits('log', 'debug'), // Log debug <line>.
41
+ log: this.primus.emits('log', 'log'), // Log log <line>.
42
+ plain: this.primus.emits('log', 'log') // Log log <line>.
43
+ };
44
+ }
45
+ });
46
+
47
+ /**
48
+ * Create the server and attach the appropriate event listeners.
49
+ *
50
+ * @api private
51
+ */
52
+ Transformer.readable('initialise', function initialise() {
53
+ if (this.server) this.server();
54
+
55
+ var server = this.primus.server
56
+ , transformer = this;
57
+
58
+ server.listeners('request').forEach(function each(fn) {
59
+ log('found existing request handlers on the HTTP server, moving Primus as first');
60
+ transformer.on('previous::request', fn, server);
61
+ });
62
+
63
+ server.listeners('upgrade').forEach(function each(fn) {
64
+ log('found existing upgrade handlers on the HTTP server, moving Primus as first');
65
+ transformer.on('previous::upgrade', fn, server);
66
+ });
67
+
68
+ //
69
+ // Remove the old listeners as we want to be the first request handler for all
70
+ // events.
71
+ //
72
+ server.removeAllListeners('request');
73
+ server.removeAllListeners('upgrade');
74
+
75
+ //
76
+ // Emit a close event.
77
+ //
78
+ this.ultron.on('close', function close() {
79
+ log('the HTTP server is closing');
80
+ transformer.emit('close');
81
+ });
82
+
83
+ //
84
+ // Start listening for incoming requests if we have a listener assigned to us.
85
+ //
86
+ if (this.listeners('request').length || this.listeners('previous::request').length) {
87
+ server.on('request', this.request.bind(this));
88
+ }
89
+
90
+ if (this.listeners('upgrade').length || this.listeners('previous::upgrade').length) {
91
+ server.on('upgrade', this.upgrade.bind(this));
92
+ }
93
+ });
94
+
95
+ /**
96
+ * Iterate all the middleware layers that we're set on our Primus instance.
97
+ *
98
+ * @param {String} type Either `http` or `upgrade`
99
+ * @param {Request} req HTTP request.
100
+ * @param {Response} res HTTP response.
101
+ * @param {Function} next Continuation callback.
102
+ * @api private
103
+ */
104
+ Transformer.readable('forEach', function forEach(type, req, res, next) {
105
+ var transformer = this
106
+ , layers = transformer.primus.layers
107
+ , primus = transformer.primus;
108
+
109
+ req.query = req.uri.query || {};
110
+
111
+ //
112
+ // Add some silly HTTP properties for connect.js compatibility.
113
+ //
114
+ req.originalUrl = req.url;
115
+
116
+ if (!layers.length) {
117
+ next();
118
+ return transformer;
119
+ }
120
+
121
+ //
122
+ // Async or sync call the middleware layer.
123
+ //
124
+ (function iterate(index) {
125
+ var layer = layers[index++];
126
+
127
+ if (!layer) return next();
128
+ if (!layer.enabled || layer.fn[type] === false) return iterate(index);
129
+
130
+ if (layer.length === 2) {
131
+ log('executing middleware (%s) synchronously', layer.name);
132
+
133
+ if (layer.fn.call(primus, req, res)) return;
134
+ return iterate(index);
135
+ }
136
+
137
+ log('executing middleware (%s) asynchronously', layer.name);
138
+ layer.fn.call(primus, req, res, function done(err) {
139
+ if (err) return middlewareError(err, req, res);
140
+
141
+ iterate(index);
142
+ });
143
+ }(0));
144
+
145
+ return transformer;
146
+ });
147
+
148
+ /**
149
+ * Start listening for incoming requests and check if we need to forward them to
150
+ * the transformers.
151
+ *
152
+ * @param {Request} req HTTP request.
153
+ * @param {Response} res HTTP response.
154
+ * @api private
155
+ */
156
+ Transformer.readable('request', function request(req, res) {
157
+ if (!this.test(req)) return this.emit('previous::request', req, res);
158
+
159
+ req.headers['primus::req::backup'] = req;
160
+ res.once('end', function gc() {
161
+ delete req.headers['primus::req::backup'];
162
+ });
163
+
164
+ //
165
+ // I want to see you're face when you're looking at the lines of code above
166
+ // while you think, WTF what is this shit, you mad bro!? Let me take a moment
167
+ // to explain this mad and sadness.
168
+ //
169
+ // There are some real-time transformers that do not give us access to the
170
+ // HTTP request that initiated their `socket` connection. They only give us
171
+ // access to the information that they think is useful, we're greedy, we want
172
+ // everything and let developers decide what they want to use instead and
173
+ // therefor want to expose this HTTP request on our `spark` object.
174
+ //
175
+ // The reason it's added to the headers is because it's currently the only
176
+ // field that is accessible through all transformers.
177
+ //
178
+
179
+ log('handling HTTP request for url: %s', req.url);
180
+ this.forEach('http', req, res, this.emits('request', req, res));
181
+ });
182
+
183
+ /**
184
+ * Starting listening for incoming upgrade requests and check if we need to
185
+ * forward them to the transformers.
186
+ *
187
+ * @param {Request} req HTTP request.
188
+ * @param {Socket} socket Socket.
189
+ * @param {Buffer} head Buffered data.
190
+ * @api private
191
+ */
192
+ Transformer.readable('upgrade', function upgrade(req, socket, head) {
193
+ if (!this.test(req)) return this.emit('previous::upgrade', req, socket, head);
194
+
195
+ //
196
+ // See Transformer#request for an explanation of this madness.
197
+ //
198
+ req.headers['primus::req::backup'] = req;
199
+ socket.once('end', function gc() {
200
+ delete req.headers['primus::req::backup'];
201
+ });
202
+
203
+ log('handling HTTP upgrade for url: %s', req.url);
204
+
205
+ //
206
+ // Add a listener for the `'error'` event before running middleware as there
207
+ // isn't a default one in Node.js >= 10. The socket is destroyed when an error
208
+ // occurs so there is no need to do anything.
209
+ //
210
+ socket.on('error', noop);
211
+
212
+ this.forEach('upgrade', req, socket, () => {
213
+ socket.removeListener('error', noop);
214
+ this.emit('upgrade', req, socket, head);
215
+ });
216
+ });
217
+
218
+ /**
219
+ * Check if we should accept this request.
220
+ *
221
+ * @param {Request} req HTTP Request.
222
+ * @returns {Boolean} Do we need to accept this request.
223
+ * @api private
224
+ */
225
+ Transformer.readable('test', function test(req) {
226
+ req.uri = url(req.url, true);
227
+
228
+ var pathname = req.uri.pathname || '/'
229
+ , route = this.primus.pathname;
230
+
231
+ return pathname.slice(0, route.length) === route;
232
+ });
233
+
234
+ //
235
+ // Expose the transformer's skeleton.
236
+ //
237
+ module.exports = Transformer;
@@ -0,0 +1,92 @@
1
+ 'use strict';
2
+ /*globals BCSocket*/
3
+
4
+ /**
5
+ * Minimum viable BrowserChannel client. This function is stringified and added
6
+ * in our client-side library.
7
+ *
8
+ * @runat client
9
+ * @api private
10
+ */
11
+ module.exports = function client() {
12
+ var primus = this
13
+ , socket;
14
+
15
+ //
16
+ // Select an available BrowserChannel factory.
17
+ //
18
+ var Factory = (function factory() {
19
+ if ('undefined' !== typeof BCSocket) return BCSocket;
20
+
21
+ try { return Primus.requires('browserchannel').BCSocket; }
22
+ catch (e) {}
23
+
24
+ return undefined;
25
+ })();
26
+
27
+ if (!Factory) return primus.critical(new Error(
28
+ 'Missing required `browserchannel` module. ' +
29
+ 'Please run `npm install --save browserchannel`'
30
+ ));
31
+
32
+ //
33
+ // Connect to the given URL.
34
+ //
35
+ primus.on('outgoing::open', function connect() {
36
+ primus.emit('outgoing::end');
37
+
38
+ var url = primus.uri({ protocol: 'http:' });
39
+
40
+ primus.socket = socket = new Factory(url, primus.merge(primus.transport, {
41
+ extraParams: primus.querystring(primus.url.query),
42
+ reconnect: false
43
+ }));
44
+
45
+ //
46
+ // Setup the Event handlers.
47
+ //
48
+ socket.onopen = primus.emits('incoming::open');
49
+ socket.onerror = primus.emits('incoming::error');
50
+ socket.onclose = primus.emits('incoming::end');
51
+ socket.onmessage = primus.emits('incoming::data', function parse(next, evt) {
52
+ next(undefined, evt.data);
53
+ });
54
+ });
55
+
56
+ //
57
+ // We need to write a new message to the socket.
58
+ //
59
+ primus.on('outgoing::data', function write(message) {
60
+ if (socket) socket.send(message);
61
+ });
62
+
63
+ //
64
+ // Attempt to reconnect the socket.
65
+ //
66
+ primus.on('outgoing::reconnect', function reconnect() {
67
+ primus.emit('outgoing::open');
68
+ });
69
+
70
+ //
71
+ // We need to close the socket.
72
+ //
73
+ primus.on('outgoing::end', function close() {
74
+ if (!socket) return;
75
+
76
+ socket.onerror = socket.onopen = socket.onclose = socket.onmessage = function () {};
77
+
78
+ //
79
+ // Bug: BrowserChannel cannot close the connection if it's already
80
+ // connecting. Bypass this behaviour by checking the readyState and
81
+ // defer the close call.
82
+ //
83
+ if (socket.readyState === socket.CONNECTING) {
84
+ socket.onopen = function onopen() {
85
+ this.close();
86
+ };
87
+ } else {
88
+ socket.close();
89
+ }
90
+ socket = null;
91
+ });
92
+ };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ var path = require('path')
4
+ , directory = require.resolve('browserchannel')
5
+ , library = path.join(path.dirname(directory), 'dist/bcsocket-uncompressed.js');
6
+
7
+ //
8
+ // Expose the module as new Transformer instance.
9
+ //
10
+ module.exports = require('../../transformer').extend({
11
+ // Creating a new real-time server.
12
+ server: require('./server'),
13
+
14
+ // The client-logic to connect with the a server.
15
+ client: require('./client'),
16
+
17
+ // The client-side library of the browserchannel.
18
+ library: require('fs').readFileSync(library, 'utf-8')
19
+ });
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ var browserchannel = require('browserchannel')
4
+ , http = require('http');
5
+
6
+ /**
7
+ * Minimum viable BrowserChannel server for Node.js that works through the
8
+ * primus interface.
9
+ *
10
+ * @runat server
11
+ * @api private
12
+ */
13
+ module.exports = function server() {
14
+ var primus = this.primus
15
+ , Spark = this.Spark;
16
+
17
+ //
18
+ // We've received a new connection, create a new Spark. The Spark will
19
+ // automatically announce it self as a new connection once it's created (after
20
+ // the next tick).
21
+ //
22
+ this.service = browserchannel.server(Object.assign(primus.options.transport, {
23
+ base: primus.pathname
24
+ }), function connection(socket) {
25
+ var spark = new Spark(
26
+ socket.headers // HTTP request headers.
27
+ , { // IP address Location.
28
+ remoteAddress: socket.address,
29
+ remotePort: 1337
30
+ }
31
+ , socket.query // Optional query string.
32
+ , socket.id // Unique connection id.
33
+ , null // We don't have have a HTTP request.
34
+ , socket // Reference to the BrowserChannel socket.
35
+ );
36
+
37
+ spark.on('outgoing::end', function end() {
38
+ if (socket) socket.stop();
39
+ }).on('outgoing::data', function write(data) {
40
+ socket.send(data);
41
+ });
42
+
43
+ socket.on('message', spark.emits('incoming::data'));
44
+ socket.on('error', spark.emits('incoming::error'));
45
+ socket.on('close', spark.emits('incoming::end', function parser(next) {
46
+ socket.removeAllListeners();
47
+ socket = null;
48
+ next();
49
+ }));
50
+ });
51
+
52
+ //
53
+ // Listen to upgrade requests.
54
+ //
55
+ this.on('request', function request(req, res) {
56
+ //
57
+ // The browser.channel returns a middleware layer.
58
+ //
59
+ this.service(req, res, function next() {
60
+ res.writeHead(404, {'content-type': 'text/plain'});
61
+ res.end(http.STATUS_CODES[404]);
62
+ });
63
+ });
64
+ };
@@ -0,0 +1,34 @@
1
+ # Transformer: Engine.IO
2
+
3
+ A transformer abstraction for the Engine.IO framework. To enable this
4
+ transformer inside of `Primus` you need to add `engine.io` to your package.json:
5
+
6
+ ```
7
+ npm install engine.io --save
8
+ ```
9
+
10
+ And tell primus to use `engine.io` as transformer.
11
+
12
+ ```js
13
+ var Primus = require('primus');
14
+
15
+ var primus = new Primus(server, { transformer: 'engine.io' });
16
+
17
+ primus.on('connection', function connection(spark) {
18
+
19
+ });
20
+ ```
21
+
22
+ # Compiling the Engine.IO library
23
+
24
+ We cannot depend on the `engine.io.js` file because it uses a UMD wrapper
25
+ that doesn't work with Primus when using RequireJS. To compile the
26
+ `engine.io-client` correctly use the `update.sh` utility which will build the
27
+ library and expose it as a property of the global object.
28
+ The steps required are the following:
29
+
30
+ 1. Fork or clone Primus
31
+ 2. cd in the directory and run `npm install`
32
+ 3. cd in the directory `transformers/engine.io/` and run `./update.sh`
33
+ 4. Commit the changes to the Primus main repository
34
+ 5. Profit
@@ -0,0 +1,111 @@
1
+ 'use strict';
2
+ /*globals eio*/
3
+
4
+ /**
5
+ * Minimum viable Engine.IO client. This function is stringified and added in
6
+ * our client-side library.
7
+ *
8
+ * @runat client
9
+ * @api private
10
+ */
11
+ module.exports = function client() {
12
+ var onmessage = this.emits('incoming::data')
13
+ , onerror = this.emits('incoming::error')
14
+ , onopen = this.emits('incoming::open')
15
+ , onclose = this.emits('incoming::end')
16
+ , primus = this
17
+ , socket;
18
+
19
+ //
20
+ // Select an available Engine.IO factory.
21
+ //
22
+ var factory = (function factory() {
23
+ if ('undefined' !== typeof eio) return eio;
24
+
25
+ try {
26
+ var Socket = Primus.requires('engine.io-client').Socket;
27
+
28
+ return function eio(options) {
29
+ return new Socket(options);
30
+ }
31
+ } catch (e) {}
32
+
33
+ return undefined;
34
+ })();
35
+
36
+ if (!factory) return primus.critical(new Error(
37
+ 'Missing required `engine.io-client` module. ' +
38
+ 'Please run `npm install --save engine.io-client`'
39
+ ));
40
+
41
+ //
42
+ // Connect to the given URL.
43
+ //
44
+ primus.on('outgoing::open', function opening() {
45
+ primus.emit('outgoing::end');
46
+
47
+ primus.socket = socket = factory(primus.merge(primus.transport,
48
+ primus.url,
49
+ primus.uri({ protocol: 'http:', query: true, object: true }), {
50
+ //
51
+ // Never remember upgrades as switching from a WIFI to a 3G connection
52
+ // could still get your connection blocked as 3G connections are usually
53
+ // behind a reverse proxy so ISP's can optimize mobile traffic by
54
+ // caching requests.
55
+ //
56
+ rememberUpgrade: false,
57
+
58
+ //
59
+ // Binary support in Engine.IO breaks a shit things. Turn it off for now.
60
+ //
61
+ forceBase64: true,
62
+
63
+ //
64
+ // Force timestamps on every single connection. Engine.IO only does this
65
+ // for polling by default, but WebSockets require an explicit `true`
66
+ // boolean.
67
+ //
68
+ timestampRequests: true,
69
+ path: this.pathname,
70
+ transports: !primus.AVOID_WEBSOCKETS
71
+ ? ['polling', 'websocket']
72
+ : ['polling']
73
+ }));
74
+
75
+ //
76
+ // Setup the Event handlers.
77
+ //
78
+ socket.on('message', onmessage);
79
+ socket.on('error', onerror);
80
+ socket.on('close', onclose);
81
+ socket.on('open', onopen);
82
+ });
83
+
84
+ //
85
+ // We need to write a new message to the socket.
86
+ //
87
+ primus.on('outgoing::data', function write(message) {
88
+ if (socket) socket.send(message);
89
+ });
90
+
91
+ //
92
+ // Attempt to reconnect the socket.
93
+ //
94
+ primus.on('outgoing::reconnect', function reconnect() {
95
+ primus.emit('outgoing::open');
96
+ });
97
+
98
+ //
99
+ // We need to close the socket.
100
+ //
101
+ primus.on('outgoing::end', function close() {
102
+ if (!socket) return;
103
+
104
+ socket.removeListener('message', onmessage);
105
+ socket.removeListener('error', onerror);
106
+ socket.removeListener('close', onclose);
107
+ socket.removeListener('open', onopen);
108
+ socket.close();
109
+ socket = null;
110
+ });
111
+ };
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ //
4
+ // Expose the module as new Transformer instance.
5
+ //
6
+ module.exports = require('../../transformer').extend({
7
+ // Creating a new real-time server.
8
+ server: require('./server'),
9
+
10
+ // The client-logic to connect with the a server.
11
+ client: require('./client'),
12
+
13
+ // The client-side library of engine.io.
14
+ library: require('fs').readFileSync(__dirname + '/library.js', 'utf-8')
15
+ });