arnaucube
arnaucube/socketioMEANseedPublic Self-hosted repository
socketioMEANseed/server/node_modules/socket.io/lib/index.js
server/node_modules/socket.io/lib/index.jsRaw
  1
  2/**
  3 * Module dependencies.
  4 */
  5
  6var http = require('http');
  7var read = require('fs').readFileSync;
  8var engine = require('engine.io');
  9var client = require('socket.io-client');
 10var clientVersion = require('socket.io-client/package').version;
 11var Client = require('./client');
 12var Emitter = require('events').EventEmitter;
 13var Namespace = require('./namespace');
 14var Adapter = require('socket.io-adapter');
 15var debug = require('debug')('socket.io:server');
 16var url = require('url');
 17
 18/**
 19 * Module exports.
 20 */
 21
 22module.exports = Server;
 23
 24/**
 25 * Socket.IO client source.
 26 */
 27
 28var clientSource = undefined;
 29var clientSourceMap = undefined;
 30
 31/**
 32 * Server constructor.
 33 *
 34 * @param {http.Server|Number|Object} srv http server, port or options
 35 * @param {Object} [opts]
 36 * @api public
 37 */
 38
 39function Server(srv, opts){
 40  if (!(this instanceof Server)) return new Server(srv, opts);
 41  if ('object' == typeof srv && !srv.listen) {
 42    opts = srv;
 43    srv = null;
 44  }
 45  opts = opts || {};
 46  this.nsps = {};
 47  this.path(opts.path || '/socket.io');
 48  this.serveClient(false !== opts.serveClient);
 49  this.adapter(opts.adapter || Adapter);
 50  this.origins(opts.origins || '*:*');
 51  this.sockets = this.of('/');
 52  if (srv) this.attach(srv, opts);
 53}
 54
 55/**
 56 * Server request verification function, that checks for allowed origins
 57 *
 58 * @param {http.IncomingMessage} req request
 59 * @param {Function} fn callback to be called with the result: `fn(err, success)`
 60 */
 61
 62Server.prototype.checkRequest = function(req, fn) {
 63  var origin = req.headers.origin || req.headers.referer;
 64
 65  // file:// URLs produce a null Origin which can't be authorized via echo-back
 66  if ('null' == origin || null == origin) origin = '*';
 67
 68  if (!!origin && typeof(this._origins) == 'function') return this._origins(origin, fn);
 69  if (this._origins.indexOf('*:*') !== -1) return fn(null, true);
 70  if (origin) {
 71    try {
 72      var parts = url.parse(origin);
 73      var defaultPort = 'https:' == parts.protocol ? 443 : 80;
 74      parts.port = parts.port != null
 75        ? parts.port
 76        : defaultPort;
 77      var ok =
 78        ~this._origins.indexOf(parts.hostname + ':' + parts.port) ||
 79        ~this._origins.indexOf(parts.hostname + ':*') ||
 80        ~this._origins.indexOf('*:' + parts.port);
 81      return fn(null, !!ok);
 82    } catch (ex) {
 83    }
 84  }
 85  fn(null, false);
 86};
 87
 88/**
 89 * Sets/gets whether client code is being served.
 90 *
 91 * @param {Boolean} v whether to serve client code
 92 * @return {Server|Boolean} self when setting or value when getting
 93 * @api public
 94 */
 95
 96Server.prototype.serveClient = function(v){
 97  if (!arguments.length) return this._serveClient;
 98  this._serveClient = v;
 99
100  if (v && !clientSource) {
101    clientSource = read(require.resolve('socket.io-client/dist/socket.io.min.js'), 'utf-8');
102    try {
103      clientSourceMap = read(require.resolve('socket.io-client/dist/socket.io.js.map'), 'utf-8');
104    } catch(err) {
105      debug('could not load sourcemap file');
106    }
107  }
108
109  return this;
110};
111
112/**
113 * Old settings for backwards compatibility
114 */
115
116var oldSettings = {
117  "transports": "transports",
118  "heartbeat timeout": "pingTimeout",
119  "heartbeat interval": "pingInterval",
120  "destroy buffer size": "maxHttpBufferSize"
121};
122
123/**
124 * Backwards compatibility.
125 *
126 * @api public
127 */
128
129Server.prototype.set = function(key, val){
130  if ('authorization' == key && val) {
131    this.use(function(socket, next) {
132      val(socket.request, function(err, authorized) {
133        if (err) return next(new Error(err));
134        if (!authorized) return next(new Error('Not authorized'));
135        next();
136      });
137    });
138  } else if ('origins' == key && val) {
139    this.origins(val);
140  } else if ('resource' == key) {
141    this.path(val);
142  } else if (oldSettings[key] && this.eio[oldSettings[key]]) {
143    this.eio[oldSettings[key]] = val;
144  } else {
145    console.error('Option %s is not valid. Please refer to the README.', key);
146  }
147
148  return this;
149};
150
151/**
152 * Sets the client serving path.
153 *
154 * @param {String} v pathname
155 * @return {Server|String} self when setting or value when getting
156 * @api public
157 */
158
159Server.prototype.path = function(v){
160  if (!arguments.length) return this._path;
161  this._path = v.replace(/\/$/, '');
162  return this;
163};
164
165/**
166 * Sets the adapter for rooms.
167 *
168 * @param {Adapter} v pathname
169 * @return {Server|Adapter} self when setting or value when getting
170 * @api public
171 */
172
173Server.prototype.adapter = function(v){
174  if (!arguments.length) return this._adapter;
175  this._adapter = v;
176  for (var i in this.nsps) {
177    if (this.nsps.hasOwnProperty(i)) {
178      this.nsps[i].initAdapter();
179    }
180  }
181  return this;
182};
183
184/**
185 * Sets the allowed origins for requests.
186 *
187 * @param {String} v origins
188 * @return {Server|Adapter} self when setting or value when getting
189 * @api public
190 */
191
192Server.prototype.origins = function(v){
193  if (!arguments.length) return this._origins;
194
195  this._origins = v;
196  return this;
197};
198
199/**
200 * Attaches socket.io to a server or port.
201 *
202 * @param {http.Server|Number} server or port
203 * @param {Object} options passed to engine.io
204 * @return {Server} self
205 * @api public
206 */
207
208Server.prototype.listen =
209Server.prototype.attach = function(srv, opts){
210  if ('function' == typeof srv) {
211    var msg = 'You are trying to attach socket.io to an express ' +
212    'request handler function. Please pass a http.Server instance.';
213    throw new Error(msg);
214  }
215
216  // handle a port as a string
217  if (Number(srv) == srv) {
218    srv = Number(srv);
219  }
220
221  if ('number' == typeof srv) {
222    debug('creating http server and binding to %d', srv);
223    var port = srv;
224    srv = http.Server(function(req, res){
225      res.writeHead(404);
226      res.end();
227    });
228    srv.listen(port);
229
230  }
231
232  // set engine.io path to `/socket.io`
233  opts = opts || {};
234  opts.path = opts.path || this.path();
235  // set origins verification
236  opts.allowRequest = opts.allowRequest || this.checkRequest.bind(this);
237
238  // initialize engine
239  debug('creating engine.io instance with opts %j', opts);
240  this.eio = engine.attach(srv, opts);
241
242  // attach static file serving
243  if (this._serveClient) this.attachServe(srv);
244
245  // Export http server
246  this.httpServer = srv;
247
248  // bind to engine events
249  this.bind(this.eio);
250
251  return this;
252};
253
254/**
255 * Attaches the static file serving.
256 *
257 * @param {Function|http.Server} srv http server
258 * @api private
259 */
260
261Server.prototype.attachServe = function(srv){
262  debug('attaching client serving req handler');
263  var url = this._path + '/socket.io.js';
264  var urlMap = this._path + '/socket.io.js.map';
265  var evs = srv.listeners('request').slice(0);
266  var self = this;
267  srv.removeAllListeners('request');
268  srv.on('request', function(req, res) {
269    if (0 === req.url.indexOf(urlMap)) {
270      self.serveMap(req, res);
271    } else if (0 === req.url.indexOf(url)) {
272      self.serve(req, res);
273    } else {
274      for (var i = 0; i < evs.length; i++) {
275        evs[i].call(srv, req, res);
276      }
277    }
278  });
279};
280
281/**
282 * Handles a request serving `/socket.io.js`
283 *
284 * @param {http.Request} req
285 * @param {http.Response} res
286 * @api private
287 */
288
289Server.prototype.serve = function(req, res){
290  // Per the standard, ETags must be quoted:
291  // https://tools.ietf.org/html/rfc7232#section-2.3
292  var expectedEtag = '"' + clientVersion + '"';
293
294  var etag = req.headers['if-none-match'];
295  if (etag) {
296    if (expectedEtag == etag) {
297      debug('serve client 304');
298      res.writeHead(304);
299      res.end();
300      return;
301    }
302  }
303
304  debug('serve client source');
305  res.setHeader('Content-Type', 'application/javascript');
306  res.setHeader('ETag', expectedEtag);
307  res.setHeader('X-SourceMap', 'socket.io.js.map');
308  res.writeHead(200);
309  res.end(clientSource);
310};
311
312/**
313 * Handles a request serving `/socket.io.js.map`
314 *
315 * @param {http.Request} req
316 * @param {http.Response} res
317 * @api private
318 */
319
320Server.prototype.serveMap = function(req, res){
321  // Per the standard, ETags must be quoted:
322  // https://tools.ietf.org/html/rfc7232#section-2.3
323  var expectedEtag = '"' + clientVersion + '"';
324
325  var etag = req.headers['if-none-match'];
326  if (etag) {
327    if (expectedEtag == etag) {
328      debug('serve client 304');
329      res.writeHead(304);
330      res.end();
331      return;
332    }
333  }
334
335  debug('serve client sourcemap');
336  res.setHeader('Content-Type', 'application/json');
337  res.setHeader('ETag', expectedEtag);
338  res.writeHead(200);
339  res.end(clientSourceMap);
340};
341
342/**
343 * Binds socket.io to an engine.io instance.
344 *
345 * @param {engine.Server} engine engine.io (or compatible) server
346 * @return {Server} self
347 * @api public
348 */
349
350Server.prototype.bind = function(engine){
351  this.engine = engine;
352  this.engine.on('connection', this.onconnection.bind(this));
353  return this;
354};
355
356/**
357 * Called with each incoming transport connection.
358 *
359 * @param {engine.Socket} conn
360 * @return {Server} self
361 * @api public
362 */
363
364Server.prototype.onconnection = function(conn){
365  debug('incoming connection with id %s', conn.id);
366  var client = new Client(this, conn);
367  client.connect('/');
368  return this;
369};
370
371/**
372 * Looks up a namespace.
373 *
374 * @param {String} name nsp name
375 * @param {Function} [fn] optional, nsp `connection` ev handler
376 * @api public
377 */
378
379Server.prototype.of = function(name, fn){
380  if (String(name)[0] !== '/') name = '/' + name;
381  
382  var nsp = this.nsps[name];
383  if (!nsp) {
384    debug('initializing namespace %s', name);
385    nsp = new Namespace(this, name);
386    this.nsps[name] = nsp;
387  }
388  if (fn) nsp.on('connect', fn);
389  return nsp;
390};
391
392/**
393 * Closes server connection
394 *
395 * @param {Function} [fn] optional, called as `fn([err])` on error OR all conns closed 
396 * @api public
397 */
398
399Server.prototype.close = function(fn){
400  for (var id in this.nsps['/'].sockets) {
401    if (this.nsps['/'].sockets.hasOwnProperty(id)) {
402      this.nsps['/'].sockets[id].onclose();
403    }
404  }
405
406  this.engine.close();
407
408  if (this.httpServer) {
409    this.httpServer.close(fn);
410  } else {
411    fn && fn();
412  }
413};
414
415/**
416 * Expose main namespace (/).
417 */
418
419var emitterMethods = Object.keys(Emitter.prototype).filter(function(key){
420  return typeof Emitter.prototype[key] === 'function';
421});
422
423emitterMethods.concat(['to', 'in', 'use', 'send', 'write', 'clients', 'compress']).forEach(function(fn){
424  Server.prototype[fn] = function(){
425    return this.sockets[fn].apply(this.sockets, arguments);
426  };
427});
428
429Namespace.flags.forEach(function(flag){
430  Object.defineProperty(Server.prototype, flag, {
431    get: function() {
432      this.sockets.flags = this.sockets.flags || {};
433      this.sockets.flags[flag] = true;
434      return this;
435    }
436  });
437});
438
439/**
440 * BC with `io.listen`
441 */
442
443Server.listen = Server;