Compare commits

...

27 Commits
1.5.1 ... 1.7.3

Author SHA1 Message Date
Damien Arrachequesne
06044efbe2 [chore] Release 1.7.3 2017-02-17 06:58:45 +01:00
Damien Arrachequesne
50128063de [chore] Bump engine.io to version 1.8.3 2017-02-17 06:57:09 +01:00
Damien Arrachequesne
1f59e4526a [chore] Release 1.7.2 (#2783) 2016-12-11 02:04:21 +01:00
Damien Arrachequesne
0a7afa85ea [chore] Bump engine.io to version 1.8.2 (#2782) 2016-12-11 01:27:19 +01:00
Serhii Sol
1e31769062 [fix] Fixes socket.use error packet (#2772)
* fix(socket): Fixes socket.use error packet which drops nodejs due to nuances of Nodejs' EventEmitter

* fix(socket): Fixes missing error event on socket

* fix(socket): test fix, should listen for clientSocket instead of server socket

* minor update
2016-12-01 02:25:13 +01:00
Damien Arrachequesne
797c9a3498 [chore] Release 1.7.1 (#2768) 2016-11-28 00:06:18 +01:00
Damien Arrachequesne
4f93a0b429 [chore] Release 1.7.0 (#2767) 2016-11-27 08:27:39 +01:00
Damien Arrachequesne
3c98130f15 [chore] Update client location and serve minified file (#2766)
Following https://github.com/socketio/socket.io-client/pull/1025, the
output files are now generated in the dist directory.
2016-11-27 08:24:27 +01:00
Damien Arrachequesne
9c23308c6e [chore] Bump engine.io to version 1.8.1 (#2765) 2016-11-27 07:55:02 +01:00
Tal Beja
955e5e0d91 [feature] Add a local flag (#2628)
That new flag will prevent the adapter (redis) from publishing the emit to the pub/sub server.

When several instances of a server receive the same event from a third party (not from a client), each server instance broadcasts the event to all his clients. With the local flag, and the change in the redis adapter, each server instance send the event only to his client, so each client receive only one unique event.
2016-11-24 23:44:52 +01:00
Zhu Liang
0ef55b26d4 [feature] serve sourcemap for socket.io-client (#2482) 2016-11-24 23:39:43 +01:00
Robbie Ferguson
4d8e2d342c [docs] Fixed grammar issues in the README.md (#2159)
Added a few periods and commas which were missing. Pluralised the word
'parameter' where it was incorrectly specified to singular on line 281.
Very minor edit. No source code changed.
2016-11-23 15:05:15 +01:00
Jérémy Lal
d48f848bb4 [docs] Comment connected socket availability for adapters (#2081) 2016-11-23 15:00:07 +01:00
Damien Arrachequesne
57b386385e [chore] Release 1.6.0 (#2757) 2016-11-20 04:26:56 +01:00
Damien Arrachequesne
9e7567daee [chore] Bump socket.io-adapter to version 0.5.0 (#2756) 2016-11-20 03:51:57 +01:00
Damien Arrachequesne
2e36799b17 [chore] Bump engine.io to version 1.8.0 (#2755) 2016-11-20 03:50:32 +01:00
Damien Arrachequesne
9bb5e9de2f [chore] Bump debug to version 2.3.3 (#2754) 2016-11-20 03:42:09 +01:00
Luca Tabone
ff2c15de68 [perf] Minor code optimizations (#2219) 2016-11-18 02:03:06 +01:00
Damien Arrachequesne
a483658607 [example] Add disconnection/reconnection logs to the chat example (#2675) 2016-11-18 01:52:45 +01:00
Julian Grinblat
4c5dbd8824 [fix] Don't drop query variables on handshake (#2745)
Parameters passed during handshake, such as tokens, were being dropped.
2016-11-16 01:17:19 +01:00
mhmeadows63
e14a10b7ce [feature] add support for Server#close(callback) (#2748) 2016-11-15 21:52:56 +01:00
Alex
5a123beea5 [feature] Add support for socket middleware (#2306) 2016-11-05 01:51:29 +01:00
Philip YoonShin
2ed5f0f5fb [chore] Update year to 2016 (#2456) 2016-11-05 01:19:15 +01:00
Julian Grinblat
e9f980c475 [feature] Add support for all event emitter methods (#2601) 2016-11-05 01:14:21 +01:00
Jamie Davis
6f44f3a8ef [test] Fix leaking clientSocket (#2721)
Test 'should be able to close sio sending a port' defined a clientSocket
but didn't set 'reconnection: false'.

Now, the default behavior of a clientSocket is 'reconnection: true'.
As a result, the clientSocket was "leaked" from the test case
and seemed to intermittently connect to the servers in subsequent
test cases. This would cause other tests to timeout unexpectedly.

It's not clear to me why this would happen, since the test case
assigns a unique port number to the socket.
However, if you go into socket.io-client and assign and log
unique IDs to each socket, then you'll see that this clientSocket
shows up in other test cases if the reconnectionDelay strikes
unluckily.
2016-11-05 01:06:04 +01:00
Atanas Palavrov
04fc0f3677 [feature] Loading client script on demand. (#2567)
Support for the serving of the client script mess with packagers like
browserify, webpack. Especcialy in projects where it is not used at all.
This patch is workaround to avoid that problem in the cases when client
script is not served.
2016-10-30 14:22:00 +01:00
Kenton Varda
d026c00d05 [fix] Make ETag header comply with standard. (#2603)
The standard says that an ETag must be surrounded in double quotes:

https://tools.ietf.org/html/rfc7232#section-2.3

Although browsers tend to be lenient, omitting the quotes can confuse/break some kinds of proxies and other tools that demand compliant formatting. For example, Sandstorm.io enforces strict HTTP usage for security reasons and will block responses with invalid ETags.
2016-10-30 14:19:00 +01:00
11 changed files with 352 additions and 51 deletions

View File

@@ -1,4 +1,45 @@
1.7.3 / 2017-02-17
===================
* [chore] Bump engine.io to version 1.8.3
1.7.2 / 2016-12-11
===================
* [chore] Bump engine.io to version 1.8.2 (#2782)
* [fix] Fixes socket.use error packet (#2772)
1.7.1 / 2016-11-28
===================
1.7.0 / 2016-11-27
===================
* [docs] Comment connected socket availability for adapters (#2081)
* [docs] Fixed grammar issues in the README.md (#2159)
* [feature] serve sourcemap for socket.io-client (#2482)
* [feature] Add a `local` flag (#2628)
* [chore] Bump engine.io to version 1.8.1 (#2765)
* [chore] Update client location and serve minified file (#2766)
1.6.0 / 2016-11-20
==================
* [fix] Make ETag header comply with standard. (#2603)
* [feature] Loading client script on demand. (#2567)
* [test] Fix leaking clientSocket (#2721)
* [feature] Add support for all event emitter methods (#2601)
* [chore] Update year to 2016 (#2456)
* [feature] Add support for socket middleware (#2306)
* [feature] add support for Server#close(callback) (#2748)
* [fix] Don't drop query variables on handshake (#2745)
* [example] Add disconnection/reconnection logs to the chat example (#2675)
* [perf] Minor code optimizations (#2219)
* [chore] Bump debug to version 2.3.3 (#2754)
* [chore] Bump engine.io to version 1.8.0 (#2755)
* [chore] Bump socket.io-adapter to version 0.5.0 (#2756)
1.5.1 / 2016-10-24
==================

View File

@@ -1,6 +1,6 @@
(The MIT License)
Copyright (c) 2014-2015 Automattic <dev@cloudup.com>
Copyright (c) 2014-2016 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

View File

@@ -147,7 +147,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/expressjs/cors).
### Server#sockets:Namespace
@@ -183,7 +183,7 @@ server.listen(3000);
Initializes and retrieves the given `Namespace` by its pathname
identifier `nsp`.
If the namespace was already initialized it returns it right away.
If the namespace was already initialized it returns it immediately.
### Server#emit
@@ -198,9 +198,14 @@ server.listen(3000);
For other available methods, see `Namespace` below.
### Server#close
### Server#close([fn:Function])
Closes socket.io server
Closes socket.io server.
The optional `fn` is passed to the `server.close([callback])` method of the
core `net` module and is called on error or when all connections are closed.
The callback is expected to implement the common single argument `err`
signature (if any).
```js
var Server = require('socket.io');
@@ -280,7 +285,7 @@ server.listen(3000);
### Namespace#use(fn:Function):Namespace
Registers a middleware, which is a function that gets executed for
every incoming `Socket` and receives as parameter the socket and a
every incoming `Socket`, and receives as parameters the socket and a
function to optionally defer execution to the next registered
middleware.
@@ -304,6 +309,25 @@ server.listen(3000);
It should be noted the `Socket` doesn't relate directly to the actual
underlying TCP/IP `socket` and it is only the name of the class.
### Socket#use(fn:Function):Socket
Registers a middleware, which is a function that gets executed for
every incoming `Packet` and receives as parameter the packet and a
function to optionally defer execution to the next registered
middleware.
```js
var io = require('socket.io')();
io.on('connection', function(socket){
socket.use(function(packet, next){
if (packet.doge === true) return next();
next(new Error('Not a doge error'));
});
```
Errors passed to middleware callbacks are sent as special `error`
packets to clients.
### Socket#rooms:Object
A hash of strings identifying the rooms this client is in, indexed by
@@ -424,7 +448,7 @@ These are reserved events (along with `connect`, `newListener` and `removeListen
### Client
The `Client` class represents an incoming transport (engine.io)
connection. A `Client` can be associated with many multiplexed `Socket`
connection. A `Client` can be associated with many multiplexed `Socket`s
that belong to different `Namespace`s.
### Client#conn

View File

@@ -10,7 +10,7 @@ $ cd socket.io
$ npm install
$ cd examples/chat
$ npm install
$ node .
$ npm start
```
And point your browser to `http://localhost:3000`. Optionally, specify

View File

@@ -8,5 +8,8 @@
"license": "BSD",
"dependencies": {
"express": "4.13.4"
},
"scripts": {
"start": "node index.js"
}
}

View File

@@ -263,4 +263,20 @@ $(function() {
socket.on('stop typing', function (data) {
removeChatTyping(data);
});
socket.on('disconnect', function () {
log('you have been disconnected');
});
socket.on('reconnect', function () {
log('you have been reconnected');
if (username) {
socket.emit('add user', username);
}
});
socket.on('reconnect_error', function () {
log('attempt to reconnect has failed');
});
});

View File

@@ -9,6 +9,7 @@ var engine = require('engine.io');
var client = require('socket.io-client');
var clientVersion = require('socket.io-client/package').version;
var Client = require('./client');
var Emitter = require('events').EventEmitter;
var Namespace = require('./namespace');
var Adapter = require('socket.io-adapter');
var debug = require('debug')('socket.io:server');
@@ -24,7 +25,8 @@ module.exports = Server;
* Socket.IO client source.
*/
var clientSource = read(require.resolve('socket.io-client/socket.io.js'), 'utf-8');
var clientSource = undefined;
var clientSourceMap = undefined;
/**
* Server constructor.
@@ -94,6 +96,16 @@ Server.prototype.checkRequest = function(req, fn) {
Server.prototype.serveClient = function(v){
if (!arguments.length) return this._serveClient;
this._serveClient = v;
if (v && !clientSource) {
clientSource = read(require.resolve('socket.io-client/dist/socket.io.min.js'), 'utf-8');
try {
clientSourceMap = read(require.resolve('socket.io-client/dist/socket.io.js.map'), 'utf-8');
} catch(err) {
debug('could not load sourcemap file');
}
}
return this;
};
@@ -249,11 +261,14 @@ Server.prototype.attach = function(srv, opts){
Server.prototype.attachServe = function(srv){
debug('attaching client serving req handler');
var url = this._path + '/socket.io.js';
var urlMap = this._path + '/socket.io.js.map';
var evs = srv.listeners('request').slice(0);
var self = this;
srv.removeAllListeners('request');
srv.on('request', function(req, res) {
if (0 === req.url.indexOf(url)) {
if (0 === req.url.indexOf(urlMap)) {
self.serveMap(req, res);
} else if (0 === req.url.indexOf(url)) {
self.serve(req, res);
} else {
for (var i = 0; i < evs.length; i++) {
@@ -272,9 +287,13 @@ Server.prototype.attachServe = function(srv){
*/
Server.prototype.serve = function(req, res){
// Per the standard, ETags must be quoted:
// https://tools.ietf.org/html/rfc7232#section-2.3
var expectedEtag = '"' + clientVersion + '"';
var etag = req.headers['if-none-match'];
if (etag) {
if (clientVersion == etag) {
if (expectedEtag == etag) {
debug('serve client 304');
res.writeHead(304);
res.end();
@@ -284,11 +303,42 @@ Server.prototype.serve = function(req, res){
debug('serve client source');
res.setHeader('Content-Type', 'application/javascript');
res.setHeader('ETag', clientVersion);
res.setHeader('ETag', expectedEtag);
res.setHeader('X-SourceMap', 'socket.io.js.map');
res.writeHead(200);
res.end(clientSource);
};
/**
* Handles a request serving `/socket.io.js.map`
*
* @param {http.Request} req
* @param {http.Response} res
* @api private
*/
Server.prototype.serveMap = function(req, res){
// Per the standard, ETags must be quoted:
// https://tools.ietf.org/html/rfc7232#section-2.3
var expectedEtag = '"' + clientVersion + '"';
var etag = req.headers['if-none-match'];
if (etag) {
if (expectedEtag == etag) {
debug('serve client 304');
res.writeHead(304);
res.end();
return;
}
}
debug('serve client sourcemap');
res.setHeader('Content-Type', 'application/json');
res.setHeader('ETag', expectedEtag);
res.writeHead(200);
res.end(clientSourceMap);
};
/**
* Binds socket.io to an engine.io instance.
*
@@ -342,10 +392,11 @@ Server.prototype.of = function(name, fn){
/**
* Closes server connection
*
* @param {Function} [fn] optional, called as `fn([err])` on error OR all conns closed
* @api public
*/
Server.prototype.close = function(){
Server.prototype.close = function(fn){
for (var id in this.nsps['/'].sockets) {
if (this.nsps['/'].sockets.hasOwnProperty(id)) {
this.nsps['/'].sockets[id].onclose();
@@ -354,8 +405,10 @@ Server.prototype.close = function(){
this.engine.close();
if(this.httpServer){
this.httpServer.close();
if (this.httpServer) {
this.httpServer.close(fn);
} else {
fn && fn();
}
};
@@ -363,18 +416,23 @@ Server.prototype.close = function(){
* Expose main namespace (/).
*/
['on', 'to', 'in', 'use', 'emit', 'send', 'write', 'clients', 'compress'].forEach(function(fn){
var emitterMethods = Object.keys(Emitter.prototype).filter(function(key){
return typeof Emitter.prototype[key] === 'function';
});
emitterMethods.concat(['to', 'in', 'use', 'send', 'write', 'clients', 'compress']).forEach(function(fn){
Server.prototype[fn] = function(){
var nsp = this.sockets[fn];
return nsp.apply(this.sockets, arguments);
return this.sockets[fn].apply(this.sockets, arguments);
};
});
Namespace.flags.forEach(function(flag){
Server.prototype.__defineGetter__(flag, function(){
this.sockets.flags = this.sockets.flags || {};
this.sockets.flags[flag] = true;
return this;
Object.defineProperty(Server.prototype, flag, {
get: function() {
this.sockets.flags = this.sockets.flags || {};
this.sockets.flags[flag] = true;
return this;
}
});
});

View File

@@ -31,7 +31,8 @@ exports.events = [
exports.flags = [
'json',
'volatile'
'volatile',
'local'
];
/**
@@ -69,10 +70,12 @@ Namespace.prototype.__proto__ = Emitter.prototype;
*/
exports.flags.forEach(function(flag){
Namespace.prototype.__defineGetter__(flag, function(){
this.flags = this.flags || {};
this.flags[flag] = true;
return this;
Object.defineProperty(Namespace.prototype, flag, {
get: function() {
this.flags = this.flags || {};
this.flags[flag] = true;
return this;
}
});
});
@@ -137,7 +140,7 @@ Namespace.prototype.run = function(socket, fn){
*/
Namespace.prototype.to =
Namespace.prototype['in'] = function(name){
Namespace.prototype.in = function(name){
this.rooms = this.rooms || [];
if (!~this.rooms.indexOf(name)) this.rooms.push(name);
return this;

View File

@@ -8,6 +8,7 @@ var parser = require('socket.io-parser');
var url = require('url');
var debug = require('debug')('socket.io:socket');
var hasBin = require('has-binary');
var assign = require('object-assign');
/**
* Module exports.
@@ -68,6 +69,7 @@ function Socket(nsp, client, query){
this.connected = true;
this.disconnected = false;
this.handshake = this.buildHandshake(query);
this.fns = [];
}
/**
@@ -81,10 +83,12 @@ Socket.prototype.__proto__ = Emitter.prototype;
*/
flags.forEach(function(flag){
Socket.prototype.__defineGetter__(flag, function(){
this.flags = this.flags || {};
this.flags[flag] = true;
return this;
Object.defineProperty(Socket.prototype, flag, {
get: function() {
this.flags = this.flags || {};
this.flags[flag] = true;
return this;
}
});
});
@@ -94,8 +98,10 @@ flags.forEach(function(flag){
* @api public
*/
Socket.prototype.__defineGetter__('request', function(){
return this.conn.request;
Object.defineProperty(Socket.prototype, 'request', {
get: function() {
return this.conn.request;
}
});
/**
@@ -109,13 +115,7 @@ Socket.prototype.buildHandshake = function(query){
function buildQuery(){
var requestQuery = url.parse(self.request.url, true).query;
//if socket-specific query exist, replace query strings in requestQuery
if(query){
query.t = requestQuery.t;
query.EIO = requestQuery.EIO;
query.transport = requestQuery.transport;
return query;
}
return requestQuery || {};
return assign({}, query, requestQuery);
}
return {
headers: this.request.headers,
@@ -283,6 +283,8 @@ Socket.prototype.leaveAll = function(){
/**
* Called by `Namespace` upon successful
* middleware execution (ie: authorization).
* Socket is added to namespace array before
* call to join, so adapters can access it.
*
* @api private
*/
@@ -345,7 +347,7 @@ Socket.prototype.onevent = function(packet){
args.push(this.ack(packet.id));
}
emit.apply(this, args);
this.dispatch(args);
};
/**
@@ -482,3 +484,63 @@ Socket.prototype.compress = function(compress){
this.flags.compress = compress;
return this;
};
/**
* Dispatch incoming event to socket listeners.
*
* @param {Array} event that will get emitted
* @api private
*/
Socket.prototype.dispatch = function(event){
debug('dispatching an event %j', event);
var self = this;
this.run(event, function(err){
process.nextTick(function(){
if (err) {
return self.error(err.data || err.message);
}
emit.apply(self, event);
});
});
}
/**
* Sets up socket middleware.
*
* @param {Function} middleware function (event, next)
* @return {Socket} self
* @api public
*/
Socket.prototype.use = function(fn){
this.fns.push(fn);
return this;
};
/**
* Executes the middleware for an incoming event.
*
* @param {Array} event that will get emitted
* @param {Function} last fn call in the middleware
* @api private
*/
Socket.prototype.run = function(event, fn){
var fns = this.fns.slice(0);
if (!fns.length) return fn(null);
function run(i){
fns[i](event, function(err){
// upon error, short-circuit
if (err) return fn(err);
// if no middleware left, summon callback
if (!fns[i + 1]) return fn(null);
// go on to next
run(i + 1);
});
}
run(0);
};

View File

@@ -1,6 +1,6 @@
{
"name": "socket.io",
"version": "1.5.1",
"version": "1.7.3",
"description": "node.js realtime framework server",
"keywords": [
"realtime",
@@ -24,12 +24,13 @@
"test": "gulp test"
},
"dependencies": {
"engine.io": "1.7.2",
"socket.io-parser": "2.3.1",
"socket.io-client": "1.5.1",
"socket.io-adapter": "0.4.0",
"debug": "2.3.3",
"engine.io": "1.8.3",
"has-binary": "0.1.7",
"debug": "2.2.0"
"object-assign": "4.1.0",
"socket.io-adapter": "0.5.0",
"socket.io-client": "1.7.3",
"socket.io-parser": "2.3.1"
},
"devDependencies": {
"babel-preset-es2015": "6.3.13",

View File

@@ -167,7 +167,7 @@ describe('socket.io', function(){
if (err) return done(err);
var ctype = res.headers['content-type'];
expect(ctype).to.be('application/javascript');
expect(res.headers.etag).to.be(clientVersion);
expect(res.headers.etag).to.be('"' + clientVersion + '"');
expect(res.text).to.match(/engine\.io/);
expect(res.status).to.be(200);
done();
@@ -179,7 +179,7 @@ describe('socket.io', function(){
io(srv);
request(srv)
.get('/socket.io/socket.io.js')
.set('If-None-Match', clientVersion)
.set('If-None-Match', '"' + clientVersion + '"')
.end(function(err, res){
if (err) return done(err);
expect(res.statusCode).to.be(304);
@@ -399,7 +399,7 @@ describe('socket.io', function(){
var net = require('net');
var server = net.createServer();
var clientSocket = ioc('ws://0.0.0.0:' + PORT);
var clientSocket = ioc('ws://0.0.0.0:' + PORT, { reconnection: false });
clientSocket.on('disconnect', function init() {
expect(Object.keys(sio.nsps['/'].sockets).length).to.equal(0);
@@ -2232,4 +2232,97 @@ describe('socket.io', function(){
});
});
});
describe('socket middleware', function(done){
var Socket = require('../lib/socket');
it('should call functions', function(done){
var srv = http();
var sio = io(srv);
var run = 0;
srv.listen(function(){
var socket = client(srv, { multiplex: false });
socket.emit('join', 'woot');
sio.on('connection', function(socket){
socket.use(function(event, next){
expect(event).to.eql(['join', 'woot']);
event.unshift('wrap');
run++;
next();
});
socket.use(function(event, next){
expect(event).to.eql(['wrap', 'join', 'woot']);
run++;
next();
});
socket.on('wrap', function(data1, data2){
expect(data1).to.be('join');
expect(data2).to.be('woot');
expect(run).to.be(2);
done();
});
});
});
});
it('should pass errors', function(done){
var srv = http();
var sio = io(srv);
srv.listen(function(){
var clientSocket = client(srv, { multiplex: false });
clientSocket.emit('join', 'woot');
clientSocket.on('error', function(err){
expect(err).to.be('Authentication error');
done();
});
sio.on('connection', function(socket){
socket.use(function(event, next){
next(new Error('Authentication error'));
});
socket.use(function(event, next){
done(new Error('nope'));
});
socket.on('join', function(){
done(new Error('nope'));
});
});
});
});
it('should pass `data` of error object', function(done){
var srv = http();
var sio = io(srv);
srv.listen(function(){
var clientSocket = client(srv, { multiplex: false });
clientSocket.emit('join', 'woot');
clientSocket.on('error', function(err){
expect(err).to.eql({ a: 'b', c: 3 });
done();
});
sio.on('connection', function(socket){
socket.use(function(event, next){
var err = new Error('Authentication error');
err.data = { a: 'b', c: 3 };
next(err);
});
socket.on('join', function(){
done(new Error('nope'));
});
});
});
});
});
});