mirror of
https://github.com/socketio/socket.io.git
synced 2026-04-30 03:00:39 -04:00
Source of bug: after connection to nsp '/', the socket's connectBuffer was being deleted. On attempt to reconnect to a different namespace, the connect buffer was deleted and we attempted to push to it. Instead of the deleting the connect buffer, it is now emptied.
221 lines
4.5 KiB
JavaScript
221 lines
4.5 KiB
JavaScript
|
|
/**
|
|
* Module dependencies.
|
|
*/
|
|
|
|
var parser = require('socket.io-parser');
|
|
var debug = require('debug')('socket.io:client');
|
|
|
|
/**
|
|
* Module exports.
|
|
*/
|
|
|
|
module.exports = Client;
|
|
|
|
/**
|
|
* Client constructor.
|
|
*
|
|
* @param {Server} server instance
|
|
* @param {Socket} connection
|
|
* @api private
|
|
*/
|
|
|
|
function Client(server, conn){
|
|
this.server = server;
|
|
this.conn = conn;
|
|
this.encoder = new parser.Encoder();
|
|
this.decoder = new parser.Decoder();
|
|
this.id = conn.id;
|
|
this.request = conn.request;
|
|
this.setup();
|
|
this.sockets = [];
|
|
this.nsps = {};
|
|
this.connectBuffer = [];
|
|
}
|
|
|
|
/**
|
|
* Sets up event listeners.
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.setup = function(){
|
|
this.onclose = this.onclose.bind(this);
|
|
this.ondata = this.ondata.bind(this);
|
|
this.ondecoded = this.ondecoded.bind(this);
|
|
this.decoder.on('decoded', this.ondecoded);
|
|
this.conn.on('data', this.ondata);
|
|
this.conn.on('close', this.onclose);
|
|
};
|
|
|
|
/**
|
|
* Connects a client to a namespace.
|
|
*
|
|
* @param {String} namespace name
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.connect = function(name){
|
|
debug('connecting to namespace %s', name);
|
|
var nsp = this.server.of(name);
|
|
if ('/' != name && !this.nsps['/']) {
|
|
this.connectBuffer.push(name);
|
|
return;
|
|
}
|
|
|
|
var self = this;
|
|
var socket = nsp.add(this, function(){
|
|
self.sockets.push(socket);
|
|
self.nsps[nsp.name] = socket;
|
|
|
|
if ('/' == nsp.name && self.connectBuffer.length > 0) {
|
|
self.connectBuffer.forEach(self.connect, self);
|
|
self.connectBuffer = [];
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Disconnects from all namespaces and closes transport.
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
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();
|
|
}
|
|
this.close();
|
|
};
|
|
|
|
/**
|
|
* Removes a socket. Called by each `Socket`.
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
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);
|
|
delete this.nsps[nsp];
|
|
} else {
|
|
debug('ignoring remove for %s', socket.id);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Closes the underlying connection.
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.close = function(){
|
|
if ('open' == this.conn.readyState) {
|
|
debug('forcing transport close');
|
|
this.conn.close();
|
|
this.onclose('forced server close');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Writes a packet to the transport.
|
|
*
|
|
* @param {Object} packet object
|
|
* @param {Boolean} whether packet is already encoded
|
|
* @param {Boolean} whether packet is volatile
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.packet = function(packet, preEncoded, volatile){
|
|
var self = this;
|
|
|
|
// this writes to the actual connection
|
|
function writeToEngine(encodedPackets) {
|
|
if (volatile && !self.conn.transport.writable) return;
|
|
for (var i = 0; i < encodedPackets.length; i++) {
|
|
self.conn.write(encodedPackets[i]);
|
|
}
|
|
}
|
|
|
|
if ('open' == this.conn.readyState) {
|
|
debug('writing packet %j', packet);
|
|
if(!preEncoded) { // not broadcasting, need to encode
|
|
this.encoder.encode(packet, function (encodedPackets) { // encode, then write results to engine
|
|
writeToEngine(encodedPackets);
|
|
});
|
|
} else { // a broadcast pre-encodes a packet
|
|
writeToEngine(packet);
|
|
}
|
|
} else {
|
|
debug('ignoring packet write %j', packet);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called with incoming transport data.
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.ondata = function(data){
|
|
this.decoder.add(data);
|
|
};
|
|
|
|
/**
|
|
* Called when parser fully decodes a packet.
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.ondecoded = function(packet) {
|
|
if (parser.CONNECT == packet.type) {
|
|
this.connect(packet.nsp);
|
|
} else {
|
|
var socket = this.nsps[packet.nsp];
|
|
if (socket) {
|
|
socket.onpacket(packet);
|
|
} else {
|
|
debug('no socket for namespace %s', packet.nsp);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Called upon transport close.
|
|
*
|
|
* @param {String} reason
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.onclose = function(reason){
|
|
debug('client close with reason %s', reason);
|
|
|
|
// ignore a potential subsequent `close` event
|
|
this.destroy();
|
|
|
|
// `nsps` and `sockets` are cleaned up seamlessly
|
|
var socket;
|
|
while (socket = this.sockets.shift()) {
|
|
socket.onclose(reason);
|
|
}
|
|
|
|
this.decoder.destroy(); // clean up decoder
|
|
};
|
|
|
|
/**
|
|
* Cleans up event listeners.
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
Client.prototype.destroy = function(){
|
|
this.conn.removeListener('data', this.ondata);
|
|
this.conn.removeListener('close', this.onclose);
|
|
this.decoder.removeListener('decoded', this.ondecoded);
|
|
};
|