Skip to content

Commit e7fea94

Browse files
ripfesterTodd Harris
authored andcommitted
Add the "loopback" flag. (#1)
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.
1 parent 1decae3 commit e7fea94

File tree

4 files changed

+65
-5
lines changed

4 files changed

+65
-5
lines changed

lib/client.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ Client.prototype.packet = function(packet, opts){
163163
opts = opts || {};
164164
var self = this;
165165

166+
if ('open' != this.conn.readyState) {
167+
debug('ignoring packet write %j', packet);
168+
return;
169+
}
170+
166171
// this writes to the actual connection
167172
function writeToEngine(encodedPackets) {
168173
if (opts.volatile && !self.conn.transport.writable) return;
@@ -171,15 +176,23 @@ Client.prototype.packet = function(packet, opts){
171176
}
172177
}
173178

174-
if ('open' == this.conn.readyState) {
179+
if (opts.loopback) {
180+
debug('writing loopback packet %j', packet);
181+
// handle loopback packets as though they came in off the wire.
182+
if (opts.preEncoded) { // a broadcast pre-encodes a packet
183+
for (var i = 0; i < packet.length; i++) {
184+
this.decoder.add(packet[i]);
185+
}
186+
} else { // no broadcasting, no need to decode
187+
this.ondecoded(packet);
188+
}
189+
} else {
175190
debug('writing packet %j', packet);
176191
if (!opts.preEncoded) { // not broadcasting, need to encode
177192
this.encoder.encode(packet, writeToEngine); // encode, then write results to engine
178193
} else { // a broadcast pre-encodes a packet
179194
writeToEngine(packet);
180195
}
181-
} else {
182-
debug('ignoring packet write %j', packet);
183196
}
184197
};
185198

lib/namespace.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ exports.events = [
3030
*/
3131

3232
exports.flags = [
33+
'loopback',
3334
'json',
3435
'volatile',
3536
'local'

lib/socket.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ exports.events = [
3737
*/
3838

3939
var flags = [
40+
'loopback',
4041
'json',
4142
'volatile',
4243
'broadcast',
@@ -155,6 +156,10 @@ Socket.prototype.emit = function(ev){
155156
throw new Error('Callbacks are not supported when broadcasting');
156157
}
157158

159+
if (this.flags.loopback) {
160+
throw new Error('Callbacks are not supported when emitting loopback messages');
161+
}
162+
158163
debug('emitting packet with ack id %d', this.nsp.ids);
159164
this.acks[this.nsp.ids] = args.pop();
160165
packet.id = this.nsp.ids++;

test/socket.io.js

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,7 @@ describe('socket.io', function(){
677677
});
678678
});
679679
});
680-
680+
681681
it('should not reuse same-namespace connections', function(done){
682682
var srv = http();
683683
var sio = io(srv);
@@ -837,6 +837,47 @@ describe('socket.io', function(){
837837
}, 500);
838838
});
839839

840+
it('should emit loopback messages locally', function(done) {
841+
var srv = http();
842+
var sio = io(srv);
843+
844+
var local_counter = 0;
845+
var remote_counter = 0;
846+
srv.listen(function(){
847+
sio.of('/chat').on('connection', function(s){
848+
setTimeout(function() {
849+
// test broadcast and local.
850+
sio.of('/chat').loopback.emit('ev1', 'data1');
851+
s.loopback.emit('ev2', 'data2');
852+
}, 50);
853+
s.on('ev1', function(data) {
854+
if (data === 'data1') {
855+
local_counter++;
856+
}
857+
});
858+
s.on('ev2', function(data) {
859+
if (data === 'data2') {
860+
local_counter++;
861+
}
862+
});
863+
});
864+
865+
var socket = client(srv, '/chat');
866+
socket.on('ev1', function() {
867+
remote_counter++;
868+
});
869+
socket.on('ev2', function() {
870+
remote_counter++;
871+
});
872+
});
873+
874+
setTimeout(function() {
875+
expect(local_counter).to.be(2);
876+
expect(remote_counter).to.be(0);
877+
done();
878+
}, 500);
879+
});
880+
840881
it('should emit volatile event', function(done) {
841882
var srv = http();
842883
var sio = io(srv);
@@ -1608,7 +1649,7 @@ describe('socket.io', function(){
16081649
});
16091650
});
16101651
});
1611-
1652+
16121653
it('should see query parameters sent from secondary namespace connections in handshake object', function(done){
16131654
var srv = http();
16141655
var sio = io(srv);

0 commit comments

Comments
 (0)