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