@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
@@ -0,0 +1,108 @@
1
+ 'use strict';
2
+
3
+ const sockjs = require('sockjs');
4
+ const url = require('url');
5
+
6
+ const PrimusError = require('../../errors').PrimusError;
7
+
8
+ /**
9
+ * Minimum viable SockJS server for Node.js that works through the primus
10
+ * interface.
11
+ *
12
+ * @runat server
13
+ * @api private
14
+ */
15
+ module.exports = function server() {
16
+ let fayeOptions = { maxLength: this.primus.options.maxLength };
17
+ let prefix = this.primus.pathname;
18
+
19
+ if (this.primus.options.compression) {
20
+ try {
21
+ fayeOptions.extensions = [ require('permessage-deflate') ];
22
+ } catch (e) {
23
+ [
24
+ '',
25
+ 'Missing required npm dependency for sockjs',
26
+ 'To use the permessage-deflate extension with the sockjs transformer, ',
27
+ 'you have to install an additional dependency.',
28
+ 'Please run the following command and try again:',
29
+ '',
30
+ ' npm install --save permessage-deflate',
31
+ ''
32
+ ].forEach((line) => console.error(`Primus: ${line}`));
33
+
34
+ throw new PrimusError(
35
+ 'Missing dependencies for transformer: "sockjs"',
36
+ this.primus
37
+ );
38
+ }
39
+ }
40
+
41
+ if (prefix.charAt(prefix.length - 1) !== '/') prefix += '(?:[^/]+)?';
42
+
43
+ this.service = sockjs.createServer();
44
+
45
+ //
46
+ // We've received a new connection, create a new Spark. The Spark will
47
+ // automatically announce it self as a new connection once it's created (after
48
+ // the next tick).
49
+ //
50
+ this.service.on('connection', (socket) => {
51
+ if (!socket) {
52
+ return;
53
+ }
54
+ const headers = socket.headers.via;
55
+
56
+ headers.via = headers._via;
57
+ socket.headers.via = null;
58
+
59
+ const spark = new this.Spark(
60
+ headers // HTTP request headers.
61
+ , socket // IP address location.
62
+ , url.parse(socket.url).query // Optional query string.
63
+ , socket.id // Unique connection id.
64
+ , null // We don't have have a HTTP request.
65
+ , socket // Reference to the SockJS socket.
66
+ );
67
+
68
+ spark.on('outgoing::end', () => socket && socket.close());
69
+ spark.on('outgoing::data', (data) => socket.write(data));
70
+
71
+ socket.on('error', spark.emits('incoming::error'));
72
+ socket.on('data', spark.emits('incoming::data'));
73
+ socket.on('close', spark.emits('incoming::end', (next) => {
74
+ socket.removeAllListeners();
75
+ socket = null;
76
+ next();
77
+ }));
78
+ });
79
+
80
+ //
81
+ // Listen to requests.
82
+ //
83
+ const handle = this.service.listener(Object.assign({
84
+ faye_server_options: fayeOptions
85
+ }, this.primus.options.transport, {
86
+ log: this.logger.plain,
87
+ prefix: prefix
88
+ })).getHandler();
89
+
90
+ //
91
+ // Here be demons. SockJS has this really horrible "security" feature where it
92
+ // limits the HTTP headers that you're allowed to see and use in your
93
+ // applications. I whole heartly disagree with this decision so we're hacking
94
+ // around this by storing the full header in an accepted header key and re-use
95
+ // that when we construct a Primus Spark.
96
+ //
97
+ this.on('upgrade', (req, socket, head) => {
98
+ req.headers._via = req.headers.via;
99
+ req.headers.via = req.headers;
100
+
101
+ handle.call(this, req, socket, head);
102
+ }).on('request', (req, res) => {
103
+ req.headers._via = req.headers.via;
104
+ req.headers.via = req.headers;
105
+
106
+ handle.call(this, req, res);
107
+ });
108
+ };
@@ -0,0 +1,12 @@
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 server.
11
+ client: require('../websockets/client')
12
+ });
@@ -0,0 +1,131 @@
1
+ 'use strict';
2
+
3
+ const http = require('http');
4
+ const url = require('url');
5
+ const uws = require('uws');
6
+
7
+ const native = uws.native;
8
+
9
+ //
10
+ // uws v0.12.0+ needs a set "no operation" callback.
11
+ //
12
+ if (native.setNoop) native.setNoop(() => {});
13
+
14
+ /**
15
+ * Server of µWebSockets transformer.
16
+ *
17
+ * @runat server
18
+ * @api private
19
+ */
20
+ module.exports = function server() {
21
+ const opts = Object.assign({
22
+ perMessageDeflate: !!this.primus.options.compression,
23
+ maxPayload: this.primus.options.maxLength
24
+ }, this.primus.options.transport);
25
+
26
+ this.service = native.server;
27
+
28
+ let flags = 0;
29
+
30
+ if (opts.perMessageDeflate) {
31
+ flags |= uws.PERMESSAGE_DEFLATE;
32
+ if (opts.perMessageDeflate.serverNoContextTakeover === false) {
33
+ flags |= uws.SLIDING_DEFLATE_WINDOW;
34
+ }
35
+ }
36
+
37
+ const group = native.server.group.create(flags, opts.maxPayload);
38
+ let upgradeReq = null;
39
+
40
+ native.server.group.onConnection(group, (socket) => {
41
+ const spark = new this.Spark(
42
+ upgradeReq.headers, // HTTP request headers.
43
+ upgradeReq, // IP address location.
44
+ url.parse(upgradeReq.url).query, // Optional query string.
45
+ null, // We don't have an unique id.
46
+ upgradeReq, // Reference to the HTTP req.
47
+ socket // Reference to the socket.
48
+ );
49
+
50
+ native.setUserData(socket, spark);
51
+
52
+ spark.ultron.on('outgoing::end', () => native.server.close(socket));
53
+ spark.on('outgoing::data', (data) => {
54
+ const opcode = Buffer.isBuffer(data)
55
+ ? uws.OPCODE_BINARY
56
+ : uws.OPCODE_TEXT;
57
+
58
+ native.server.send(socket, data, opcode, undefined, true);
59
+ });
60
+ });
61
+
62
+ native.server.group.onDisconnection(group, (socket, code, msg, spark) => {
63
+ native.clearUserData(socket);
64
+ spark.ultron.remove('outgoing::end');
65
+ spark.emit('incoming::end');
66
+ });
67
+
68
+ native.server.group.onMessage(group, (msg, spark) => {
69
+ //
70
+ // Binary data is passed zero-copy as an `ArrayBuffer` so we first have to
71
+ // convert it to a `Buffer` and then copy it to a new `Buffer`.
72
+ //
73
+ if ('string' !== typeof msg) msg = Buffer.from(Buffer.from(msg));
74
+
75
+ spark.emit('incoming::data', msg);
76
+ });
77
+
78
+ native.server.group.onPing(group, (msg, spark) => spark.emit('incoming::pong'));
79
+
80
+ //
81
+ // Listen to upgrade requests.
82
+ //
83
+ this.on('upgrade', (req, soc) => {
84
+ const secKey = req.headers['sec-websocket-key'];
85
+
86
+ if (soc.readable && soc.writable && secKey && secKey.length === 24) {
87
+ soc.setNoDelay(opts.noDelay);
88
+
89
+ let socketHandle = soc._handle;
90
+ let sslState = null;
91
+
92
+ if (soc.ssl) {
93
+ socketHandle = soc._parent._handle;
94
+ sslState = soc.ssl._external;
95
+ }
96
+
97
+ const ticket = native.transfer(
98
+ socketHandle.fd === -1 ? socketHandle : socketHandle.fd,
99
+ sslState
100
+ );
101
+
102
+ soc.on('close', () => {
103
+ upgradeReq = req;
104
+ native.upgrade(
105
+ group,
106
+ ticket,
107
+ secKey,
108
+ req.headers['sec-websocket-extensions']
109
+ );
110
+
111
+ //
112
+ // Delete references to destroyed socket.
113
+ //
114
+ req.client = req.connection = req.socket = null;
115
+ upgradeReq = null;
116
+ });
117
+ }
118
+
119
+ soc.destroy();
120
+ });
121
+
122
+ //
123
+ // Listen to non-upgrade requests.
124
+ //
125
+ this.on('request', (req, res) => {
126
+ res.writeHead(426, { 'content-type': 'text/plain' });
127
+ res.end(http.STATUS_CODES[426]);
128
+ });
129
+
130
+ this.once('close', () => native.server.group.close(group));
131
+ };
@@ -0,0 +1,112 @@
1
+ 'use strict';
2
+ /*globals MozWebSocket */
3
+
4
+ /**
5
+ * Minimum viable WebSocket 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 WebSocket factory.
17
+ //
18
+ var Factory = (function factory() {
19
+ if ('undefined' !== typeof WebSocket) return WebSocket;
20
+ if ('undefined' !== typeof MozWebSocket) return MozWebSocket;
21
+
22
+ try { return Primus.requires('ws'); }
23
+ catch (e) {}
24
+
25
+ return undefined;
26
+ })();
27
+
28
+ if (!Factory) return primus.critical(new Error(
29
+ 'Missing required `ws` module. Please run `npm install --save ws`'
30
+ ));
31
+
32
+ //
33
+ // Connect to the given URL.
34
+ //
35
+ primus.on('outgoing::open', function opening() {
36
+ primus.emit('outgoing::end');
37
+
38
+ //
39
+ // FireFox will throw an error when we try to establish a connection from
40
+ // a secure page to an unsecured WebSocket connection. This is inconsistent
41
+ // behaviour between different browsers. This should ideally be solved in
42
+ // Primus when we connect.
43
+ //
44
+ try {
45
+ var options = {
46
+ protocol: primus.url.protocol === 'ws+unix:' ? 'ws+unix:' : 'ws:',
47
+ query: true
48
+ };
49
+
50
+ //
51
+ // Only allow primus.transport object in Node.js, it will throw in
52
+ // browsers with a TypeError if we supply to much arguments.
53
+ //
54
+ if (Factory.length === 3) {
55
+ if ('ws+unix:' === options.protocol) {
56
+ options.pathname =
57
+ '/' +
58
+ primus.url.hostname +
59
+ primus.url.pathname +
60
+ ':' +
61
+ primus.pathname;
62
+ }
63
+ primus.socket = socket = new Factory(
64
+ primus.uri(options), // URL
65
+ [], // Sub protocols
66
+ primus.transport // options.
67
+ );
68
+ } else {
69
+ primus.socket = socket = new Factory(primus.uri(options));
70
+ socket.binaryType = 'arraybuffer';
71
+ }
72
+ } catch (e) { return primus.emit('error', e); }
73
+
74
+ //
75
+ // Setup the Event handlers.
76
+ //
77
+ socket.onopen = primus.emits('incoming::open');
78
+ socket.onerror = primus.emits('incoming::error');
79
+ socket.onclose = primus.emits('incoming::end');
80
+ socket.onmessage = primus.emits('incoming::data', function parse(next, evt) {
81
+ next(undefined, evt.data);
82
+ });
83
+ });
84
+
85
+ //
86
+ // We need to write a new message to the socket.
87
+ //
88
+ primus.on('outgoing::data', function write(message) {
89
+ if (!socket || socket.readyState !== Factory.OPEN) return;
90
+
91
+ try { socket.send(message); }
92
+ catch (e) { primus.emit('incoming::error', e); }
93
+ });
94
+
95
+ //
96
+ // Attempt to reconnect the socket.
97
+ //
98
+ primus.on('outgoing::reconnect', function reconnect() {
99
+ primus.emit('outgoing::open');
100
+ });
101
+
102
+ //
103
+ // We need to close the socket.
104
+ //
105
+ primus.on('outgoing::end', function close() {
106
+ if (!socket) return;
107
+
108
+ socket.onerror = socket.onopen = socket.onclose = socket.onmessage = function () {};
109
+ socket.close();
110
+ socket = null;
111
+ });
112
+ };
@@ -0,0 +1,12 @@
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 server.
11
+ client: require('./client')
12
+ });
@@ -0,0 +1,77 @@
1
+ 'use strict';
2
+
3
+ const http = require('http');
4
+ const url = require('url');
5
+ const ws = require('ws');
6
+
7
+ /**
8
+ * Minimum viable WebSocket server for Node.js that works through the Primus
9
+ * interface.
10
+ *
11
+ * @runat server
12
+ * @api private
13
+ */
14
+ module.exports = function server() {
15
+ this.service = new ws.Server(Object.assign({
16
+ perMessageDeflate: !!this.primus.options.compression,
17
+ maxPayload: this.primus.options.maxLength
18
+ }, this.primus.options.transport, {
19
+ clientTracking: false,
20
+ noServer: true
21
+ }));
22
+
23
+ /**
24
+ * Noop! Pointless, empty function that will actually be really useful.
25
+ *
26
+ * @param {Error} err We failed at something.
27
+ * @api private
28
+ */
29
+ const noop = (err) => err && this.logger.error(err);
30
+
31
+ //
32
+ // Listen to upgrade requests.
33
+ //
34
+ this.on('upgrade', (req, socket, head) => {
35
+ this.service.handleUpgrade(req, socket, head, (socket) => {
36
+ const spark = new this.Spark(
37
+ req.headers // HTTP request headers.
38
+ , req // IP address location.
39
+ , url.parse(req.url).query // Optional query string.
40
+ , null // We don't have an unique id.
41
+ , req // Reference to the HTTP req.
42
+ , socket // Reference to the WebSocket.
43
+ );
44
+
45
+ spark.on('outgoing::end', () => socket && socket.close());
46
+ spark.on('outgoing::data', (data) => {
47
+ if (socket.readyState !== socket.OPEN) return;
48
+ socket.send(data, noop);
49
+ });
50
+
51
+ socket.on('message',
52
+ spark.emits('incoming::data', (next, data, isBinary) => {
53
+ next(undefined, isBinary === false ? data.toString() : data);
54
+ })
55
+ );
56
+ socket.on('error', spark.emits('incoming::error'));
57
+ socket.on('ping', spark.emits('incoming::pong', (next) => {
58
+ next(undefined, null);
59
+ }));
60
+ socket.on('close', spark.emits('incoming::end', (next) => {
61
+ socket.removeAllListeners();
62
+ socket = null;
63
+ next();
64
+ }));
65
+ });
66
+ });
67
+
68
+ //
69
+ // Listen to non-upgrade requests.
70
+ //
71
+ this.on('request', (req, res) => {
72
+ res.writeHead(426, { 'content-type': 'text/plain' });
73
+ res.end(http.STATUS_CODES[426]);
74
+ });
75
+
76
+ this.once('close', () => this.service.close());
77
+ };
@@ -0,0 +1,26 @@
1
+ {
2
+ "websockets": {
3
+ "server": "ws",
4
+ "client": "ws"
5
+ },
6
+ "engine.io": {
7
+ "server": "engine.io",
8
+ "client": "engine.io-client"
9
+ },
10
+ "browserchannel": {
11
+ "server": "browserchannel",
12
+ "client": "browserchannel"
13
+ },
14
+ "sockjs": {
15
+ "server": "sockjs",
16
+ "client": "sockjs-client"
17
+ },
18
+ "faye": {
19
+ "server": "faye-websocket",
20
+ "client": "faye-websocket"
21
+ },
22
+ "uws": {
23
+ "server": "uws",
24
+ "client": "ws"
25
+ }
26
+ }