mirror of
https://github.com/socketio/socket.io.git
synced 2026-01-12 00:17:56 -05:00
Compare commits
62 Commits
1.2.0
...
dynamic-na
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
213577204a | ||
|
|
dd72676c25 | ||
|
|
3bf7be73a0 | ||
|
|
0360553c99 | ||
|
|
de5cbdb833 | ||
|
|
a93d05a9f3 | ||
|
|
5ce06d3088 | ||
|
|
816bfec783 | ||
|
|
0a17c90d7a | ||
|
|
3645741b86 | ||
|
|
f2a7322b5a | ||
|
|
97c6568f65 | ||
|
|
8814825a35 | ||
|
|
58eaecad27 | ||
|
|
94157e650e | ||
|
|
0935b81da2 | ||
|
|
afa871bb8a | ||
|
|
1b01e16a6c | ||
|
|
bd6f638c8f | ||
|
|
83b36e54ac | ||
|
|
2f0d9d05af | ||
|
|
429eb0cb7c | ||
|
|
ac8e8598d7 | ||
|
|
9ba6d47ec7 | ||
|
|
3d49cafd03 | ||
|
|
77ca2dcbda | ||
|
|
7e4aa4fa64 | ||
|
|
b46e480f65 | ||
|
|
5e92dd8663 | ||
|
|
f981d3f050 | ||
|
|
f57505fee7 | ||
|
|
f8f1b132a1 | ||
|
|
f7f83bc09f | ||
|
|
b8ded0d725 | ||
|
|
086ccd2708 | ||
|
|
864857cb6f | ||
|
|
e5a7e422f9 | ||
|
|
f5b75151bd | ||
|
|
0523b655da | ||
|
|
d9415a38e4 | ||
|
|
ca82c09bf2 | ||
|
|
b9aaa1607c | ||
|
|
d1304c5d82 | ||
|
|
bd479a9cd6 | ||
|
|
a116d7765a | ||
|
|
1c6620d564 | ||
|
|
dba462e6da | ||
|
|
19c4422361 | ||
|
|
0ce0ce1dbc | ||
|
|
8242dd01ef | ||
|
|
17960ed038 | ||
|
|
cc7ce79251 | ||
|
|
1b77c27f7b | ||
|
|
1f8bb8a0ec | ||
|
|
d9996f0470 | ||
|
|
24d06d76dd | ||
|
|
4e4bbf918e | ||
|
|
b49f5c82f2 | ||
|
|
5bd67195de | ||
|
|
73fe547956 | ||
|
|
973e6cc982 | ||
|
|
136fe960b7 |
31
History.md
31
History.md
@@ -1,4 +1,35 @@
|
||||
|
||||
1.3.2 / 2015-01-19
|
||||
==================
|
||||
|
||||
* no change on this release
|
||||
|
||||
1.3.1 / 2015-01-19
|
||||
==================
|
||||
|
||||
* no change on this release
|
||||
* package: bump `engine.io`
|
||||
|
||||
1.3.0 / 2015-01-19
|
||||
==================
|
||||
|
||||
* package: bump `engine.io`
|
||||
* add test for reconnection after server restarts [rase-]
|
||||
* update license with up-to-date year range [fay-jai]
|
||||
* fix leaving unknown rooms [defunctzombie]
|
||||
* allow null origins when allowed origins is a function [drewblaisdell]
|
||||
* fix tests on node 0.11
|
||||
* package: fix `npm test` to run on windows
|
||||
* package: bump `debug` v2.1.0 [coderaiser]
|
||||
* added tests for volatile [rase-]
|
||||
|
||||
1.2.1 / 2014-11-21
|
||||
==================
|
||||
|
||||
* fix protocol violations and improve error handling (GH-1880)
|
||||
* package: bump `engine.io` for websocket leak fix [3rd-Eden]
|
||||
* style tweaks
|
||||
|
||||
1.2.0 / 2014-10-27
|
||||
==================
|
||||
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014 Automattic <dev@cloudup.com>
|
||||
Copyright (c) 2014-2015 Automattic <dev@cloudup.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
52
Readme.md
52
Readme.md
@@ -2,8 +2,8 @@
|
||||
# socket.io
|
||||
|
||||
[](http://travis-ci.org/Automattic/socket.io)
|
||||
[](http://badge.fury.io/js/socket.io)
|
||||

|
||||

|
||||

|
||||
|
||||
## How to use
|
||||
|
||||
@@ -240,6 +240,40 @@ server.listen(3000);
|
||||
Hash of `Socket` objects that are connected to this namespace indexed
|
||||
by `id`.
|
||||
|
||||
### Namespace#clients(fn:Function)
|
||||
|
||||
Gets a list of client IDs connected to this namespace (across all nodes if applicable).
|
||||
|
||||
An example to get all clients in a namespace:
|
||||
|
||||
```js
|
||||
var io = require('socket.io')();
|
||||
io.of('/chat').clients(function(error, clients){
|
||||
if (error) throw error;
|
||||
console.log(clients); // => [PZDoMHjiu8PYfRiKAAAF, Anw2LatarvGVVXEIAAAD]
|
||||
});
|
||||
```
|
||||
|
||||
An example to get all clients in namespace's room:
|
||||
|
||||
```js
|
||||
var io = require('socket.io')();
|
||||
io.of('/chat').in('general').clients(function(error, clients){
|
||||
if (error) throw error;
|
||||
console.log(clients); // => [Anw2LatarvGVVXEIAAAD]
|
||||
});
|
||||
```
|
||||
|
||||
As with broadcasting, the default is all clients from the default namespace ('/'):
|
||||
|
||||
```js
|
||||
var io = require('socket.io')();
|
||||
io.clients(function(error, clients){
|
||||
if (error) throw error;
|
||||
console.log(clients); // => [6em3d4TJP8Et9EMNAAAA, G5p55dHhGgUnLUctAAAB]
|
||||
});
|
||||
```
|
||||
|
||||
### Namespace#use(fn:Function):Namespace
|
||||
|
||||
Registers a middleware, which is a function that gets executed for
|
||||
@@ -274,7 +308,7 @@ server.listen(3000);
|
||||
|
||||
### Socket#conn:Socket
|
||||
|
||||
A reference to the underyling `Client` transport connection (engine.io
|
||||
A reference to the underlying `Client` transport connection (engine.io
|
||||
`Socket` object).
|
||||
|
||||
### Socket#request:Request
|
||||
@@ -341,6 +375,18 @@ server.listen(3000);
|
||||
});
|
||||
```
|
||||
|
||||
### Socket#compress(v:Boolean):Socket
|
||||
|
||||
Sets a modifier for a subsequent event emission that the event data will
|
||||
only be _compressed_ if the value is `true`. Defaults to `true` when you don't call the method.
|
||||
|
||||
```js
|
||||
var io = require('socket.io')();
|
||||
io.on('connection', function(socket){
|
||||
socket.compress(false).emit('an event', { some: 'data' });
|
||||
});
|
||||
```
|
||||
|
||||
### Client
|
||||
|
||||
The `Client` class represents an incoming transport (engine.io)
|
||||
|
||||
@@ -42,9 +42,12 @@ function Client(server, conn){
|
||||
Client.prototype.setup = function(){
|
||||
this.onclose = this.onclose.bind(this);
|
||||
this.ondata = this.ondata.bind(this);
|
||||
this.onerror = this.onerror.bind(this);
|
||||
this.ondecoded = this.ondecoded.bind(this);
|
||||
|
||||
this.decoder.on('decoded', this.ondecoded);
|
||||
this.conn.on('data', this.ondata);
|
||||
this.conn.on('error', this.onerror);
|
||||
this.conn.on('close', this.onclose);
|
||||
};
|
||||
|
||||
@@ -56,26 +59,40 @@ Client.prototype.setup = function(){
|
||||
*/
|
||||
|
||||
Client.prototype.connect = function(name){
|
||||
debug('connecting to namespace %s', name);
|
||||
if (!this.server.nsps[name]) {
|
||||
this.packet({ type: parser.ERROR, nsp: name, data : 'Invalid namespace'});
|
||||
return;
|
||||
}
|
||||
var nsp = this.server.of(name);
|
||||
if ('/' != name && !this.nsps['/']) {
|
||||
this.connectBuffer.push(name);
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var socket = nsp.add(this, function(){
|
||||
self.sockets.push(socket);
|
||||
self.nsps[nsp.name] = socket;
|
||||
debug('connecting to namespace %s', name);
|
||||
|
||||
if ('/' == nsp.name && self.connectBuffer.length > 0) {
|
||||
self.connectBuffer.forEach(self.connect, self);
|
||||
self.connectBuffer = [];
|
||||
function connectNamespace() {
|
||||
var nsp = self.server.of(name);
|
||||
if ('/' != name && !self.nsps['/']) {
|
||||
self.connectBuffer.push(name);
|
||||
return;
|
||||
}
|
||||
|
||||
var socket = nsp.add(self, function(){
|
||||
self.sockets.push(socket);
|
||||
self.nsps[nsp.name] = socket;
|
||||
|
||||
if ('/' == nsp.name && self.connectBuffer.length > 0) {
|
||||
self.connectBuffer.forEach(self.connect, self);
|
||||
self.connectBuffer = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (self.server.nsps[name]) {
|
||||
// Namespace already created, connect
|
||||
connectNamespace();
|
||||
return;
|
||||
}
|
||||
|
||||
self.server.checkNamespace(name, function(allow) {
|
||||
if (allow) {
|
||||
connectNamespace();
|
||||
return
|
||||
}
|
||||
|
||||
self.packet({ type: parser.ERROR, nsp: name, data : 'Invalid namespace'});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -132,17 +149,18 @@ Client.prototype.close = function(){
|
||||
* @param {Object} packet object
|
||||
* @param {Boolean} whether packet is already encoded
|
||||
* @param {Boolean} whether packet is volatile
|
||||
* @param {Boolean} whether packet should be compressed
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Client.prototype.packet = function(packet, preEncoded, volatile){
|
||||
Client.prototype.packet = function(packet, preEncoded, volatile, compress){
|
||||
var self = this;
|
||||
|
||||
// this writes to the actual connection
|
||||
function writeToEngine(encodedPackets) {
|
||||
if (volatile && !self.conn.transport.writable) return;
|
||||
for (var i = 0; i < encodedPackets.length; i++) {
|
||||
self.conn.write(encodedPackets[i]);
|
||||
self.conn.write(encodedPackets[i], { compress: compress });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +185,12 @@ Client.prototype.packet = function(packet, preEncoded, volatile){
|
||||
*/
|
||||
|
||||
Client.prototype.ondata = function(data){
|
||||
this.decoder.add(data);
|
||||
// try/catch is needed for protocol violations (GH-1880)
|
||||
try {
|
||||
this.decoder.add(data);
|
||||
} catch(e) {
|
||||
this.onerror(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -189,6 +212,20 @@ Client.prototype.ondecoded = function(packet) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles an error.
|
||||
*
|
||||
* @param {Objcet} error object
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Client.prototype.onerror = function(err){
|
||||
this.sockets.forEach(function(socket){
|
||||
socket.onerror(err);
|
||||
});
|
||||
this.onclose('client error');
|
||||
};
|
||||
|
||||
/**
|
||||
* Called upon transport close.
|
||||
*
|
||||
@@ -219,6 +256,7 @@ Client.prototype.onclose = function(reason){
|
||||
|
||||
Client.prototype.destroy = function(){
|
||||
this.conn.removeListener('data', this.ondata);
|
||||
this.conn.removeListener('error', this.onerror);
|
||||
this.conn.removeListener('close', this.onclose);
|
||||
this.decoder.removeListener('decoded', this.ondecoded);
|
||||
};
|
||||
|
||||
61
lib/index.js
61
lib/index.js
@@ -43,6 +43,7 @@ function Server(srv, opts){
|
||||
}
|
||||
opts = opts || {};
|
||||
this.nsps = {};
|
||||
this.nspValidators = [];
|
||||
this.path(opts.path || '/socket.io');
|
||||
this.serveClient(false !== opts.serveClient);
|
||||
this.adapter(opts.adapter || Adapter);
|
||||
@@ -62,14 +63,17 @@ Server.prototype.checkRequest = function(req, fn) {
|
||||
var origin = req.headers.origin || req.headers.referer;
|
||||
|
||||
// file:// URLs produce a null Origin which can't be authorized via echo-back
|
||||
if ('null' == origin) origin = '*';
|
||||
if ('null' == origin || null == origin) origin = '*';
|
||||
|
||||
if (!!origin && typeof(this._origins) == 'function') return this._origins(origin, fn);
|
||||
if (this._origins.indexOf('*:*') !== -1) return fn(null, true);
|
||||
if (origin) {
|
||||
try {
|
||||
var parts = url.parse(origin);
|
||||
parts.port = parts.port || 80;
|
||||
var defaultPort = 'https:' == parts.protocol ? 443 : 80;
|
||||
parts.port = parts.port != null
|
||||
? parts.port
|
||||
: defaultPort;
|
||||
var ok =
|
||||
~this._origins.indexOf(parts.hostname + ':' + parts.port) ||
|
||||
~this._origins.indexOf(parts.hostname + ':*') ||
|
||||
@@ -134,6 +138,53 @@ Server.prototype.set = function(key, val){
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets up server middleware to validate incoming namespaces not already created on the server.
|
||||
*
|
||||
* @return {Server} self
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.useNamespaceValidator = function(fn){
|
||||
this.nspValidators.push(fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes the middleware for an incoming namespace not already created on the server.
|
||||
*
|
||||
* @param name of incomming namespace
|
||||
* @param {Function} last fn call in the middleware
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Server.prototype.checkNamespace = function(name, fn){
|
||||
var fns = this.nspValidators.slice(0);
|
||||
if (!fns.length) return fn(false);
|
||||
|
||||
var namespaceAllowed = false; // Deny unknown namespaces by default
|
||||
|
||||
function run(i){
|
||||
fns[i](name, function(err, allow){
|
||||
// upon error, short-circuit
|
||||
if (err) return fn(false);
|
||||
|
||||
// if one piece of middleware explicitly denies namespace, short-circuit
|
||||
if (allow === false) return fn(false);
|
||||
|
||||
namespaceAllowed = namespaceAllowed || allow === true;
|
||||
|
||||
// if no middleware left, summon callback
|
||||
if (!fns[i + 1]) return fn(namespaceAllowed);
|
||||
|
||||
// go on to next
|
||||
run(i + 1);
|
||||
});
|
||||
}
|
||||
|
||||
run(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the client serving path.
|
||||
*
|
||||
@@ -194,7 +245,7 @@ Server.prototype.origins = function(v){
|
||||
Server.prototype.listen =
|
||||
Server.prototype.attach = function(srv, opts){
|
||||
if ('function' == typeof srv) {
|
||||
var msg = 'You are trying to attach socket.io to an express' +
|
||||
var msg = 'You are trying to attach socket.io to an express ' +
|
||||
'request handler function. Please pass a http.Server instance.';
|
||||
throw new Error(msg);
|
||||
}
|
||||
@@ -251,7 +302,7 @@ Server.prototype.attachServe = function(srv){
|
||||
var self = this;
|
||||
srv.removeAllListeners('request');
|
||||
srv.on('request', function(req, res) {
|
||||
if (0 == req.url.indexOf(url)) {
|
||||
if (0 === req.url.indexOf(url)) {
|
||||
self.serve(req, res);
|
||||
} else {
|
||||
for (var i = 0; i < evs.length; i++) {
|
||||
@@ -358,7 +409,7 @@ Server.prototype.close = function(){
|
||||
* Expose main namespace (/).
|
||||
*/
|
||||
|
||||
['on', 'to', 'in', 'use', 'emit', 'send', 'write'].forEach(function(fn){
|
||||
['on', 'to', 'in', 'use', 'emit', 'send', 'write', 'clients'].forEach(function(fn){
|
||||
Server.prototype[fn] = function(){
|
||||
var nsp = this.sockets[fn];
|
||||
return nsp.apply(this.sockets, arguments);
|
||||
|
||||
@@ -7,7 +7,7 @@ var Socket = require('./socket');
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var parser = require('socket.io-parser');
|
||||
var debug = require('debug')('socket.io:namespace');
|
||||
var hasBin = require('has-binary-data');
|
||||
var hasBin = require('has-binary');
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
@@ -240,3 +240,15 @@ Namespace.prototype.write = function(){
|
||||
this.emit.apply(this, args);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a list of clients.
|
||||
*
|
||||
* @return {Namespace} self
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Namespace.prototype.clients = function(fn){
|
||||
this.adapter.clients(this.rooms, fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ var Emitter = require('events').EventEmitter;
|
||||
var parser = require('socket.io-parser');
|
||||
var url = require('url');
|
||||
var debug = require('debug')('socket.io:socket');
|
||||
var hasBin = require('has-binary-data');
|
||||
var hasBin = require('has-binary');
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
@@ -202,7 +202,8 @@ Socket.prototype.write = function(){
|
||||
Socket.prototype.packet = function(packet, preEncoded){
|
||||
packet.nsp = this.nsp.name;
|
||||
var volatile = this.flags && this.flags.volatile;
|
||||
this.client.packet(packet, preEncoded, volatile);
|
||||
var compress = !this.flags || false !== this.flags.compress;
|
||||
this.client.packet(packet, preEncoded, volatile, compress);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -242,7 +243,10 @@ Socket.prototype.leave = function(room, fn){
|
||||
this.adapter.del(this.id, room, function(err){
|
||||
if (err) return fn && fn(err);
|
||||
debug('left room %s', room);
|
||||
self.rooms.splice(self.rooms.indexOf(room), 1);
|
||||
var idx = self.rooms.indexOf(room);
|
||||
if (idx >= 0) {
|
||||
self.rooms.splice(idx, 1);
|
||||
}
|
||||
fn && fn(null);
|
||||
});
|
||||
return this;
|
||||
@@ -380,10 +384,26 @@ Socket.prototype.ondisconnect = function(){
|
||||
this.onclose('client namespace disconnect');
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles a client error.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Socket.prototype.onerror = function(err){
|
||||
if (this.listeners('error').length) {
|
||||
this.emit('error', err);
|
||||
} else {
|
||||
console.error('Missing error handler on `socket`.');
|
||||
console.error(err.stack);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called upon closing. Called by `Client`.
|
||||
*
|
||||
* @param {String} reason
|
||||
* @param {Error} optional error object
|
||||
* @api private
|
||||
*/
|
||||
|
||||
@@ -428,3 +448,17 @@ Socket.prototype.disconnect = function(close){
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the compress flag.
|
||||
*
|
||||
* @param {Boolean} if `true`, compresses the sending data
|
||||
* @return {Socket} self
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Socket.prototype.compress = function(compress){
|
||||
this.flags = this.flags || {};
|
||||
this.flags.compress = compress;
|
||||
return this;
|
||||
};
|
||||
|
||||
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.2",
|
||||
"description": "node.js realtime framework server",
|
||||
"keywords": [
|
||||
"realtime",
|
||||
@@ -16,15 +16,15 @@
|
||||
"url": "git://github.com/Automattic/socket.io"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "make test"
|
||||
"test": "mocha --reporter dot --slow 200ms --bail"
|
||||
},
|
||||
"dependencies": {
|
||||
"engine.io": "1.4.2",
|
||||
"socket.io-parser": "2.2.2",
|
||||
"socket.io-client": "1.2.0",
|
||||
"socket.io-adapter": "0.3.1",
|
||||
"has-binary-data": "0.1.3",
|
||||
"debug": "0.7.4"
|
||||
"engine.io": "automattic/engine.io#ddc64a",
|
||||
"socket.io-parser": "2.2.3",
|
||||
"socket.io-client": "automattic/socket.io-client#210d65",
|
||||
"socket.io-adapter": "automattic/socket.io-adapter#ae79d8",
|
||||
"has-binary": "0.1.6",
|
||||
"debug": "2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "1.16.2",
|
||||
|
||||
@@ -7,7 +7,7 @@ var ioc = require('socket.io-client');
|
||||
var request = require('supertest');
|
||||
var expect = require('expect.js');
|
||||
|
||||
// creates a socket.io client for the given server
|
||||
// Creates a socket.io client for the given server
|
||||
function client(srv, nsp, opts){
|
||||
if ('object' == typeof nsp) {
|
||||
opts = nsp;
|
||||
@@ -15,7 +15,7 @@ function client(srv, nsp, opts){
|
||||
}
|
||||
var addr = srv.address();
|
||||
if (!addr) addr = srv.listen().address();
|
||||
var url = 'ws://' + addr.address + ':' + addr.port + (nsp || '');
|
||||
var url = 'ws://localhost:' + addr.port + (nsp || '');
|
||||
return ioc(url, opts);
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ describe('socket.io', function(){
|
||||
expect(s.handshake.time.split(' ').length > 0); // Is "multipart" string representation
|
||||
|
||||
// Address, xdomain, secure, issued and url set
|
||||
expect(s.handshake.address).to.be('127.0.0.1');
|
||||
expect(s.handshake.address).to.contain('127.0.0.1');
|
||||
expect(s.handshake.xdomain).to.be.a('boolean');
|
||||
expect(s.handshake.secure).to.be.a('boolean');
|
||||
expect(s.handshake.issued).to.be.a('number');
|
||||
@@ -273,8 +273,9 @@ describe('socket.io', function(){
|
||||
|
||||
it('should allow request when origin defined as function and same is supplied', function(done) {
|
||||
var sockets = io({ origins: function(origin,callback){
|
||||
if(origin == 'http://foo.example')
|
||||
if (origin == 'http://foo.example') {
|
||||
return callback(null, true);
|
||||
}
|
||||
return callback(null, false);
|
||||
} }).listen('54016');
|
||||
request.get('http://localhost:54016/socket.io/default/')
|
||||
@@ -288,8 +289,9 @@ describe('socket.io', function(){
|
||||
|
||||
it('should allow request when origin defined as function and different is supplied', function(done) {
|
||||
var sockets = io({ origins: function(origin,callback){
|
||||
if(origin == 'http://foo.example')
|
||||
if (origin == 'http://foo.example') {
|
||||
return callback(null, true);
|
||||
}
|
||||
return callback(null, false);
|
||||
} }).listen('54017');
|
||||
request.get('http://localhost:54017/socket.io/default/')
|
||||
@@ -300,6 +302,32 @@ describe('socket.io', function(){
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow request when origin defined as function and no origin is supplied', function(done) {
|
||||
var sockets = io({ origins: function(origin,callback){
|
||||
if (origin == '*') {
|
||||
return callback(null, true);
|
||||
}
|
||||
return callback(null, false);
|
||||
} }).listen('54021');
|
||||
request.get('http://localhost:54021/socket.io/default/')
|
||||
.query({ transport: 'polling' })
|
||||
.end(function (err, res) {
|
||||
expect(res.status).to.be(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should default to port 443 when protocol is https', function(done) {
|
||||
var sockets = io({ origins: 'https://foo.example:443' }).listen('54036');
|
||||
request.get('http://localhost:54036/socket.io/default/')
|
||||
.set('origin', 'https://foo.example')
|
||||
.query({ transport: 'polling' })
|
||||
.end(function (err, res) {
|
||||
expect(res.status).to.be(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('close', function(){
|
||||
@@ -445,7 +473,7 @@ describe('socket.io', function(){
|
||||
var c1 = client(srv, '/');
|
||||
var c2 = client(srv, '/abc');
|
||||
});
|
||||
|
||||
|
||||
it('should be equivalent for "" and "/" on client', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
@@ -454,7 +482,7 @@ describe('socket.io', function(){
|
||||
});
|
||||
var c1 = client(srv, '');
|
||||
});
|
||||
|
||||
|
||||
it('should work with `of` and many sockets', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
@@ -569,6 +597,211 @@ describe('socket.io', function(){
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not reuse same-namespace connections', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
var connections = 0;
|
||||
|
||||
srv.listen(function() {
|
||||
var clientSocket1 = client(srv);
|
||||
var clientSocket2 = client(srv);
|
||||
sio.on('connection', function() {
|
||||
connections++;
|
||||
if(connections === 2) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should find all clients in a namespace', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
var chatSids = [];
|
||||
var otherSid = null;
|
||||
srv.listen(function(){
|
||||
var c1 = client(srv, '/chat');
|
||||
var c2 = client(srv, '/chat', {forceNew: true});
|
||||
var c3 = client(srv, '/other', {forceNew: true});
|
||||
var total = 3;
|
||||
sio.of('/chat').on('connection', function(socket){
|
||||
chatSids.push(socket.id);
|
||||
--total || getClients();
|
||||
});
|
||||
sio.of('/other').on('connection', function(socket){
|
||||
otherSid = socket.id;
|
||||
--total || getClients();
|
||||
});
|
||||
});
|
||||
function getClients() {
|
||||
sio.of('/chat').clients(function(error, sids) {
|
||||
expect(error).to.be.undefined;
|
||||
expect(sids).to.contain(chatSids[0]);
|
||||
expect(sids).to.contain(chatSids[1]);
|
||||
expect(sids).to.not.contain(otherSid);
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should find all clients in a namespace room', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
var chatFooSid = null;
|
||||
var chatBarSid = null;
|
||||
var otherSid = null;
|
||||
srv.listen(function(){
|
||||
var c1 = client(srv, '/chat');
|
||||
var c2 = client(srv, '/chat', {forceNew: true});
|
||||
var c3 = client(srv, '/other', {forceNew: true});
|
||||
var chatIndex = 0;
|
||||
var total = 3;
|
||||
sio.of('/chat').on('connection', function(socket){
|
||||
if (chatIndex++) {
|
||||
socket.join('foo', function() {
|
||||
chatFooSid = socket.id;
|
||||
--total || getClients();
|
||||
});
|
||||
} else {
|
||||
socket.join('bar', function() {
|
||||
chatBarSid = socket.id;
|
||||
--total || getClients();
|
||||
});
|
||||
}
|
||||
});
|
||||
sio.of('/other').on('connection', function(socket){
|
||||
socket.join('foo', function() {
|
||||
otherSid = socket.id;
|
||||
--total || getClients();
|
||||
});
|
||||
});
|
||||
});
|
||||
function getClients() {
|
||||
sio.of('/chat').in('foo').clients(function(error, sids) {
|
||||
expect(error).to.be.undefined;
|
||||
expect(sids).to.contain(chatFooSid);
|
||||
expect(sids).to.not.contain(chatBarSid);
|
||||
expect(sids).to.not.contain(otherSid);
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should find all clients across namespace rooms', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
var chatFooSid = null;
|
||||
var chatBarSid = null;
|
||||
var otherSid = null;
|
||||
srv.listen(function(){
|
||||
var c1 = client(srv, '/chat');
|
||||
var c2 = client(srv, '/chat', {forceNew: true});
|
||||
var c3 = client(srv, '/other', {forceNew: true});
|
||||
var chatIndex = 0;
|
||||
var total = 3;
|
||||
sio.of('/chat').on('connection', function(socket){
|
||||
if (chatIndex++) {
|
||||
socket.join('foo', function() {
|
||||
chatFooSid = socket.id;
|
||||
--total || getClients();
|
||||
});
|
||||
} else {
|
||||
socket.join('bar', function() {
|
||||
chatBarSid = socket.id;
|
||||
--total || getClients();
|
||||
});
|
||||
}
|
||||
});
|
||||
sio.of('/other').on('connection', function(socket){
|
||||
socket.join('foo', function() {
|
||||
otherSid = socket.id;
|
||||
--total || getClients();
|
||||
});
|
||||
});
|
||||
});
|
||||
function getClients() {
|
||||
sio.of('/chat').clients(function(error, sids) {
|
||||
expect(error).to.be.undefined;
|
||||
expect(sids).to.contain(chatFooSid);
|
||||
expect(sids).to.contain(chatBarSid);
|
||||
expect(sids).to.not.contain(otherSid);
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow connections to dynamic namespaces', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
srv.listen(function(){
|
||||
var namespace = '/dynamic';
|
||||
var dynamic = client(srv,namespace);
|
||||
sio.useNamespaceValidator(function(nsp, next) {
|
||||
expect(nsp).to.be(namespace);
|
||||
next(null, true);
|
||||
});
|
||||
dynamic.on('error', function(err) {
|
||||
expect().fail();
|
||||
});
|
||||
dynamic.on('connect', function() {
|
||||
expect(sio.nsps[namespace]).to.be.a(Namespace);
|
||||
expect(sio.nsps[namespace].sockets.length).to.be(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow connections to dynamic namespaces if not supported', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
srv.listen(function(){
|
||||
var namespace = '/dynamic';
|
||||
sio.useNamespaceValidator(function(nsp, next) {
|
||||
expect(nsp).to.be(namespace);
|
||||
next(null, false);
|
||||
});
|
||||
sio.on('connect', function(socket) {
|
||||
if (socket.nsp.name === namespace) {
|
||||
expect().fail();
|
||||
}
|
||||
});
|
||||
|
||||
var dynamic = client(srv,namespace);
|
||||
dynamic.on('connect', function(){
|
||||
expect().fail();
|
||||
});
|
||||
dynamic.on('error', function(err) {
|
||||
expect(err).to.be("Invalid namespace");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should not allow connections to dynamic namespaces if there is an error', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
srv.listen(function(){
|
||||
var namespace = '/dynamic';
|
||||
sio.useNamespaceValidator(function(nsp, next) {
|
||||
expect(nsp).to.be(namespace);
|
||||
next(new Error(), true);
|
||||
});
|
||||
sio.on('connect', function(socket) {
|
||||
if (socket.nsp.name === namespace) {
|
||||
expect().fail();
|
||||
}
|
||||
});
|
||||
|
||||
var dynamic = client(srv,namespace);
|
||||
dynamic.on('connect', function(){
|
||||
expect().fail();
|
||||
});
|
||||
dynamic.on('error', function(err) {
|
||||
expect(err).to.be("Invalid namespace");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('socket', function(){
|
||||
@@ -637,6 +870,39 @@ describe('socket.io', function(){
|
||||
});
|
||||
});
|
||||
|
||||
it('should error with null messages', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
srv.listen(function(){
|
||||
var socket = client(srv);
|
||||
sio.on('connection', function(s){
|
||||
s.on('message', function(a){
|
||||
expect(a).to.be(null);
|
||||
done();
|
||||
});
|
||||
socket.send(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle transport null messages', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
srv.listen(function(){
|
||||
var socket = client(srv);
|
||||
sio.on('connection', function(s){
|
||||
s.on('error', function(err){
|
||||
expect(err).to.be.an(Error);
|
||||
s.on('disconnect', function(reason){
|
||||
expect(reason).to.be('client error');
|
||||
done();
|
||||
});
|
||||
});
|
||||
s.client.ondata(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit events', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
@@ -765,6 +1031,208 @@ describe('socket.io', function(){
|
||||
});
|
||||
});
|
||||
|
||||
it('should not emit volatile event after regular event (polling)', function(done) {
|
||||
var srv = http();
|
||||
var sio = io(srv, { transports: ['polling'] });
|
||||
|
||||
var counter = 0;
|
||||
srv.listen(function(){
|
||||
sio.on('connection', function(s){
|
||||
s.emit('ev', 'data');
|
||||
s.volatile.emit('ev', 'data');
|
||||
});
|
||||
|
||||
var socket = client(srv, { transports: ['polling'] });
|
||||
socket.on('ev', function() {
|
||||
counter++;
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
expect(counter).to.be(1);
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
it('should not emit volatile event after regular event (ws)', function(done) {
|
||||
var srv = http();
|
||||
var sio = io(srv, { transports: ['websocket'] });
|
||||
|
||||
var counter = 0;
|
||||
srv.listen(function(){
|
||||
sio.on('connection', function(s){
|
||||
s.emit('ev', 'data');
|
||||
s.volatile.emit('ev', 'data');
|
||||
});
|
||||
|
||||
var socket = client(srv, { transports: ['websocket'] });
|
||||
socket.on('ev', function() {
|
||||
counter++;
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
expect(counter).to.be(1);
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
it('should emit volatile event (polling)', function(done) {
|
||||
var srv = http();
|
||||
var sio = io(srv, { transports: ['polling'] });
|
||||
|
||||
var counter = 0;
|
||||
srv.listen(function(){
|
||||
sio.on('connection', function(s){
|
||||
// Wait to make sure there are no packets being sent for opening the connection
|
||||
setTimeout(function() {
|
||||
s.volatile.emit('ev', 'data');
|
||||
}, 20);
|
||||
});
|
||||
|
||||
var socket = client(srv, { transports: ['polling'] });
|
||||
socket.on('ev', function() {
|
||||
counter++;
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
expect(counter).to.be(1);
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
it('should emit volatile event (ws)', function(done) {
|
||||
var srv = http();
|
||||
var sio = io(srv, { transports: ['websocket'] });
|
||||
|
||||
var counter = 0;
|
||||
srv.listen(function(){
|
||||
sio.on('connection', function(s){
|
||||
// Wait to make sure there are no packets being sent for opening the connection
|
||||
setTimeout(function() {
|
||||
s.volatile.emit('ev', 'data');
|
||||
}, 20);
|
||||
});
|
||||
|
||||
var socket = client(srv, { transports: ['websocket'] });
|
||||
socket.on('ev', function() {
|
||||
counter++;
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
expect(counter).to.be(1);
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
it('should emit only one consecutive volatile event (polling)', function(done) {
|
||||
var srv = http();
|
||||
var sio = io(srv, { transports: ['polling'] });
|
||||
|
||||
var counter = 0;
|
||||
srv.listen(function(){
|
||||
sio.on('connection', function(s){
|
||||
// Wait to make sure there are no packets being sent for opening the connection
|
||||
setTimeout(function() {
|
||||
s.volatile.emit('ev', 'data');
|
||||
s.volatile.emit('ev', 'data');
|
||||
}, 20);
|
||||
});
|
||||
|
||||
var socket = client(srv, { transports: ['polling'] });
|
||||
socket.on('ev', function() {
|
||||
counter++;
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
expect(counter).to.be(1);
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
it('should emit only one consecutive volatile event (ws)', function(done) {
|
||||
var srv = http();
|
||||
var sio = io(srv, { transports: ['websocket'] });
|
||||
|
||||
var counter = 0;
|
||||
srv.listen(function(){
|
||||
sio.on('connection', function(s){
|
||||
// Wait to make sure there are no packets being sent for opening the connection
|
||||
setTimeout(function() {
|
||||
s.volatile.emit('ev', 'data');
|
||||
s.volatile.emit('ev', 'data');
|
||||
}, 20);
|
||||
});
|
||||
|
||||
var socket = client(srv, { transports: ['websocket'] });
|
||||
socket.on('ev', function() {
|
||||
counter++;
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
expect(counter).to.be(1);
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
it('should emit regular events after trying a failed volatile event (polling)', function(done) {
|
||||
var srv = http();
|
||||
var sio = io(srv, { transports: ['polling'] });
|
||||
|
||||
var counter = 0;
|
||||
srv.listen(function(){
|
||||
sio.on('connection', function(s){
|
||||
// Wait to make sure there are no packets being sent for opening the connection
|
||||
setTimeout(function() {
|
||||
s.emit('ev', 'data');
|
||||
s.volatile.emit('ev', 'data');
|
||||
s.emit('ev', 'data');
|
||||
}, 20);
|
||||
});
|
||||
|
||||
var socket = client(srv, { transports: ['polling'] });
|
||||
socket.on('ev', function() {
|
||||
counter++;
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
expect(counter).to.be(2);
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
it('should emit regular events after trying a failed volatile event (ws)', function(done) {
|
||||
var srv = http();
|
||||
var sio = io(srv, { transports: ['websocket'] });
|
||||
|
||||
var counter = 0;
|
||||
srv.listen(function(){
|
||||
sio.on('connection', function(s){
|
||||
// Wait to make sure there are no packets being sent for opening the connection
|
||||
setTimeout(function() {
|
||||
s.emit('ev', 'data');
|
||||
s.volatile.emit('ev', 'data');
|
||||
s.emit('ev', 'data');
|
||||
}, 20);
|
||||
});
|
||||
|
||||
var socket = client(srv, { transports: ['websocket'] });
|
||||
socket.on('ev', function() {
|
||||
counter++;
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
expect(counter).to.be(2);
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
it('should emit message events through `send`', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
@@ -987,7 +1455,7 @@ describe('socket.io', function(){
|
||||
var sio = io(srv);
|
||||
srv.listen(function() {
|
||||
var addr = srv.listen().address();
|
||||
var url = 'ws://' + addr.address + ':' + addr.port + '?key1=1&key2=2';
|
||||
var url = 'ws://localhost:' + addr.port + '?key1=1&key2=2';
|
||||
var socket = ioc(url);
|
||||
sio.on('connection', function(s) {
|
||||
var parsed = require('url').parse(s.request.url);
|
||||
@@ -1000,7 +1468,7 @@ describe('socket.io', function(){
|
||||
});
|
||||
|
||||
it('should handle very large json', function(done){
|
||||
this.timeout();
|
||||
this.timeout(30000);
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
var received = 0;
|
||||
@@ -1027,6 +1495,8 @@ describe('socket.io', function(){
|
||||
});
|
||||
|
||||
it('should handle very large binary data', function(done){
|
||||
this.timeout(10000);
|
||||
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
var received = 0;
|
||||
@@ -1051,6 +1521,96 @@ describe('socket.io', function(){
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to emit after server close and restart', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
|
||||
sio.on('connection', function(socket){
|
||||
socket.on('ev', function(data){
|
||||
expect(data).to.be('payload');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
srv.listen(function(){
|
||||
var port = srv.address().port;
|
||||
var clientSocket = client(srv, { reconnectionAttempts: 10, reconnectionDelay: 100 });
|
||||
clientSocket.once('connect', function(){
|
||||
srv.close(function(){
|
||||
srv.listen(port, function(){
|
||||
clientSocket.on('reconnect', function(){
|
||||
clientSocket.emit('ev', 'payload');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should enable compression by default', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
srv.listen(function(){
|
||||
var socket = client(srv);
|
||||
sio.on('connection', function(s){
|
||||
s.conn.once('packetCreate', function(packet) {
|
||||
expect(packet.options.compress).to.be(true);
|
||||
done();
|
||||
});
|
||||
s.emit('woot', 'hi');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should disable compression', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
srv.listen(function(){
|
||||
var socket = client(srv);
|
||||
sio.on('connection', function(s){
|
||||
s.conn.once('packetCreate', function(packet) {
|
||||
expect(packet.options.compress).to.be(false);
|
||||
done();
|
||||
});
|
||||
s.compress(false).emit('woot', 'hi');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should error with raw binary and warn', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
srv.listen(function(){
|
||||
var socket = client(srv);
|
||||
sio.on('connection', function(s){
|
||||
s.conn.on('upgrade', function(){
|
||||
console.log('\033[96mNote: warning expected and normal in test.\033[39m');
|
||||
socket.io.engine.write('5woooot');
|
||||
setTimeout(function(){
|
||||
done();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not crash with raw binary', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
srv.listen(function(){
|
||||
var socket = client(srv);
|
||||
sio.on('connection', function(s){
|
||||
s.once('error', function(err){
|
||||
expect(err.message).to.match(/Illegal attachments/);
|
||||
done();
|
||||
});
|
||||
s.conn.on('upgrade', function(){
|
||||
socket.io.engine.write('5woooot');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('messaging many', function(){
|
||||
@@ -1350,6 +1910,29 @@ describe('socket.io', function(){
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should properly cleanup left rooms', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
|
||||
srv.listen(function(){
|
||||
var socket = client(srv);
|
||||
sio.on('connection', function(s){
|
||||
s.join('a', function(){
|
||||
expect(s.rooms).to.eql([s.id, 'a']);
|
||||
s.join('b', function(){
|
||||
expect(s.rooms).to.eql([s.id, 'a', 'b']);
|
||||
s.leave('unknown', function(){
|
||||
expect(s.rooms).to.eql([s.id, 'a', 'b']);
|
||||
s.leaveAll();
|
||||
expect(s.rooms).to.eql([]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('middleware', function(done){
|
||||
|
||||
Reference in New Issue
Block a user