@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.
- package/LICENSE +22 -0
- package/README.md +36 -0
- package/changes.json +30 -0
- package/dist/primus.js +3663 -0
- package/errors.js +51 -0
- package/index.js +1153 -0
- package/middleware/access-control.js +20 -0
- package/middleware/authorization.js +57 -0
- package/middleware/error.js +41 -0
- package/middleware/forwarded.js +18 -0
- package/middleware/no-cache.js +25 -0
- package/middleware/primus.js +47 -0
- package/middleware/spec.js +36 -0
- package/middleware/xss.js +28 -0
- package/package.json +140 -0
- package/parsers/binary.js +54 -0
- package/parsers/ejson.js +41 -0
- package/parsers/json.js +35 -0
- package/parsers/msgpack.js +55 -0
- package/parsers.json +12 -0
- package/primus.js +1158 -0
- package/spark.js +515 -0
- package/transformer.js +237 -0
- package/transformers/browserchannel/client.js +92 -0
- package/transformers/browserchannel/index.js +19 -0
- package/transformers/browserchannel/server.js +64 -0
- package/transformers/engine.io/README.md +34 -0
- package/transformers/engine.io/client.js +111 -0
- package/transformers/engine.io/index.js +15 -0
- package/transformers/engine.io/library.js +2273 -0
- package/transformers/engine.io/server.js +63 -0
- package/transformers/faye/client.js +103 -0
- package/transformers/faye/index.js +12 -0
- package/transformers/faye/server.js +85 -0
- package/transformers/sockjs/README.md +31 -0
- package/transformers/sockjs/client.js +86 -0
- package/transformers/sockjs/index.js +15 -0
- package/transformers/sockjs/library.js +4201 -0
- package/transformers/sockjs/server.js +108 -0
- package/transformers/uws/index.js +12 -0
- package/transformers/uws/server.js +131 -0
- package/transformers/websockets/client.js +112 -0
- package/transformers/websockets/index.js +12 -0
- package/transformers/websockets/server.js +77 -0
- package/transformers.json +26 -0
package/index.js
ADDED
|
@@ -0,0 +1,1153 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var PrimusError = require('./errors').PrimusError
|
|
4
|
+
, EventEmitter = require('eventemitter3')
|
|
5
|
+
, Transformer = require('./transformer')
|
|
6
|
+
, log = require('diagnostics')('primus')
|
|
7
|
+
, Spark = require('./spark')
|
|
8
|
+
, fuse = require('fusing')
|
|
9
|
+
, fs = require('fs')
|
|
10
|
+
, vm = require('vm');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Primus is a universal wrapper for real-time frameworks that provides a common
|
|
14
|
+
* interface for server and client interaction.
|
|
15
|
+
*
|
|
16
|
+
* @constructor
|
|
17
|
+
* @param {HTTP.Server} server HTTP or HTTPS server instance.
|
|
18
|
+
* @param {Object} options Configuration
|
|
19
|
+
* @api public
|
|
20
|
+
*/
|
|
21
|
+
function Primus(server, options) {
|
|
22
|
+
if (!(this instanceof Primus)) return new Primus(server, options);
|
|
23
|
+
|
|
24
|
+
this.fuse();
|
|
25
|
+
|
|
26
|
+
if ('object' !== typeof server) {
|
|
27
|
+
var message = 'The first argument of the constructor must be ' +
|
|
28
|
+
'an HTTP or HTTPS server instance';
|
|
29
|
+
throw new PrimusError(message, this);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
options = options || {};
|
|
33
|
+
options.maxLength = options.maxLength || 10485760; // Maximum allowed packet size.
|
|
34
|
+
options.transport = options.transport || {}; // Transformer specific options.
|
|
35
|
+
options.pingInterval = 'pingInterval' in options // Heartbeat interval.
|
|
36
|
+
? options.pingInterval
|
|
37
|
+
: 30000;
|
|
38
|
+
|
|
39
|
+
if ('timeout' in options) {
|
|
40
|
+
throw new PrimusError('The `timeout` option has been removed', this);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
var primus = this
|
|
44
|
+
, key;
|
|
45
|
+
|
|
46
|
+
this.auth = options.authorization || null; // Do we have an authorization handler.
|
|
47
|
+
this.connections = Object.create(null); // Connection storage.
|
|
48
|
+
this.ark = Object.create(null); // Plugin storage.
|
|
49
|
+
this.layers = []; // Middleware layers.
|
|
50
|
+
this.heartbeatInterval = null; // The heartbeat interval.
|
|
51
|
+
this.transformer = null; // Reference to the real-time engine instance.
|
|
52
|
+
this.encoder = null; // Shorthand to the parser's encoder.
|
|
53
|
+
this.decoder = null; // Shorthand to the parser's decoder.
|
|
54
|
+
this.connected = 0; // Connection counter.
|
|
55
|
+
this.whitelist = []; // Forwarded-for white listing.
|
|
56
|
+
this.options = options; // The configuration.
|
|
57
|
+
this.transformers = { // Message transformers.
|
|
58
|
+
outgoing: [],
|
|
59
|
+
incoming: []
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
this.server = server;
|
|
63
|
+
this.pathname = 'string' === typeof options.pathname
|
|
64
|
+
? options.pathname.charAt(0) !== '/'
|
|
65
|
+
? '/'+ options.pathname
|
|
66
|
+
: options.pathname
|
|
67
|
+
: '/primus';
|
|
68
|
+
|
|
69
|
+
//
|
|
70
|
+
// Create a specification file with the information that people might need to
|
|
71
|
+
// connect to the server.
|
|
72
|
+
//
|
|
73
|
+
this.spec = {
|
|
74
|
+
pingInterval: options.pingInterval,
|
|
75
|
+
pathname: this.pathname,
|
|
76
|
+
version: this.version
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
//
|
|
80
|
+
// Create a pre-bound Spark constructor. Doing a Spark.bind(Spark, this) doesn't
|
|
81
|
+
// work as we cannot extend the constructor of it anymore. The added benefit of
|
|
82
|
+
// approach listed below is that the prototype extensions are only applied to
|
|
83
|
+
// the Spark of this Primus instance.
|
|
84
|
+
//
|
|
85
|
+
this.Spark = function Sparky(headers, address, query, id, request, socket) {
|
|
86
|
+
Spark.call(this, primus, headers, address, query, id, request, socket);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
this.Spark.prototype = Object.create(Spark.prototype, {
|
|
90
|
+
constructor: {
|
|
91
|
+
configurable: true,
|
|
92
|
+
value: this.Spark,
|
|
93
|
+
writable: true
|
|
94
|
+
},
|
|
95
|
+
__initialise: {
|
|
96
|
+
value: Spark.prototype.__initialise.slice(),
|
|
97
|
+
configurable: true,
|
|
98
|
+
writable: true
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
//
|
|
103
|
+
// Copy over the original Spark static properties and methods so readable and
|
|
104
|
+
// writable can also be used.
|
|
105
|
+
//
|
|
106
|
+
for (key in Spark) {
|
|
107
|
+
this.Spark[key] = Spark[key];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.parsers(options.parser);
|
|
111
|
+
this.initialise(options.transformer, options);
|
|
112
|
+
|
|
113
|
+
//
|
|
114
|
+
// If the plugins are supplied through the options, also initialise them.
|
|
115
|
+
// This also allows us to use plugins when creating a client constructor
|
|
116
|
+
// with the `Primus.createSocket({})` method.
|
|
117
|
+
//
|
|
118
|
+
if ('string' === typeof options.plugin) {
|
|
119
|
+
options.plugin.split(/[, ]+/).forEach(function register(name) {
|
|
120
|
+
primus.plugin(name, name);
|
|
121
|
+
});
|
|
122
|
+
} else if ('object' === typeof options.plugin) {
|
|
123
|
+
for (key in options.plugin) {
|
|
124
|
+
this.plugin(key, options.plugin[key]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
//
|
|
129
|
+
// - Cluster node 0.10 lets the Operating System decide to which worker a request
|
|
130
|
+
// goes. This can result in a not even distribution where some workers are
|
|
131
|
+
// used at 10% while others at 90%. In addition to that the load balancing
|
|
132
|
+
// isn't sticky.
|
|
133
|
+
//
|
|
134
|
+
// - Cluster node 0.12 implements a custom round robin algorithm. This solves the
|
|
135
|
+
// not even distribution of work but it does not address our sticky session
|
|
136
|
+
// requirement.
|
|
137
|
+
//
|
|
138
|
+
// Projects like `sticky-session` attempt to implement sticky sessions but they
|
|
139
|
+
// are using `net` server instead of a HTTP server in combination with the
|
|
140
|
+
// remoteAddress of the connection to load balance. This does not work when you
|
|
141
|
+
// address your servers behind a load balancer as the IP is set to the load
|
|
142
|
+
// balancer, not the connecting clients. All in all, it only causes more
|
|
143
|
+
// scalability problems. So we've opted-in to warn users about the
|
|
144
|
+
// risks of using Primus in a cluster.
|
|
145
|
+
//
|
|
146
|
+
if (!options.iknowclusterwillbreakconnections && require('cluster').isWorker) [
|
|
147
|
+
'',
|
|
148
|
+
'The `cluster` module does not implement sticky sessions. Learn more about',
|
|
149
|
+
'this issue at:',
|
|
150
|
+
'',
|
|
151
|
+
'http://github.com/primus/primus#can-i-use-cluster',
|
|
152
|
+
''
|
|
153
|
+
].forEach(function warn(line) {
|
|
154
|
+
console.error('Primus: '+ line);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
//
|
|
159
|
+
// Fuse and spice-up the Primus prototype with EventEmitter and predefine
|
|
160
|
+
// awesomeness.
|
|
161
|
+
//
|
|
162
|
+
fuse(Primus, EventEmitter);
|
|
163
|
+
|
|
164
|
+
//
|
|
165
|
+
// Lazy read the primus.js JavaScript client.
|
|
166
|
+
//
|
|
167
|
+
Object.defineProperty(Primus.prototype, 'client', {
|
|
168
|
+
get: function read() {
|
|
169
|
+
if (!read.primus) {
|
|
170
|
+
read.primus = fs.readFileSync(__dirname + '/dist/primus.js', 'utf-8');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return read.primus;
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
//
|
|
178
|
+
// Lazy compile the primus.js JavaScript client for Node.js
|
|
179
|
+
//
|
|
180
|
+
Object.defineProperty(Primus.prototype, 'Socket', {
|
|
181
|
+
get: function () {
|
|
182
|
+
const sandbox = Object.keys(global).reduce((acc, key) => {
|
|
183
|
+
if (key !== 'global' && key !== 'require') acc[key] = global[key];
|
|
184
|
+
return acc;
|
|
185
|
+
}, {
|
|
186
|
+
__dirname: process.cwd(),
|
|
187
|
+
__filename: 'primus.js',
|
|
188
|
+
require: require,
|
|
189
|
+
|
|
190
|
+
//
|
|
191
|
+
// The following globals are introduced so libraries that use `instanceof`
|
|
192
|
+
// checks for type checking do not fail as the code is run in a new
|
|
193
|
+
// context.
|
|
194
|
+
//
|
|
195
|
+
Uint8Array: Uint8Array,
|
|
196
|
+
Object: Object,
|
|
197
|
+
RegExp: RegExp,
|
|
198
|
+
Array: Array,
|
|
199
|
+
Error: Error,
|
|
200
|
+
Date: Date
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
vm.runInNewContext(this.library(true), sandbox, { filename: 'primus.js' });
|
|
204
|
+
return sandbox[this.options.global || 'Primus'];
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
//
|
|
209
|
+
// Expose the current version number.
|
|
210
|
+
//
|
|
211
|
+
Primus.prototype.version = require('./package.json').version;
|
|
212
|
+
|
|
213
|
+
//
|
|
214
|
+
// A list of supported transformers and the required Node.js modules.
|
|
215
|
+
//
|
|
216
|
+
Primus.transformers = require('./transformers.json');
|
|
217
|
+
Primus.parsers = require('./parsers.json');
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Simple function to output common errors.
|
|
221
|
+
*
|
|
222
|
+
* @param {String} what What is missing.
|
|
223
|
+
* @param {Object} where Either Primus.parsers or Primus.transformers.
|
|
224
|
+
* @returns {Object}
|
|
225
|
+
* @api private
|
|
226
|
+
*/
|
|
227
|
+
Primus.readable('is', function is(what, where) {
|
|
228
|
+
var missing = Primus.parsers !== where
|
|
229
|
+
? 'transformer'
|
|
230
|
+
: 'parser'
|
|
231
|
+
, dependency = where[what];
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
missing: function write() {
|
|
235
|
+
console.error('Primus:');
|
|
236
|
+
console.error('Primus: Missing required npm dependency for '+ what);
|
|
237
|
+
console.error('Primus: Please run the following command and try again:');
|
|
238
|
+
console.error('Primus:');
|
|
239
|
+
console.error('Primus: npm install --save %s', dependency.server);
|
|
240
|
+
console.error('Primus:');
|
|
241
|
+
|
|
242
|
+
return 'Missing dependencies for '+ missing +': "'+ what + '"';
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
unknown: function write() {
|
|
246
|
+
console.error('Primus:');
|
|
247
|
+
console.error('Primus: Unsupported %s: "%s"', missing, what);
|
|
248
|
+
console.error('Primus: We only support the following %ss:', missing);
|
|
249
|
+
console.error('Primus:');
|
|
250
|
+
console.error('Primus: %s', Object.keys(where).join(', '));
|
|
251
|
+
console.error('Primus:');
|
|
252
|
+
|
|
253
|
+
return 'Unsupported '+ missing +': "'+ what +'"';
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Initialise the real-time engine that was chosen.
|
|
260
|
+
*
|
|
261
|
+
* @param {Mixed} Transformer The name of the transformer or a constructor;
|
|
262
|
+
* @param {Object} options Options.
|
|
263
|
+
* @api private
|
|
264
|
+
*/
|
|
265
|
+
Primus.readable('initialise', function initialise(Transformer, options) {
|
|
266
|
+
Transformer = Transformer || 'websockets';
|
|
267
|
+
|
|
268
|
+
var primus = this
|
|
269
|
+
, transformer;
|
|
270
|
+
|
|
271
|
+
if ('string' === typeof Transformer) {
|
|
272
|
+
log('transformer `%s` is a string, attempting to resolve location', Transformer);
|
|
273
|
+
Transformer = transformer = Transformer.toLowerCase();
|
|
274
|
+
this.spec.transformer = transformer;
|
|
275
|
+
|
|
276
|
+
//
|
|
277
|
+
// This is a unknown transformer, it could be people made a typo.
|
|
278
|
+
//
|
|
279
|
+
if (!(Transformer in Primus.transformers)) {
|
|
280
|
+
log('the supplied transformer %s is not supported, please use %s', transformer, Primus.transformers);
|
|
281
|
+
throw new PrimusError(this.is(Transformer, Primus.transformers).unknown(), this);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
Transformer = require('./transformers/'+ transformer);
|
|
286
|
+
this.transformer = new Transformer(this);
|
|
287
|
+
} catch (e) {
|
|
288
|
+
if (e.code === 'MODULE_NOT_FOUND') {
|
|
289
|
+
log('the supplied transformer `%s` is missing', transformer);
|
|
290
|
+
throw new PrimusError(this.is(transformer, Primus.transformers).missing(), this);
|
|
291
|
+
} else {
|
|
292
|
+
log(e);
|
|
293
|
+
throw e;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
log('received a custom transformer');
|
|
298
|
+
this.spec.transformer = 'custom';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if ('function' !== typeof Transformer) {
|
|
302
|
+
throw new PrimusError('The given transformer is not a constructor', this);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
this.transformer = this.transformer || new Transformer(this);
|
|
306
|
+
|
|
307
|
+
this.on('connection', function connection(stream) {
|
|
308
|
+
this.connected++;
|
|
309
|
+
this.connections[stream.id] = stream;
|
|
310
|
+
|
|
311
|
+
log('connection: %s currently serving %d concurrent', stream.id, this.connected);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
this.on('disconnection', function disconnected(stream) {
|
|
315
|
+
this.connected--;
|
|
316
|
+
delete this.connections[stream.id];
|
|
317
|
+
|
|
318
|
+
log('disconnection: %s currently serving %d concurrent', stream.id, this.connected);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
//
|
|
322
|
+
// Add our default middleware layers.
|
|
323
|
+
//
|
|
324
|
+
this.use('forwarded', require('./middleware/forwarded'));
|
|
325
|
+
this.use('cors', require('./middleware/access-control'));
|
|
326
|
+
this.use('primus.js', require('./middleware/primus'));
|
|
327
|
+
this.use('spec', require('./middleware/spec'));
|
|
328
|
+
this.use('x-xss', require('./middleware/xss'));
|
|
329
|
+
this.use('no-cache', require('./middleware/no-cache'));
|
|
330
|
+
this.use('authorization', require('./middleware/authorization'));
|
|
331
|
+
|
|
332
|
+
//
|
|
333
|
+
// Set the heartbeat interval.
|
|
334
|
+
//
|
|
335
|
+
if (options.pingInterval) {
|
|
336
|
+
this.heartbeatInterval = setInterval(
|
|
337
|
+
this.heartbeat.bind(this),
|
|
338
|
+
options.pingInterval
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
//
|
|
343
|
+
// Emit the initialised event after the next tick so we have some time to
|
|
344
|
+
// attach listeners.
|
|
345
|
+
//
|
|
346
|
+
process.nextTick(function tock() {
|
|
347
|
+
primus.emit('initialised', primus.transformer, primus.parser, options);
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Add a new authorization handler.
|
|
353
|
+
*
|
|
354
|
+
* @param {Function} auth The authorization handler.
|
|
355
|
+
* @returns {Primus}
|
|
356
|
+
* @api public
|
|
357
|
+
*/
|
|
358
|
+
Primus.readable('authorize', function authorize(auth) {
|
|
359
|
+
if ('function' !== typeof auth) {
|
|
360
|
+
throw new PrimusError('Authorize only accepts functions', this);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (auth.length < 2) {
|
|
364
|
+
throw new PrimusError('Authorize function requires more arguments', this);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
log('setting an authorization function');
|
|
368
|
+
this.auth = auth;
|
|
369
|
+
return this;
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Iterate over the connections.
|
|
374
|
+
*
|
|
375
|
+
* @param {Function} fn The function that is called every iteration.
|
|
376
|
+
* @param {Function} done Optional callback, if you want to iterate asynchronously.
|
|
377
|
+
* @returns {Primus}
|
|
378
|
+
* @api public
|
|
379
|
+
*/
|
|
380
|
+
Primus.readable('forEach', function forEach(fn, done) {
|
|
381
|
+
if (!done) {
|
|
382
|
+
for (var id in this.connections) {
|
|
383
|
+
if (fn(this.spark(id), id, this.connections) === false) break;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return this;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
var ids = Object.keys(this.connections)
|
|
390
|
+
, primus = this;
|
|
391
|
+
|
|
392
|
+
log('iterating over %d connections', ids.length);
|
|
393
|
+
|
|
394
|
+
function pushId(spark) {
|
|
395
|
+
ids.push(spark.id);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
//
|
|
399
|
+
// We are going to iterate through the connections asynchronously so
|
|
400
|
+
// we should handle new connections as they come in.
|
|
401
|
+
//
|
|
402
|
+
primus.on('connection', pushId);
|
|
403
|
+
|
|
404
|
+
(function iterate() {
|
|
405
|
+
var id = ids.shift()
|
|
406
|
+
, spark;
|
|
407
|
+
|
|
408
|
+
if (!id) {
|
|
409
|
+
primus.removeListener('connection', pushId);
|
|
410
|
+
return done();
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
spark = primus.spark(id);
|
|
414
|
+
|
|
415
|
+
//
|
|
416
|
+
// The connection may have already been closed.
|
|
417
|
+
//
|
|
418
|
+
if (!spark) return iterate();
|
|
419
|
+
|
|
420
|
+
fn(spark, function next(err, forward) {
|
|
421
|
+
if (err || forward === false) {
|
|
422
|
+
primus.removeListener('connection', pushId);
|
|
423
|
+
return done(err);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
iterate();
|
|
427
|
+
});
|
|
428
|
+
}());
|
|
429
|
+
|
|
430
|
+
return this;
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Send a ping packet to all clients to ensure that they are still connected.
|
|
435
|
+
*
|
|
436
|
+
* @returns {Primus}
|
|
437
|
+
* @api private
|
|
438
|
+
*/
|
|
439
|
+
Primus.readable('heartbeat', function heartbeat() {
|
|
440
|
+
this.forEach(function forEach(spark) {
|
|
441
|
+
spark.heartbeat();
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
return this;
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Broadcast the message to all connections.
|
|
449
|
+
*
|
|
450
|
+
* @param {Mixed} data The data you want to send.
|
|
451
|
+
* @returns {Primus}
|
|
452
|
+
* @api public
|
|
453
|
+
*/
|
|
454
|
+
Primus.readable('write', function write(data) {
|
|
455
|
+
this.forEach(function forEach(spark) {
|
|
456
|
+
spark.write(data);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
return this;
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Install message parsers.
|
|
464
|
+
*
|
|
465
|
+
* @param {Mixed} parser Parse name or parser Object.
|
|
466
|
+
* @returns {Primus}
|
|
467
|
+
* @api private
|
|
468
|
+
*/
|
|
469
|
+
Primus.readable('parsers', function parsers(parser) {
|
|
470
|
+
parser = parser || 'json';
|
|
471
|
+
|
|
472
|
+
if ('string' === typeof parser) {
|
|
473
|
+
log('transformer `%s` is a string, attempting to resolve location', parser);
|
|
474
|
+
parser = parser.toLowerCase();
|
|
475
|
+
this.spec.parser = parser;
|
|
476
|
+
|
|
477
|
+
//
|
|
478
|
+
// This is a unknown parser, it could be people made a typo.
|
|
479
|
+
//
|
|
480
|
+
if (!(parser in Primus.parsers)) {
|
|
481
|
+
log('the supplied parser `%s` is not supported please use %s', parser, Primus.parsers);
|
|
482
|
+
throw new PrimusError(this.is(parser, Primus.parsers).unknown(), this);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
try { parser = require('./parsers/'+ parser); }
|
|
486
|
+
catch (e) {
|
|
487
|
+
if (e.code === 'MODULE_NOT_FOUND') {
|
|
488
|
+
log('the supplied parser `%s` is missing', parser);
|
|
489
|
+
throw new PrimusError(this.is(parser, Primus.parsers).missing(), this);
|
|
490
|
+
} else {
|
|
491
|
+
log(e);
|
|
492
|
+
throw e;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
this.spec.parser = 'custom';
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if ('object' !== typeof parser) {
|
|
500
|
+
throw new PrimusError('The given parser is not an Object', this);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
this.encoder = parser.encoder;
|
|
504
|
+
this.decoder = parser.decoder;
|
|
505
|
+
this.parser = parser;
|
|
506
|
+
|
|
507
|
+
return this;
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Register a new message transformer. This allows you to easily manipulate incoming
|
|
512
|
+
* and outgoing data which is particularity handy for plugins that want to send
|
|
513
|
+
* meta data together with the messages.
|
|
514
|
+
*
|
|
515
|
+
* @param {String} type Incoming or outgoing
|
|
516
|
+
* @param {Function} fn A new message transformer.
|
|
517
|
+
* @returns {Primus}
|
|
518
|
+
* @api public
|
|
519
|
+
*/
|
|
520
|
+
Primus.readable('transform', function transform(type, fn) {
|
|
521
|
+
if (!(type in this.transformers)) {
|
|
522
|
+
throw new PrimusError('Invalid transformer type', this);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (~this.transformers[type].indexOf(fn)) {
|
|
526
|
+
log('the %s message transformer already exists, not adding it', type);
|
|
527
|
+
return this;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
this.transformers[type].push(fn);
|
|
531
|
+
return this;
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Gets a spark by its id.
|
|
536
|
+
*
|
|
537
|
+
* @param {String} id The spark's id.
|
|
538
|
+
* @returns {Spark}
|
|
539
|
+
* @api private
|
|
540
|
+
*/
|
|
541
|
+
Primus.readable('spark', function spark(id) {
|
|
542
|
+
return this.connections[id];
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Generate a client library.
|
|
547
|
+
*
|
|
548
|
+
* @param {Boolean} nodejs Don't include the library, as we're running on Node.js.
|
|
549
|
+
* @returns {String} The client library.
|
|
550
|
+
* @api public
|
|
551
|
+
*/
|
|
552
|
+
Primus.readable('library', function compile(nodejs) {
|
|
553
|
+
var library = [ !nodejs ? this.transformer.library : null ]
|
|
554
|
+
, global = this.options.global || 'Primus'
|
|
555
|
+
, parser = this.parser.library || ''
|
|
556
|
+
, client = this.client;
|
|
557
|
+
|
|
558
|
+
//
|
|
559
|
+
// Add a simple export wrapper so it can be used as Node.js, AMD or browser
|
|
560
|
+
// client.
|
|
561
|
+
//
|
|
562
|
+
client = [
|
|
563
|
+
'(function UMDish(name, context, definition, plugins) {',
|
|
564
|
+
' context[name] = definition.call(context);',
|
|
565
|
+
' for (var i = 0; i < plugins.length; i++) {',
|
|
566
|
+
' plugins[i](context[name])',
|
|
567
|
+
' }',
|
|
568
|
+
' if (typeof module !== "undefined" && module.exports) {',
|
|
569
|
+
' module.exports = context[name];',
|
|
570
|
+
' } else if (typeof define === "function" && define.amd) {',
|
|
571
|
+
' define(function reference() { return context[name]; });',
|
|
572
|
+
' }',
|
|
573
|
+
'})("'+ global +'", this || {}, function wrapper() {',
|
|
574
|
+
' var define, module, exports',
|
|
575
|
+
' , Primus = '+ client.slice(client.indexOf('return ') + 7, -4) +';',
|
|
576
|
+
''
|
|
577
|
+
].join('\n');
|
|
578
|
+
|
|
579
|
+
//
|
|
580
|
+
// Replace some basic content.
|
|
581
|
+
//
|
|
582
|
+
client = client
|
|
583
|
+
.replace('null; // @import {primus::pathname}', '"'+ this.pathname.toString() +'"')
|
|
584
|
+
.replace('null; // @import {primus::version}', '"'+ this.version +'"')
|
|
585
|
+
.replace('null; // @import {primus::client}', this.transformer.client.toString())
|
|
586
|
+
.replace('null; // @import {primus::auth}', (!!this.auth).toString())
|
|
587
|
+
.replace('null; // @import {primus::encoder}', this.encoder.toString())
|
|
588
|
+
.replace('null; // @import {primus::decoder}', this.decoder.toString());
|
|
589
|
+
|
|
590
|
+
//
|
|
591
|
+
// As we're given a `pingInterval` value on the server side, we need to update
|
|
592
|
+
// the `pingTimeout` on the client.
|
|
593
|
+
//
|
|
594
|
+
if (this.options.pingInterval) {
|
|
595
|
+
const value = this.options.pingInterval + Math.round(this.options.pingInterval / 2);
|
|
596
|
+
|
|
597
|
+
log('updating the default value of the client `pingTimeout` option');
|
|
598
|
+
client = client.replace(
|
|
599
|
+
'options.pingTimeout : 45e3;',
|
|
600
|
+
`options.pingTimeout : ${value};`
|
|
601
|
+
);
|
|
602
|
+
} else {
|
|
603
|
+
log('setting the default value of the client `pingTimeout` option to `false`');
|
|
604
|
+
client = client.replace(
|
|
605
|
+
'options.pingTimeout : 45e3;',
|
|
606
|
+
'options.pingTimeout : false;'
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
//
|
|
611
|
+
// Add the parser inside the closure, to prevent global leaking.
|
|
612
|
+
//
|
|
613
|
+
if (parser && parser.length) {
|
|
614
|
+
log('adding parser to the client file');
|
|
615
|
+
client += parser;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
//
|
|
619
|
+
// Iterate over the parsers, and register the client side plugins. If there's
|
|
620
|
+
// a library bundled, add it the library array as there were some issues with
|
|
621
|
+
// frameworks that get included in module wrapper as it forces strict mode.
|
|
622
|
+
//
|
|
623
|
+
var name, plugin;
|
|
624
|
+
|
|
625
|
+
for (name in this.ark) {
|
|
626
|
+
plugin = this.ark[name];
|
|
627
|
+
name = JSON.stringify(name);
|
|
628
|
+
|
|
629
|
+
if (plugin.library) {
|
|
630
|
+
log('adding the library of the %s plugin to the client file', name);
|
|
631
|
+
library.push(plugin.library);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
if (!plugin.client) continue;
|
|
635
|
+
|
|
636
|
+
log('adding the client code of the %s plugin to the client file', name);
|
|
637
|
+
client += 'Primus.prototype.ark['+ name +'] = '+ plugin.client.toString() +';\n';
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
//
|
|
641
|
+
// Close the export wrapper and return the client. If we need to add
|
|
642
|
+
// a library, we should add them after we've created our closure and module
|
|
643
|
+
// exports. Some libraries seem to fail hard once they are wrapped in our
|
|
644
|
+
// closure so I'll rather expose a global variable instead of having to monkey
|
|
645
|
+
// patch too much code.
|
|
646
|
+
//
|
|
647
|
+
return client + [
|
|
648
|
+
' return Primus;',
|
|
649
|
+
'},',
|
|
650
|
+
'['
|
|
651
|
+
].concat(library.filter(Boolean).map(function expose(library) {
|
|
652
|
+
return [
|
|
653
|
+
'function (Primus) {',
|
|
654
|
+
library,
|
|
655
|
+
'}'
|
|
656
|
+
].join('\n');
|
|
657
|
+
}).join(',\n'))
|
|
658
|
+
.concat(']);')
|
|
659
|
+
.join('\n');
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Save the library to disk.
|
|
664
|
+
*
|
|
665
|
+
* @param {String} dir The location that we need to save the library.
|
|
666
|
+
* @param {function} fn Optional callback, if you want an async save.
|
|
667
|
+
* @returns {Primus}
|
|
668
|
+
* @api public
|
|
669
|
+
*/
|
|
670
|
+
Primus.readable('save', function save(path, fn) {
|
|
671
|
+
if (!fn) fs.writeFileSync(path, this.library(), 'utf-8');
|
|
672
|
+
else fs.writeFile(path, this.library(), 'utf-8', fn);
|
|
673
|
+
|
|
674
|
+
return this;
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Register a new Primus plugin.
|
|
679
|
+
*
|
|
680
|
+
* ```js
|
|
681
|
+
* primus.plugin('ack', {
|
|
682
|
+
* //
|
|
683
|
+
* // Only ran on the server.
|
|
684
|
+
* //
|
|
685
|
+
* server: function (primus, options) {
|
|
686
|
+
* // do stuff
|
|
687
|
+
* },
|
|
688
|
+
*
|
|
689
|
+
* //
|
|
690
|
+
* // Runs on the client, it's automatically bundled.
|
|
691
|
+
* //
|
|
692
|
+
* client: function (primus, options) {
|
|
693
|
+
* // do client stuff
|
|
694
|
+
* },
|
|
695
|
+
*
|
|
696
|
+
* //
|
|
697
|
+
* // Optional library that needs to be bundled on the client (should be a string)
|
|
698
|
+
* //
|
|
699
|
+
* library: ''
|
|
700
|
+
* });
|
|
701
|
+
* ```
|
|
702
|
+
*
|
|
703
|
+
* @param {String} name The name of the plugin.
|
|
704
|
+
* @param {Object} energon The plugin that contains client and server extensions.
|
|
705
|
+
* @returns {Mixed}
|
|
706
|
+
* @api public
|
|
707
|
+
*/
|
|
708
|
+
Primus.readable('plugin', function plugin(name, energon) {
|
|
709
|
+
if (!name) return this.ark;
|
|
710
|
+
|
|
711
|
+
if (!energon) {
|
|
712
|
+
if ('string' === typeof name) return this.ark[name];
|
|
713
|
+
if ('object' === typeof name) {
|
|
714
|
+
energon = name;
|
|
715
|
+
name = energon.name;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if ('string' !== typeof name || !name) {
|
|
720
|
+
throw new PrimusError('Plugin name must be a non empty string', this);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if ('string' === typeof energon) {
|
|
724
|
+
log('plugin was passed as a string, attempting to require %s', energon);
|
|
725
|
+
energon = require(energon);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
//
|
|
729
|
+
// Plugin accepts an object or a function only.
|
|
730
|
+
//
|
|
731
|
+
if (!/^(object|function)$/.test(typeof energon)) {
|
|
732
|
+
throw new PrimusError('Plugin should be an object or function', this);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
//
|
|
736
|
+
// Plugin require a client, server or both to be specified in the object.
|
|
737
|
+
//
|
|
738
|
+
if (!energon.server && !energon.client) {
|
|
739
|
+
throw new PrimusError('Plugin is missing a client or server function', this);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
//
|
|
743
|
+
// Don't allow duplicate plugins or plugin override as this is most likely
|
|
744
|
+
// unintentional.
|
|
745
|
+
//
|
|
746
|
+
if (name in this.ark) {
|
|
747
|
+
throw new PrimusError('Plugin name already defined', this);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
log('adding %s as new plugin', name);
|
|
751
|
+
this.ark[name] = energon;
|
|
752
|
+
this.emit('plugin', name, energon);
|
|
753
|
+
|
|
754
|
+
if (!energon.server) return this;
|
|
755
|
+
|
|
756
|
+
log('calling the %s plugin\'s server code', name);
|
|
757
|
+
energon.server.call(this, this, this.options);
|
|
758
|
+
|
|
759
|
+
return this;
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Remove plugin from the ark.
|
|
764
|
+
*
|
|
765
|
+
* @param {String} name Name of the plugin we need to remove from the ark.
|
|
766
|
+
* @returns {Boolean} Successful removal of the plugin.
|
|
767
|
+
* @api public
|
|
768
|
+
*/
|
|
769
|
+
Primus.readable('plugout', function plugout(name) {
|
|
770
|
+
if (!(name in this.ark)) return false;
|
|
771
|
+
|
|
772
|
+
this.emit('plugout', name, this.ark[name]);
|
|
773
|
+
delete this.ark[name];
|
|
774
|
+
|
|
775
|
+
return true;
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Add a new middleware layer. If no middleware name has been provided we will
|
|
780
|
+
* attempt to take the name of the supplied function. If that fails, well fuck,
|
|
781
|
+
* just random id it.
|
|
782
|
+
*
|
|
783
|
+
* @param {String} name The name of the middleware.
|
|
784
|
+
* @param {Function} fn The middleware that's called each time.
|
|
785
|
+
* @param {Object} options Middleware configuration.
|
|
786
|
+
* @param {Number} level 0 based optional index for the middleware.
|
|
787
|
+
* @returns {Primus}
|
|
788
|
+
* @api public
|
|
789
|
+
*/
|
|
790
|
+
Primus.readable('use', function use(name, fn, options, level) {
|
|
791
|
+
if ('function' === typeof name) {
|
|
792
|
+
level = options;
|
|
793
|
+
options = fn;
|
|
794
|
+
fn = name;
|
|
795
|
+
name = fn.name || 'pid_'+ Date.now();
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (!level && 'number' === typeof options) {
|
|
799
|
+
level = options;
|
|
800
|
+
options = {};
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
options = options || {};
|
|
804
|
+
|
|
805
|
+
//
|
|
806
|
+
// No or only 1 argument means that we need to initialise the middleware, this
|
|
807
|
+
// is a special initialisation process where we pass in a reference to the
|
|
808
|
+
// initialised Primus instance so a pre-compiling process can be done.
|
|
809
|
+
//
|
|
810
|
+
if (fn.length < 2) {
|
|
811
|
+
log('automatically configuring middleware `%s`', name);
|
|
812
|
+
fn = fn.call(this, options);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
//
|
|
816
|
+
// Make sure that we have a function that takes at least 2 arguments.
|
|
817
|
+
//
|
|
818
|
+
if ('function' !== typeof fn || fn.length < 2) {
|
|
819
|
+
throw new PrimusError('Middleware should be a function that accepts at least 2 args');
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
var layer = {
|
|
823
|
+
length: fn.length, // Amount of arguments indicates if it's async.
|
|
824
|
+
enabled: true, // Middleware is enabled by default.
|
|
825
|
+
name: name, // Used for lookups.
|
|
826
|
+
fn: fn // The actual middleware.
|
|
827
|
+
}, index = this.indexOfLayer(name);
|
|
828
|
+
|
|
829
|
+
//
|
|
830
|
+
// Override middleware layer if we already have a middleware layer with
|
|
831
|
+
// exactly the same name.
|
|
832
|
+
//
|
|
833
|
+
if (!~index) {
|
|
834
|
+
if (level >= 0 && level < this.layers.length) {
|
|
835
|
+
log('adding middleware `%s` to the supplied index at %d', name, level);
|
|
836
|
+
this.layers.splice(level, 0, layer);
|
|
837
|
+
} else {
|
|
838
|
+
this.layers.push(layer);
|
|
839
|
+
}
|
|
840
|
+
} else {
|
|
841
|
+
this.layers[index] = layer;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
return this;
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* Remove a middleware layer from the stack.
|
|
849
|
+
*
|
|
850
|
+
* @param {String} name The name of the middleware.
|
|
851
|
+
* @returns {Primus}
|
|
852
|
+
* @api public
|
|
853
|
+
*/
|
|
854
|
+
Primus.readable('remove', function remove(name) {
|
|
855
|
+
var index = this.indexOfLayer(name);
|
|
856
|
+
|
|
857
|
+
if (~index) {
|
|
858
|
+
log('removing middleware `%s`', name);
|
|
859
|
+
this.layers.splice(index, 1);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
return this;
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Enable a given middleware layer.
|
|
867
|
+
*
|
|
868
|
+
* @param {String} name The name of the middleware.
|
|
869
|
+
* @returns {Primus}
|
|
870
|
+
* @api public
|
|
871
|
+
*/
|
|
872
|
+
Primus.readable('enable', function enable(name) {
|
|
873
|
+
var index = this.indexOfLayer(name);
|
|
874
|
+
|
|
875
|
+
if (~index) {
|
|
876
|
+
log('enabling middleware `%s`', name);
|
|
877
|
+
this.layers[index].enabled = true;
|
|
878
|
+
}
|
|
879
|
+
return this;
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Disable a given middleware layer.
|
|
884
|
+
*
|
|
885
|
+
* @param {String} name The name of the middleware.
|
|
886
|
+
* @returns {Primus}
|
|
887
|
+
* @api public
|
|
888
|
+
*/
|
|
889
|
+
Primus.readable('disable', function disable(name) {
|
|
890
|
+
var index = this.indexOfLayer(name);
|
|
891
|
+
|
|
892
|
+
if (~index) {
|
|
893
|
+
log('disabling middleware `%s`', name);
|
|
894
|
+
this.layers[index].enabled = false;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
return this;
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Find the index of a given middleware layer by name.
|
|
902
|
+
*
|
|
903
|
+
* @param {String} name The name of the layer.
|
|
904
|
+
* @returns {Number}
|
|
905
|
+
* @api private
|
|
906
|
+
*/
|
|
907
|
+
Primus.readable('indexOfLayer', function indexOfLayer(name) {
|
|
908
|
+
for (var i = 0, length = this.layers.length; i < length; i++) {
|
|
909
|
+
if (this.layers[i].name === name) return i;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
return -1;
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Destroy the created Primus instance.
|
|
917
|
+
*
|
|
918
|
+
* Options:
|
|
919
|
+
* - close (boolean) Close the given server.
|
|
920
|
+
* - reconnect (boolean) Trigger a client-side reconnect.
|
|
921
|
+
* - timeout (number) Close all active connections after x milliseconds.
|
|
922
|
+
*
|
|
923
|
+
* @param {Object} options Destruction instructions.
|
|
924
|
+
* @param {Function} fn Callback.
|
|
925
|
+
* @returns {Primus}
|
|
926
|
+
* @api public
|
|
927
|
+
*/
|
|
928
|
+
Primus.readable('destroy', function destroy(options, fn) {
|
|
929
|
+
if ('function' === typeof options) {
|
|
930
|
+
fn = options;
|
|
931
|
+
options = null;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
options = options || {};
|
|
935
|
+
if (options.reconnect) options.close = true;
|
|
936
|
+
|
|
937
|
+
var primus = this;
|
|
938
|
+
|
|
939
|
+
clearInterval(primus.heartbeatInterval);
|
|
940
|
+
|
|
941
|
+
setTimeout(function close() {
|
|
942
|
+
var transformer = primus.transformer;
|
|
943
|
+
|
|
944
|
+
//
|
|
945
|
+
// Ensure that the transformer receives the `close` event only once.
|
|
946
|
+
//
|
|
947
|
+
if (transformer) transformer.ultron.destroy();
|
|
948
|
+
|
|
949
|
+
//
|
|
950
|
+
// Close the connections that are left open.
|
|
951
|
+
//
|
|
952
|
+
primus.forEach(function shutdown(spark) {
|
|
953
|
+
spark.end(undefined, { reconnect: options.reconnect });
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
if (options.close !== false) {
|
|
957
|
+
//
|
|
958
|
+
// Closing a server that isn't started yet would throw an error.
|
|
959
|
+
//
|
|
960
|
+
try {
|
|
961
|
+
primus.server.close(function closed() {
|
|
962
|
+
primus.close(options, fn);
|
|
963
|
+
});
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
catch (e) {}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
primus.close(options, fn);
|
|
970
|
+
}, +options.timeout || 0);
|
|
971
|
+
|
|
972
|
+
return this;
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* Free resources after emitting a final `close` event.
|
|
977
|
+
*
|
|
978
|
+
* @param {Object} options Destruction instructions.
|
|
979
|
+
* @param {Function} fn Callback.
|
|
980
|
+
* @returns {Primus}
|
|
981
|
+
* @api private
|
|
982
|
+
*/
|
|
983
|
+
Primus.readable('close', function close(options, fn) {
|
|
984
|
+
var primus = this;
|
|
985
|
+
//
|
|
986
|
+
// Emit a final `close` event before removing all the listeners
|
|
987
|
+
// from all the event emitters.
|
|
988
|
+
//
|
|
989
|
+
primus.asyncemit('close', options, function done(err) {
|
|
990
|
+
if (err) {
|
|
991
|
+
if (fn) return fn(err);
|
|
992
|
+
throw err;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
var transformer = primus.transformer
|
|
996
|
+
, server = primus.server;
|
|
997
|
+
|
|
998
|
+
//
|
|
999
|
+
// If we don't have a server we are most likely destroying an already
|
|
1000
|
+
// destroyed Primus instance.
|
|
1001
|
+
//
|
|
1002
|
+
if (!server) return fn && fn();
|
|
1003
|
+
|
|
1004
|
+
server.removeAllListeners('request');
|
|
1005
|
+
server.removeAllListeners('upgrade');
|
|
1006
|
+
|
|
1007
|
+
//
|
|
1008
|
+
// Re-add the original listeners so that the server can be used again.
|
|
1009
|
+
//
|
|
1010
|
+
transformer.listeners('previous::request').forEach(function add(listener) {
|
|
1011
|
+
server.on('request', listener);
|
|
1012
|
+
});
|
|
1013
|
+
transformer.listeners('previous::upgrade').forEach(function add(listener) {
|
|
1014
|
+
server.on('upgrade', listener);
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
transformer.emit('close', options);
|
|
1018
|
+
transformer.removeAllListeners();
|
|
1019
|
+
|
|
1020
|
+
primus.removeAllListeners();
|
|
1021
|
+
|
|
1022
|
+
//
|
|
1023
|
+
// Null some potentially heavy objects to free some more memory instantly.
|
|
1024
|
+
//
|
|
1025
|
+
primus.transformers.outgoing.length = primus.transformers.incoming.length = 0;
|
|
1026
|
+
primus.transformer = primus.encoder = primus.decoder = primus.server = null;
|
|
1027
|
+
primus.connected = 0;
|
|
1028
|
+
|
|
1029
|
+
primus.connections = Object.create(null);
|
|
1030
|
+
primus.ark = Object.create(null);
|
|
1031
|
+
|
|
1032
|
+
if (fn) fn();
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1035
|
+
return this;
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* Async emit an event. We make a really broad assumption here and that is they
|
|
1040
|
+
* have the same amount of arguments as the supplied arguments (excluding the
|
|
1041
|
+
* event name).
|
|
1042
|
+
*
|
|
1043
|
+
* @returns {Primus}
|
|
1044
|
+
* @api private
|
|
1045
|
+
*/
|
|
1046
|
+
Primus.readable('asyncemit', require('asyncemit'));
|
|
1047
|
+
|
|
1048
|
+
//
|
|
1049
|
+
// Alias for destroy.
|
|
1050
|
+
//
|
|
1051
|
+
Primus.readable('end', Primus.prototype.destroy);
|
|
1052
|
+
|
|
1053
|
+
/**
|
|
1054
|
+
* Checks if the given event is an emitted event by Primus.
|
|
1055
|
+
*
|
|
1056
|
+
* @param {String} evt The event name.
|
|
1057
|
+
* @returns {Boolean}
|
|
1058
|
+
* @api public
|
|
1059
|
+
*/
|
|
1060
|
+
Primus.readable('reserved', function reserved(evt) {
|
|
1061
|
+
return (/^(incoming|outgoing)::/).test(evt)
|
|
1062
|
+
|| evt in reserved.events;
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* The actual events that are used by Primus.
|
|
1067
|
+
*
|
|
1068
|
+
* @type {Object}
|
|
1069
|
+
* @api public
|
|
1070
|
+
*/
|
|
1071
|
+
Primus.prototype.reserved.events = {
|
|
1072
|
+
'disconnection': 1,
|
|
1073
|
+
'initialised': 1,
|
|
1074
|
+
'connection': 1,
|
|
1075
|
+
'plugout': 1,
|
|
1076
|
+
'plugin': 1,
|
|
1077
|
+
'close': 1,
|
|
1078
|
+
'log': 1
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* Add a createSocket interface so we can create a Server client with the
|
|
1083
|
+
* specified `transformer` and `parser`.
|
|
1084
|
+
*
|
|
1085
|
+
* ```js
|
|
1086
|
+
* var Socket = Primus.createSocket({ transformer: transformer, parser: parser })
|
|
1087
|
+
* , socket = new Socket(url);
|
|
1088
|
+
* ```
|
|
1089
|
+
*
|
|
1090
|
+
* @param {Object} options The transformer / parser we need.
|
|
1091
|
+
* @returns {Socket}
|
|
1092
|
+
* @api public
|
|
1093
|
+
*/
|
|
1094
|
+
Primus.createSocket = function createSocket(options) {
|
|
1095
|
+
// Make sure the temporary Primus we create below doesn't start a heartbeat
|
|
1096
|
+
options = Object.assign({}, options, { pingInterval: false });
|
|
1097
|
+
|
|
1098
|
+
var primus = new Primus(new EventEmitter(), options);
|
|
1099
|
+
return primus.Socket;
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* Create a new Primus server.
|
|
1104
|
+
*
|
|
1105
|
+
* @param {Function} fn Request listener.
|
|
1106
|
+
* @param {Object} options Configuration.
|
|
1107
|
+
* @returns {Pipe}
|
|
1108
|
+
* @api public
|
|
1109
|
+
*/
|
|
1110
|
+
Primus.createServer = function createServer(fn, options) {
|
|
1111
|
+
if ('object' === typeof fn) {
|
|
1112
|
+
options = fn;
|
|
1113
|
+
fn = null;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
options = options || {};
|
|
1117
|
+
|
|
1118
|
+
var server = require('create-server')(Primus.prototype.merge.call(Primus, {
|
|
1119
|
+
http: function warn() {
|
|
1120
|
+
if (!options.iknowhttpsisbetter) [
|
|
1121
|
+
'',
|
|
1122
|
+
'We\'ve detected that you\'re using a HTTP instead of a HTTPS server.',
|
|
1123
|
+
'Please be aware that real-time connections have less chance of being blocked',
|
|
1124
|
+
'by firewalls and anti-virus scanners if they are encrypted (using SSL). If',
|
|
1125
|
+
'you run your server behind a reverse and HTTPS terminating proxy ignore',
|
|
1126
|
+
'this message, if not, you\'ve been warned.',
|
|
1127
|
+
''
|
|
1128
|
+
].forEach(function each(line) {
|
|
1129
|
+
console.log('primus: '+ line);
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
}, options));
|
|
1133
|
+
|
|
1134
|
+
//
|
|
1135
|
+
// Now that we've got a server, we can setup the Primus and start listening.
|
|
1136
|
+
//
|
|
1137
|
+
var application = new Primus(server, options);
|
|
1138
|
+
|
|
1139
|
+
if (fn) application.on('connection', fn);
|
|
1140
|
+
return application;
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1143
|
+
//
|
|
1144
|
+
// Expose the constructors of our Spark and Transformer so it can be extended by
|
|
1145
|
+
// a third party if needed.
|
|
1146
|
+
//
|
|
1147
|
+
Primus.Transformer = Transformer;
|
|
1148
|
+
Primus.Spark = Spark;
|
|
1149
|
+
|
|
1150
|
+
//
|
|
1151
|
+
// Expose the module.
|
|
1152
|
+
//
|
|
1153
|
+
module.exports = Primus;
|