Files
socket.io/lib/client.js
Guillermo Rauch ce6fa8611b Path restructuring
2010-07-19 15:13:11 -07:00

152 lines
4.0 KiB
JavaScript

var urlparse = require('url').parse,
frame = '\ufffdm\ufffd',
Client = module.exports = function(listener, req, res, options, head){
this.listener = listener;
this.options({
timeout: 12000,
closeTimeout: 0
}, options);
this.connections = 0;
this.connected = false;
this._heartbeats = 0;
this.upgradeHead = head;
this._onConnect(req, res);
};
sys.inherits(Client, require('./utils').options);
Client.prototype.send = function(message){
if (!this.connected || !(this.connection.readyState === 'open' ||
this.connection.readyState === 'writeOnly')){
return this._queue(message);
}
this._write(this._encode(message));
return this;
};
Client.prototype.broadcast = function(message){
if (!('sessionId' in this)) return this;
this.listener.broadcast(message, this.sessionId);
return this;
};
Client.prototype._onMessage = function(data){
var messages = this._decode(data);
if (messages === false) return this.listener.options.log('Bad message received from client ' + this.sessionId);
for (var i = 0, l = messages.length; i < l; i++){
if (messages[i].substr(0, 3) == '\ufffdh\ufffd'){
return this._onHeartbeat(data.substr(3));
}
this.listener._onClientMessage(messages[i], this);
}
};
Client.prototype._onConnect = function(req, res){
var self = this;
this.request = req;
this.response = res;
this.connection = this.request.connection;
if (this._disconnectTimeout) clearTimeout(this._disconnectTimeout);
};
Client.prototype._encode = function(messages){
var ret = '', message,
messages = messages instanceof Array ? messages : [];
for (var i = 0, l = messages.length; i < l; i++){
message = String(messages[i]);
ret += message.length + frame + message;
}
return frame + ret;
};
Client.prototype._decode = function(data){
if (data.substr(0, 3) !== frame) return false;
var messages = [];
do {
for (var i = 0, n, number = '';; i++){
var n = data.substr(i, 1);
if (n === frame){
number = Number(number);
break;
}
number += n;
}
messages.push(data.substr(i, i + number)); // here
data = data.substr(i + number);
} while(data !== '');
return messages;
};
Client.prototype._payload = function(){
var payload = [];
this.connections++;
this.connected = true;
if (!this.handshaked){
this._generateSessionId();
payload.push(this.sessionId);
this.handshaked = true;
}
payload = payload.concat(this._writeQueue || []);
this._writeQueue = [];
if (payload.length) this._write(this._encode(payload));
if (this.connections === 1) this.listener._onClientConnect(this);
if (this.listener.options.timeout) this._heartbeat();
};
Client.prototype._heartbeat = function(){
var self = this;
this.send('\ufffdh\ufffd' + ++this._heartbeats);
this._heartbitTimeout = setTimeout(function(){
self.close();
}, this.listener.options.timeout);
};
Client.prototype._onHeartbeat = function(h){
if (h === this._heartbeats) clearTimeout(this._heartbitTimeout);
};
Client.prototype._onClose = function(){
var self = this;
clearTimeout(this._heartbeatTimeout);
this.connected = false;
this._disconnectTimeout = setTimeout(function(){
self._onDisconnect();
}, this.options.closeTimeout);
};
Client.prototype._onDisconnect = function(){
if (!this.finalized){
this._writeQueue = [];
this.connected = false;
this.finalized = true;
if (this.handshaked) this.listener._onClientDisconnect(this);
}
};
Client.prototype._queue = function(message){
if (!('_writeQueue' in this)){
this._writeQueue = [];
}
this._writeQueue.push(message);
return this;
};
Client.prototype._generateSessionId = function(){
if (this.sessionId) return this.listener.options.log('This client already has a session id');
this.sessionId = Math.random().toString().substr(2);
return this;
};
Client.prototype._verifyOrigin = function(origin){
var parts = urlparse(origin), origins = this.listener.options.origins;
return origins.indexOf('*:*') !== -1 ||
origins.indexOf(parts.host + ':' + parts.port) !== -1 ||
origins.indexOf(parts.host + ':*') !== -1 ||
origins.indexOf('*:' + parts.port) !== -1;
};