Compare commits

..

9 Commits

Author SHA1 Message Date
Roman Shtylman
213577204a Merge branch 'dynamic-namespaces' of https://github.com/markitx/socket.io into dynamic-namespace
Conflicts:
	lib/client.js
	test/socket.io.js
2015-02-04 18:17:49 -08:00
Dylan Lingelbach
dd72676c25 Make name more descriptive 2015-02-04 13:43:21 -06:00
Dylan Lingelbach
3bf7be73a0 Style cleanup 2015-02-04 13:40:40 -06:00
Dylan Lingelbach
0360553c99 Merge branch 'master' into dynamic-namespaces 2015-02-04 13:10:40 -06:00
Dylan Lingelbach
de5cbdb833 Improve tests 2015-02-04 12:33:18 -06:00
Dylan Lingelbach
0ce0ce1dbc Merge pull request #1 from markitx/dynamic-namespaces
Dynamic namespaces
2015-01-09 11:56:51 -06:00
Dylan Lingelbach
cc7ce79251 Ensure server sends connect message 2014-12-19 15:32:20 -06:00
Dylan Lingelbach
1b77c27f7b Fix test 2014-12-19 15:27:08 -06:00
Dylan Lingelbach
1f8bb8a0ec Support dynamic namespaces 2014-12-19 13:22:41 -06:00
14 changed files with 286 additions and 460 deletions

1
.gitignore vendored
View File

