source: Dev/trunk/src/client/dojox/xmpp/TransportSession.js @ 532

Last change on this file since 532 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 13.8 KB
Line 
1dojo.provide("dojox.xmpp.TransportSession");
2dojo.require("dojox.xmpp.bosh");
3dojo.require("dojox.xmpp.util");
4dojo.require("dojox.data.dom");
5
6dojox.xmpp.TransportSession = function(props) {
7        // we have to set this here because "this" doesn't work
8        // in the dojo.extend call.
9        this.sendTimeout = (this.wait+20)*1000;
10
11        //mixin any options that we want to provide to this service
12        if (props && dojo.isObject(props)) {
13                dojo.mixin(this, props);
14                if(this.useScriptSrcTransport){
15                        this.transportIframes = [];
16                }
17        }
18       
19};
20
21dojo.extend(dojox.xmpp.TransportSession, {
22
23                /* options/defaults */
24                rid: 0,
25                hold: 1,
26                polling:1000,
27                secure: false,
28                wait: 60,
29                lang: 'en',
30                submitContentType: 'text/xml; charset=utf=8',
31                serviceUrl: '/httpbind',
32                defaultResource: "dojoIm",
33                domain: 'imserver.com',
34                sendTimeout: 0, //(this.wait+20)*1000
35               
36                useScriptSrcTransport:false,
37               
38               
39                keepAliveTimer:null,
40
41                //status
42                state: "NotReady",
43                transmitState: "Idle",
44
45                protocolPacketQueue: [],
46                outboundQueue: [],
47                outboundRequests: {},
48                inboundQueue: [],
49                deferredRequests: {},
50                matchTypeIdAttribute: {},
51
52                open: function() {
53                        this.status = "notReady";
54                        this.rid = Math.round(Math.random() * 1000000000);
55                        this.protocolPacketQueue = [];
56                        this.outboundQueue = [];
57                        this.outboundRequests = {};
58                        this.inboundQueue = [];
59                        this.deferredRequests = {};
60                        this.matchTypeIdAttribute = {};
61                       
62               
63                        this.keepAliveTimer = setTimeout(dojo.hitch(this, "_keepAlive"), 10000);
64                       
65                        if(this.useScriptSrcTransport){
66                                dojox.xmpp.bosh.initialize({
67                                        iframes: this.hold+1,
68                                        load: dojo.hitch(this, function(){
69                                                this._sendLogin();
70                                        })
71                                });
72                        } else {
73                                this._sendLogin();
74                        }
75                },
76               
77                _sendLogin: function() {
78                                var rid = this.rid++;
79                                var req = {
80                                        content: this.submitContentType,
81                                        hold: this.hold,
82                                        rid: rid,
83                                        to: this.domain,
84                                        secure: this.secure,
85                                        wait: this.wait,
86                                        "xml:lang": this.lang,
87                                        "xmpp:version": "1.0",
88                                        xmlns: dojox.xmpp.xmpp.BODY_NS,
89                                        "xmlns:xmpp": "urn:xmpp:xbosh"
90                                };
91
92                                var msg = dojox.xmpp.util.createElement("body", req, true);
93                                this.addToOutboundQueue(msg, rid);
94                },
95
96                _sendRestart: function(){
97                        var rid = this.rid++;
98                        var req = {
99                                rid: rid,
100                                sid: this.sid,
101                                to: this.domain,
102                                "xmpp:restart": "true",
103                                "xml:lang": this.lang,
104                                xmlns: dojox.xmpp.xmpp.BODY_NS,
105                                "xmlns:xmpp": "urn:xmpp:xbosh"
106                        };
107
108                        var msg = dojox.xmpp.util.createElement("body", req, true);
109                        this.addToOutboundQueue(msg, rid);
110                },
111               
112                processScriptSrc: function(msg, rid) {
113                        //console.log("processScriptSrc::", rid, msg);
114                //      var msgDom = dojox.xml.DomParser.parse(msg);
115                        var msgDom = dojox.xml.parser.parse(msg, "text/xml");
116                        //console.log("parsed mgs", msgDom);
117                        //console.log("Queue", this.outboundQueue);
118                        if(msgDom) {
119                                this.processDocument(msgDom, rid);
120                        } else {
121                                //console.log("Recived bad document from server",msg);
122                        }
123                },
124               
125                _keepAlive: function(){
126                        if (this.state=="wait" || this.isTerminated()) {
127                                return;
128                        }
129                        this._dispatchPacket();
130                        this.keepAliveTimer = setTimeout(dojo.hitch(this, "_keepAlive"), 10000);
131                },
132               
133               
134                close: function(protocolMsg){
135
136       
137                        var rid = this.rid++;
138                        var req = {
139                               
140                                sid: this.sid,
141                                rid: rid,
142                                type: "terminate"
143                        };
144                        var envelope = null;
145
146                        if (protocolMsg) {
147                                envelope = new dojox.string.Builder(dojox.xmpp.util.createElement("body", req, false));
148                                envelope.append(protocolMsg);
149                                envelope.append("</body>");
150                        } else {
151                                envelope = new dojox.string.Builder(dojox.xmpp.util.createElement("body", req, false));
152                        }
153
154                //      this.sendXml(envelope,rid);
155                        this.addToOutboundQueue(envelope.toString(), rid);
156                        this.state=="Terminate";
157                },
158
159                dispatchPacket: function(msg, protocolMatchType, matchId, matchProperty){
160                        // summary:
161                        //              Main Packet dispatcher, most calls should be made with this other
162                        //              than a few setup calls which use add items to the queue directly
163                        //              protocolMatchType, matchId, and matchProperty are optional params
164                        //              that allow a deferred to be tied to a protocol response instad of the whole
165                        //              rid
166       
167                //      //console.log("In dispatchPacket ", msg, protocolMatchType, matchId, matchProperty);
168                        if (msg){
169                                this.protocolPacketQueue.push(msg);
170                        }
171                       
172                        var def = new dojo.Deferred();
173                        //def.rid = req.rid;
174
175                        if (protocolMatchType && matchId){
176                                def.protocolMatchType = protocolMatchType;
177                                def.matchId = matchId;
178                                def.matchProperty = matchProperty || "id";
179                                if(def.matchProperty != "id") {
180                                        this.matchTypeIdAttribute[protocolMatchType] = def.matchProperty;
181                                }
182                        }
183
184                        this.deferredRequests[def.protocolMatchType + "-" +def.matchId]=def;
185                        if(!this.dispatchTimer) {
186                                this.dispatchTimer = setTimeout(dojo.hitch(this, "_dispatchPacket"), 600);
187                        }
188                        return def;
189                },
190       
191                _dispatchPacket: function(){
192                       
193                        clearTimeout(this.dispatchTimer);
194                        delete this.dispatchTimer;
195                       
196                        if (!this.sid){
197                                console.debug("TransportSession::dispatchPacket() No SID, packet dropped.")
198                                return;
199                        }
200
201                        if (!this.authId){
202                                //FIXME according to original nodes, this should wait a little while and try
203                                //              again up to three times to see if we get this data.
204                                console.debug("TransportSession::dispatchPacket() No authId, packet dropped [FIXME]")
205                                return;
206                        }
207
208               
209
210                        //if there is a pending request with the server, don't poll
211                        if (this.transmitState != "error" && (this.protocolPacketQueue.length == 0) && (this.outboundQueue.length > 0)) {
212                                return;
213                        }
214
215                        if (this.state=="wait" || this.isTerminated()) {
216                                return;
217                        }
218
219                        var req = {
220                                sid: this.sid,
221                                xmlns: dojox.xmpp.xmpp.BODY_NS
222                        }
223
224                        var envelope
225                        if (this.protocolPacketQueue.length > 0){
226                                req.rid= this.rid++;
227                                envelope = new dojox.string.Builder(dojox.xmpp.util.createElement("body", req, false));
228                                envelope.append(this.processProtocolPacketQueue());
229                                envelope.append("</body>");
230                                delete this.lastPollTime;
231                        } else {
232                                //console.log("Nothing to send, I'm just polling.");
233                                if(this.lastPollTime) {
234                                        var now = new Date().getTime();
235                                        if(now - this.lastPollTime < this.polling) {
236                                                //console.log("Waiting to poll ", this.polling - (now - this.lastPollTime)+10);
237                                                this.dispatchTimer = setTimeout(dojo.hitch(this, "_dispatchPacket"), this.polling - (now - this.lastPollTime)+10);
238                                                return;
239                                        }
240                               
241                                }
242                                req.rid= this.rid++;
243                                this.lastPollTime = new Date().getTime();
244                                envelope = new dojox.string.Builder(dojox.xmpp.util.createElement("body", req, true));
245
246                        }
247
248               
249                        this.addToOutboundQueue(envelope.toString(),req.rid);
250
251                },
252
253                redispatchPacket: function(rid){
254                        var env = this.outboundRequests[rid];
255                        this.sendXml(env, rid);
256                },
257
258                addToOutboundQueue: function(msg, rid){
259                        this.outboundQueue.push({msg: msg,rid: rid});
260                        this.outboundRequests[rid]=msg;
261                        this.sendXml(msg, rid);
262                },
263
264                removeFromOutboundQueue: function(rid){
265                        for(var i=0; i<this.outboundQueue.length;i++){
266                                if (rid == this.outboundQueue[i]["rid"]){
267                                        this.outboundQueue.splice(i, 1);
268                                        break;
269                                }
270                        }
271                        delete this.outboundRequests[rid];
272                },
273
274                processProtocolPacketQueue: function(){
275                        var packets = new dojox.string.Builder();
276                        for(var i=0; i<this.protocolPacketQueue.length;i++){
277                                packets.append(this.protocolPacketQueue[i]);
278                        }
279                        this.protocolPacketQueue=[];
280                        return packets.toString();
281                },
282
283                sendXml: function(message, rid){
284                        if(this.isTerminated()) {
285                                return false;
286                        }
287                        //console.log("TransportSession::sendXml()"+ new Date().getTime() + " RID: ", rid, " MSG: ", message);
288                        this.transmitState = "transmitting";
289                        var def = null;
290                        if(this.useScriptSrcTransport) {
291                                //console.log("using script src to transmit");
292                                def = dojox.xmpp.bosh.get({
293                                        rid: rid,
294                                        url: this.serviceUrl+'?'+encodeURIComponent(message),
295                                        error: dojo.hitch(this, function(res, io){
296                                                this.setState("Terminate", "error");
297                                                return false;
298                                        }),
299                                        timeout: this.sendTimeout
300                                });
301                        } else {
302                                def = dojo.rawXhrPost({
303                                        contentType: "text/xml",
304                                        url: this.serviceUrl,
305                                        postData: message,
306                                        handleAs: "xml",
307                                        error: dojo.hitch(this, function(res, io) {
308                                                ////console.log("foo", res, io.xhr.responseXML, io.xhr.status);
309                                                return this.processError(io.xhr.responseXML, io.xhr.status , rid);
310                                        }),
311                                        timeout: this.sendTimeout
312                                });
313                        }
314                        //process the result document
315                        def.addCallback(this, function(res){
316                                return this.processDocument(res, rid);
317                        });
318                        return def;
319                },
320
321                processDocument: function(doc, rid){
322                        if(this.isTerminated() || !doc.firstChild) {
323                                return false;
324                        }
325                        //console.log("TransportSession:processDocument() ", doc, rid);
326                        this.transmitState = "idle";
327
328                        var body = doc.firstChild;
329                        if (body.nodeName != 'body'){
330                                //console.log("TransportSession::processDocument() firstChild is not <body> element ", doc, " RID: ", rid);
331                        }
332
333                        if (this.outboundQueue.length<1){return false;}
334
335                        var expectedId = this.outboundQueue[0]["rid"];
336                        //console.log("expectedId", expectedId);
337                        if (rid==expectedId){
338                                this.removeFromOutboundQueue(rid);
339                                this.processResponse(body, rid);
340                                this.processInboundQueue();
341                        }else{
342                                //console.log("TransportSession::processDocument() rid: ", rid, " expected: ", expectedId);
343                                var gap = rid-expectedId;
344                       
345                                if (gap < this.hold + 2){
346                                        this.addToInboundQueue(doc,rid);
347                                }else{
348                                        //console.log("TransportSession::processDocument() RID is outside of the expected response window");
349                                }
350                        }
351                        return doc;
352                },
353
354                processInboundQueue: function(){
355                        while (this.inboundQueue.length > 0) {
356                                var item = this.inboundQueue.shift();
357                                this.processDocument(item["doc"], item["rid"]);
358                        }
359                },
360
361                addToInboundQueue: function(doc,rid){
362                        for (var i=0; i<this.inboundQueue.length;i++){
363                                if (rid < this.inboundQueue[i]["rid"]){continue;}
364                                this.inboundQueue.splice(i,0,{doc: doc, rid: rid});
365                        }
366                },
367
368                processResponse: function(body,rid){
369                        ////console.log("TransportSession:processResponse() ", body, " RID: ", rid);
370
371                        if (body.getAttribute("type")=='terminate'){
372                                var reasonNode = body.firstChild.firstChild;
373                                var errorMessage = "";
374                                        if(reasonNode.nodeName == "conflict") {
375                                                errorMessage = "conflict"
376                                        }
377                                this.setState("Terminate", errorMessage);
378       
379                                return;
380                        }
381
382                        if ((this.state != 'Ready')&&(this.state != 'Terminate')) {
383                                var sid=body.getAttribute("sid");
384                                if (sid){
385                                        this.sid=sid;
386                                } else {
387                                        throw new Error("No sid returned during xmpp session startup");
388                                }
389
390                                this.authId = body.getAttribute("authid");
391                                if (this.authId == "") {
392                                        if (this.authRetries-- < 1) {
393                                                console.error("Unable to obtain Authorization ID");
394                                                this.terminateSession();
395                                        }
396                                }
397                                this.wait= body.getAttribute("wait");
398                                if( body.getAttribute("polling")){
399                                        this.polling= parseInt(body.getAttribute("polling"))*1000;
400                                }
401                       
402                                //console.log("Polling value ", this.polling);
403                                this.inactivity = body.getAttribute("inactivity");
404                                this.setState("Ready");
405                        }
406
407                        dojo.forEach(body.childNodes, function(node){
408                                this.processProtocolResponse(node, rid);
409                        }, this);
410
411                        //need to make sure, since if you use sendXml directly instead of using
412                        //dispatch packets, there wont' be a call back function here
413                        //normally the deferred will get fired by a child message at the protocol level
414                        //but if it hasn't fired by now, go ahead and fire it with the full body
415                        /*if (this.deferredRequests[rid] && this.deferredRequests[rid].fired==-1){
416                                this.deferredRequests[rid].callback(body);
417                        }*/
418
419                        //delete from the list of outstanding requests
420                        //delete this.deferredRequests[rid];
421
422                        if (this.transmitState == "idle"){
423                                this.dispatchPacket();
424                        }
425                },
426
427
428                processProtocolResponse: function(msg, rid){
429                        // summary:
430                        //              process the individual protocol messages and if there
431                        //              is a matching set of protocolMatchType, matchId, and matchPropery
432                        //              fire off the deferred
433
434                        this.onProcessProtocolResponse(msg);
435                        var key = msg.nodeName + "-" +msg.getAttribute("id");
436                        var def = this.deferredRequests[key];
437                        if (def){
438                                def.callback(msg);
439                                delete this.deferredRequests[key];
440                        }
441                },
442
443                setState: function(state, message){
444                        if (this.state != state) {
445                                if (this["on"+state]){
446                                        this["on"+state](state, this.state, message);
447                                }
448                                this.state=state;
449                        }
450                },
451               
452                isTerminated: function() {
453                       
454                        return this.state=="Terminate";
455                },
456
457                processError: function(err, httpStatusCode,rid){
458                        //console.log("Processing server error ", err, httpStatusCode,rid);
459                        if(this.isTerminated()) {
460                                return false;
461                        }
462                       
463                       
464                        if(httpStatusCode != 200) {
465                                if(httpStatusCode >= 400 && httpStatusCode < 500){
466                                        /* Any status code between 400 and 500 should terminate
467                                         * the connection */
468                                        this.setState("Terminate", errorMessage);
469                                        return false;
470                                }else{
471                                        this.removeFromOutboundQueue(rid);
472                                        setTimeout(dojo.hitch(this, function(){ this.dispatchPacket(); }), 200);
473                                        return true;
474                                }
475                                return false;
476                        }
477                       
478                        if (err && err.dojoType && err.dojoType=="timeout"){
479                                //console.log("Wait timeout");
480                        }
481                       
482                        this.removeFromOutboundQueue(rid);
483                        //FIXME conditional processing if request will be needed based on type of error.
484                        if(err && err.firstChild) {
485                        //console.log("Error ", err.firstChild.getAttribute("type") + " status code " + httpStatusCode);
486                       
487                                if (err.firstChild.getAttribute("type")=='terminate'){
488                                        var reasonNode = err.firstChild.firstChild;
489                                        var errorMessage = "";
490                                        if(reasonNode && reasonNode.nodeName == "conflict") {
491                                                errorMessage = "conflict"
492                                        }
493                                        this.setState("Terminate", errorMessage);
494                                        return false;
495                                }
496                        }
497                        this.transmitState = "error";
498                        setTimeout(dojo.hitch(this, function(){ this.dispatchPacket(); }), 200);
499                        //console.log("Error: ", arguments);
500                        return true;
501                },
502
503                //events
504                onTerminate: function(newState, oldState, message){ },
505                onProcessProtocolResponse: function(msg){},
506                onReady: function(newState, oldState){}
507});
Note: See TracBrowser for help on using the repository browser.