Skip to content

Commit 5a123be

Browse files
Alexdarrachequesne
authored andcommitted
[feature] Add support for socket middleware (#2306)
1 parent 2ed5f0f commit 5a123be

File tree

3 files changed

+172
-1
lines changed

3 files changed

+172
-1
lines changed

Readme.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,25 @@ server.listen(3000);
304304
It should be noted the `Socket` doesn't relate directly to the actual
305305
underlying TCP/IP `socket` and it is only the name of the class.
306306

307+
### Socket#use(fn:Function):Socket
308+
309+
Registers a middleware, which is a function that gets executed for
310+
every incoming `Packet` and receives as parameter the packet and a
311+
function to optionally defer execution to the next registered
312+
middleware.
313+
314+
```js
315+
var io = require('socket.io')();
316+
io.on('connection', function(socket){
317+
socket.use(function(packet, next){
318+
if (packet.doge === true) return next();
319+
next(new Error('Not a doge error'));
320+
});
321+
```
322+
323+
Errors passed to middleware callbacks are sent as special `error`
324+
packets to clients.
325+
307326
### Socket#rooms:Object
308327
309328
A hash of strings identifying the rooms this client is in, indexed by

lib/socket.js

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ function Socket(nsp, client, query){
6868
this.connected = true;
6969
this.disconnected = false;
7070
this.handshake = this.buildHandshake(query);
71+
this.fns = [];
7172
}
7273

7374
/**
@@ -345,7 +346,7 @@ Socket.prototype.onevent = function(packet){
345346
args.push(this.ack(packet.id));
346347
}
347348

348-
emit.apply(this, args);
349+
this.dispatch(args);
349350
};
350351

351352
/**
@@ -482,3 +483,63 @@ Socket.prototype.compress = function(compress){
482483
this.flags.compress = compress;
483484
return this;
484485
};
486+
487+
/**
488+
* Dispatch incoming event to socket listeners.
489+
*
490+
* @param {Array} event that will get emitted
491+
* @api private
492+
*/
493+
494+
Socket.prototype.dispatch = function(event){
495+
debug('dispatching an event %j', event);
496+
var self = this;
497+
this.run(event, function(err){
498+
process.nextTick(function(){
499+
if (err) {
500+
return self.emit('error', err.data || err.message);
501+
}
502+
emit.apply(self, event);
503+
});
504+
});
505+
}
506+
507+
/**
508+
* Sets up socket middleware.
509+
*
510+
* @param {Function} middleware function (event, next)
511+
* @return {Socket} self
512+
* @api public
513+
*/
514+
515+
Socket.prototype.use = function(fn){
516+
this.fns.push(fn);
517+
return this;
518+
};
519+
520+
/**
521+
* Executes the middleware for an incoming event.
522+
*
523+
* @param {Array} event that will get emitted
524+
* @param {Function} last fn call in the middleware
525+
* @api private
526+
*/
527+
Socket.prototype.run = function(event, fn){
528+
var fns = this.fns.slice(0);
529+
if (!fns.length) return fn(null);
530+
531+
function run(i){
532+
fns[i](event, function(err){
533+
// upon error, short-circuit
534+
if (err) return fn(err);
535+
536+
// if no middleware left, summon callback
537+
if (!fns[i + 1]) return fn(null);
538+
539+
// go on to next
540+
run(i + 1);
541+
});
542+
}
543+
544+
run(0);
545+
};

test/socket.io.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2232,4 +2232,95 @@ describe('socket.io', function(){
22322232
});
22332233
});
22342234
});
2235+
2236+
describe('socket middleware', function(done){
2237+
var Socket = require('../lib/socket');
2238+
2239+
it('should call functions', function(done){
2240+
var srv = http();
2241+
var sio = io(srv);
2242+
var run = 0;
2243+
2244+
srv.listen(function(){
2245+
var socket = client(srv, { multiplex: false });
2246+
2247+
socket.emit('join', 'woot');
2248+
2249+
sio.on('connection', function(socket){
2250+
socket.use(function(event, next){
2251+
expect(event).to.eql(['join', 'woot']);
2252+
event.unshift('wrap');
2253+
run++;
2254+
next();
2255+
});
2256+
socket.use(function(event, next){
2257+
expect(event).to.eql(['wrap', 'join', 'woot']);
2258+
run++;
2259+
next();
2260+
});
2261+
socket.on('wrap', function(data1, data2){
2262+
expect(data1).to.be('join');
2263+
expect(data2).to.be('woot');
2264+
expect(run).to.be(2);
2265+
done();
2266+
});
2267+
});
2268+
});
2269+
});
2270+
2271+
it('should pass errors', function(done){
2272+
var srv = http();
2273+
var sio = io(srv);
2274+
2275+
srv.listen(function(){
2276+
var socket = client(srv, { multiplex: false });
2277+
2278+
socket.emit('join', 'woot');
2279+
2280+
sio.on('connection', function(socket){
2281+
socket.use(function(event, next){
2282+
next(new Error('Authentication error'));
2283+
});
2284+
socket.use(function(event, next){
2285+
done(new Error('nope'));
2286+
});
2287+
2288+
socket.on('join', function(){
2289+
done(new Error('nope'));
2290+
});
2291+
socket.on('error', function(err){
2292+
expect(err).to.be('Authentication error');
2293+
done();
2294+
});
2295+
});
2296+
});
2297+
});
2298+
2299+
it('should pass `data` of error object', function(done){
2300+
var srv = http();
2301+
var sio = io(srv);
2302+
2303+
srv.listen(function(){
2304+
var socket = client(srv, { multiplex: false });
2305+
2306+
socket.emit('join', 'woot');
2307+
2308+
sio.on('connection', function(socket){
2309+
socket.use(function(event, next){
2310+
var err = new Error('Authentication error');
2311+
err.data = { a: 'b', c: 3 };
2312+
next(err);
2313+
});
2314+
2315+
socket.on('join', function(){
2316+
done(new Error('nope'));
2317+
});
2318+
socket.on('error', function(err){
2319+
expect(err).to.eql({ a: 'b', c: 3 });
2320+
done();
2321+
});
2322+
});
2323+
});
2324+
});
2325+
});
22352326
});

0 commit comments

Comments
 (0)