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