JSJaCHttpBindingConnection.js
Summary
All stuff related to HTTP Binding
Author: Stefan Strigler steve@zeank.in-berlin.de
Version: $Revision$
function JSJaCHttpBindingConnection(oArg) {
this.base = JSJaCConnection;
this.base(oArg);
this._hold = JSJACHBC_MAX_HOLD;
this._inactivity = 0;
this._last_requests = new Object();
this._last_rid = 0;
this._min_polling = 0;
this._pause = 0;
this._wait = JSJACHBC_MAX_WAIT;
}
JSJaCHttpBindingConnection.prototype = new JSJaCConnection();
JSJaCHttpBindingConnection.prototype.inherit = function(oArg) {
if (oArg.jid) {
var oJid = new JSJaCJID(oArg.jid);
this.domain = oJid.getDomain();
this.username = oJid.getNode();
this.resource = oJid.getResource();
} else {
this.domain = oArg.domain || 'localhost';
this.username = oArg.username;
this.resource = oArg.resource;
}
this._sid = oArg.sid;
this._rid = oArg.rid;
this._min_polling = oArg.polling;
this._inactivity = oArg.inactivity;
this._setHold(oArg.requests-1);
this.setPollInterval(this._timerval);
if (oArg.wait)
this._wait = oArg.wait;
this._connected = true;
this._handleEvent('onconnect');
this._interval= setInterval(JSJaC.bind(this._checkQueue, this),
JSJAC_CHECKQUEUEINTERVAL);
this._inQto = setInterval(JSJaC.bind(this._checkInQ, this),
JSJAC_CHECKINQUEUEINTERVAL);
this._timeout = setTimeout(JSJaC.bind(this._process, this),
this.getPollInterval());
};
JSJaCHttpBindingConnection.prototype.setPollInterval = function(timerval) {
if (timerval && !isNaN(timerval)) {
if (!this.isPolling())
this._timerval = 100;
else if (this._min_polling && timerval < this._min_polling*1000)
this._timerval = this._min_polling*1000;
else if (this._inactivity && timerval > this._inactivity*1000)
this._timerval = this._inactivity*1000;
else
this._timerval = timerval;
}
return this._timerval;
};
JSJaCHttpBindingConnection.prototype.isPolling = function() { return (this._hold == 0) };
JSJaCHttpBindingConnection.prototype._getFreeSlot = function() {
for (var i=0; i<this._hold+1; i++)
if (typeof(this._req[i]) == 'undefined' || typeof(this._req[i].r) == 'undefined' || this._req[i].r.readyState == 4)
return i;
return -1;
};
JSJaCHttpBindingConnection.prototype._getHold = function() { return this._hold; };
JSJaCHttpBindingConnection.prototype._getRequestString = function(raw, last) {
raw = raw || '';
var reqstr = '';
if (this._rid <= this._last_rid && typeof(this._last_requests[this._rid]) != 'undefined')
reqstr = this._last_requests[this._rid].xml;
else {
var xml = '';
while (this._pQueue.length) {
var curNode = this._pQueue[0];
xml += curNode;
this._pQueue = this._pQueue.slice(1,this._pQueue.length);
}
reqstr = "<body rid='"+this._rid+"' sid='"+this._sid+"' xmlns='http://jabber.org/protocol/httpbind' ";
if (JSJAC_HAVEKEYS) {
reqstr += "key='"+this._keys.getKey()+"' ";
if (this._keys.lastKey()) {
this._keys = new JSJaCKeys(hex_sha1,this.oDbg);
reqstr += "newkey='"+this._keys.getKey()+"' ";
}
}
if (last)
reqstr += "type='terminate'";
else if (this._reinit) {
if (JSJACHBC_USE_BOSH_VER)
reqstr += "xmpp:restart='true' xmlns:xmpp='urn:xmpp:xbosh'";
this._reinit = false;
}
if (xml != '' || raw != '') {
reqstr += ">" + raw + xml + "</body>";
} else {
reqstr += "/>";
}
this._last_requests[this._rid] = new Object();
this._last_requests[this._rid].xml = reqstr;
this._last_rid = this._rid;
for (var i in this._last_requests)
if (this._last_requests.hasOwnProperty(i) &&
i < this._rid-this._hold)
delete(this._last_requests[i]);
}
return reqstr;
};
JSJaCHttpBindingConnection.prototype._getInitialRequestString = function() {
var reqstr = "<body content='text/xml; charset=utf-8' hold='"+this._hold+"' xmlns='http://jabber.org/protocol/httpbind' to='"+this.authhost+"' wait='"+this._wait+"' rid='"+this._rid+"'";
if (this.host || this.port)
reqstr += " route='xmpp:"+this.host+":"+this.port+"'";
if (this.secure)
reqstr += " secure='"+this.secure+"'";
if (JSJAC_HAVEKEYS) {
this._keys = new JSJaCKeys(hex_sha1,this.oDbg);
key = this._keys.getKey();
reqstr += " newkey='"+key+"'";
}
if (this._xmllang)
reqstr += " xml:lang='"+this._xmllang + "'";
if (JSJACHBC_USE_BOSH_VER) {
reqstr += " ver='" + JSJACHBC_BOSH_VERSION + "'";
reqstr += " xmlns:xmpp='urn:xmpp:xbosh'";
if (this.authtype == 'sasl' || this.authtype == 'saslanon')
reqstr += " xmpp:version='1.0'";
}
reqstr += "/>";
return reqstr;
};
JSJaCHttpBindingConnection.prototype._getStreamID = function(slot) {
this.oDbg.log(this._req[slot].r.responseText,4);
if (!this._req[slot].r.responseXML || !this._req[slot].r.responseXML.documentElement) {
this._handleEvent('onerror',JSJaCError('503','cancel','service-unavailable'));
return;
}
var body = this._req[slot].r.responseXML.documentElement;
if (body.getAttribute('authid')) {
this.streamid = body.getAttribute('authid');
this.oDbg.log("got streamid: "+this.streamid,2);
}
if (!this._parseStreamFeatures(body) || !this.streamid) {
this._timeout = setTimeout(JSJaC.bind(this._sendEmpty, this),
this.getPollInterval());
return;
}
this._timeout = setTimeout(JSJaC.bind(this._process, this),
this.getPollInterval());
if (this.register)
this._doInBandReg();
else
this._doAuth();
};
JSJaCHttpBindingConnection.prototype._getSuspendVars = function() {
return ('host,port,secure,_rid,_last_rid,_wait,_min_polling,_inactivity,_hold,_last_requests,_pause').split(',');
};
JSJaCHttpBindingConnection.prototype._handleInitialResponse = function(slot) {
try {
this.oDbg.log(this._req[slot].r.getAllResponseHeaders(),4);
this.oDbg.log(this._req[slot].r.responseText,4);
} catch(ex) {
this.oDbg.log("No response",4);
}
if (this._req[slot].r.status != 200 || !this._req[slot].r.responseXML) {
this.oDbg.log("initial response broken (status: "+this._req[slot].r.status+")",1);
this._handleEvent('onerror',JSJaCError('503','cancel','service-unavailable'));
return;
}
var body = this._req[slot].r.responseXML.documentElement;
if (!body || body.tagName != 'body' || body.namespaceURI != 'http://jabber.org/protocol/httpbind') {
this.oDbg.log("no body element or incorrect body in initial response",1);
this._handleEvent("onerror",JSJaCError("500","wait","internal-service-error"));
return;
}
if (body.getAttribute("type") == "terminate") {
this.oDbg.log("invalid response:\n" + this._req[slot].r.responseText,1);
clearTimeout(this._timeout);
this._connected = false;
this.oDbg.log("Disconnected.",1);
this._handleEvent('ondisconnect');
this._handleEvent('onerror',JSJaCError('503','cancel','service-unavailable'));
return;
}
this._sid = body.getAttribute('sid');
this.oDbg.log("got sid: "+this._sid,2);
if (body.getAttribute('polling'))
this._min_polling = body.getAttribute('polling');
if (body.getAttribute('inactivity'))
this._inactivity = body.getAttribute('inactivity');
if (body.getAttribute('requests'))
this._setHold(body.getAttribute('requests')-1);
this.oDbg.log("set hold to " + this._getHold(),2);
if (body.getAttribute('ver'))
this._bosh_version = body.getAttribute('ver');
if (body.getAttribute('maxpause'))
this._pause = Number.min(body.getAttribute('maxpause'), JSJACHBC_MAXPAUSE);
this.setPollInterval(this._timerval);
this._connected = true;
this._inQto = setInterval(JSJaC.bind(this._checkInQ, this),
JSJAC_CHECKINQUEUEINTERVAL);
this._interval= setInterval(JSJaC.bind(this._checkQueue, this),
JSJAC_CHECKQUEUEINTERVAL);
this._getStreamID(slot);
};
JSJaCHttpBindingConnection.prototype._parseResponse = function(req) {
if (!this.connected() || !req)
return null;
var r = req.r;
try {
if (r.status == 404 || r.status == 403) {
this._abort();
return null;
}
if (r.status != 200 || !r.responseXML) {
this._errcnt++;
var errmsg = "invalid response ("+r.status+"):\n" + r.getAllResponseHeaders()+"\n"+r.responseText;
if (!r.responseXML)
errmsg += "\nResponse failed to parse!";
this.oDbg.log(errmsg,1);
if (this._errcnt > JSJAC_ERR_COUNT) {
this._abort();
return null;
}
if (this.connected()) {
this.oDbg.log("repeating ("+this._errcnt+")",1);
this._setStatus('proto_error_fallback');
setTimeout(JSJaC.bind(this._resume, this),
this.getPollInterval());
}
return null;
}
} catch (e) {
this.oDbg.log("XMLHttpRequest error: status not available", 1);
this._errcnt++;
if (this._errcnt > JSJAC_ERR_COUNT) {
this._abort();
} else {
if (this.connected()) {
this.oDbg.log("repeating ("+this._errcnt+")",1);
this._setStatus('proto_error_fallback');
setTimeout(JSJaC.bind(this._resume, this),
this.getPollInterval());
}
}
return null;
}
var body = r.responseXML.documentElement;
if (!body || body.tagName != 'body' ||
body.namespaceURI != 'http://jabber.org/protocol/httpbind') {
this.oDbg.log("invalid response:\n" + r.responseText,1);
clearTimeout(this._timeout);
clearInterval(this._interval);
clearInterval(this._inQto);
this._connected = false;
this.oDbg.log("Disconnected.",1);
this._handleEvent('ondisconnect');
this._setStatus('internal_server_error');
this._handleEvent('onerror',
JSJaCError('500','wait','internal-server-error'));
return null;
}
if (typeof(req.rid) != 'undefined' && this._last_requests[req.rid]) {
if (this._last_requests[req.rid].handled) {
this.oDbg.log("already handled "+req.rid,2);
return null;
} else
this._last_requests[req.rid].handled = true;
}
if (body.getAttribute("type") == "terminate") {
this.oDbg.log("session terminated:\n" + r.responseText,1);
clearTimeout(this._timeout);
clearInterval(this._interval);
clearInterval(this._inQto);
var condition = body.getAttribute('condition');
if (condition == "remote-stream-error")
if (body.getElementsByTagName("conflict").length > 0)
this._setStatus("session-terminate-conflict");
if (condition == null)
condition = 'session-terminate';
this._handleEvent('onerror',JSJaCError('503','cancel',condition));
this._connected = false;
this.oDbg.log("Disconnected.",1);
this._handleEvent('ondisconnect');
return null;
}
this._errcnt = 0;
return r.responseXML.documentElement;
};
JSJaCHttpBindingConnection.prototype._reInitStream = function(to,cb,arg) {
this._reinit = true;
cb.call(this,arg);
};
JSJaCHttpBindingConnection.prototype._resume = function() {
if (this._pause == 0 && this._rid >= this._last_rid)
this._rid = this._last_rid-1;
this._process();
};
JSJaCHttpBindingConnection.prototype._setHold = function(hold) {
if (!hold || isNaN(hold) || hold < 0)
hold = 0;
else if (hold > JSJACHBC_MAX_HOLD)
hold = JSJACHBC_MAX_HOLD;
this._hold = hold;
return this._hold;
};
JSJaCHttpBindingConnection.prototype._setupRequest = function(async) {
var req = new Object();
var r = XmlHttp.create();
try {
r.open("POST",this._httpbase,async);
r.setRequestHeader('Content-Type','text/xml; charset=utf-8');
} catch(e) { this.oDbg.log(e,1); }
req.r = r;
this._rid++;
req.rid = this._rid;
return req;
};
JSJaCHttpBindingConnection.prototype._suspend = function() {
if (this._pause == 0)
return;
var slot = this._getFreeSlot();
this._req[slot] = this._setupRequest(false);
var reqstr = "<body pause='"+this._pause+"' xmlns='http://jabber.org/protocol/httpbind' sid='"+this._sid+"' rid='"+this._rid+"'";
if (JSJAC_HAVEKEYS) {
reqstr += " key='"+this._keys.getKey()+"'";
if (this._keys.lastKey()) {
this._keys = new JSJaCKeys(hex_sha1,this.oDbg);
reqstr += " newkey='"+this._keys.getKey()+"'";
}
}
reqstr += ">";
while (this._pQueue.length) {
var curNode = this._pQueue[0];
reqstr += curNode;
this._pQueue = this._pQueue.slice(1,this._pQueue.length);
}
reqstr += "</body>";
this.oDbg.log("Disconnecting: " + reqstr,4);
this._req[slot].r.send(reqstr);
};
Documentation generated by
JSDoc on Fri Jun 28 11:55:35 2024