Skip to content

Commit

Permalink
Add the "loopback" flag. (#1)
Browse files Browse the repository at this point in the history
The "loopback" flag is used to emit messages on the local endpoint of
a socket; e.g., allowing the server to send a message to the server's
handler directly.

This is especially useful in conjunction with rooms, as it allows the
server to proactively trigger behavior for each socket in a room without
having to maintain a separate list of said sockets or relying on the
client endpoint to trigger the behavior.
  • Loading branch information
ripfester authored and Todd Harris committed May 2, 2018
1 parent 1decae3 commit 913c9ab
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 12 deletions.
32 changes: 23 additions & 9 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,24 @@ Client.prototype.packet = function(packet, opts){
opts = opts || {};
var self = this;

if ('open' != this.conn.readyState) {
debug('ignoring packet write %j', packet);
return;
}

if (opts.loopback) {
debug('writing loopback packet %j', packet);
// handle loopback packets as though they came in off the wire.
if (opts.preEncoded) { // a broadcast pre-encodes a packet
for (var i = 0; i < packet.length; i++) {
this.decoder.add(packet[i]);
}
} else { // no broadcasting, no need to decode
this.ondecoded(packet);
}
return;
}

// this writes to the actual connection
function writeToEngine(encodedPackets) {
if (opts.volatile && !self.conn.transport.writable) return;
Expand All @@ -171,15 +189,11 @@ Client.prototype.packet = function(packet, opts){
}
}

if ('open' == this.conn.readyState) {
debug('writing packet %j', packet);
if (!opts.preEncoded) { // not broadcasting, need to encode
this.encoder.encode(packet, writeToEngine); // encode, then write results to engine
} else { // a broadcast pre-encodes a packet
writeToEngine(packet);
}
} else {
debug('ignoring packet write %j', packet);
debug('writing packet %j', packet);
if (!opts.preEncoded) { // not broadcasting, need to encode
this.encoder.encode(packet, writeToEngine); // encode, then write results to engine
} else { // a broadcast pre-encodes a packet
writeToEngine(packet);
}
};

Expand Down
1 change: 1 addition & 0 deletions lib/namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ exports.events = [
*/

exports.flags = [
'loopback',
'json',
'volatile',
'local'
Expand Down
7 changes: 6 additions & 1 deletion lib/socket.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ exports.events = [
*/

var flags = [
'loopback',
'json',
'volatile',
'broadcast',
Expand Down Expand Up @@ -155,6 +156,10 @@ Socket.prototype.emit = function(ev){
throw new Error('Callbacks are not supported when broadcasting');
}

if (this.flags.loopback) {
throw new Error('Callbacks are not supported when emitting loopback messages');
}

debug('emitting packet with ack id %d', this.nsp.ids);
this.acks[this.nsp.ids] = args.pop();
packet.id = this.nsp.ids++;
Expand Down Expand Up @@ -311,7 +316,7 @@ Socket.prototype.onconnect = function(){
};

/**
* Called with each packet. Called by `Client`.
* Called with each packet. Called by `Client` and `emit` with `loopback` flag.
*
* @param {Object} packet
* @api private
Expand Down
33 changes: 31 additions & 2 deletions test/socket.io.js
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ describe('socket.io', function(){
});
});
});

it('should not reuse same-namespace connections', function(done){
var srv = http();
var sio = io(srv);
Expand Down Expand Up @@ -837,6 +837,35 @@ describe('socket.io', function(){
}, 500);
});

it('should emit loopback messages locally', function(done) {
var srv = http();
var sio = io(srv);

var local_counter = 0;
var remote_counter = 0;
srv.listen(function(){
sio.of('/chat').on('connection', function(s){
setTimeout(function() {
sio.of('/chat').loopback.emit('ev', 'data');
}, 50);
s.on('ev', function() {
local_counter++;
});
});

var socket = client(srv, '/chat');
socket.on('ev', function() {
remote_counter++;
});
});

setTimeout(function() {
expect(local_counter).to.be(1);
expect(remote_counter).to.be(0);
done();
}, 500);
});

it('should emit volatile event', function(done) {
var srv = http();
var sio = io(srv);
Expand Down Expand Up @@ -1608,7 +1637,7 @@ describe('socket.io', function(){
});
});
});

it('should see query parameters sent from secondary namespace connections in handshake object', function(done){
var srv = http();
var sio = io(srv);
Expand Down

0 comments on commit 913c9ab

Please sign in to comment.