Files
socket.io/lib/transport.js
2010-10-20 19:21:31 -03:00

141 lines
3.6 KiB
JavaScript

/**
* Socket.IO client
*
* @author Guillermo Rauch <guillermo@learnboost.com>
* @license The MIT license.
* @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
*/
// abstract
(function(){
var frame = '~m~',
stringify = function(message){
if (Object.prototype.toString.call(message) == '[object Object]'){
if (!('JSON' in window)){
if ('console' in window && console.error) console.error('Trying to encode as JSON, but JSON.stringify is missing.');
return '{ "$error": "Invalid message" }';
}
return '~j~' + JSON.stringify(message);
} else {
return String(message);
}
};
Transport = io.Transport = function(base, options){
this.base = base;
this.options = {
timeout: 15000 // based on heartbeat interval default
};
io.util.merge(this.options, options);
};
Transport.prototype.send = function(){
throw new Error('Missing send() implementation');
};
Transport.prototype.connect = function(){
throw new Error('Missing connect() implementation');
};
Transport.prototype.disconnect = function(){
throw new Error('Missing disconnect() implementation');
};
Transport.prototype._encode = function(messages){
var ret = '', message,
messages = io.util.isArray(messages) ? messages : [messages];
for (var i = 0, l = messages.length; i < l; i++){
message = messages[i] === null || messages[i] === undefined ? '' : stringify(messages[i]);
ret += frame + message.length + frame + message;
}
return ret;
};
Transport.prototype._decode = function(data){
var messages = [], number, n;
do {
if (data.substr(0, 3) !== frame) return messages;
data = data.substr(3);
number = '', n = '';
for (var i = 0, l = data.length; i < l; i++){
n = Number(data.substr(i, 1));
if (data.substr(i, 1) == n){
number += n;
} else {
data = data.substr(number.length + frame.length);
number = Number(number);
break;
}
}
messages.push(data.substr(0, number)); // here
data = data.substr(number);
} while(data !== '');
return messages;
};
Transport.prototype._onData = function(data){
this._setTimeout();
var msgs = this._decode(data);
if (msgs && msgs.length){
for (var i = 0, l = msgs.length; i < l; i++){
this._onMessage(msgs[i]);
}
}
};
Transport.prototype._setTimeout = function(){
var self = this;
if (this._timeout) clearTimeout(this._timeout);
this._timeout = setTimeout(function(){
self._onTimeout();
}, this.options.timeout);
};
Transport.prototype._onTimeout = function(){
this._onDisconnect();
};
Transport.prototype._onMessage = function(message){
if (!this.sessionid){
this.sessionid = message;
this._onConnect();
} else if (message.substr(0, 3) == '~h~'){
this._onHeartbeat(message.substr(3));
} else if (message.substr(0, 3) == '~j~'){
this.base._onMessage(JSON.parse(message.substr(3)));
} else {
this.base._onMessage(message);
}
},
Transport.prototype._onHeartbeat = function(heartbeat){
this.send('~h~' + heartbeat); // echo
};
Transport.prototype._onConnect = function(){
this.connected = true;
this.connecting = false;
this.base._onConnect();
this._setTimeout();
};
Transport.prototype._onDisconnect = function(){
this.connecting = false;
this.connected = false;
this.sessionid = null;
this.base._onDisconnect();
};
Transport.prototype._prepareUrl = function(){
return (this.base.options.secure ? 'https' : 'http')
+ '://' + this.base.host
+ ':' + this.base.options.port
+ '/' + this.base.options.resource
+ '/' + this.type
+ (this.sessionid ? ('/' + this.sessionid) : '/');
};
})();