@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/spark.js ADDED
@@ -0,0 +1,515 @@
1
+ 'use strict';
2
+
3
+ var ParserError = require('./errors').ParserError
4
+ , log = require('diagnostics')('primus:spark')
5
+ , parse = require('querystring').parse
6
+ , forwarded = require('forwarded-for')
7
+ , nanoid = require('nanoid').nanoid
8
+ , Ultron = require('ultron')
9
+ , fuse = require('fusing')
10
+ , u2028 = /\u2028/g
11
+ , u2029 = /\u2029/g;
12
+
13
+ /**
14
+ * The Spark is an indefinable, indescribable energy or soul of a transformer
15
+ * which can be used to create new transformers. In our case, it's a simple
16
+ * wrapping interface.
17
+ *
18
+ * @constructor
19
+ * @param {Primus} primus Reference to the Primus server. (Set using .bind)
20
+ * @param {Object} headers The request headers for this connection.
21
+ * @param {Object} address The object that holds the remoteAddress and port.
22
+ * @param {Object} query The query string of request.
23
+ * @param {String} id An optional id of the socket, or we will generate one.
24
+ * @param {Request} request The HTTP Request instance that initialised the spark.
25
+ * @param {Mixed} socket Reference to the transformer socket.
26
+ * @api public
27
+ */
28
+ function Spark(primus, headers, address, query, id, request, socket) {
29
+ this.fuse();
30
+
31
+ var writable = this.writable
32
+ , spark = this
33
+ , idgen = primus.options.idGenerator;
34
+
35
+ query = query || {};
36
+ id = idgen ? idgen() : (id || nanoid());
37
+ headers = headers || {};
38
+ address = address || {};
39
+ request = request || headers['primus::req::backup'];
40
+
41
+ writable('id', id); // Unique id for socket.
42
+ writable('primus', primus); // References to Primus.
43
+ writable('remote', address); // The remote address location.
44
+ writable('headers', headers); // The request headers.
45
+ writable('request', request); // Reference to an HTTP request.
46
+ writable('socket', socket); // Reference to the transformer's socket
47
+ writable('writable', true); // Silly stream compatibility.
48
+ writable('readable', true); // Silly stream compatibility.
49
+ writable('queue', []); // Data queue for data events.
50
+ writable('query', query); // The query string.
51
+ writable('ultron', new Ultron(this)); // Our event listening cleanup.
52
+ writable('alive', true); // Flag used to detect zombie sparks.
53
+
54
+ //
55
+ // Parse our query string.
56
+ //
57
+ if ('string' === typeof this.query) {
58
+ this.query = parse(this.query);
59
+ }
60
+
61
+ this.__initialise.forEach(function execute(initialise) {
62
+ initialise.call(spark);
63
+ });
64
+ }
65
+
66
+ fuse(Spark, require('stream'), { merge: false, mixin: false });
67
+
68
+ //
69
+ // Internal readyState's to prevent writes against close sockets.
70
+ //
71
+ Spark.OPENING = 1; // Only here for primus.js readyState number compatibility.
72
+ Spark.CLOSED = 2; // The connection is closed.
73
+ Spark.OPEN = 3; // The connection is open.
74
+
75
+ //
76
+ // Make sure that we emit `readyState` change events when a new readyState is
77
+ // checked. This way plugins can correctly act according to this.
78
+ //
79
+ Spark.readable('readyState', {
80
+ get: function get() {
81
+ return this.__readyState;
82
+ },
83
+ set: function set(readyState) {
84
+ if (this.__readyState === readyState) return readyState;
85
+
86
+ this.__readyState = readyState;
87
+ this.emit('readyStateChange');
88
+
89
+ return readyState;
90
+ }
91
+ }, true);
92
+
93
+ Spark.writable('__readyState', Spark.OPEN);
94
+
95
+ //
96
+ // Lazy parse interface for IP address information. As nobody is always
97
+ // interested in this, we're going to defer parsing until it's actually needed.
98
+ //
99
+ Spark.get('address', function address() {
100
+ return this.request.forwarded || forwarded(this.remote, this.headers, this.primus.whitelist);
101
+ });
102
+
103
+ /**
104
+ * Checks if the given event is an emitted event by Primus.
105
+ *
106
+ * @param {String} evt The event name.
107
+ * @returns {Boolean}
108
+ * @api public
109
+ */
110
+ Spark.readable('reserved', function reserved(evt) {
111
+ return (/^(incoming|outgoing)::/).test(evt)
112
+ || evt in reserved.events;
113
+ });
114
+
115
+ /**
116
+ * The actual events that are used by the Spark.
117
+ *
118
+ * @type {Object}
119
+ * @api public
120
+ */
121
+ Spark.prototype.reserved.events = {
122
+ readyStateChange: 1,
123
+ heartbeat: 1,
124
+ error: 1,
125
+ data: 1,
126
+ end: 1
127
+ };
128
+
129
+ /**
130
+ * Allows for adding initialise listeners without people overriding our default
131
+ * initializer. If they are feeling adventures and really want want to hack it
132
+ * up, they can remove it from the __initialise array.
133
+ *
134
+ * @returns {Function} The last added initialise hook.
135
+ * @api public
136
+ */
137
+ Spark.readable('initialise', {
138
+ get: function get() {
139
+ return this.__initialise[this.__initialise.length - 1];
140
+ },
141
+
142
+ set: function set(initialise) {
143
+ if ('function' === typeof initialise) this.__initialise.push(initialise);
144
+ }
145
+ }, true);
146
+
147
+ /**
148
+ * Send a heartbeat to the client.
149
+ *
150
+ * Checks if any message has been received from the client before sending
151
+ * another heartbeat. If not, we can assume it's dead (no response to our last
152
+ * ping), so we should close.
153
+ *
154
+ * This is intentionally writable so it can be overwritten for custom heartbeat
155
+ * policies.
156
+ *
157
+ * @returns {undefined}
158
+ * @api public
159
+ */
160
+ Spark.writable('heartbeat', function heartbeat() {
161
+ var spark = this;
162
+ if (!spark.alive) {
163
+ //
164
+ // Set the `reconnect` option to `true` so we don't send a
165
+ // `primus::server::close` packet to an already broken connection.
166
+ //
167
+ spark.end(undefined, { reconnect: true });
168
+ } else {
169
+ const now = Date.now();
170
+
171
+ spark.alive = false;
172
+ spark.emit('outgoing::ping', now);
173
+ spark._write(`primus::ping::${now}`);
174
+ }
175
+ });
176
+
177
+ /**
178
+ * Attach hooks and automatically announce a new connection.
179
+ *
180
+ * @type {Array}
181
+ * @api private
182
+ */
183
+ Spark.readable('__initialise', [function initialise() {
184
+ var primus = this.primus
185
+ , ultron = this.ultron
186
+ , spark = this;
187
+
188
+ //
189
+ // Prevent double initialization of the spark. If we already have an
190
+ // `incoming::data` handler we assume that all other cases are handled as well.
191
+ //
192
+ if (this.listeners('incoming::data').length) {
193
+ return log('already has incoming::data listeners, bailing out');
194
+ }
195
+
196
+ //
197
+ // We've received new data from our client, decode and emit it.
198
+ //
199
+ ultron.on('incoming::data', function message(raw) {
200
+ primus.decoder.call(spark, raw, function decoding(err, data) {
201
+ //
202
+ // Do a "save" emit('error') when we fail to parse a message. We don't
203
+ // want to throw here as listening to errors should be optional.
204
+ //
205
+ if (err) {
206
+ log('failed to decode the incoming data for %s', spark.id);
207
+ return new ParserError('Failed to decode incoming data: '+ err.message, spark, err);
208
+ }
209
+
210
+ //
211
+ // Handle "primus::" prefixed protocol messages.
212
+ //
213
+ if (spark.protocol(data)) return;
214
+
215
+ spark.transforms(primus, spark, 'incoming', data, raw);
216
+ });
217
+ });
218
+
219
+ //
220
+ // We've received a pong event. This is fired upon receipt of a
221
+ // `pimus::pong::<timestamp>` message.
222
+ //
223
+ ultron.on('incoming::pong', function pong() {
224
+ spark.alive = true;
225
+ spark.emit('heartbeat');
226
+ });
227
+
228
+ //
229
+ // The client has disconnected.
230
+ //
231
+ ultron.on('incoming::end', function disconnect() {
232
+ //
233
+ // The socket is closed, sending data over it will throw an error.
234
+ //
235
+ log('transformer closed connection for %s', spark.id);
236
+ spark.end(undefined, { reconnect: true });
237
+ });
238
+
239
+ ultron.on('incoming::error', function error(err) {
240
+ //
241
+ // Ensure that the error we emit is always an Error instance. There are
242
+ // transformers that used to emit only strings. A string is not an Error.
243
+ //
244
+ if ('string' === typeof err) {
245
+ err = new Error(err);
246
+ }
247
+
248
+ if (spark.listeners('error').length) spark.emit('error', err);
249
+ spark.primus.emit('log', 'error', err);
250
+
251
+ log('transformer received error `%s` for %s', err.message, spark.id);
252
+ spark.end();
253
+ });
254
+
255
+ //
256
+ // End is triggered by both incoming and outgoing events.
257
+ //
258
+ ultron.on('end', function end() {
259
+ primus.emit('disconnection', spark);
260
+ });
261
+
262
+ //
263
+ // Announce a new connection. This allows the transformers to change or listen
264
+ // to events before we announce it.
265
+ //
266
+ process.nextTick(function tick() {
267
+ primus.asyncemit('connection', spark, function damn(err) {
268
+ if (!err) {
269
+ if (spark.queue) spark.queue.forEach(function each(packet) {
270
+ spark.emit('data', packet.data, packet.raw);
271
+ });
272
+
273
+ spark.queue = null;
274
+ return;
275
+ }
276
+
277
+ spark.emit('incoming::error', err);
278
+ });
279
+ });
280
+ }]);
281
+
282
+ /**
283
+ * Execute the set of message transformers from Primus on the incoming or
284
+ * outgoing message.
285
+ * This function and it's content should be in sync with Primus#transforms in
286
+ * primus.js.
287
+ *
288
+ * @param {Primus} primus Reference to the Primus instance with message transformers.
289
+ * @param {Spark|Primus} connection Connection that receives or sends data.
290
+ * @param {String} type The type of message, 'incoming' or 'outgoing'.
291
+ * @param {Mixed} data The data to send or that has been received.
292
+ * @param {String} raw The raw encoded data.
293
+ * @returns {Spark}
294
+ * @api public
295
+ */
296
+ Spark.readable('transforms', function transforms(primus, connection, type, data, raw) {
297
+ var packet = { data: data, raw: raw }
298
+ , fns = primus.transformers[type];
299
+
300
+ //
301
+ // Iterate in series over the message transformers so we can allow optional
302
+ // asynchronous execution of message transformers which could for example
303
+ // retrieve additional data from the server, do extra decoding or even
304
+ // message validation.
305
+ //
306
+ (function transform(index, done) {
307
+ var transformer = fns[index++];
308
+
309
+ if (!transformer) return done();
310
+
311
+ if (1 === transformer.length) {
312
+ if (false === transformer.call(connection, packet)) {
313
+ //
314
+ // When false is returned by an incoming transformer it means that's
315
+ // being handled by the transformer and we should not emit the `data`
316
+ // event.
317
+ //
318
+ return;
319
+ }
320
+
321
+ return transform(index, done);
322
+ }
323
+
324
+ transformer.call(connection, packet, function finished(err, arg) {
325
+ if (err) return connection.emit('error', err);
326
+ if (false === arg) return;
327
+
328
+ transform(index, done);
329
+ });
330
+ }(0, function done() {
331
+ //
332
+ // We always emit 2 arguments for the data event, the first argument is the
333
+ // parsed data and the second argument is the raw string that we received.
334
+ // This allows you, for example, to do some validation on the parsed data
335
+ // and then save the raw string in your database without the stringify
336
+ // overhead.
337
+ //
338
+ if ('incoming' === type) {
339
+ //
340
+ // This is pretty bad edge case, it's possible that the async version of
341
+ // the `connection` event listener takes so long that we cannot assign
342
+ // `data` handlers and we are already receiving data as the connection is
343
+ // already established. In this edge case we need to queue the data and
344
+ // pass it to the data event once we're listening.
345
+ //
346
+ if (connection.queue) return connection.queue.push(packet);
347
+ return connection.emit('data', packet.data, packet.raw);
348
+ }
349
+
350
+ connection._write(packet.data);
351
+ }));
352
+
353
+ return this;
354
+ });
355
+
356
+ /**
357
+ * Really dead simple protocol parser. We simply assume that every message that
358
+ * is prefixed with `primus::` could be used as some sort of protocol definition
359
+ * for Primus.
360
+ *
361
+ * @param {String} msg The data.
362
+ * @returns {Boolean} Is a protocol message.
363
+ * @api private
364
+ */
365
+ Spark.readable('protocol', function protocol(msg) {
366
+ if (
367
+ 'string' !== typeof msg
368
+ || msg.indexOf('primus::') !== 0
369
+ ) return false;
370
+
371
+ var last = msg.indexOf(':', 8)
372
+ , value = msg.slice(last + 2);
373
+
374
+ switch (msg.slice(8, last)) {
375
+ case 'pong':
376
+ this.emit('incoming::pong', +value);
377
+ break;
378
+
379
+ case 'id':
380
+ this._write('primus::id::'+ this.id);
381
+ break;
382
+
383
+ //
384
+ // Unknown protocol, somebody is probably sending `primus::` prefixed
385
+ // messages.
386
+ //
387
+ default:
388
+ log('message `%s` was prefixed with primus:: but not supported', msg);
389
+ return false;
390
+ }
391
+
392
+ log('processed a primus protocol message `%s`', msg);
393
+ return true;
394
+ });
395
+
396
+ /**
397
+ * Send a new message to a given spark.
398
+ *
399
+ * @param {Mixed} data The data that needs to be written.
400
+ * @returns {Boolean} Always returns true.
401
+ * @api public
402
+ */
403
+ Spark.readable('write', function write(data) {
404
+ var primus = this.primus;
405
+
406
+ //
407
+ // The connection is closed, return false.
408
+ //
409
+ if (Spark.CLOSED === this.readyState) {
410
+ log('attempted to write but readyState was already set to CLOSED for %s', this.id);
411
+ return false;
412
+ }
413
+
414
+ this.transforms(primus, this, 'outgoing', data);
415
+
416
+ return true;
417
+ });
418
+
419
+ /**
420
+ * The actual message writer.
421
+ *
422
+ * @param {Mixed} data The message that needs to be written.
423
+ * @returns {Boolean}
424
+ * @api private
425
+ */
426
+ Spark.readable('_write', function _write(data) {
427
+ var primus = this.primus
428
+ , spark = this;
429
+
430
+ //
431
+ // The connection is closed, normally this would already be done in the
432
+ // `spark.write` method, but as `_write` is used internally, we should also
433
+ // add the same check here to prevent potential crashes by writing to a dead
434
+ // socket.
435
+ //
436
+ if (Spark.CLOSED === spark.readyState) {
437
+ log('attempted to _write but readyState was already set to CLOSED for %s', spark.id);
438
+ return false;
439
+ }
440
+
441
+ primus.encoder.call(spark, data, function encoded(err, packet) {
442
+ //
443
+ // Do a "safe" emit('error') when we fail to parse a message. We don't
444
+ // want to throw here as listening to errors should be optional.
445
+ //
446
+ if (err) return new ParserError('Failed to encode outgoing data: '+ err.message, spark, err);
447
+ if (!packet) return log('nothing to write, bailing out for %s', spark.id);
448
+
449
+ //
450
+ // Hack 1: \u2028 and \u2029 are allowed inside a JSON string, but JavaScript
451
+ // defines them as newline separators. Unescaped control characters are not
452
+ // allowed inside JSON strings, so this causes an error at parse time. We
453
+ // work around this issue by escaping these characters. This can cause
454
+ // errors with JSONP requests or if the string is just evaluated.
455
+ //
456
+ if ('string' === typeof packet) {
457
+ if (~packet.indexOf('\u2028')) packet = packet.replace(u2028, '\\u2028');
458
+ if (~packet.indexOf('\u2029')) packet = packet.replace(u2029, '\\u2029');
459
+ }
460
+
461
+ spark.emit('outgoing::data', packet);
462
+ });
463
+
464
+ return true;
465
+ });
466
+
467
+ /**
468
+ * End the connection.
469
+ *
470
+ * Options:
471
+ * - reconnect (boolean) Trigger client-side reconnect.
472
+ *
473
+ * @param {Mixed} data Optional closing data.
474
+ * @param {Object} options End instructions.
475
+ * @api public
476
+ */
477
+ Spark.readable('end', function end(data, options) {
478
+ if (Spark.CLOSED === this.readyState) return this;
479
+
480
+ options = options || {};
481
+ if (data !== undefined) this.write(data);
482
+
483
+ //
484
+ // If we want to trigger a reconnect do not send
485
+ // `primus::server::close`, otherwise bypass the .write method
486
+ // as this message should not be transformed.
487
+ //
488
+ if (!options.reconnect) this._write('primus::server::close');
489
+
490
+ //
491
+ // This seems redundant but there are cases where the above writes
492
+ // can trigger another `end` call. An example is with Engine.IO
493
+ // when calling `end` on the client and `end` on the spark right
494
+ // after. The `end` call on the spark comes before the `incoming::end`
495
+ // event and the result is an attempt of writing to a closed socket.
496
+ // When this happens Engine.IO closes the connection and without
497
+ // this check the following instructions could be executed twice.
498
+ //
499
+ if (Spark.CLOSED === this.readyState) return this;
500
+
501
+ log('emitting final events for spark %s', this.id);
502
+
503
+ this.readyState = Spark.CLOSED;
504
+ this.emit('outgoing::end');
505
+ this.emit('end');
506
+ this.ultron.destroy();
507
+ this.ultron = this.queue = null;
508
+
509
+ return this;
510
+ });
511
+
512
+ //
513
+ // Expose the module.
514
+ //
515
+ module.exports = Spark;