mirror of
https://github.com/socketio/socket.io.git
synced 2026-01-11 16:08:24 -05:00
Compare commits
152 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1a3e8db2a | ||
|
|
dad82c3343 | ||
|
|
a6cbf6b205 | ||
|
|
6d5d7e4411 | ||
|
|
ddb3445f3d | ||
|
|
6eb9807f17 | ||
|
|
7b0073c00f | ||
|
|
0313ad0ea3 | ||
|
|
d310d42472 | ||
|
|
709ceba00a | ||
|
|
b29312bc61 | ||
|
|
51901160df | ||
|
|
ab1b36e13f | ||
|
|
f7a2f35590 | ||
|
|
c348737fe6 | ||
|
|
dde3737165 | ||
|
|
8df4c7931b | ||
|
|
1dfacc6647 | ||
|
|
35a0fe0377 | ||
|
|
3b00312fa7 | ||
|
|
7b420295ef | ||
|
|
c504315982 | ||
|
|
520b5c37d5 | ||
|
|
09f446eca0 | ||
|
|
aecf3bf71c | ||
|
|
6c0a79e2b9 | ||
|
|
8683206e31 | ||
|
|
988d9d2346 | ||
|
|
b73d9bea4e | ||
|
|
d4fb6a5904 | ||
|
|
0517c609ba | ||
|
|
aa9438fee2 | ||
|
|
aee466cc71 | ||
|
|
65f75d7732 | ||
|
|
279c52a4c5 | ||
|
|
67d1a67b1c | ||
|
|
aae68d74b1 | ||
|
|
bf5897cedc | ||
|
|
a1a1c6657a | ||
|
|
2a4f7ae161 | ||
|
|
dba0576ca9 | ||
|
|
ba90f0991b | ||
|
|
9a54e9cace | ||
|
|
9df4cb64b8 | ||
|
|
c8f525868a | ||
|
|
a39d7c2ae4 | ||
|
|
4ba266d404 | ||
|
|
6b49779420 | ||
|
|
b1775b1cb8 | ||
|
|
ef52967301 | ||
|
|
fde38b1bd7 | ||
|
|
75da57abb2 | ||
|
|
21ea31a34e | ||
|
|
e2ebd4349b | ||
|
|
b771f74a22 | ||
|
|
8744007dc7 | ||
|
|
3658928ee7 | ||
|
|
210e688732 | ||
|
|
ae5420b727 | ||
|
|
d5b3bb46f8 | ||
|
|
575d35a371 | ||
|
|
e41483be9e | ||
|
|
c79e787671 | ||
|
|
a2f6bb99d6 | ||
|
|
3e4061bfef | ||
|
|
4c1d691fe5 | ||
|
|
514aeb4fcd | ||
|
|
876833198d | ||
|
|
4d36d6c01a | ||
|
|
43fa075ec4 | ||
|
|
91c7bde34b | ||
|
|
874a4a8535 | ||
|
|
93b571406e | ||
|
|
98af793f07 | ||
|
|
3f72dd3322 | ||
|
|
d0d38220fb | ||
|
|
f8aa157d5d | ||
|
|
0e13e9a268 | ||
|
|
916872587f | ||
|
|
a7072845ea | ||
|
|
425409945b | ||
|
|
5826ef0bb5 | ||
|
|
14e32a9bad | ||
|
|
1dde0f3947 | ||
|
|
342faf2197 | ||
|
|
12c6ee132d | ||
|
|
c9d909c993 | ||
|
|
125ab51338 | ||
|
|
b31ac18554 | ||
|
|
53cdd8f1fc | ||
|
|
81aea995ed | ||
|
|
1b2d902f33 | ||
|
|
9536437907 | ||
|
|
56fe26661d | ||
|
|
29974ac777 | ||
|
|
ef23c74bea | ||
|
|
49423d70bc | ||
|
|
715c7f99b4 | ||
|
|
f2ea965c6b | ||
|
|
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 | ||
|
|
8242dd01ef | ||
|
|
17960ed038 | ||
|
|
d9996f0470 | ||
|
|
24d06d76dd | ||
|
|
4e4bbf918e | ||
|
|
b49f5c82f2 | ||
|
|
5bd67195de | ||
|
|
73fe547956 | ||
|
|
973e6cc982 | ||
|
|
136fe960b7 |
14
.travis.yml
14
.travis.yml
@@ -1,16 +1,20 @@
|
||||
sudo: false
|
||||
before_install:
|
||||
- npm install -g npm@'>=1.4.3'
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.8"
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
- "0.12"
|
||||
- "4"
|
||||
|
||||
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"
|
||||
|
||||
101
History.md
101
History.md
@@ -1,4 +1,105 @@
|
||||
|
||||
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]
|
||||
|
||||
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
|
||||
==================
|
||||
|
||||
* 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
|
||||
|
||||
72
Readme.md
72
Readme.md
@@ -1,9 +1,10 @@
|
||||
|
||||
# socket.io
|
||||
|
||||
[](http://travis-ci.org/Automattic/socket.io)
|
||||
[](http://badge.fury.io/js/socket.io)
|
||||

|
||||
[](https://travis-ci.org/socketio/socket.io)
|
||||

|
||||

|
||||
[](http://slack.socket.io)
|
||||
|
||||
## How to use
|
||||
|
||||
@@ -85,7 +86,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/learnboost/engine.io#methods-1)
|
||||
[options](https://github.com/socketio/engine.io#methods-1)
|
||||
as reference.
|
||||
|
||||
### Server(srv:http#Server, opts:Object)
|
||||
@@ -127,7 +128,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/Automattic/socket.io-adapter).
|
||||
[socket.io-adapter](https://github.com/socketio/socket.io-adapter).
|
||||
|
||||
If no arguments are supplied this method returns the current value.
|
||||
|
||||
@@ -144,7 +145,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/troygoode/node-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/expressjs/cors)
|
||||
|
||||
|
||||
### Server#sockets:Namespace
|
||||
@@ -240,6 +241,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 +309,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
|
||||
@@ -313,7 +348,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/Automattic/socket.io-adapter).
|
||||
[socket.io-adapter](https://github.com/socketio/socket.io-adapter).
|
||||
|
||||
### Socket#leave(name:String[, fn:Function]):Socket
|
||||
|
||||
@@ -324,10 +359,9 @@ 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/Automattic/socket.io-adapter).
|
||||
[socket.io-adapter](https://github.com/socketio/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`.
|
||||
@@ -341,6 +375,22 @@ 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
|
||||
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)
|
||||
@@ -359,7 +409,7 @@ server.listen(3000);
|
||||
|
||||
## Debug / logging
|
||||
|
||||
Socket.IO is powered by [debug](http://github.com/visionmedia/debug).
|
||||
Socket.IO is powered by [debug](https://github.com/visionmedia/debug).
|
||||
In order to see all the debug output, run your app with the environment variable
|
||||
`DEBUG` including the desired scope.
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@ 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) {
|
||||
@@ -32,10 +30,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', {
|
||||
@@ -64,9 +62,7 @@ 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
|
||||
|
||||
@@ -7,7 +7,7 @@ $(function() {
|
||||
'#3b88eb', '#3824aa', '#a700ff', '#d300e7'
|
||||
];
|
||||
|
||||
// Initialize varibles
|
||||
// Initialize variables
|
||||
var $window = $(window);
|
||||
var $usernameInput = $('.usernameInput'); // Input for username
|
||||
var $messages = $('.messages'); // Messages area
|
||||
|
||||
@@ -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 = [];
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -57,11 +60,12 @@ Client.prototype.setup = function(){
|
||||
|
||||
Client.prototype.connect = function(name){
|
||||
debug('connecting to namespace %s', name);
|
||||
if (!this.server.nsps[name]) {
|
||||
var nsp = this.server.nsps[name];
|
||||
if (!nsp) {
|
||||
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;
|
||||
@@ -69,7 +73,7 @@ Client.prototype.connect = function(name){
|
||||
|
||||
var self = this;
|
||||
var socket = nsp.add(this, function(){
|
||||
self.sockets.push(socket);
|
||||
self.sockets[socket.id] = socket;
|
||||
self.nsps[nsp.name] = socket;
|
||||
|
||||
if ('/' == nsp.name && self.connectBuffer.length > 0) {
|
||||
@@ -86,12 +90,12 @@ Client.prototype.connect = function(name){
|
||||
*/
|
||||
|
||||
Client.prototype.disconnect = function(){
|
||||
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();
|
||||
for (var id in this.sockets) {
|
||||
if (this.sockets.hasOwnProperty(id)) {
|
||||
this.sockets[id].disconnect();
|
||||
}
|
||||
}
|
||||
this.sockets = {};
|
||||
this.close();
|
||||
};
|
||||
|
||||
@@ -102,10 +106,9 @@ Client.prototype.disconnect = function(){
|
||||
*/
|
||||
|
||||
Client.prototype.remove = function(socket){
|
||||
var i = this.sockets.indexOf(socket);
|
||||
if (~i) {
|
||||
var nsp = this.sockets[i].nsp.name;
|
||||
this.sockets.splice(i, 1);
|
||||
if (this.sockets.hasOwnProperty(socket.id)) {
|
||||
var nsp = this.sockets[socket.id].nsp.name;
|
||||
delete this.sockets[socket.id];
|
||||
delete this.nsps[nsp];
|
||||
} else {
|
||||
debug('ignoring remove for %s', socket.id);
|
||||
@@ -130,25 +133,25 @@ Client.prototype.close = function(){
|
||||
* Writes a packet to the transport.
|
||||
*
|
||||
* @param {Object} packet object
|
||||
* @param {Boolean} whether packet is already encoded
|
||||
* @param {Boolean} whether packet is volatile
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Client.prototype.packet = function(packet, preEncoded, volatile){
|
||||
Client.prototype.packet = function(packet, opts){
|
||||
opts = opts || {};
|
||||
var self = this;
|
||||
|
||||
// this writes to the actual connection
|
||||
function writeToEngine(encodedPackets) {
|
||||
if (volatile && !self.conn.transport.writable) return;
|
||||
if (opts.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: opts.compress });
|
||||
}
|
||||
}
|
||||
|
||||
if ('open' == this.conn.readyState) {
|
||||
debug('writing packet %j', packet);
|
||||
if(!preEncoded) { // not broadcasting, need to encode
|
||||
if (!opts.preEncoded) { // not broadcasting, need to encode
|
||||
this.encoder.encode(packet, function (encodedPackets) { // encode, then write results to engine
|
||||
writeToEngine(encodedPackets);
|
||||
});
|
||||
@@ -167,7 +170,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 +197,22 @@ Client.prototype.ondecoded = function(packet) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles an error.
|
||||
*
|
||||
* @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.onclose('client error');
|
||||
};
|
||||
|
||||
/**
|
||||
* Called upon transport close.
|
||||
*
|
||||
@@ -203,10 +227,12 @@ Client.prototype.onclose = function(reason){
|
||||
this.destroy();
|
||||
|
||||
// `nsps` and `sockets` are cleaned up seamlessly
|
||||
var socket;
|
||||
while (socket = this.sockets.shift()) {
|
||||
socket.onclose(reason);
|
||||
for (var id in this.sockets) {
|
||||
if (this.sockets.hasOwnProperty(id)) {
|
||||
this.sockets[id].onclose(reason);
|
||||
}
|
||||
}
|
||||
this.sockets = {};
|
||||
|
||||
this.decoder.destroy(); // clean up decoder
|
||||
};
|
||||
@@ -219,6 +245,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);
|
||||
};
|
||||
|
||||
37
lib/index.js
37
lib/index.js
@@ -62,14 +62,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 + ':*') ||
|
||||
@@ -194,7 +197,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);
|
||||
}
|
||||
@@ -219,7 +222,7 @@ Server.prototype.attach = function(srv, opts){
|
||||
opts = opts || {};
|
||||
opts.path = opts.path || this.path();
|
||||
// set origins verification
|
||||
opts.allowRequest = this.checkRequest.bind(this);
|
||||
opts.allowRequest = opts.allowRequest || this.checkRequest.bind(this);
|
||||
|
||||
// initialize engine
|
||||
debug('creating engine.io instance with opts %j', opts);
|
||||
@@ -251,7 +254,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++) {
|
||||
@@ -327,13 +330,14 @@ Server.prototype.onconnection = function(conn){
|
||||
Server.prototype.of = function(name, fn){
|
||||
if (String(name)[0] !== '/') name = '/' + name;
|
||||
|
||||
if (!this.nsps[name]) {
|
||||
var nsp = this.nsps[name];
|
||||
if (!nsp) {
|
||||
debug('initializing namespace %s', name);
|
||||
var nsp = new Namespace(this, name);
|
||||
nsp = new Namespace(this, name);
|
||||
this.nsps[name] = nsp;
|
||||
}
|
||||
if (fn) this.nsps[name].on('connect', fn);
|
||||
return this.nsps[name];
|
||||
if (fn) nsp.on('connect', fn);
|
||||
return nsp;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -343,9 +347,11 @@ Server.prototype.of = function(name, fn){
|
||||
*/
|
||||
|
||||
Server.prototype.close = function(){
|
||||
this.nsps['/'].sockets.forEach(function(socket){
|
||||
socket.onclose();
|
||||
});
|
||||
for (var id in this.nsps['/'].sockets) {
|
||||
if (this.nsps['/'].sockets.hasOwnProperty(id)) {
|
||||
this.nsps['/'].sockets[id].onclose();
|
||||
}
|
||||
}
|
||||
|
||||
this.engine.close();
|
||||
|
||||
@@ -358,7 +364,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', 'compress'].forEach(function(fn){
|
||||
Server.prototype[fn] = function(){
|
||||
var nsp = this.sockets[fn];
|
||||
return nsp.apply(this.sockets, arguments);
|
||||
@@ -366,8 +372,9 @@ Server.prototype.close = function(){
|
||||
});
|
||||
|
||||
Namespace.flags.forEach(function(flag){
|
||||
Server.prototype.__defineGetter__(flag, function(name){
|
||||
this.flags.push(name);
|
||||
Server.prototype.__defineGetter__(flag, function(){
|
||||
this.sockets.flags = this.sockets.flags || {};
|
||||
this.sockets.flags[flag] = true;
|
||||
return this;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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.
|
||||
@@ -29,7 +29,10 @@ exports.events = [
|
||||
* Flags.
|
||||
*/
|
||||
|
||||
exports.flags = ['json'];
|
||||
exports.flags = [
|
||||
'json',
|
||||
'volatile'
|
||||
];
|
||||
|
||||
/**
|
||||
* `EventEmitter#emit` reference.
|
||||
@@ -48,11 +51,10 @@ 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();
|
||||
}
|
||||
|
||||
@@ -158,7 +160,7 @@ Namespace.prototype.add = function(client, fn){
|
||||
if (err) return socket.error(err.data || err.message);
|
||||
|
||||
// track socket
|
||||
self.sockets.push(socket);
|
||||
self.sockets[socket.id] = socket;
|
||||
|
||||
// it's paramount that the internal `onconnect` logic
|
||||
// fires before user-set events to prevent state order
|
||||
@@ -185,9 +187,8 @@ Namespace.prototype.add = function(client, fn){
|
||||
*/
|
||||
|
||||
Namespace.prototype.remove = function(socket){
|
||||
var i = this.sockets.indexOf(socket);
|
||||
if (~i) {
|
||||
this.sockets.splice(i, 1);
|
||||
if (this.sockets.hasOwnProperty(socket.id)) {
|
||||
delete this.sockets[socket.id];
|
||||
} else {
|
||||
debug('ignoring remove for %s', socket.id);
|
||||
}
|
||||
@@ -240,3 +241,32 @@ 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);
|
||||
// delete rooms flag for scenario:
|
||||
// .in('room').clients() (GH-1978)
|
||||
delete this.rooms;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the compress flag.
|
||||
*
|
||||
* @param {Boolean} 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;
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
@@ -59,11 +59,10 @@ function Socket(nsp, client){
|
||||
this.nsp = nsp;
|
||||
this.server = nsp.server;
|
||||
this.adapter = this.nsp.adapter;
|
||||
this.id = client.id;
|
||||
this.request = client.request;
|
||||
this.id = nsp.name + '#' + client.id;
|
||||
this.client = client;
|
||||
this.conn = client.conn;
|
||||
this.rooms = [];
|
||||
this.rooms = {};
|
||||
this.acks = {};
|
||||
this.connected = true;
|
||||
this.disconnected = false;
|
||||
@@ -132,10 +131,11 @@ 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 || (this.flags && this.flags.broadcast)) {
|
||||
if (this._rooms || flags.broadcast) {
|
||||
throw new Error('Callbacks are not supported when broadcasting');
|
||||
}
|
||||
|
||||
@@ -144,15 +144,18 @@ Socket.prototype.emit = function(ev){
|
||||
packet.id = this.nsp.ids++;
|
||||
}
|
||||
|
||||
if (this._rooms || (this.flags && this.flags.broadcast)) {
|
||||
if (this._rooms || flags.broadcast) {
|
||||
this.adapter.broadcast(packet, {
|
||||
except: [this.id],
|
||||
rooms: this._rooms,
|
||||
flags: this.flags
|
||||
flags: flags
|
||||
});
|
||||
} else {
|
||||
// dispatch packet
|
||||
this.packet(packet);
|
||||
this.packet(packet, {
|
||||
volatile: flags.volatile,
|
||||
compress: flags.compress
|
||||
});
|
||||
}
|
||||
|
||||
// reset flags
|
||||
@@ -196,13 +199,15 @@ Socket.prototype.write = function(){
|
||||
* Writes a packet.
|
||||
*
|
||||
* @param {Object} packet object
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Socket.prototype.packet = function(packet, preEncoded){
|
||||
Socket.prototype.packet = function(packet, opts){
|
||||
packet.nsp = this.nsp.name;
|
||||
var volatile = this.flags && this.flags.volatile;
|
||||
this.client.packet(packet, preEncoded, volatile);
|
||||
opts = opts || {};
|
||||
opts.compress = false !== opts.compress;
|
||||
this.client.packet(packet, opts);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -217,11 +222,14 @@ Socket.prototype.packet = function(packet, preEncoded){
|
||||
Socket.prototype.join = function(room, fn){
|
||||
debug('joining room %s', room);
|
||||
var self = this;
|
||||
if (~this.rooms.indexOf(room)) return this;
|
||||
if (this.rooms.hasOwnProperty(room)) {
|
||||
fn && fn(null);
|
||||
return this;
|
||||
}
|
||||
this.adapter.add(this.id, room, function(err){
|
||||
if (err) return fn && fn(err);
|
||||
debug('joined room %s', room);
|
||||
self.rooms.push(room);
|
||||
self.rooms[room] = room;
|
||||
fn && fn(null);
|
||||
});
|
||||
return this;
|
||||
@@ -242,7 +250,7 @@ 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);
|
||||
delete self.rooms[room];
|
||||
fn && fn(null);
|
||||
});
|
||||
return this;
|
||||
@@ -256,7 +264,7 @@ Socket.prototype.leave = function(room, fn){
|
||||
|
||||
Socket.prototype.leaveAll = function(){
|
||||
this.adapter.delAll(this.id);
|
||||
this.rooms = [];
|
||||
this.rooms = {};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -268,9 +276,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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -349,6 +357,8 @@ Socket.prototype.ack = function(id){
|
||||
type: type,
|
||||
data: args
|
||||
});
|
||||
|
||||
sent = true;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -380,10 +390,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 +454,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;
|
||||
};
|
||||
|
||||
25
package.json
25
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io",
|
||||
"version": "1.2.0",
|
||||
"version": "1.4.3",
|
||||
"description": "node.js realtime framework server",
|
||||
"keywords": [
|
||||
"realtime",
|
||||
@@ -11,27 +11,30 @@
|
||||
"socket",
|
||||
"io"
|
||||
],
|
||||
"main": "./lib/index",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"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": "1.6.6",
|
||||
"socket.io-parser": "2.2.6",
|
||||
"socket.io-client": "1.4.3",
|
||||
"socket.io-adapter": "0.4.0",
|
||||
"has-binary": "0.1.7",
|
||||
"debug": "2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "1.16.2",
|
||||
"expect.js": "0.3.1",
|
||||
"supertest": "0.8.2",
|
||||
"istanbul": "0.2.3",
|
||||
"mocha": "2.3.4",
|
||||
"superagent": "0.17.0",
|
||||
"istanbul": "0.2.3"
|
||||
"supertest": "0.8.2",
|
||||
"zuul-ngrok": "3.1.0"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
|
||||
@@ -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,58 @@ 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();
|
||||
});
|
||||
});
|
||||
|
||||
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(){
|
||||
@@ -314,12 +368,12 @@ describe('socket.io', function(){
|
||||
var clientSocket = client(srv, { reconnection: false });
|
||||
|
||||
clientSocket.on('disconnect', function init() {
|
||||
expect(sio.nsps['/'].sockets.length).to.equal(0);
|
||||
expect(Object.keys(sio.nsps['/'].sockets).length).to.equal(0);
|
||||
server.listen(PORT);
|
||||
});
|
||||
|
||||
clientSocket.on('connect', function init() {
|
||||
expect(sio.nsps['/'].sockets.length).to.equal(1);
|
||||
expect(Object.keys(sio.nsps['/'].sockets).length).to.equal(1);
|
||||
sio.close();
|
||||
});
|
||||
|
||||
@@ -341,12 +395,12 @@ describe('socket.io', function(){
|
||||
var clientSocket = ioc('ws://0.0.0.0:' + PORT);
|
||||
|
||||
clientSocket.on('disconnect', function init() {
|
||||
expect(sio.nsps['/'].sockets.length).to.equal(0);
|
||||
expect(Object.keys(sio.nsps['/'].sockets).length).to.equal(0);
|
||||
server.listen(PORT);
|
||||
});
|
||||
|
||||
clientSocket.on('connect', function init() {
|
||||
expect(sio.nsps['/'].sockets.length).to.equal(1);
|
||||
expect(Object.keys(sio.nsps['/'].sockets).length).to.equal(1);
|
||||
sio.close();
|
||||
});
|
||||
|
||||
@@ -377,6 +431,12 @@ 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){
|
||||
@@ -445,7 +505,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 +514,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 +629,220 @@ 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.not.be.ok();
|
||||
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.not.be.ok();
|
||||
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.not.be.ok();
|
||||
expect(sids).to.contain(chatFooSid);
|
||||
expect(sids).to.contain(chatBarSid);
|
||||
expect(sids).to.not.contain(otherSid);
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
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){
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should disable compression', 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('socket', function(){
|
||||
@@ -637,6 +911,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 +1072,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 +1496,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,9 +1509,9 @@ 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 sio = io(srv, { perMessageDeflate: false });
|
||||
var received = 0;
|
||||
srv.listen(function(){
|
||||
var socket = client(srv);
|
||||
@@ -1027,8 +1536,9 @@ describe('socket.io', function(){
|
||||
});
|
||||
|
||||
it('should handle very large binary data', function(done){
|
||||
this.timeout(30000);
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
var sio = io(srv, { perMessageDeflate: false });
|
||||
var received = 0;
|
||||
srv.listen(function(){
|
||||
var socket = client(srv);
|
||||
@@ -1051,6 +1561,145 @@ 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, '/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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should disable compression', 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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(){
|
||||
@@ -1315,15 +1964,15 @@ describe('socket.io', function(){
|
||||
var socket = client(srv);
|
||||
sio.on('connection', function(s){
|
||||
s.join('a', function(){
|
||||
expect(s.rooms).to.eql([s.id, 'a']);
|
||||
expect(Object.keys(s.rooms)).to.eql([s.id, 'a']);
|
||||
s.join('b', function(){
|
||||
expect(s.rooms).to.eql([s.id, 'a', 'b']);
|
||||
expect(Object.keys(s.rooms)).to.eql([s.id, 'a', 'b']);
|
||||
s.join( 'c', function(){
|
||||
expect(s.rooms).to.eql([s.id, 'a', 'b', 'c']);
|
||||
expect(Object.keys(s.rooms)).to.eql([s.id, 'a', 'b', 'c']);
|
||||
s.leave('b', function(){
|
||||
expect(s.rooms).to.eql([s.id, 'a', 'c']);
|
||||
expect(Object.keys(s.rooms)).to.eql([s.id, 'a', 'c']);
|
||||
s.leaveAll();
|
||||
expect(s.rooms).to.eql([]);
|
||||
expect(Object.keys(s.rooms)).to.eql([]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -1350,6 +1999,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(Object.keys(s.rooms)).to.eql([s.id, 'a']);
|
||||
s.join('b', function(){
|
||||
expect(Object.keys(s.rooms)).to.eql([s.id, 'a', 'b']);
|
||||
s.leave('unknown', function(){
|
||||
expect(Object.keys(s.rooms)).to.eql([s.id, 'a', 'b']);
|
||||
s.leaveAll();
|
||||
expect(Object.keys(s.rooms)).to.eql([]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('middleware', function(done){
|
||||
|
||||
Reference in New Issue
Block a user