@@ -9,4 +9,3 @@ lib-cov
benchmarks/*.png
node_modules
coverage
.idea

View File

@@ -1,20 +1,16 @@
sudo: false
before_install:
- npm install -g npm@'>=1.4.3'
language: node_js
node_js:
- "0.8"
- "0.10"
- "0.12"
- "4"
- "0.11"
git:
depth: 1
#matrix:
#fast_finish: true
#allow_failures:
#- node_js: "0.11"
matrix:
fast_finish: true
allow_failures:
- node_js: "0.11"
notifications:
irc: "irc.freenode.org#socket.io"

View File

@@ -1,90 +1,4 @@
1.4.6 / 2016-05-02
==================
* package: bump engine.io
1.4.5 / 2016-01-26
==================
* fix closing the underlying `http.Server`
1.4.4 / 2016-01-10
==================
* package: bump `engine.io`
1.4.3 / 2016-01-08
==================
* bump `socket.io-client`
1.4.2 / 2016-01-07
==================
* bump `engine.io`
1.4.1 / 2016-01-07
==================
* version bump
1.4.0 / 2015-11-28
==================
* socket.io: increase large binary data test timeout
* package: bump `engine.io` for release
* trigger callback even when joining an already joined room
* package: bump parser
* namespace: clear rooms flag after a clients call (fixes #1978)
* package: bump `socket.io-parser`
* fixed tests with large data
* fixed a typo in the example code
* package: bump mocha
* package: bump `has-binary` and `zuul-ngrok`
* package: bump `engine.io` and `socket.io-client`
* README: clarified documentation of Socket.in
* README: fixed up legacy repo links
* test: better timeout for stress test
* socket: don't set request property which has a getter
* removed proxy index file
* support flags on namespace
* improve Socket#packet and Client#packet
* socket: warn node_redis-style about missing `error`
* test: added failing test
* test: increase timeout for large binary data test
* package: bump `has-binary` to work with all objects (fixes #1955)
* fix origin verification default https port [evanlucas]
* support compression [nkzawa]
* changed type of `Client#sockets`, `Namespace#sockets` and `Socket#rooms` to maps (instead of arrays)
1.3.7 / 2015-09-21
==================
* package: bump `socket.io-client` for node4 compatibility
* package: bump `engine.io` for node4 compatibility
1.3.6 / 2015-07-14
==================
* package: bump `engine.io` to fix build on windows
1.3.5 / 2015-03-03
==================
* package: bump `socket.io-parser`
1.3.4 / 2015-02-14
==================
* package: bump `socket.io-client`
1.3.3 / 2015-02-03
==================
* socket: warn node_redis-style about missing `error`
* package: bump parser to better handle bad binary packets
1.3.2 / 2015-01-19
==================

View File

@@ -1,12 +1,9 @@
# socket.io
[![Build Status](https://secure.travis-ci.org/socketio/socket.io.svg)](https://travis-ci.org/socketio/socket.io)
[![Dependency Status](https://david-dm.org/socketio/socket.io.svg)](https://david-dm.org/socketio/socket.io)
[![devDependency Status](https://david-dm.org/socketio/socket.io/dev-status.svg)](https://david-dm.org/socketio/socket.io#info=devDependencies)
[![Build Status](https://secure.travis-ci.org/Automattic/socket.io.svg)](http://travis-ci.org/Automattic/socket.io)
![NPM version](https://badge.fury.io/js/socket.io.svg)
![Downloads](https://img.shields.io/npm/dm/socket.io.svg?style=flat)
[![](http://slack.socket.io/badge.svg?)](http://slack.socket.io)
![Downloads](http://img.shields.io/npm/dm/socket.io.svg?style=flat)
## How to use
@@ -88,7 +85,7 @@ server.listen(3000);
The same options passed to socket.io are always passed to
the `engine.io` `Server` that gets created. See engine.io
[options](https://github.com/socketio/engine.io#methods-1)
[options](https://github.com/learnboost/engine.io#methods-1)
as reference.
### Server(srv:http#Server, opts:Object)
@@ -130,7 +127,7 @@ server.listen(3000);
Sets the adapter `v`. Defaults to an instance of the `Adapter` that
ships with socket.io which is memory based. See
[socket.io-adapter](https://github.com/socketio/socket.io-adapter).
[socket.io-adapter](https://github.com/Automattic/socket.io-adapter).
If no arguments are supplied this method returns the current value.
@@ -147,7 +144,7 @@ server.listen(3000);
__Potential drawbacks__:
* in some situations, when it is not possible to determine `origin` it may have value of `*`
* As this function will be executed for every request, it is advised to make this function work as fast as possible
* If `socket.io` is used together with `Express`, the CORS headers will be affected only for `socket.io` requests. For Express can use [cors](https://github.com/expressjs/cors)
* If `socket.io` is used together with `Express`, the CORS headers will be affected only for `socket.io` requests. For Express can use [cors](https://github.com/troygoode/node-cors/)
### Server#sockets:Namespace
@@ -301,10 +298,9 @@ server.listen(3000);
clients. A `Socket` belongs to a certain `Namespace` (by default `/`)
and uses an underlying `Client` to communicate.
### Socket#rooms:Object
### Socket#rooms:Array
A hash of strings identifying the rooms this socket is in, indexed by
room name.
A list of strings identifying the rooms this socket is in.
### Socket#client:Client
@@ -351,7 +347,7 @@ server.listen(3000);
The mechanics of joining rooms are handled by the `Adapter`
that has been configured (see `Server#adapter` above), defaulting to
[socket.io-adapter](https://github.com/socketio/socket.io-adapter).
[socket.io-adapter](https://github.com/Automattic/socket.io-adapter).
### Socket#leave(name:String[, fn:Function]):Socket
@@ -362,9 +358,10 @@ server.listen(3000);
The mechanics of leaving rooms are handled by the `Adapter`
that has been configured (see `Server#adapter` above), defaulting to
[socket.io-adapter](https://github.com/socketio/socket.io-adapter).
[socket.io-adapter](https://github.com/Automattic/socket.io-adapter).
### Socket#to(room:String):Socket
### Socket#in(room:String):Socket
Sets a modifier for a subsequent event emission that the event will
only be _broadcasted_ to sockets that have joined the given `room`.
@@ -378,10 +375,6 @@ server.listen(3000);
});
```
### Socket#in(room:String):Socket
Same as `Socket#to`
### Socket#compress(v:Boolean):Socket
Sets a modifier for a subsequent event emission that the event data will
@@ -412,7 +405,7 @@ server.listen(3000);
## Debug / logging
Socket.IO is powered by [debug](https://github.com/visionmedia/debug).
Socket.IO is powered by [debug](http://github.com/visionmedia/debug).
In order to see all the debug output, run your app with the environment variable
`DEBUG` including the desired scope.

View File

@@ -14,6 +14,8 @@ app.use(express.static(__dirname + '/public'));
// Chatroom
// usernames which are currently connected to the chat
var usernames = {};
var numUsers = 0;
io.on('connection', function (socket) {
@@ -30,10 +32,10 @@ io.on('connection', function (socket) {
// when the client emits 'add user', this listens and executes
socket.on('add user', function (username) {
if (addedUser) return;
// we store the username in the socket session for this client
socket.username = username;
// add the client's username to the global list
usernames[username] = username;
++numUsers;
addedUser = true;
socket.emit('login', {
@@ -62,7 +64,9 @@ io.on('connection', function (socket) {
// when the user disconnects.. perform this
socket.on('disconnect', function () {
// remove the username from global usernames list
if (addedUser) {
delete usernames[socket.username];
--numUsers;
// echo globally that this client has left

View File

@@ -7,7 +7,7 @@ $(function() {
'#3b88eb', '#3824aa', '#a700ff', '#d300e7'
];
// Initialize variables
// Initialize varibles
var $window = $(window);
var $usernameInput = $('.usernameInput'); // Input for username
var $messages = $('.messages'); // Messages area

2
index.js Normal file
View File

@@ -0,0 +1,2 @@
module.exports = require('./lib');

View File

@@ -16,7 +16,7 @@ module.exports = Client;
* Client constructor.
*
* @param {Server} server instance
* @param {Socket} conn
* @param {Socket} connection
* @api private
*/
@@ -28,7 +28,7 @@ function Client(server, conn){
this.id = conn.id;
this.request = conn.request;
this.setup();
this.sockets = {};
this.sockets = [];
this.nsps = {};
this.connectBuffer = [];
}
@@ -54,32 +54,45 @@ Client.prototype.setup = function(){
/**
* Connects a client to a namespace.
*
* @param {String} name namespace
* @param {String} namespace name
* @api private
*/
Client.prototype.connect = function(name){
debug('connecting to namespace %s', name);
var nsp = this.server.nsps[name];
if (!nsp) {
this.packet({ type: parser.ERROR, nsp: name, data : 'Invalid namespace'});
return;
}
if ('/' != name && !this.nsps['/']) {
this.connectBuffer.push(name);
return;
}
var self = this;
var socket = nsp.add(this, function(){
self.sockets[socket.id] = 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'});
});
};
@@ -90,12 +103,12 @@ Client.prototype.connect = function(name){
*/
Client.prototype.disconnect = function(){
for (var id in this.sockets) {
if (this.sockets.hasOwnProperty(id)) {
this.sockets[id].disconnect();
}
var socket;
// we don't use a for loop because the length of
// `sockets` changes upon each iteration
while (socket = this.sockets.shift()) {
socket.disconnect();
}
this.sockets = {};
this.close();
};
@@ -106,9 +119,10 @@ Client.prototype.disconnect = function(){
*/
Client.prototype.remove = function(socket){
if (this.sockets.hasOwnProperty(socket.id)) {
var nsp = this.sockets[socket.id].nsp.name;
delete this.sockets[socket.id];
var i = this.sockets.indexOf(socket);
if (~i) {
var nsp = this.sockets[i].nsp.name;
this.sockets.splice(i, 1);
delete this.nsps[nsp];
} else {
debug('ignoring remove for %s', socket.id);
@@ -133,25 +147,26 @@ Client.prototype.close = function(){
* Writes a packet to the transport.
*
* @param {Object} packet object
* @param {Object} opts
* @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, opts){
opts = opts || {};
Client.prototype.packet = function(packet, preEncoded, volatile, compress){
var self = this;
// this writes to the actual connection
function writeToEngine(encodedPackets) {
if (opts.volatile && !self.conn.transport.writable) return;
if (volatile && !self.conn.transport.writable) return;
for (var i = 0; i < encodedPackets.length; i++) {
self.conn.write(encodedPackets[i], { compress: opts.compress });
self.conn.write(encodedPackets[i], { compress: compress });
}
}
if ('open' == this.conn.readyState) {
debug('writing packet %j', packet);
if (!opts.preEncoded) { // not broadcasting, need to encode
if(!preEncoded) { // not broadcasting, need to encode
this.encoder.encode(packet, function (encodedPackets) { // encode, then write results to engine
writeToEngine(encodedPackets);
});
@@ -200,16 +215,14 @@ Client.prototype.ondecoded = function(packet) {
/**
* Handles an error.
*
* @param {Object} err object
* @param {Objcet} error object
* @api private
*/
Client.prototype.onerror = function(err){
for (var id in this.sockets) {
if (this.sockets.hasOwnProperty(id)) {
this.sockets[id].onerror(err);
}
}
this.sockets.forEach(function(socket){
socket.onerror(err);
});
this.onclose('client error');
};
@@ -227,12 +240,10 @@ Client.prototype.onclose = function(reason){
this.destroy();
// `nsps` and `sockets` are cleaned up seamlessly
for (var id in this.sockets) {
if (this.sockets.hasOwnProperty(id)) {
this.sockets[id].onclose(reason);
}
var socket;
while (socket = this.sockets.shift()) {
socket.onclose(reason);
}
this.sockets = {};
this.decoder.destroy(); // clean up decoder
};

View File

@@ -5,6 +5,7 @@
var http = require('http');
var read = require('fs').readFileSync;
var parse = require('url').parse;
var engine = require('engine.io');
var client = require('socket.io-client');
var clientVersion = require('socket.io-client/package').version;
@@ -29,8 +30,8 @@ var clientSource = read(require.resolve('socket.io-client/socket.io.js'), 'utf-8
/**
* Server constructor.
*
* @param {http.Server|Number|Object} srv http server, port or options
* @param {Object} opts
* @param {http.Server|Number|Object} http server, port or options
* @param {Object} options
* @api public
*/
@@ -42,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);
@@ -53,8 +55,8 @@ function Server(srv, opts){
/**
* Server request verification function, that checks for allowed origins
*
* @param {http.IncomingMessage} req request
* @param {Function} fn callback to be called with the result: `fn(err, success)`
* @param {http.IncomingMessage} request
* @param {Function} callback to be called with the result: `fn(err, success)`
*/
Server.prototype.checkRequest = function(req, fn) {
@@ -86,7 +88,7 @@ Server.prototype.checkRequest = function(req, fn) {
/**
* Sets/gets whether client code is being served.
*
* @param {Boolean} v whether to serve client code
* @param {Boolean} whether to serve client code
* @return {Server|Boolean} self when setting or value when getting
* @api public
*/
@@ -136,10 +138,57 @@ 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.
*
* @param {String} v pathname
* @param {String} pathname
* @return {Server|String} self when setting or value when getting
* @api public
*/
@@ -153,7 +202,7 @@ Server.prototype.path = function(v){
/**
* Sets the adapter for rooms.
*
* @param {Adapter} v pathname
* @param {Adapter} pathname
* @return {Server|Adapter} self when setting or value when getting
* @api public
*/
@@ -172,7 +221,7 @@ Server.prototype.adapter = function(v){
/**
* Sets the allowed origins for requests.
*
* @param {String} v origins
* @param {String} origins
* @return {Server|Adapter} self when setting or value when getting
* @api public
*/
@@ -221,7 +270,7 @@ Server.prototype.attach = function(srv, opts){
opts = opts || {};
opts.path = opts.path || this.path();
// set origins verification
opts.allowRequest = opts.allowRequest || this.checkRequest.bind(this);
opts.allowRequest = this.checkRequest.bind(this);
// initialize engine
debug('creating engine.io instance with opts %j', opts);
@@ -242,7 +291,7 @@ Server.prototype.attach = function(srv, opts){
/**
* Attaches the static file serving.
*
* @param {Function|http.Server} srv http server
* @param {Function|http.Server} http server
* @api private
*/
@@ -292,7 +341,7 @@ Server.prototype.serve = function(req, res){
/**
* Binds socket.io to an engine.io instance.
*
* @param {engine.Server} engine engine.io (or compatible) server
* @param {engine.Server} engine.io (or compatible) server
* @return {Server} self
* @api public
*/
@@ -306,7 +355,7 @@ Server.prototype.bind = function(engine){
/**
* Called with each incoming transport connection.
*
* @param {engine.Socket} conn
* @param {engine.Socket} socket
* @return {Server} self
* @api public
*/
@@ -321,22 +370,21 @@ Server.prototype.onconnection = function(conn){
/**
* Looks up a namespace.
*
* @param {String} name nsp name
* @param {Function} fn optional, nsp `connection` ev handler
* @param {String} nsp name
* @param {Function} optional, nsp `connection` ev handler
* @api public
*/
Server.prototype.of = function(name, fn){
if (String(name)[0] !== '/') name = '/' + name;
var nsp = this.nsps[name];
if (!nsp) {
if (!this.nsps[name]) {
debug('initializing namespace %s', name);
nsp = new Namespace(this, name);
var nsp = new Namespace(this, name);
this.nsps[name] = nsp;
}
if (fn) nsp.on('connect', fn);
return nsp;
if (fn) this.nsps[name].on('connect', fn);
return this.nsps[name];
};
/**
@@ -346,11 +394,9 @@ Server.prototype.of = function(name, fn){
*/
Server.prototype.close = function(){
for (var id in this.nsps['/'].sockets) {
if (this.nsps['/'].sockets.hasOwnProperty(id)) {
this.nsps['/'].sockets[id].onclose();
}
}
this.nsps['/'].sockets.forEach(function(socket){
socket.onclose();
});
this.engine.close();
@@ -363,7 +409,7 @@ Server.prototype.close = function(){
* Expose main namespace (/).
*/
['on', 'to', 'in', 'use', 'emit', 'send', 'write', 'clients', 'compress'].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);
@@ -371,9 +417,8 @@ Server.prototype.close = function(){
});
Namespace.flags.forEach(function(flag){
Server.prototype.__defineGetter__(flag, function(){
this.sockets.flags = this.sockets.flags || {};
this.sockets.flags[flag] = true;
Server.prototype.__defineGetter__(flag, function(name){
this.flags.push(name);
return this;
});
});

View File

@@ -29,10 +29,7 @@ exports.events = [
* Flags.
*/
exports.flags = [
'json',
'volatile'
];
exports.flags = ['json'];
/**
* `EventEmitter#emit` reference.
@@ -51,10 +48,11 @@ var emit = Emitter.prototype.emit;
function Namespace(server, name){
this.name = name;
this.server = server;
this.sockets = {};
this.sockets = [];
this.connected = {};
this.fns = [];
this.ids = 0;
this.acks = {};
this.initAdapter();
}
@@ -104,7 +102,7 @@ Namespace.prototype.use = function(fn){
* Executes the middleware for an incoming client.
*
* @param {Socket} socket that will get added
* @param {Function} fn last fn call in the middleware
* @param {Function} last fn call in the middleware
* @api private
*/
@@ -160,7 +158,7 @@ Namespace.prototype.add = function(client, fn){
if (err) return socket.error(err.data || err.message);
// track socket
self.sockets[socket.id] = socket;
self.sockets.push(socket);
// it's paramount that the internal `onconnect` logic
// fires before user-set events to prevent state order
@@ -187,8 +185,9 @@ Namespace.prototype.add = function(client, fn){
*/
Namespace.prototype.remove = function(socket){
if (this.sockets.hasOwnProperty(socket.id)) {
delete this.sockets[socket.id];
var i = this.sockets.indexOf(socket);
if (~i) {
this.sockets.splice(i, 1);
} else {
debug('ignoring remove for %s', socket.id);
}
@@ -251,22 +250,5 @@ Namespace.prototype.write = function(){
Namespace.prototype.clients = function(fn){
this.adapter.clients(this.rooms, fn);
// delete rooms flag for scenario:
// .in('room').clients() (GH-1978)
delete this.rooms;
return this;
};
/**
* Sets the compress flag.
*
* @param {Boolean} compress if `true`, compresses the sending data
* @return {Socket} self
* @api public
*/
Namespace.prototype.compress = function(compress){
this.flags = this.flags || {};
this.flags.compress = compress;
return this;
};

View File

@@ -59,10 +59,11 @@ function Socket(nsp, client){
this.nsp = nsp;
this.server = nsp.server;
this.adapter = this.nsp.adapter;
this.id = nsp.name + '#' + client.id;
this.id = client.id;
this.request = client.request;
this.client = client;
this.conn = client.conn;
this.rooms = {};
this.rooms = [];
this.acks = {};
this.connected = true;
this.disconnected = false;
@@ -88,7 +89,7 @@ flags.forEach(function(flag){
});
/**
* `request` engine.io shortcut.
* `request` engine.io shorcut.
*
* @api public
*/
@@ -131,11 +132,10 @@ Socket.prototype.emit = function(ev){
var packet = {};
packet.type = hasBin(args) ? parser.BINARY_EVENT : parser.EVENT;
packet.data = args;
var flags = this.flags || {};
// access last argument to see if it's an ACK callback
if ('function' == typeof args[args.length - 1]) {
if (this._rooms || flags.broadcast) {
if (this._rooms || (this.flags && this.flags.broadcast)) {
throw new Error('Callbacks are not supported when broadcasting');
}
@@ -144,18 +144,15 @@ Socket.prototype.emit = function(ev){
packet.id = this.nsp.ids++;
}
if (this._rooms || flags.broadcast) {
if (this._rooms || (this.flags && this.flags.broadcast)) {
this.adapter.broadcast(packet, {
except: [this.id],
rooms: this._rooms,
flags: flags
flags: this.flags
});
} else {
// dispatch packet
this.packet(packet, {
volatile: flags.volatile,
compress: flags.compress
});
this.packet(packet);
}
// reset flags
@@ -199,22 +196,21 @@ Socket.prototype.write = function(){
* Writes a packet.
*
* @param {Object} packet object
* @param {Object} opts options
* @api private
*/
Socket.prototype.packet = function(packet, opts){
Socket.prototype.packet = function(packet, preEncoded){
packet.nsp = this.nsp.name;
opts = opts || {};
opts.compress = false !== opts.compress;
this.client.packet(packet, opts);
var volatile = this.flags && this.flags.volatile;
var compress = !this.flags || false !== this.flags.compress;
this.client.packet(packet, preEncoded, volatile, compress);
};
/**
* Joins a room.
*
* @param {String} room
* @param {Function} fn optional, callback
* @param {Function} optional, callback
* @return {Socket} self
* @api private
*/
@@ -222,14 +218,11 @@ Socket.prototype.packet = function(packet, opts){
Socket.prototype.join = function(room, fn){
debug('joining room %s', room);
var self = this;
if (this.rooms.hasOwnProperty(room)) {
fn && fn(null);
return this;
}
if (~this.rooms.indexOf(room)) return this;
this.adapter.add(this.id, room, function(err){
if (err) return fn && fn(err);
debug('joined room %s', room);
self.rooms[room] = room;
self.rooms.push(room);
fn && fn(null);
});
return this;
@@ -239,7 +232,7 @@ Socket.prototype.join = function(room, fn){
* Leaves a room.
*
* @param {String} room
* @param {Function} fn optional, callback
* @param {Function} optional, callback
* @return {Socket} self
* @api private
*/
@@ -250,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);
delete self.rooms[room];
var idx = self.rooms.indexOf(room);
if (idx >= 0) {
self.rooms.splice(idx, 1);
}
fn && fn(null);
});
return this;
@@ -264,7 +260,7 @@ Socket.prototype.leave = function(room, fn){
Socket.prototype.leaveAll = function(){
this.adapter.delAll(this.id);
this.rooms = {};
this.rooms = [];
};
/**
@@ -276,9 +272,9 @@ Socket.prototype.leaveAll = function(){
Socket.prototype.onconnect = function(){
debug('socket connected - writing packet');
this.nsp.connected[this.id] = this;
this.join(this.id);
this.packet({ type: parser.CONNECT });
this.nsp.connected[this.id] = this;
};
/**
@@ -338,7 +334,7 @@ Socket.prototype.onevent = function(packet){
/**
* Produces an ack callback to emit with an event.
*
* @param {Number} id packet id
* @param {Number} packet id
* @api private
*/
@@ -357,8 +353,6 @@ Socket.prototype.ack = function(id){
type: type,
data: args
});
sent = true;
};
};
@@ -409,7 +403,7 @@ Socket.prototype.onerror = function(err){
* Called upon closing. Called by `Client`.
*
* @param {String} reason
* @throw {Error} optional error object
* @param {Error} optional error object
* @api private
*/
@@ -428,7 +422,7 @@ Socket.prototype.onclose = function(reason){
/**
* Produces an `error` packet.
*
* @param {Object} err error object
* @param {Object} error object
* @api private
*/
@@ -439,7 +433,7 @@ Socket.prototype.error = function(err){
/**
* Disconnects this client.
*
* @param {Boolean} close if `true`, closes the underlying connection
* @param {Boolean} if `true`, closes the underlying connection
* @return {Socket} self
* @api public
*/
@@ -458,7 +452,7 @@ Socket.prototype.disconnect = function(close){
/**
* Sets the compress flag.
*
* @param {Boolean} compress if `true`, compresses the sending data
* @param {Boolean} if `true`, compresses the sending data
* @return {Socket} self
* @api public
*/

View File

@@ -1,6 +1,6 @@
{
"name": "socket.io",
"version": "1.4.6",
"version": "1.3.2",
"description": "node.js realtime framework server",
"keywords": [
"realtime",
@@ -11,8 +11,6 @@
"socket",
"io"
],
"main": "./lib/index",
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/Automattic/socket.io"
@@ -21,20 +19,19 @@
"test": "mocha --reporter dot --slow 200ms --bail"
},
"dependencies": {
"engine.io": "1.6.9",
"socket.io-parser": "2.2.6",
"socket.io-client": "1.4.6",
"socket.io-adapter": "0.4.0",
"has-binary": "0.1.7",
"debug": "2.2.0"
"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",
"expect.js": "0.3.1",
"istanbul": "0.4.1",
"mocha": "2.3.4",
"superagent": "1.6.1",
"supertest": "1.1.0",
"zuul-ngrok": "3.2.0"
"supertest": "0.8.2",
"superagent": "0.17.0",
"istanbul": "0.2.3"
},
"contributors": [
{

View File

@@ -1,11 +0,0 @@
var server = require('http').createServer();
var ioc = require('socket.io-client');
var io = require('../..')(server);
var srv = server.listen(function() {
var socket = ioc('ws://localhost:' + server.address().port);
socket.on('connect', function() {
io.close();
socket.close();
});
});

View File

@@ -3,7 +3,6 @@ var http = require('http').Server;
var io = require('..');
var fs = require('fs');
var join = require('path').join;
var exec = require('child_process').exec;
var ioc = require('socket.io-client');
var request = require('supertest');
var expect = require('expect.js');
@@ -329,32 +328,6 @@ describe('socket.io', function(){
done();
});
});
it('should allow request if custom function in opts.allowRequest returns true', function(done){
var sockets = io(http().listen(54022), { allowRequest: function (req, callback) {
return callback(null, true);
}, origins: 'http://foo.example:*' });
request.get('http://localhost:54022/socket.io/default/')
.query({ transport: 'polling' })
.end(function (err, res) {
expect(res.status).to.be(200);
done();
});
});
it('should disallow request if custom function in opts.allowRequest returns false', function(done){
var sockets = io(http().listen(54023), { allowRequest: function (req, callback) {
return callback(null, false);
} });
request.get('http://localhost:54023/socket.io/default/')
.set('origin', 'http://foo.example')
.query({ transport: 'polling' })
.end(function (err, res) {
expect(res.status).to.be(400);
done();
});
});
});
describe('close', function(){
@@ -369,12 +342,12 @@ describe('socket.io', function(){
var clientSocket = client(srv, { reconnection: false });
clientSocket.on('disconnect', function init() {
expect(Object.keys(sio.nsps['/'].sockets).length).to.equal(0);
expect(sio.nsps['/'].sockets.length).to.equal(0);
server.listen(PORT);
});
clientSocket.on('connect', function init() {
expect(Object.keys(sio.nsps['/'].sockets).length).to.equal(1);
expect(sio.nsps['/'].sockets.length).to.equal(1);
sio.close();
});
@@ -396,12 +369,12 @@ describe('socket.io', function(){
var clientSocket = ioc('ws://0.0.0.0:' + PORT);
clientSocket.on('disconnect', function init() {
expect(Object.keys(sio.nsps['/'].sockets).length).to.equal(0);
expect(sio.nsps['/'].sockets.length).to.equal(0);
server.listen(PORT);
});
clientSocket.on('connect', function init() {
expect(Object.keys(sio.nsps['/'].sockets).length).to.equal(1);
expect(sio.nsps['/'].sockets.length).to.equal(1);
sio.close();
});
@@ -413,16 +386,6 @@ describe('socket.io', function(){
});
});
describe('graceful close', function(){
function fixture(filename) {
return process.execPath + ' ' +
join(__dirname, 'fixtures', filename);
}
it('should stop socket and timers', function(done){
exec(fixture('server-close.js'), done);
});
});
});
describe('namespaces', function(){
@@ -442,12 +405,6 @@ describe('socket.io', function(){
expect(sio.emit).to.be.a('function');
expect(sio.send).to.be.a('function');
expect(sio.write).to.be.a('function');
expect(sio.clients).to.be.a('function');
expect(sio.compress).to.be.a('function');
expect(sio.json).to.be(sio);
expect(sio.volatile).to.be(sio);
expect(sio.sockets.flags).to.eql({ json: true, volatile: true });
delete sio.sockets.flags;
});
it('should automatically connect', function(done){
@@ -640,7 +597,7 @@ describe('socket.io', function(){
});
});
});
it('should not reuse same-namespace connections', function(done){
var srv = http();
var sio = io(srv);
@@ -679,7 +636,7 @@ describe('socket.io', function(){
});
function getClients() {
sio.of('/chat').clients(function(error, sids) {
expect(error).to.not.be.ok();
expect(error).to.be.undefined;
expect(sids).to.contain(chatSids[0]);
expect(sids).to.contain(chatSids[1]);
expect(sids).to.not.contain(otherSid);
@@ -722,7 +679,7 @@ describe('socket.io', function(){
});
function getClients() {
sio.of('/chat').in('foo').clients(function(error, sids) {
expect(error).to.not.be.ok();
expect(error).to.be.undefined;
expect(sids).to.contain(chatFooSid);
expect(sids).to.not.contain(chatBarSid);
expect(sids).to.not.contain(otherSid);
@@ -765,7 +722,7 @@ describe('socket.io', function(){
});
function getClients() {
sio.of('/chat').clients(function(error, sids) {
expect(error).to.not.be.ok();
expect(error).to.be.undefined;
expect(sids).to.contain(chatFooSid);
expect(sids).to.contain(chatBarSid);
expect(sids).to.not.contain(otherSid);
@@ -774,83 +731,74 @@ describe('socket.io', function(){
}
});
it('should not emit volatile event after regular event', function(done) {
var srv = http();
var sio = io(srv);
var counter = 0;
srv.listen(function(){
sio.of('/chat').on('connection', function(s){
// Wait to make sure there are no packets being sent for opening the connection
setTimeout(function() {
sio.of('/chat').emit('ev', 'data');
sio.of('/chat').volatile.emit('ev', 'data');
}, 20);
});
var socket = client(srv, '/chat');
socket.on('ev', function() {
counter++;
});
});
setTimeout(function() {
expect(counter).to.be(1);
done();
}, 200);
});
it('should emit volatile event', function(done) {
var srv = http();
var sio = io(srv);
var counter = 0;
srv.listen(function(){
sio.of('/chat').on('connection', function(s){
// Wait to make sure there are no packets being sent for opening the connection
setTimeout(function() {
sio.of('/chat').volatile.emit('ev', 'data');
}, 20);
});
var socket = client(srv, '/chat');
socket.on('ev', function() {
counter++;
});
});
setTimeout(function() {
expect(counter).to.be(1);
done();
}, 200);
});
it('should enable compression by default', function(done){
it('should allow connections to dynamic namespaces', function(done){
var srv = http();
var sio = io(srv);
srv.listen(function(){
var socket = client(srv, '/chat');
sio.of('/chat').on('connection', function(s){
s.conn.once('packetCreate', function(packet) {
expect(packet.options.compress).to.be(true);
done();
});
sio.of('/chat').emit('woot', 'hi');
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 disable compression', function(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 socket = client(srv, '/chat');
sio.of('/chat').on('connection', function(s){
s.conn.once('packetCreate', function(packet) {
expect(packet.options.compress).to.be(false);
done();
});
sio.of('/chat').compress(false).emit('woot', 'hi');
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();
});
});
});
@@ -1522,7 +1470,7 @@ describe('socket.io', function(){
it('should handle very large json', function(done){
this.timeout(30000);
var srv = http();
var sio = io(srv, { perMessageDeflate: false });
var sio = io(srv);
var received = 0;
srv.listen(function(){
var socket = client(srv);
@@ -1547,9 +1495,10 @@ describe('socket.io', function(){
});
it('should handle very large binary data', function(done){
this.timeout(30000);
this.timeout(10000);
var srv = http();
var sio = io(srv, { perMessageDeflate: false });
var sio = io(srv);
var received = 0;
srv.listen(function(){
var socket = client(srv);
@@ -1603,13 +1552,13 @@ describe('socket.io', function(){
var srv = http();
var sio = io(srv);
srv.listen(function(){
var socket = client(srv, '/chat');
sio.of('/chat').on('connection', function(s){
var socket = client(srv);
sio.on('connection', function(s){
s.conn.once('packetCreate', function(packet) {
expect(packet.options.compress).to.be(true);
done();
});
sio.of('/chat').emit('woot', 'hi');
s.emit('woot', 'hi');
});
});
});
@@ -1618,13 +1567,13 @@ describe('socket.io', function(){
var srv = http();
var sio = io(srv);
srv.listen(function(){
var socket = client(srv, '/chat');
sio.of('/chat').on('connection', function(s){
var socket = client(srv);
sio.on('connection', function(s){
s.conn.once('packetCreate', function(packet) {
expect(packet.options.compress).to.be(false);
done();
});
sio.of('/chat').compress(false).emit('woot', 'hi');
s.compress(false).emit('woot', 'hi');
});
});
});
@@ -1662,55 +1611,6 @@ describe('socket.io', function(){
});
});
});
it('should handle empty binary packet', 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('5');
});
});
});
});
it('should not crash when messing with Object prototype', function(done){
Object.prototype.foo = 'bar';
var srv = http();
var sio = io(srv);
srv.listen(function(){
var socket = client(srv);
sio.on('connection', function(s){
s.disconnect(true);
sio.close();
setTimeout(function(){
done();
}, 100);
});
});
});
it('should always trigger the callback (if provided) when joining a room', 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(){
s.join('a', done);
});
});
});
});
});
describe('messaging many', function(){
@@ -1975,15 +1875,15 @@ describe('socket.io', function(){
var socket = client(srv);
sio.on('connection', function(s){
s.join('a', function(){
expect(Object.keys(s.rooms)).to.eql([s.id, 'a']);
expect(s.rooms).to.eql([s.id, 'a']);
s.join('b', function(){
expect(Object.keys(s.rooms)).to.eql([s.id, 'a', 'b']);
expect(s.rooms).to.eql([s.id, 'a', 'b']);
s.join( 'c', function(){
expect(Object.keys(s.rooms)).to.eql([s.id, 'a', 'b', 'c']);
expect(s.rooms).to.eql([s.id, 'a', 'b', 'c']);
s.leave('b', function(){
expect(Object.keys(s.rooms)).to.eql([s.id, 'a', 'c']);
expect(s.rooms).to.eql([s.id, 'a', 'c']);
s.leaveAll();
expect(Object.keys(s.rooms)).to.eql([]);
expect(s.rooms).to.eql([]);
done();
});
});
@@ -2019,13 +1919,13 @@ describe('socket.io', function(){
var socket = client(srv);
sio.on('connection', function(s){
s.join('a', function(){
expect(Object.keys(s.rooms)).to.eql([s.id, 'a']);
expect(s.rooms).to.eql([s.id, 'a']);
s.join('b', function(){
expect(Object.keys(s.rooms)).to.eql([s.id, 'a', 'b']);
expect(s.rooms).to.eql([s.id, 'a', 'b']);
s.leave('unknown', function(){
expect(Object.keys(s.rooms)).to.eql([s.id, 'a', 'b']);
expect(s.rooms).to.eql([s.id, 'a', 'b']);
s.leaveAll();
expect(Object.keys(s.rooms)).to.eql([]);
expect(s.rooms).to.eql([]);
done();
});
});