1 (function (globalObject) { 2 //============================================================================== 3 // PACKAGE MANAGEMENT 4 //============================================================================== 5 6 // JSDOC helpers 7 8 /** @namespace 9 @name net 10 @private */ 11 /** @namespace 12 @name net.user1 13 @private */ 14 /** @namespace 15 @name net.user1.events 16 @private */ 17 /** @namespace 18 @name net.user1.logger 19 @private */ 20 /** @namespace 21 @name net.user1.orbiter 22 */ 23 /** @namespace 24 @name net.user1.utils 25 */ 26 27 // create utils package 28 if (typeof globalObject.net == "undefined") { 29 globalObject.net = {}; 30 } 31 var net = globalObject.net; 32 net.user1 = net.user1 ? net.user1 : {}; 33 net.user1.utils = net.user1.utils ? net.user1.utils : {}; 34 35 // Convenience method to create packages 36 /** @function */ 37 net.user1.utils.createPackage = function (packageName) { 38 var parts = packageName.split("."); 39 var part = globalObject; 40 41 for (var i = 0; i < parts.length; i++) { 42 part = part[parts[i]] === undefined ? (part[parts[i]] = {}) : part[parts[i]]; 43 } 44 }; 45 //============================================================================== 46 // PACKAGE DECLARATIONS 47 //============================================================================== 48 net.user1.utils.createPackage("net.user1.logger"); 49 net.user1.utils.createPackage("net.user1.events"); 50 net.user1.utils.createPackage("net.user1.orbiter"); 51 net.user1.utils.createPackage("net.user1.orbiter.filters"); 52 net.user1.utils.createPackage("net.user1.orbiter.snapshot"); 53 net.user1.utils.createPackage("net.user1.orbiter.upc"); 54 net.user1.utils.createPackage("net.user1.utils"); 55 /** @function */ 56 net.user1.utils.extend = function (subclass, superclass) { 57 function superclassConstructor () {}; 58 superclassConstructor.prototype = superclass.prototype; 59 subclass.superclass = superclass.prototype; 60 subclass.prototype = new superclassConstructor(); 61 subclass.prototype.constructor = subclass; 62 }; 63 //============================================================================== 64 // ABSTRACT ERROR FUNCTION 65 //============================================================================== 66 67 // JSDOC helpers 68 69 /** @private */ 70 net.user1.utils.abstractError = function () { 71 throw new Error("Could not invoke abstract method. This method must be implemented by a subclass."); 72 }; 73 //============================================================================== 74 // CONNECTION REFUSAL REASON CONSTANTS 75 //============================================================================== 76 /** @class */ 77 net.user1.orbiter.ConnectionRefusalReason = new Object(); 78 /** @constant */ 79 net.user1.orbiter.ConnectionRefusalReason.BANNED = "BANNED"; 80 //============================================================================== 81 // CLASS DECLARATION 82 //============================================================================== 83 /** @class */ 84 net.user1.orbiter.ConnectionRefusal = function (reason, 85 description) { 86 /** 87 * @field 88 */ 89 this.bannedAt = NaN; 90 /** 91 * @field 92 */ 93 this.banDuration = NaN; 94 /** 95 * @field 96 */ 97 this.banReason = null; 98 /** 99 * @field 100 */ 101 this.reason = reason; 102 /** 103 * @field 104 */ 105 this.description = description; 106 107 var banDetails; 108 switch (reason) { 109 case net.user1.orbiter.ConnectionRefusalReason.BANNED: 110 banDetails = description.split(net.user1.orbiter.Tokens.RS); 111 this.bannedAt = parseFloat(banDetails[0]); 112 this.banDuration = parseFloat(banDetails[1]); 113 this.banReason = banDetails[2]; 114 break; 115 } 116 } 117 //============================================================================== 118 // CLASS DECLARATION 119 //============================================================================== 120 /** @class */ 121 net.user1.orbiter.VersionNumber = function (major, minor, revision, build) { 122 this.major = major; 123 this.minor = minor; 124 this.revision = revision; 125 this.build = build == undefined ? -1 : build; 126 }; 127 128 //============================================================================== 129 // INSTANCE METHODS 130 //============================================================================== 131 net.user1.orbiter.VersionNumber.prototype.fromVersionString = function (value) { 132 var upcVersionParts = value.split("."); 133 this.major = upcVersionParts[0]; 134 this.minor = upcVersionParts[1]; 135 this.revision = upcVersionParts[2]; 136 this.build = upcVersionParts.length == 4 ? upcVersionParts[4] : -1; 137 } 138 139 net.user1.orbiter.VersionNumber.prototype.toStringVerbose = function () { 140 var versionString = this.major + "." + this.minor + "." + this.revision 141 + ((this.build == -1) ? "" : " (Build " + this.build + ")"); 142 return versionString; 143 } 144 145 net.user1.orbiter.VersionNumber.prototype.toString = function () { 146 var versionString = this.major + "." + this.minor + "." + this.revision 147 + ((this.build == -1) ? "" : "." + this.build); 148 return versionString; 149 } 150 //============================================================================== 151 // PRODUCT CONSTANTS 152 //============================================================================== 153 /** @class 154 @private */ 155 net.user1.orbiter.Product = new Object(); 156 157 /** @private */ 158 net.user1.orbiter.Product.clientType = "OrbiterMicro"; 159 net.user1.orbiter.Product.clientVersion = new net.user1.orbiter.VersionNumber(2,1,2,21); 160 net.user1.orbiter.Product.upcVersion = new net.user1.orbiter.VersionNumber(1,10,3); 161 //============================================================================== 162 // A COLLECTION OF ARRAY UTILITIES 163 //============================================================================== 164 /** @class */ 165 net.user1.utils.ArrayUtil = new Object(); 166 167 net.user1.utils.ArrayUtil.indexOf = function (arr, obj) { 168 if (arr.indexOf ) { 169 return arr.indexOf(obj); 170 } 171 172 for (var i = arr.length; --i >= 0; ) { 173 if (arr[i] === obj) { 174 return i; 175 } 176 } 177 178 return -1; 179 }; 180 181 net.user1.utils.ArrayUtil.remove = function (array, item) { 182 var itemIndex; 183 184 if (item == null) { 185 return false; 186 } else { 187 itemIndex = net.user1.utils.ArrayUtil.indexOf(array, item); 188 if (itemIndex == -1) { 189 return false; 190 } else { 191 array.splice(itemIndex, 1); 192 return true; 193 } 194 } 195 }; 196 197 net.user1.utils.ArrayUtil.isArray = function (value) { 198 return Object.prototype.toString.call(value) === '[object Array]'; 199 }; 200 //============================================================================== 201 // A COLLECTION OF OBJECT UTILITIES 202 //============================================================================== 203 /** @class */ 204 net.user1.utils.ObjectUtil = new Object(); 205 206 net.user1.utils.ObjectUtil.combine = function () { 207 var source = arguments.length == 1 ? arguments[0] : arguments; 208 var master = new Object(); 209 210 var object; 211 for (var i = 0; i < source.length; i++) { 212 object = source[i]; 213 for (var key in object) { 214 if (object.hasOwnProperty(key)) { 215 master[key] = object[key]; 216 } 217 } 218 } 219 return master; 220 }; 221 222 net.user1.utils.ObjectUtil.length = function (object) { 223 var len = 0; 224 for (var p in object) { 225 len++; 226 } 227 return len; 228 }; 229 230 //============================================================================== 231 // CLASS DECLARATION 232 //============================================================================== 233 /** @class A minimal in-memory storage map to mirror LocalData's persistent map. */ 234 net.user1.utils.MemoryStore = function () { 235 this.clear(); 236 }; 237 238 net.user1.utils.MemoryStore.prototype.write = function (record, field, value) { 239 if (typeof this.data[record] === "undefined") { 240 this.data[record] = new Object(); 241 } 242 this.data[record][field] = value 243 }; 244 245 net.user1.utils.MemoryStore.prototype.read = function (record, field) { 246 if (typeof this.data[record] !== "undefined" 247 && typeof this.data[record][field] !== "undefined") { 248 return this.data[record][field]; 249 } else { 250 return null; 251 } 252 }; 253 254 net.user1.utils.MemoryStore.prototype.remove = function (record, field) { 255 if (typeof this.data[record] !== "undefined") { 256 delete this.data[record][field]; 257 } 258 }; 259 260 net.user1.utils.MemoryStore.prototype.clear = function () { 261 this.data = new Object(); 262 }; 263 //============================================================================== 264 // CLASS DECLARATION 265 //============================================================================== 266 /** @class 267 * A minimal version of the browser localStorage object, 268 * for use in environments without native localStorage support. 269 * Provides in-memory storage only, with no persistence. 270 */ 271 net.user1.utils.LocalStorage = function () { 272 this.data = new net.user1.utils.MemoryStore(); 273 }; 274 275 net.user1.utils.LocalStorage.prototype.setItem = function (key, value) { 276 this.data.write("localStorage", key, value); 277 }; 278 279 net.user1.utils.LocalStorage.prototype.getItem = function (key) { 280 return this.data.read("localStorage", key); 281 }; 282 283 net.user1.utils.LocalStorage.prototype.removeItem = function (key) { 284 this.data.remove("localStorage", key); 285 }; 286 //============================================================================== 287 // CLASS DECLARATION 288 //============================================================================== 289 /** @class*/ 290 net.user1.utils.LocalData = new Object(); 291 292 if (typeof localStorage === "undefined") { 293 net.user1.utils.LocalData.data = new net.user1.utils.LocalStorage(); 294 } else { 295 net.user1.utils.LocalData.data = localStorage; 296 } 297 298 net.user1.utils.LocalData.write = function (record, field, value) { 299 // localStorage can't store objects, so combine record and field for keys 300 net.user1.utils.LocalData.data.setItem(record+field, value); 301 }; 302 303 net.user1.utils.LocalData.read = function (record, field) { 304 var value = net.user1.utils.LocalData.data.getItem(record+field); 305 return value == null ? null : value; 306 }; 307 308 net.user1.utils.LocalData.remove = function (record, field) { 309 var value = net.user1.utils.LocalData.data.getItem(record+field); 310 if (value != null) { 311 this.data.removeItem(record+field); 312 } 313 }; 314 //============================================================================== 315 // MESSAGE CONSTANTS 316 //============================================================================== 317 /** @class */ 318 net.user1.orbiter.Messages = new Object(); 319 /** @constant */ 320 net.user1.orbiter.Messages.CLIENT_HEARTBEAT = "CLIENT_HEARTBEAT"; 321 //============================================================================== 322 // RECEIVE MESSAGE BROADCAST TYPE CONSTANTS 323 //============================================================================== 324 /** @class 325 @private */ 326 net.user1.orbiter.ReceiveMessageBroadcastType = new Object(); 327 net.user1.orbiter.ReceiveMessageBroadcastType.TO_SERVER = "0"; 328 net.user1.orbiter.ReceiveMessageBroadcastType.TO_ROOMS = "1"; 329 net.user1.orbiter.ReceiveMessageBroadcastType.TO_CLIENTS = "2"; 330 //============================================================================== 331 // ROOM ID PARSING UTILITIES 332 //============================================================================== 333 /** @class */ 334 net.user1.orbiter.RoomIDParser = new Object(); 335 336 net.user1.orbiter.RoomIDParser.getSimpleRoomID = function (fullRoomID) { 337 if (fullRoomID.indexOf(".") == -1) { 338 return fullRoomID; 339 } else { 340 return fullRoomID.slice(fullRoomID.lastIndexOf(".")+1); 341 } 342 }; 343 344 net.user1.orbiter.RoomIDParser.getQualifier = function (fullRoomID) { 345 if (fullRoomID.indexOf(".") == -1) { 346 return ""; 347 } else { 348 return fullRoomID.slice(0, fullRoomID.lastIndexOf(".")); 349 } 350 }; 351 352 net.user1.orbiter.RoomIDParser.splitID = function (fullRoomID) { 353 return [getQualifier(fullRoomID), getSimpleRoomID(fullRoomID)]; 354 }; 355 //============================================================================== 356 // CLASS DECLARATION 357 //============================================================================== 358 /** @class */ 359 net.user1.utils.UDictionary = function () { 360 }; 361 //============================================================================== 362 // TOKEN CONSTANTS 363 //============================================================================== 364 /** @class 365 @private */ 366 net.user1.orbiter.Tokens = new Object(); 367 368 /** @private */ 369 net.user1.orbiter.Tokens.RS = "|"; 370 /** @private */ 371 net.user1.orbiter.Tokens.WILDCARD = "*"; 372 /** @private */ 373 net.user1.orbiter.Tokens.GLOBAL_ATTR = ""; 374 /** @private */ 375 net.user1.orbiter.Tokens.CUSTOM_CLASS_ATTR = "_CLASS"; 376 /** @private */ 377 net.user1.orbiter.Tokens.MAX_CLIENTS_ATTR = "_MAX_CLIENTS"; 378 /** @private */ 379 net.user1.orbiter.Tokens.REMOVE_ON_EMPTY_ATTR = "_DIE_ON_EMPTY"; 380 /** @private */ 381 net.user1.orbiter.Tokens.PASSWORD_ATTR = "_PASSWORD"; 382 /** @private */ 383 net.user1.orbiter.Tokens.ROLES_ATTR = "_ROLES"; 384 //============================================================================== 385 // CLASS DECLARATION 386 //============================================================================== 387 /** @class */ 388 net.user1.orbiter.System = function (window) { 389 this.window = window; 390 this.clientType = net.user1.orbiter.Product.clientType; 391 this.clientVersion = net.user1.orbiter.Product.clientVersion; 392 this.upcVersion = net.user1.orbiter.Product.upcVersion; 393 } 394 395 //============================================================================== 396 // INSTANCE METHODS 397 //============================================================================== 398 net.user1.orbiter.System.prototype.getClientType = function () { 399 return this.clientType; 400 } 401 402 /** @returns net.user1.orbiter.VersionNumber */ 403 net.user1.orbiter.System.prototype.getClientVersion = function () { 404 return this.clientVersion; 405 } 406 407 /** @returns net.user1.orbiter.VersionNumber */ 408 net.user1.orbiter.System.prototype.getUPCVersion = function () { 409 return this.upcVersion; 410 } 411 412 /** @returns Boolean */ 413 net.user1.orbiter.System.prototype.isJavaScriptCompatible = function () { 414 // Assume non-browser environments can do cross-origin XMLHttpRequests 415 if (this.window == null && typeof XMLHttpRequest != "undefined") { 416 return true; 417 } 418 419 if (this.window != null) { 420 // Standards-based browsers that support cross-origin requests 421 if (typeof XMLHttpRequest != "undefined" 422 && typeof new XMLHttpRequest().withCredentials != "undefined") { 423 return true; 424 } 425 426 // Versions of IE that support proprietary cross-origin requests 427 if (typeof XDomainRequest != "undefined" 428 && this.window.location.protocol != "file:") { 429 return true; 430 } 431 432 // Browsers that can communicate between windows 433 if (this.window.postMessage != null) { 434 return true; 435 } 436 } 437 438 // This environment has no way to connect to Union Server 439 return false; 440 } 441 442 /** 443 * <p> 444 * Returns true if the host environment supports direct cross-origin HTTP 445 * requests using CORS (see: <a href="http://www.w3.org/TR/cors/">http://www.w3.org/TR/cors/</a>). 446 * When hasHTTPDirectConnection() returns true, then Orbiter can safely use 447 * the HTTPDirectConnection class to communicate with Union Server over HTTP. When 448 * hasHTTPDirectConnection() returns false, Orbiter cannot use 449 * HTTPDirectConnection, and must instead use the HTTPIFrameConnection class to 450 * communicate with Union Server over HTTP. 451 * </p> 452 * 453 * <p> 454 * Note that Orbiter applications that use Orbiter's connect() or setServer() 455 * methods to connect to Union Server do not need to perform a capabilities check 456 * via hasHTTPDirectConnection(). The connect() and setServer() methods check 457 * the host environment's capabilities automatically, and choose the appropriate 458 * connection type for the environment. The hasHTTPDirectConnection() method is 459 * required in one situation only: when the application explicitly wishes to 460 * communicate over HTTP without trying a WebSocket connection first. 461 * </p> 462 * 463 * @returns Boolean 464 * 465 * @see net.user1.orbiter.HTTPDirectConnection 466 * @see net.user1.orbiter.HTTPIFrameConnection 467 * @see net.user1.orbiter.Orbiter#connect 468 * @see net.user1.orbiter.Orbiter#setServer 469 **/ 470 net.user1.orbiter.System.prototype.hasHTTPDirectConnection = function() { 471 // -If XHR has a "withCredentials" flag then CORS is supported. 472 // -In IE, if XDomainRequest is available, and the file wasn't loaded 473 // locally, then CORS is supported 474 // -In non-browser environments, assume cross-origin XMLHttpRequests are allowed 475 if ((typeof XMLHttpRequest != "undefined" && typeof new XMLHttpRequest().withCredentials != "undefined") 476 || (typeof XDomainRequest != "undefined" && this.window != null && this.window.location.protocol != "file:") 477 || (this.window == null && typeof XMLHttpRequest != "undefined")) { 478 return true; 479 } else { 480 return false; 481 } 482 } 483 484 /** 485 * <p> 486 * Returns true if the host environment supports WebSocket connections. 487 * When hasWebSocket() returns true, then Orbiter can safely use 488 * the WebSocketConnection class to communicate with Union Server over a 489 * persistent TCP/IP socket. When hasWebSocket() returns false, Orbiter cannot use 490 * WebSocketConnection, and must instead use HTTP communications (via either the 491 * HTTPDirectConnection class or the HTTPIFrameConnection class). 492 * </p> 493 * 494 * <p> 495 * Note that Orbiter applications that use Orbiter's connect() or setServer() 496 * methods to connect to Union Server do not need to perform a capabilities check 497 * via hasWebSocket(). The connect() and setServer() methods check 498 * the host environment's capabilities automatically, and choose the appropriate 499 * connection type for the environment. The hasWebSocket() method is 500 * required in one situation only: when the application explicitly wishes to 501 * determine whether WebSocket is supported for the purpose of application flow 502 * or user feedback. 503 * </p> 504 * 505 * @returns Boolean 506 * 507 * @see net.user1.orbiter.WebSocketConnection 508 * @see net.user1.orbiter.Orbiter#connect 509 **/ 510 net.user1.orbiter.System.prototype.hasWebSocket = function() { 511 return (typeof WebSocket !== "undefined" || typeof MozWebSocket !== "undefined"); 512 } 513 514 net.user1.orbiter.System.prototype.toString = function () { 515 return "[object System]"; 516 } 517 //============================================================================== 518 // A COLLECTION OF NUMERIC FORMATTING FUNCTIONS 519 //============================================================================== 520 /** @class */ 521 net.user1.utils.NumericFormatter = new Object(); 522 523 net.user1.utils.NumericFormatter.dateToLocalHrMinSec = function (date) { 524 var timeString = net.user1.utils.NumericFormatter.addLeadingZero(date.getHours()) + ":" 525 + net.user1.utils.NumericFormatter.addLeadingZero(date.getMinutes()) + ":" 526 + net.user1.utils.NumericFormatter.addLeadingZero(date.getSeconds()); 527 return timeString; 528 } 529 530 net.user1.utils.NumericFormatter.dateToLocalHrMinSecMs = function (date) { 531 return net.user1.utils.NumericFormatter.dateToLocalHrMinSec(date) + "." + net.user1.utils.NumericFormatter.addTrailingZeros(date.getMilliseconds()); 532 } 533 534 net.user1.utils.NumericFormatter.addLeadingZero = function (n) { 535 return ((n>9)?"":"0")+n; 536 } 537 538 net.user1.utils.NumericFormatter.addTrailingZeros = function (n) { 539 var ns = n.toString(); 540 541 if (ns.length == 1) { 542 return ns + "00"; 543 } else if (ns.length == 2) { 544 return ns + "0"; 545 } else { 546 return ns; 547 } 548 } 549 550 net.user1.utils.NumericFormatter.msToElapsedDayHrMinSec = function (ms) { 551 var sec = Math.floor(ms/1000); 552 553 var min = Math.floor(sec/60); 554 sec = sec % 60; 555 var timeString = net.user1.utils.NumericFormatter.addLeadingZero(sec); 556 557 var hr = Math.floor(min/60); 558 min = min % 60; 559 timeString = net.user1.utils.NumericFormatter.addLeadingZero(min) + ":" + timeString; 560 561 var day = Math.floor(hr/24); 562 hr = hr % 24; 563 timeString = net.user1.utils.NumericFormatter.addLeadingZero(hr) + ":" + timeString; 564 565 if (day > 0) { 566 timeString = day + "d " + timeString; 567 } 568 569 return timeString; 570 }; 571 //============================================================================== 572 // CLASS DECLARATION 573 //============================================================================== 574 /** @class */ 575 net.user1.events.EventListener = function (listener, 576 thisArg, 577 priority) { 578 this.listener = listener; 579 this.thisArg = thisArg; 580 this.priority = priority; 581 }; 582 583 //============================================================================== 584 // INSTANCE METHODS 585 //============================================================================== 586 net.user1.events.EventListener.prototype.getListenerFunction = function () { 587 return this.listener; 588 }; 589 590 net.user1.events.EventListener.prototype.getThisArg = function () { 591 return this.thisArg; 592 }; 593 594 net.user1.events.EventListener.prototype.getPriority = function () { 595 return this.priority; 596 }; 597 598 net.user1.events.EventListener.prototype.toString = function () { 599 return "[object EventListener]"; 600 }; 601 //============================================================================== 602 // CLASS DECLARATION 603 //============================================================================== 604 /** @class */ 605 net.user1.events.EventDispatcher = function (target) { 606 this.listeners = new Object(); 607 608 if (typeof target !== "undefined") { 609 this.target = target; 610 } else { 611 this.target = this; 612 } 613 }; 614 615 //============================================================================== 616 // INSTANCE METHODS 617 //============================================================================== 618 /** 619 * Registers a function or method to be invoked when the specified event type 620 * occurs. 621 * 622 * @param type The string name of the event (for example, "READY") 623 * @param listener A reference to the function or method to invoke. 624 * @param thisArg A reference to the object on which the listener will be invoked 625 * (i.e., the value of "this" within the listener's function body). 626 * @param priority An integer indicating the listener's priority. Listeners with 627 * higher priority are invoked before listeners with lower priority. 628 * Listeners with equal priority are invoked in the order they were 629 * added. Listener priority defaults to 0. 630 * @return {Boolean} true if the listener was added; false if the listener was 631 * already registered for the event. 632 * 633 * @example 634 * <pre> 635 * // Invoke readyListener() on 'this' when READY occurs: 636 * orbiter.addEventListener(net.user1.orbiter.OrbiterEvent.READY, readyListener, this); 637 * </pre> 638 */ 639 net.user1.events.EventDispatcher.prototype.addEventListener = function (type, 640 listener, 641 thisArg, 642 priority) { 643 if (typeof this.listeners[type] === "undefined") { 644 this.listeners[type] = new Array(); 645 } 646 var listenerArray = this.listeners[type]; 647 648 if (this.hasListener(type, listener, thisArg)) { 649 return false; 650 } 651 priority = priority || 0; 652 653 var newListener = new net.user1.events.EventListener(listener, 654 thisArg, 655 priority); 656 var added = false; 657 var thisListener; 658 for (var i = listenerArray.length; --i >= 0;) { 659 thisListener = listenerArray[i]; 660 if (priority <= thisListener.getPriority()) { 661 listenerArray.splice(i+1, 0, newListener); 662 added = true; 663 break; 664 } 665 } 666 if (!added) { 667 listenerArray.unshift(newListener); 668 } 669 return true; 670 }; 671 672 net.user1.events.EventDispatcher.prototype.removeEventListener = function (type, 673 listener, 674 thisArg) { 675 var listenerArray = this.listeners[type]; 676 if (typeof listenerArray === "undefined") { 677 return false; 678 } 679 680 var foundListener = false; 681 for (var i = 0; i < listenerArray.length; i++) { 682 if (listenerArray[i].getListenerFunction() === listener 683 && listenerArray[i].getThisArg() === thisArg) { 684 foundListener = true; 685 listenerArray.splice(i, 1); 686 break; 687 } 688 } 689 690 if (listenerArray.length == 0) { 691 delete this.listeners[type]; 692 } 693 694 return foundListener; 695 }; 696 697 net.user1.events.EventDispatcher.prototype.hasListener = function (type, 698 listener, 699 thisArg) { 700 var listenerArray = this.listeners[type]; 701 if (typeof listenerArray === "undefined") { 702 return false; 703 } 704 705 for (var i = 0; i < listenerArray.length; i++) { 706 if (listenerArray[i].getListenerFunction() === listener 707 && listenerArray[i].getThisArg() === thisArg) { 708 return true; 709 } 710 } 711 return false; 712 }; 713 714 net.user1.events.EventDispatcher.prototype.getListeners = function (type) { 715 return this.listeners[type]; 716 }; 717 718 net.user1.events.EventDispatcher.prototype.dispatchEvent = function (event) { 719 var listenerArray = this.listeners[event.type]; 720 if (typeof listenerArray === "undefined") { 721 return; 722 } 723 if (typeof event.type === "undefined") { 724 throw new Error("Event dispatch failed. No event name specified by " + event); 725 } 726 event.target = this.target; 727 var numListeners = listenerArray.length; 728 for (var i = 0; i < numListeners; i++) { 729 listenerArray[i].getListenerFunction().apply(listenerArray[i].getThisArg(), [event]); 730 } 731 }; 732 733 //============================================================================== 734 // TOSTRING 735 //============================================================================== 736 737 net.user1.events.EventDispatcher.prototype.toString = function () { 738 return "[object EventDispatcher]"; 739 }; 740 //============================================================================== 741 // CLASS DECLARATION 742 //============================================================================== 743 /** @class */ 744 net.user1.events.Event = function (type) { 745 if (type !== undefined) { 746 this.type = type; 747 } else { 748 throw new Error("Event creation failed. No type specified. Event: " + this); 749 } 750 this.target = null; 751 }; 752 753 net.user1.events.Event.prototype.toString = function () { 754 return "[object Event]"; 755 }; 756 //============================================================================== 757 // CLASS DECLARATION 758 //============================================================================== 759 /** @class 760 761 The ConsoleLogger class outputs Orbiter's log to the host environment's console, 762 if a console is available. 763 764 */ 765 net.user1.logger.ConsoleLogger = function (log) { 766 this.log = log; 767 this.log.addEventListener(net.user1.logger.LogEvent.UPDATE, this.updateListener, this); 768 // Print all messages already in the log 769 var history = this.log.getHistory(); 770 for (var i = 0; i < history.length; i++) { 771 this.out(history[i]); 772 } 773 }; 774 775 //============================================================================== 776 // INSTANCE METHODS 777 //============================================================================== 778 /** @private */ 779 net.user1.logger.ConsoleLogger.prototype.updateListener = function (e) { 780 var timeStamp = e.getTimeStamp(); 781 var level = e.getLevel(); 782 var bufferSpace = (level == net.user1.logger.Logger.INFO 783 || level == net.user1.logger.Logger.WARN) ? " " : ""; 784 785 this.out(timeStamp + (timeStamp == "" ? "" : " ") 786 + e.getLevel() + ": " + bufferSpace + e.getMessage()); 787 }; 788 789 /** @private */ 790 net.user1.logger.ConsoleLogger.prototype.out = function (value) { 791 if (typeof console === "undefined" || typeof console.log === "undefined") { 792 return; 793 } 794 console.log(value); 795 }; 796 797 /** @private */ 798 net.user1.logger.ConsoleLogger.prototype.dispose = function () { 799 this.log.removeEventListener(net.user1.logger.LogEvent.UPDATE, this.updateListener, this); 800 this.log = log = null; 801 }; 802 //============================================================================== 803 // CLASS DECLARATION 804 //============================================================================== 805 /** @class 806 @extends net.user1.events.Event 807 */ 808 net.user1.logger.LogEvent = function (type, message, level, timeStamp) { 809 net.user1.events.Event.call(this, type); 810 811 this.message = message; 812 this.level = level; 813 this.timeStamp = timeStamp; 814 }; 815 816 //============================================================================== 817 // INHERITANCE 818 //============================================================================== 819 net.user1.utils.extend(net.user1.logger.LogEvent, net.user1.events.Event); 820 821 //============================================================================== 822 // STATIC VARIABLES 823 //============================================================================== 824 /** @constant */ 825 net.user1.logger.LogEvent.UPDATE = "UPDATE"; 826 /** @constant */ 827 net.user1.logger.LogEvent.LEVEL_CHANGE = "LEVEL_CHANGE"; 828 829 //============================================================================== 830 // INSTANCE METHODS 831 //============================================================================== 832 net.user1.logger.LogEvent.prototype.getMessage = function () { 833 return this.message; 834 }; 835 836 net.user1.logger.LogEvent.prototype.getLevel = function () { 837 return this.level; 838 }; 839 840 net.user1.logger.LogEvent.prototype.getTimeStamp = function () { 841 return this.timeStamp; 842 }; 843 844 net.user1.logger.LogEvent.prototype.toString = function () { 845 return "[object LogEvent]"; 846 }; 847 848 //============================================================================== 849 // CLASS DECLARATION 850 //============================================================================== 851 /** @class 852 853 The Logger class dispatches the following events: 854 855 <ul class="summary"> 856 <li class="fixedFont">{@link net.user1.logger.LogEvent.LEVEL_CHANGE}</li> 857 <li class="fixedFont">{@link net.user1.logger.LogEvent.UPDATE}</li> 858 </ul> 859 860 To register for events, use {@link net.user1.events.EventDispatcher#addEventListener}. 861 862 863 @extends net.user1.events.EventDispatcher 864 */ 865 net.user1.logger.Logger = function (historyLength) { 866 // Invoke superclass constructor 867 net.user1.events.EventDispatcher.call(this); 868 869 // Instance variables 870 this.suppressionTerms = new Array(); 871 this.timeStampEnabled = false; 872 this.logLevel = 0; 873 this.messages = new Array(); 874 this.historyLength = 0; 875 876 // Initialization 877 this.setHistoryLength(historyLength == null ? 100 : historyLength); 878 this.enableTimeStamp(); 879 this.setLevel(net.user1.logger.Logger.INFO); 880 }; 881 882 //============================================================================== 883 // INHERITANCE 884 //============================================================================== 885 net.user1.utils.extend(net.user1.logger.Logger, net.user1.events.EventDispatcher); 886 887 //============================================================================== 888 // STATIC VARIABLES 889 //============================================================================== 890 /** @constant */ 891 net.user1.logger.Logger.FATAL = "FATAL"; 892 /** @constant */ 893 net.user1.logger.Logger.ERROR = "ERROR"; 894 /** @constant */ 895 net.user1.logger.Logger.WARN = "WARN"; 896 /** @constant */ 897 net.user1.logger.Logger.INFO = "INFO"; 898 /** @constant */ 899 net.user1.logger.Logger.DEBUG = "DEBUG"; 900 net.user1.logger.Logger.logLevels = new Array(net.user1.logger.Logger.FATAL, 901 net.user1.logger.Logger.ERROR, 902 net.user1.logger.Logger.WARN, 903 net.user1.logger.Logger.INFO, 904 net.user1.logger.Logger.DEBUG); 905 906 //============================================================================== 907 // INSTANCE METHODS 908 //============================================================================== 909 net.user1.logger.Logger.prototype.setLevel = function (level) { 910 if (level !== undefined) { 911 for (var i = 0; i < net.user1.logger.Logger.logLevels.length; i++) { 912 if (net.user1.logger.Logger.logLevels[i].toLowerCase() == level.toLowerCase()) { 913 this.logLevel = i; 914 this.dispatchEvent(new net.user1.logger.LogEvent(net.user1.logger.LogEvent.LEVEL_CHANGE, null, level)); 915 return; 916 } 917 } 918 } 919 920 this.warn("Invalid log level specified: " + level); 921 }; 922 923 net.user1.logger.Logger.prototype.getLevel = function () { 924 return net.user1.logger.Logger.logLevels[this.logLevel]; 925 }; 926 927 net.user1.logger.Logger.prototype.fatal = function (msg) { 928 this.addEntry(0, net.user1.logger.Logger.FATAL, msg); 929 }; 930 931 net.user1.logger.Logger.prototype.error = function (msg) { 932 this.addEntry(1, net.user1.logger.Logger.ERROR, msg); 933 }; 934 935 net.user1.logger.Logger.prototype.warn = function (msg) { 936 this.addEntry(2, net.user1.logger.Logger.WARN, msg); 937 }; 938 939 net.user1.logger.Logger.prototype.info = function (msg) { 940 this.addEntry(3, net.user1.logger.Logger.INFO, msg); 941 }; 942 943 net.user1.logger.Logger.prototype.debug = function (msg) { 944 this.addEntry(4, net.user1.logger.Logger.DEBUG, msg); 945 }; 946 947 net.user1.logger.Logger.prototype.addSuppressionTerm = function (term) { 948 this.debug("Added suppression term. Log messages containing '" 949 + term + "' will now be ignored."); 950 this.suppressionTerms.push(term); 951 }; 952 953 net.user1.logger.Logger.prototype.removeSuppressionTerm = function (term) { 954 var termIndex = net.user1.utils.ArrayUtil.indexOf(this.suppressionTerms, term); 955 if (termIndex != -1) { 956 this.suppressionTerms.splice(termIndex, 1); 957 this.debug("Removed suppression term. Log messages containing '" 958 + term + "' will now be shown."); 959 return true; 960 } 961 return false; 962 }; 963 964 /** @private */ 965 net.user1.logger.Logger.prototype.addEntry = function (level, levelName, msg) { 966 var timeStamp = ""; 967 var time; 968 969 // Abort if the log's level is lower than the message's level. 970 if (this.logLevel < level) { 971 return; 972 } 973 974 // Don't log messages if they contain any of the suppression terms. 975 for (var i = this.suppressionTerms.length; --i >= 0;) { 976 if (msg.indexOf(this.suppressionTerms[i]) != -1) { 977 return; 978 } 979 } 980 981 if (this.timeStampEnabled) { 982 time = new Date(); 983 timeStamp = time.getMonth()+1 + "/" + String(time.getDate()) 984 + "/" + String(time.getFullYear()).substr(2) 985 + " " + net.user1.utils.NumericFormatter.dateToLocalHrMinSecMs(time) 986 + " UTC" + (time.getTimezoneOffset() >= 0 ? "-" : "+") 987 + Math.abs(time.getTimezoneOffset() / 60); 988 } 989 990 // Log the message. 991 this.addToHistory(levelName, msg, timeStamp); 992 993 var e = new net.user1.logger.LogEvent(net.user1.logger.LogEvent.UPDATE, 994 msg, levelName, timeStamp); 995 this.dispatchEvent(e); 996 }; 997 998 /** @private */ 999 net.user1.logger.Logger.prototype.setHistoryLength = function (newHistoryLength) { 1000 this.historyLength = newHistoryLength; 1001 1002 if (this.messages.length > this.historyLength) { 1003 this.messages.splice(this.historyLength); 1004 } 1005 }; 1006 1007 net.user1.logger.Logger.prototype.getHistoryLength = function () { 1008 return this.historyLength; 1009 }; 1010 1011 /** @private */ 1012 net.user1.logger.Logger.prototype.addToHistory = function (level, msg, timeStamp) { 1013 this.messages.push(timeStamp + (timeStamp == "" ? "" : " ") + level + ": " + msg); 1014 if (this.messages.length > this.historyLength) { 1015 this.messages.shift(); 1016 } 1017 }; 1018 1019 net.user1.logger.Logger.prototype.getHistory = function () { 1020 return this.messages.slice(0); 1021 }; 1022 1023 net.user1.logger.Logger.prototype.enableTimeStamp = function () { 1024 this.timeStampEnabled = true; 1025 }; 1026 1027 net.user1.logger.Logger.prototype.disableTimeStamp = function () { 1028 this.timeStampEnabled = false; 1029 }; 1030 1031 net.user1.logger.Logger.prototype.toString = function () { 1032 return "[object Logger]"; 1033 }; 1034 //============================================================================== 1035 // CLASS DECLARATION 1036 //============================================================================== 1037 /** @class 1038 @extends net.user1.events.Event 1039 */ 1040 net.user1.orbiter.ConnectionManagerEvent = function (type, connection, status) { 1041 net.user1.events.Event.call(this, type); 1042 1043 this.connection = connection 1044 this.status = status; 1045 }; 1046 1047 //============================================================================== 1048 // INHERITANCE 1049 //============================================================================== 1050 net.user1.utils.extend(net.user1.orbiter.ConnectionManagerEvent, net.user1.events.Event); 1051 1052 //============================================================================== 1053 // STATIC VARIABLES 1054 //============================================================================== 1055 1056 /** @constant */ 1057 net.user1.orbiter.ConnectionManagerEvent.BEGIN_CONNECT = "BEGIN_CONNECT"; 1058 /** @constant */ 1059 net.user1.orbiter.ConnectionManagerEvent.SELECT_CONNECTION = "SELECT_CONNECTION"; 1060 /** @constant */ 1061 net.user1.orbiter.ConnectionManagerEvent.READY = "READY"; 1062 /** @constant */ 1063 net.user1.orbiter.ConnectionManagerEvent.CONNECT_FAILURE = "CONNECT_FAILURE"; 1064 /** @constant */ 1065 net.user1.orbiter.ConnectionManagerEvent.CLIENT_KILL_CONNECT = "CLIENT_KILL_CONNECT"; 1066 /** @constant */ 1067 net.user1.orbiter.ConnectionManagerEvent.SERVER_KILL_CONNECT = "SERVER_KILL_CONNECT"; 1068 /** @constant */ 1069 net.user1.orbiter.ConnectionManagerEvent.DISCONNECT = "DISCONNECT"; 1070 /** @constant */ 1071 net.user1.orbiter.ConnectionManagerEvent.CONNECTION_STATE_CHANGE = "CONNECTION_STATE_CHANGE"; 1072 /** @constant */ 1073 net.user1.orbiter.ConnectionManagerEvent.SESSION_TERMINATED = "SESSION_TERMINATED"; 1074 1075 //============================================================================== 1076 // INSTANCE METHODS 1077 //============================================================================== 1078 1079 net.user1.orbiter.ConnectionManagerEvent.prototype.getConnection = function () { 1080 return this.connection; 1081 } 1082 1083 net.user1.orbiter.ConnectionManagerEvent.prototype.getStatus = function () { 1084 return this.status; 1085 } 1086 1087 net.user1.orbiter.ConnectionManagerEvent.prototype.toString = function () { 1088 return "[object ConnectionManagerEvent]"; 1089 } 1090 1091 //============================================================================== 1092 // CLASS DECLARATION 1093 //============================================================================== 1094 /** @class 1095 1096 The ConnectionManager class dispatches the following events: 1097 1098 <ul class="summary"> 1099 <li class="fixedFont">{@link net.user1.orbiter.ConnectionManagerEvent.BEGIN_CONNECT}</li> 1100 <li class="fixedFont">{@link net.user1.orbiter.ConnectionManagerEvent.SELECT_CONNECTION}</li> 1101 <li class="fixedFont">{@link net.user1.orbiter.ConnectionManagerEvent.CONNECT_FAILURE}</li> 1102 <li class="fixedFont">{@link net.user1.orbiter.ConnectionManagerEvent.DISCONNECT}</li> 1103 <li class="fixedFont">{@link net.user1.orbiter.ConnectionManagerEvent.SERVER_KILL_CONNECT}</li> 1104 <li class="fixedFont">{@link net.user1.orbiter.ConnectionManagerEvent.CLIENT_KILL_CONNECT}</li> 1105 <li class="fixedFont">{@link net.user1.orbiter.ConnectionManagerEvent.READY}</li> 1106 </ul> 1107 1108 To register for events, use {@link net.user1.events.EventDispatcher#addEventListener}. 1109 1110 @extends net.user1.events.EventDispatcher 1111 1112 * @see net.user1.orbiter.Orbiter#connect 1113 */ 1114 net.user1.orbiter.ConnectionManager = function (orbiter) { 1115 // Call superconstructor 1116 net.user1.events.EventDispatcher.call(this); 1117 1118 // Variables 1119 this.orbiter = orbiter; 1120 this.connectAttemptCount = 0; 1121 this.connectAbortCount = 0; 1122 this.readyCount = 0; 1123 this.connectFailedCount = 0; 1124 this.setConnectionState(net.user1.orbiter.ConnectionState.NOT_CONNECTED); 1125 this.readyTimeout = 0; 1126 this.connections = new Array(); 1127 this.activeConnection = null; 1128 this.inProgressConnection = null; 1129 this.currentConnectionIndex = 0; 1130 this.attemptedConnections = null; 1131 this.setReadyTimeout(net.user1.orbiter.ConnectionManager.DEFAULT_READY_TIMEOUT); 1132 1133 // Initialization 1134 // Make all Orbiter instances in this VM share the same server affinity 1135 this.setGlobalAffinity(true); 1136 }; 1137 1138 //============================================================================== 1139 // INHERITANCE 1140 //============================================================================== 1141 net.user1.utils.extend(net.user1.orbiter.ConnectionManager, net.user1.events.EventDispatcher); 1142 1143 //============================================================================== 1144 // STATIC VARIABLES 1145 //============================================================================== 1146 net.user1.orbiter.ConnectionManager.DEFAULT_READY_TIMEOUT = 10000; 1147 1148 // ============================================================================= 1149 // CONNECT AND DISCONNECT 1150 // ============================================================================= 1151 net.user1.orbiter.ConnectionManager.prototype.connect = function () { 1152 if (this.connections.length == 0) { 1153 this.orbiter.getLog().error("[CONNECTION_MANAGER] No connections defined. Connection request ignored."); 1154 return; 1155 } 1156 1157 this.connectAttemptCount++; 1158 this.attemptedConnections = new Array(); 1159 1160 switch (this.connectionState) { 1161 case net.user1.orbiter.ConnectionState.CONNECTION_IN_PROGRESS: 1162 this.orbiter.getLog().info("[CONNECTION_MANAGER] Connection attempt already in " 1163 + "progress. Existing attempt must be aborted before" 1164 + " new connection attempt begins..."); 1165 this.disconnect(); 1166 break; 1167 1168 case net.user1.orbiter.ConnectionState.READY: 1169 this.orbiter.getLog().info("[CONNECTION_MANAGER] Existing connection to Union" 1170 + " must be disconnected before new connection" 1171 + " attempt begins."); 1172 this.disconnect(); 1173 break; 1174 } 1175 this.setConnectionState(net.user1.orbiter.ConnectionState.CONNECTION_IN_PROGRESS); 1176 1177 this.orbiter.getLog().debug("[CONNECTION_MANAGER] Searching for most recent valid connection."); 1178 var originalConnectionIndex = this.currentConnectionIndex; 1179 while (!this.getCurrentConnection().isValid()) { 1180 this.advance(); 1181 if (this.currentConnectionIndex == originalConnectionIndex) { 1182 // Couldn't find a valid connection, so start the connection with 1183 // the first connection in the connection list 1184 this.orbiter.getLog().debug("[CONNECTION_MANAGER] No valid connection found. Starting connection attempt with first connection."); 1185 this.currentConnectionIndex = 0; 1186 break; 1187 } 1188 } 1189 1190 this.dispatchBeginConnect(); 1191 this.connectCurrentConnection(); 1192 }; 1193 1194 net.user1.orbiter.ConnectionManager.prototype.disconnect = function () { 1195 if (this.connections.length == 0) { 1196 this.dispatchConnectFailure("No connections defined. Disconnection attempt failed."); 1197 return; 1198 } 1199 1200 switch (this.connectionState) { 1201 // Currently connected 1202 case net.user1.orbiter.ConnectionState.READY: 1203 this.orbiter.getLog().info("[CONNECTION_MANAGER] Closing existing connection: " 1204 + this.getActiveConnection().toString()); 1205 this.setConnectionState(net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS); 1206 this.disconnectConnection(this.getActiveConnection()); 1207 break; 1208 1209 // Currently attempting to connect 1210 case net.user1.orbiter.ConnectionState.CONNECTION_IN_PROGRESS: 1211 this.orbiter.getLog().info("[CONNECTION_MANAGER] Aborting existing connection attempt: " 1212 + this.getInProgressConnection().toString()); 1213 this.connectAbortCount++; 1214 this.setConnectionState(net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS); 1215 this.disconnectConnection(this.getInProgressConnection()); 1216 this.orbiter.getLog().info("[CONNECTION_MANAGER] Connection abort complete."); 1217 break; 1218 1219 // Currently attempting to disconnect 1220 case net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS: 1221 this.orbiter.getLog().info("[CONNECTION_MANAGER] Disconnection request ignored." 1222 + " Already disconnecting."); 1223 break; 1224 } 1225 }; 1226 1227 /** @private */ 1228 net.user1.orbiter.ConnectionManager.prototype.disconnectConnection = function (connection) { 1229 connection.disconnect(); 1230 }; 1231 1232 /** @private */ 1233 net.user1.orbiter.ConnectionManager.prototype.connectCurrentConnection = function () { 1234 // If there are no Connections defined, fail immediately 1235 if (this.connections.length == 0) { 1236 this.setConnectionState(net.user1.orbiter.ConnectionState.NOT_CONNECTED); 1237 this.connectFailedCount++; 1238 this.dispatchConnectFailure("No connections defined. Connection attempt failed."); 1239 return; 1240 } 1241 1242 this.inProgressConnection = this.getCurrentConnection(); 1243 1244 // If the requested connection has already been attempted this round, 1245 // ignore it. 1246 if (net.user1.utils.ArrayUtil.indexOf(this.attemptedConnections, this.inProgressConnection) != -1) { 1247 this.advanceAndConnect(); 1248 return; 1249 } 1250 1251 this.dispatchSelectConnection(this.inProgressConnection); 1252 this.orbiter.getLog().info("[CONNECTION_MANAGER] Attempting connection via " 1253 + this.inProgressConnection.toString() + ". (Connection " 1254 + (this.attemptedConnections.length+1) + " of " 1255 + this.connections.length + ". Attempt " + this.connectAttemptCount +" since last successful connection)."); 1256 this.addConnectionListeners(this.inProgressConnection); 1257 this.inProgressConnection.connect(); 1258 }; 1259 1260 /** @private */ 1261 net.user1.orbiter.ConnectionManager.prototype.advanceAndConnect = function () { 1262 if (!this.connectAttemptComplete()) { 1263 this.advance(); 1264 this.connectCurrentConnection(); 1265 } else { 1266 // Tried all connections, so give up and dispatch CONNECT_FAILURE 1267 this.connectFailedCount++; 1268 this.setConnectionState(net.user1.orbiter.ConnectionState.NOT_CONNECTED); 1269 this.orbiter.getLog().info("[CONNECTION_MANAGER] Connection failed for all specified hosts and ports."); 1270 this.dispatchConnectFailure("Connection failed for all specified hosts and ports."); 1271 } 1272 }; 1273 1274 /** @private */ 1275 net.user1.orbiter.ConnectionManager.prototype.connectAttemptComplete = function () { 1276 return this.attemptedConnections.length == this.connections.length; 1277 }; 1278 1279 /** @private */ 1280 net.user1.orbiter.ConnectionManager.prototype.advance = function () { 1281 this.currentConnectionIndex++; 1282 if (this.currentConnectionIndex == this.connections.length) { 1283 this.currentConnectionIndex = 0; 1284 } 1285 }; 1286 1287 // ============================================================================= 1288 // CONNECTION OBJECT MANAGEMENT 1289 // ============================================================================= 1290 net.user1.orbiter.ConnectionManager.prototype.addConnection = function (connection) { 1291 if (connection != null) { 1292 this.orbiter.getLog().info("[CONNECTION_MANAGER] New connection added. " 1293 + connection.toString() + "."); 1294 connection.setOrbiter(this.orbiter); 1295 this.connections.push(connection); 1296 } 1297 }; 1298 1299 net.user1.orbiter.ConnectionManager.prototype.removeConnection = function (connection) { 1300 if (connection != null) { 1301 connection.disconnect(); 1302 this.removeConnectionListeners(connection); 1303 return net.user1.utils.ArrayUtil.remove(this.connections, connection); 1304 } else { 1305 return false; 1306 } 1307 }; 1308 1309 net.user1.orbiter.ConnectionManager.prototype.removeAllConnections = function () { 1310 if (this.connections.length == 0) { 1311 this.orbiter.getLog().info("[CONNECTION_MANAGER] removeAllConnections() ignored. " + 1312 " No connections to remove."); 1313 return; 1314 } 1315 1316 this.orbiter.getLog().info("[CONNECTION_MANAGER] Removing all connections..."); 1317 this.disconnect(); 1318 while (this.connections.length > 0) { 1319 this.removeConnection(this.connections[0]); 1320 } 1321 this.currentConnectionIndex = 0; 1322 this.orbiter.getLog().info("[CONNECTION_MANAGER] All connections removed."); 1323 }; 1324 1325 // ============================================================================= 1326 // CONNECTION ACCESS 1327 // ============================================================================= 1328 net.user1.orbiter.ConnectionManager.prototype.getActiveConnection = function () { 1329 return this.activeConnection; 1330 }; 1331 1332 net.user1.orbiter.ConnectionManager.prototype.getInProgressConnection = function () { 1333 return this.inProgressConnection; 1334 }; 1335 1336 net.user1.orbiter.ConnectionManager.prototype.getConnections = function () { 1337 return this.connections.slice(); 1338 }; 1339 1340 /** @private */ 1341 net.user1.orbiter.ConnectionManager.prototype.getCurrentConnection = function () { 1342 return this.connections[this.currentConnectionIndex]; 1343 }; 1344 1345 // ============================================================================= 1346 // CONNECTION LISTENER REGISTRATION 1347 // ============================================================================= 1348 /** @private */ 1349 net.user1.orbiter.ConnectionManager.prototype.addConnectionListeners = function(connection) { 1350 if (connection != null) { 1351 connection.addEventListener(net.user1.orbiter.ConnectionEvent.READY, this.readyListener, this); 1352 connection.addEventListener(net.user1.orbiter.ConnectionEvent.CONNECT_FAILURE, this.connectFailureListener, this); 1353 connection.addEventListener(net.user1.orbiter.ConnectionEvent.DISCONNECT, this.disconnectListener, this); 1354 connection.addEventListener(net.user1.orbiter.ConnectionEvent.CLIENT_KILL_CONNECT, this.clientKillConnectListener, this); 1355 connection.addEventListener(net.user1.orbiter.ConnectionEvent.SERVER_KILL_CONNECT, this.serverKillConnectListener, this); 1356 } 1357 }; 1358 1359 /** @private */ 1360 net.user1.orbiter.ConnectionManager.prototype.removeConnectionListeners = function (connection) { 1361 if (connection != null) { 1362 connection.removeEventListener(net.user1.orbiter.ConnectionEvent.READY, this.readyListener, this); 1363 connection.removeEventListener(net.user1.orbiter.ConnectionEvent.CONNECT_FAILURE, this.connectFailureListener, this); 1364 connection.removeEventListener(net.user1.orbiter.ConnectionEvent.DISCONNECT, this.disconnectListener, this); 1365 connection.removeEventListener(net.user1.orbiter.ConnectionEvent.CLIENT_KILL_CONNECT, this.clientKillConnectListener, this); 1366 connection.removeEventListener(net.user1.orbiter.ConnectionEvent.SERVER_KILL_CONNECT, this.serverKillConnectListener, this); 1367 } 1368 }; 1369 1370 // ============================================================================= 1371 // CONNECTION STATE ACCESS 1372 // ============================================================================= 1373 net.user1.orbiter.ConnectionManager.prototype.isReady = function () { 1374 return this.connectionState == net.user1.orbiter.ConnectionState.READY; 1375 } 1376 1377 net.user1.orbiter.ConnectionManager.prototype.setConnectionState = function (state) { 1378 var changed = false; 1379 if (state != this.connectionState) { 1380 changed = true; 1381 } 1382 this.connectionState = state; 1383 if (changed) { 1384 this.dispatchConnectionStateChange(); 1385 } 1386 }; 1387 1388 net.user1.orbiter.ConnectionManager.prototype.getConnectionState = function () { 1389 return this.connectionState; 1390 }; 1391 1392 // ============================================================================= 1393 // CONNECTION COUNT MANAGEMENT 1394 // ============================================================================= 1395 net.user1.orbiter.ConnectionManager.prototype.getReadyCount = function () { 1396 return this.readyCount; 1397 }; 1398 1399 net.user1.orbiter.ConnectionManager.prototype.getConnectFailedCount = function () { 1400 return this.connectFailedCount; 1401 }; 1402 1403 net.user1.orbiter.ConnectionManager.prototype.getConnectAttemptCount = function () { 1404 return this.connectAttemptCount; 1405 }; 1406 1407 net.user1.orbiter.ConnectionManager.prototype.getConnectAbortCount = function () { 1408 return this.connectAbortCount; 1409 }; 1410 1411 // ============================================================================= 1412 // CURRENT CONNECTION LISTENERS 1413 // ============================================================================= 1414 /** @private */ 1415 net.user1.orbiter.ConnectionManager.prototype.readyListener = function (e) { 1416 this.setConnectionState(net.user1.orbiter.ConnectionState.READY); 1417 this.inProgressConnection = null; 1418 this.activeConnection = e.target; 1419 this.readyCount++; 1420 this.connectFailedCount = 0; 1421 this.connectAttemptCount = 0; 1422 this.connectAbortCount = 0; 1423 this.dispatchReady(); 1424 }; 1425 1426 /** @private */ 1427 net.user1.orbiter.ConnectionManager.prototype.connectFailureListener = function (e) { 1428 var failedConnection = e.target; 1429 this.orbiter.getLog().warn("[CONNECTION_MANAGER] Connection failed for " 1430 + failedConnection.toString() 1431 + ". Status: [" + e.getStatus() + "]"); 1432 1433 this.removeConnectionListeners(failedConnection); 1434 this.inProgressConnection = null; 1435 1436 if (this.connectionState == net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS) { 1437 this.dispatchConnectFailure("Connection closed by client."); 1438 } else { 1439 if (failedConnection.getHost() != failedConnection.getRequestedHost()) { 1440 this.orbiter.getLog().info("[CONNECTION_MANAGER] Connection failed for affinity address [" + failedConnection.getHost() + "]. Removing affinity."); 1441 this.clearAffinity(failedConnection.getRequestedHost()); 1442 } 1443 1444 this.attemptedConnections.push(failedConnection); 1445 this.advanceAndConnect(); 1446 } 1447 }; 1448 1449 /** @private */ 1450 net.user1.orbiter.ConnectionManager.prototype.disconnectListener = function (e) { 1451 this.setConnectionState(net.user1.orbiter.ConnectionState.NOT_CONNECTED); 1452 this.removeConnectionListeners(e.target); 1453 this.activeConnection = null; 1454 this.dispatchDisconnect(e.target); 1455 }; 1456 1457 /** @private */ 1458 net.user1.orbiter.ConnectionManager.prototype.clientKillConnectListener = function (e) { 1459 this.dispatchClientKillConnect(e.target); 1460 // This event is always followed by a DISCONNECT event 1461 }; 1462 1463 /** @private */ 1464 net.user1.orbiter.ConnectionManager.prototype.serverKillConnectListener = function (e) { 1465 this.dispatchServerKillConnect(e.target); 1466 // This event is always followed by a DISCONNECT event 1467 }; 1468 1469 // ============================================================================= 1470 // READY TIMEOUT MANAGEMENT 1471 // ============================================================================= 1472 1473 net.user1.orbiter.ConnectionManager.prototype.setReadyTimeout = function (milliseconds) { 1474 if (milliseconds > 0) { 1475 this.readyTimeout = milliseconds; 1476 this.orbiter.getLog().info("[CONNECTION_MANAGER] Ready timeout set to " + milliseconds + " ms."); 1477 if (milliseconds < 3000) { 1478 this.orbiter.getLog().warn("[CONNECTION_MANAGER] Current ready timeout (" 1479 + milliseconds + ") may not allow sufficient time" 1480 + " to connect to Union Server over a typical" 1481 + " internet connection."); 1482 } 1483 } else { 1484 this.orbiter.getLog().warn("[CONNECTION_MANAGER] Invalid ready timeout specified: " 1485 + milliseconds + ". Duration must be greater than zero."); 1486 } 1487 }; 1488 1489 net.user1.orbiter.ConnectionManager.prototype.getReadyTimeout = function () { 1490 return this.readyTimeout; 1491 }; 1492 1493 // ============================================================================= 1494 // SERVER AFFINITY 1495 // ============================================================================= 1496 net.user1.orbiter.ConnectionManager.prototype.getAffinity = function (host) { 1497 var address = this.affinityData.read("affinity", host+"address"); 1498 var until = parseFloat(this.affinityData.read("affinity", host+"until")); 1499 1500 if (address != null) { 1501 var now = new Date().getTime(); 1502 if (now >= until) { 1503 this.orbiter.getLog().warn("[CONNECTION_MANAGER] Affinity duration expired for address [" 1504 + address + "], host [" + host + "]. Removing affinity."); 1505 this.clearAffinity(host); 1506 } else { 1507 return address; 1508 } 1509 } 1510 1511 return host; 1512 }; 1513 1514 /** 1515 * @private 1516 */ 1517 net.user1.orbiter.ConnectionManager.prototype.setAffinity = function (host, address, duration) { 1518 var until = new Date().getTime() + (duration*60*1000); 1519 // Don't use JSON stringify for affinity values because not all JavaScript 1520 // environments support JSON natively (e.g., non-browser VMs) 1521 this.affinityData.write("affinity", host+"address", address); 1522 this.affinityData.write("affinity", host+"until", until); 1523 1524 this.orbiter.getLog().info("[CONNECTION_MANAGER] Assigning affinity address [" 1525 + address + "] for supplied host [" +host + "]. Duration (minutes): " 1526 + duration); 1527 }; 1528 1529 /** 1530 * @private 1531 */ 1532 net.user1.orbiter.ConnectionManager.prototype.clearAffinity = function (host) { 1533 this.affinityData.remove("affinity", host+"address"); 1534 this.affinityData.remove("affinity", host+"until"); 1535 }; 1536 1537 net.user1.orbiter.ConnectionManager.prototype.setGlobalAffinity = function (enabled) { 1538 if (enabled) { 1539 this.orbiter.getLog().info("[CONNECTION_MANAGER] Global server affinity selected." 1540 + " Using current environment's shared server affinity."); 1541 this.affinityData = net.user1.utils.LocalData; 1542 } else { 1543 this.orbiter.getLog().info("[CONNECTION_MANAGER] Local server affinity selected." 1544 + " The current client will maintain its own, individual server affinity."); 1545 this.affinityData = new net.user1.utils.MemoryStore(); 1546 } 1547 }; 1548 1549 // ============================================================================= 1550 // EVENT DISPATCHING 1551 // ============================================================================= 1552 1553 /** @private */ 1554 net.user1.orbiter.ConnectionManager.prototype.dispatchBeginConnect = function () { 1555 this.dispatchEvent(new net.user1.orbiter.ConnectionManagerEvent(net.user1.orbiter.ConnectionManagerEvent.BEGIN_CONNECT)); 1556 }; 1557 1558 /** @private */ 1559 net.user1.orbiter.ConnectionManager.prototype.dispatchSelectConnection = function (connection) { 1560 this.dispatchEvent(new net.user1.orbiter.ConnectionManagerEvent(net.user1.orbiter.ConnectionManagerEvent.SELECT_CONNECTION, 1561 connection)); 1562 }; 1563 1564 /** @private */ 1565 net.user1.orbiter.ConnectionManager.prototype.dispatchConnectFailure = function (status) { 1566 this.dispatchEvent(new net.user1.orbiter.ConnectionManagerEvent(net.user1.orbiter.ConnectionManagerEvent.CONNECT_FAILURE, 1567 null, status)); 1568 }; 1569 1570 /** @private */ 1571 net.user1.orbiter.ConnectionManager.prototype.dispatchDisconnect = function (connection) { 1572 this.dispatchEvent(new net.user1.orbiter.ConnectionManagerEvent(net.user1.orbiter.ConnectionManagerEvent.DISCONNECT, 1573 connection)); 1574 }; 1575 1576 /** @private */ 1577 net.user1.orbiter.ConnectionManager.prototype.dispatchServerKillConnect = function (connection) { 1578 this.dispatchEvent(new net.user1.orbiter.ConnectionManagerEvent(net.user1.orbiter.ConnectionManagerEvent.SERVER_KILL_CONNECT, 1579 connection)); 1580 }; 1581 1582 /** @private */ 1583 net.user1.orbiter.ConnectionManager.prototype.dispatchClientKillConnect = function (connection) { 1584 this.dispatchEvent(new net.user1.orbiter.ConnectionManagerEvent(net.user1.orbiter.ConnectionManagerEvent.CLIENT_KILL_CONNECT, 1585 connection)); 1586 }; 1587 1588 /** @private */ 1589 net.user1.orbiter.ConnectionManager.prototype.dispatchReady = function () { 1590 this.dispatchEvent(new net.user1.orbiter.ConnectionManagerEvent(net.user1.orbiter.ConnectionManagerEvent.READY)); 1591 }; 1592 1593 /** @private */ 1594 net.user1.orbiter.ConnectionManager.prototype.dispatchConnectionStateChange = function () { 1595 this.dispatchEvent(new net.user1.orbiter.ConnectionManagerEvent(net.user1.orbiter.ConnectionManagerEvent.CONNECTION_STATE_CHANGE)); 1596 }; 1597 1598 /** @private */ 1599 net.user1.orbiter.ConnectionManager.prototype.dispatchSessionTerminated = function () { 1600 this.dispatchEvent(new net.user1.orbiter.ConnectionManagerEvent(net.user1.orbiter.ConnectionManagerEvent.SESSION_TERMINATED)); 1601 }; 1602 1603 // ============================================================================= 1604 // DISPOSAL 1605 // ============================================================================= 1606 net.user1.orbiter.ConnectionManager.prototype.dispose = function () { 1607 this.removeAllConnections(); 1608 this.attemptedConnections = null; 1609 this.activeConnection = null; 1610 this.inProgressConnection = null; 1611 this.connections = null; 1612 }; 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 //============================================================================== 1636 // CLASS DECLARATION 1637 //============================================================================== 1638 /** @class */ 1639 net.user1.orbiter.ConnectionMonitor = function (orbiter) { 1640 // Instance variables 1641 this.connectionTimeout = 0; 1642 this.heartbeatIntervalID = -1; 1643 this.heartbeatCounter = 0; 1644 this.heartbeatEnabled = true; 1645 this.heartbeats = new net.user1.utils.UDictionary(); 1646 1647 this.oldestHeartbeat = 0; 1648 this.heartBeatFrequency = -1; 1649 1650 this.sharedPing = false; 1651 1652 this.autoReconnectMinMS = 0; 1653 this.autoReconnectMaxMS = 0; 1654 this.autoReconnectFrequency = -1; 1655 this.autoReconnectDelayFirstAttempt = false; 1656 this.autoReconnectTimeoutID = -1; 1657 this.autoReconnectAttemptLimit = -1; 1658 1659 this.orbiter = orbiter; 1660 this.msgManager = orbiter.getMessageManager(); 1661 this.log = orbiter.getLog(); 1662 1663 this.disposed = false; 1664 1665 // Initialization 1666 this.orbiter.addEventListener(net.user1.orbiter.OrbiterEvent.READY, this.connectReadyListener, this); 1667 this.orbiter.addEventListener(net.user1.orbiter.OrbiterEvent.CLOSE, this.connectCloseListener, this); 1668 this.disableHeartbeatLogging(); 1669 }; 1670 1671 //============================================================================== 1672 // STATIC VARIABLES 1673 //============================================================================== 1674 net.user1.orbiter.ConnectionMonitor.DEFAULT_HEARTBEAT_FREQUENCY = 10000; 1675 net.user1.orbiter.ConnectionMonitor.MIN_HEARTBEAT_FREQUENCY = 20; 1676 net.user1.orbiter.ConnectionMonitor.DEFAULT_AUTORECONNECT_FREQUENCY = -1; 1677 net.user1.orbiter.ConnectionMonitor.DEFAULT_AUTORECONNECT_ATTEMPT_LIMIT = -1; 1678 net.user1.orbiter.ConnectionMonitor.DEFAULT_CONNECTION_TIMEOUT = 60000; 1679 1680 //============================================================================== 1681 // CONNECTION MONITORING 1682 //============================================================================== 1683 /** @private */ 1684 net.user1.orbiter.ConnectionMonitor.prototype.connectReadyListener = function (e) { 1685 this.msgManager.addMessageListener(net.user1.orbiter.Messages.CLIENT_HEARTBEAT, this.heartbeatMessageListener, this); 1686 this.startHeartbeat(); 1687 this.stopReconnect(); 1688 } 1689 1690 /** @private */ 1691 net.user1.orbiter.ConnectionMonitor.prototype.connectCloseListener = function (e) { 1692 this.stopHeartbeat(); 1693 1694 var numAttempts = this.orbiter.getConnectionManager().getConnectAttemptCount(); 1695 if (numAttempts == 0) { 1696 this.selectReconnectFrequency(); 1697 } 1698 1699 if (this.autoReconnectFrequency > -1) { 1700 if (this.autoReconnectTimeoutID != -1) { 1701 return; 1702 } else { 1703 // Defer reconnection until after all other listeners have processed the 1704 // CLOSE event 1705 var self = this; 1706 setTimeout(function () { 1707 // If another listener disposed of Orbiter, or disabled autoreconnect, quit 1708 if (!self.disposed && self.autoReconnectFrequency != -1) { 1709 self.log.warn("[CONNECTION_MONITOR] Disconnection detected."); 1710 if (self.autoReconnectDelayFirstAttempt 1711 && ( 1712 (numAttempts == 0) 1713 || 1714 (numAttempts == 1 && self.orbiter.getConnectionManager().getReadyCount() == 0) 1715 ) 1716 ) { 1717 self.log.info("[CONNECTION_MONITOR] Delaying reconnection attempt" 1718 + " by " + self.autoReconnectFrequency + " ms..."); 1719 self.scheduleReconnect(self.autoReconnectFrequency); 1720 } else { 1721 self.doReconnect(); 1722 } 1723 } 1724 }, 1); 1725 } 1726 } 1727 } 1728 1729 //============================================================================== 1730 // HEARTBEAT 1731 //============================================================================== 1732 1733 net.user1.orbiter.ConnectionMonitor.prototype.enableHeartbeat = function () { 1734 this.log.info("[CONNECTION_MONITOR] Heartbeat enabled."); 1735 this.heartbeatEnabled = true; 1736 this.startHeartbeat(); 1737 } 1738 1739 net.user1.orbiter.ConnectionMonitor.prototype.disableHeartbeat = function () { 1740 this.log.info("[CONNECTION_MONITOR] Heartbeat disabled."); 1741 this.heartbeatEnabled = false; 1742 this.stopHeartbeat(); 1743 } 1744 1745 /** @private */ 1746 net.user1.orbiter.ConnectionMonitor.prototype.startHeartbeat = function () { 1747 if (!this.heartbeatEnabled) { 1748 this.log.info("[CONNECTION_MONITOR] Heartbeat is currently disabled. Ignoring start request."); 1749 return; 1750 } 1751 1752 this.stopHeartbeat(); 1753 1754 this.heartbeats = new net.user1.utils.UDictionary(); 1755 1756 var currentObj = this; 1757 var callback = this.heartbeatTimerListener; 1758 this.heartbeatIntervalID = setInterval(function () { 1759 callback.call(currentObj); 1760 }, this.heartBeatFrequency); 1761 1762 } 1763 1764 /** @private */ 1765 net.user1.orbiter.ConnectionMonitor.prototype.stopHeartbeat = function () { 1766 clearInterval(this.heartbeatIntervalID); 1767 this.heartbeats = null; 1768 } 1769 1770 /** @private */ 1771 net.user1.orbiter.ConnectionMonitor.prototype.heartbeatTimerListener = function () { 1772 if (!this.orbiter.isReady()) { 1773 this.log.info("[CONNECTION_MONITOR] Orbiter is not connected. Stopping heartbeat."); 1774 this.stopHeartbeat(); 1775 return; 1776 } 1777 1778 var timeSinceOldestHeartbeat; 1779 var now = new Date().getTime(); 1780 1781 this.heartbeats[this.heartbeatCounter] = now; 1782 this.orbiter.getMessageManager().sendUPC("u2", 1783 net.user1.orbiter.Messages.CLIENT_HEARTBEAT, 1784 this.orbiter.getClientID(), 1785 "", 1786 this.heartbeatCounter); 1787 this.heartbeatCounter++; 1788 1789 // Assign the oldest heartbeat 1790 if (net.user1.utils.ObjectUtil.length(this.heartbeats) == 1) { 1791 this.oldestHeartbeat = now; 1792 } else { 1793 this.oldestHeartbeat = Number.MAX_VALUE; 1794 for (var p in this.heartbeats) { 1795 if (this.heartbeats[p] < this.oldestHeartbeat) { 1796 this.oldestHeartbeat = this.heartbeats[p]; 1797 } 1798 } 1799 } 1800 // Close connection if too much time has passed since the last response 1801 timeSinceOldestHeartbeat = now - this.oldestHeartbeat; 1802 if (timeSinceOldestHeartbeat > this.connectionTimeout) { 1803 this.log.warn("[CONNECTION_MONITOR] No response from server in " + 1804 timeSinceOldestHeartbeat + "ms. Starting automatic disconnect."); 1805 this.orbiter.disconnect(); 1806 } 1807 } 1808 1809 /** @private */ 1810 net.user1.orbiter.ConnectionMonitor.prototype.heartbeatMessageListener = function (fromClientID, id) { 1811 var ping = new Date().getTime() - this.heartbeats[parseInt(id)]; 1812 if (typeof this.orbiter.self().setAttribute === "undefined") { 1813 // OrbiterMicro 1814 this.orbiter.self().ping = ping; 1815 this.orbiter.getMessageManager().sendUPC("u3", 1816 this.orbiter.getClientID(), 1817 "", 1818 "_PING", 1819 ping.toString(), 1820 "", 1821 this.sharedPing ? "4" : "0"); 1822 } else { 1823 // Orbiter 1824 this.orbiter.self().setAttribute("_PING", 1825 ping.toString(), 1826 null, 1827 this.sharedPing); 1828 } 1829 delete this.heartbeats[parseInt(id)]; 1830 } 1831 1832 //============================================================================== 1833 // RECONNECTION 1834 //============================================================================== 1835 /** @private */ 1836 net.user1.orbiter.ConnectionMonitor.prototype.reconnectTimerListener = function (e) { 1837 this.stopReconnect(); 1838 if (this.orbiter.getConnectionManager().connectionState == net.user1.orbiter.ConnectionState.NOT_CONNECTED) { 1839 this.doReconnect(); 1840 } 1841 } 1842 1843 /** @private */ 1844 net.user1.orbiter.ConnectionMonitor.prototype.stopReconnect = function () { 1845 clearTimeout(this.autoReconnectTimeoutID); 1846 this.autoReconnectTimeoutID = -1 1847 } 1848 1849 /** @private */ 1850 net.user1.orbiter.ConnectionMonitor.prototype.scheduleReconnect = function (milliseconds) { 1851 // Reset the timer 1852 this.stopReconnect(); 1853 var currentObj = this; 1854 var callback = this.reconnectTimerListener; 1855 this.autoReconnectTimeoutID = setTimeout(function () { 1856 currentObj.autoReconnectTimeoutID = -1; 1857 callback.call(currentObj); 1858 }, milliseconds); 1859 }; 1860 1861 /** @private */ 1862 net.user1.orbiter.ConnectionMonitor.prototype.selectReconnectFrequency = function () { 1863 if (this.autoReconnectMinMS == -1) { 1864 this.autoReconnectFrequency = -1; 1865 } else if (this.autoReconnectMinMS == this.autoReconnectMaxMS) { 1866 this.autoReconnectFrequency = this.autoReconnectMinMS; 1867 } else { 1868 this.autoReconnectFrequency = getRandInt(this.autoReconnectMinMS, this.autoReconnectMaxMS); 1869 this.log.info("[CONNECTION_MONITOR] Random auto-reconnect frequency selected: [" + 1870 this.autoReconnectFrequency + "] ms."); 1871 } 1872 1873 function getRandInt (min, max) { 1874 return min + Math.floor(Math.random()*(max+1 - min)); 1875 } 1876 }; 1877 1878 /** @private */ 1879 net.user1.orbiter.ConnectionMonitor.prototype.doReconnect = function () { 1880 var numActualAttempts = this.orbiter.getConnectionManager().getConnectAttemptCount(); 1881 var numReconnectAttempts; 1882 1883 if (this.orbiter.getConnectionManager().getReadyCount() == 0) { 1884 numReconnectAttempts = numActualAttempts - 1; 1885 } else { 1886 numReconnectAttempts = numActualAttempts; 1887 } 1888 1889 if (this.autoReconnectAttemptLimit != -1 1890 && numReconnectAttempts > 0 1891 && numReconnectAttempts % (this.autoReconnectAttemptLimit) == 0) { 1892 this.log.warn("[CONNECTION_MONITOR] Automatic reconnect attempt limit reached." 1893 + " No further automatic connection attempts will be made until" 1894 + " the next manual connection attempt."); 1895 return; 1896 } 1897 1898 this.scheduleReconnect(this.autoReconnectFrequency); 1899 1900 this.log.warn("[CONNECTION_MONITOR] Attempting automatic reconnect. (Next attempt in " 1901 + this.autoReconnectFrequency + "ms.)"); 1902 this.orbiter.connect(); 1903 } 1904 1905 //============================================================================== 1906 // CONFIGURATION 1907 //============================================================================== 1908 1909 net.user1.orbiter.ConnectionMonitor.prototype.restoreDefaults = function () { 1910 this.setAutoReconnectFrequency(net.user1.orbiter.ConnectionMonitor.DEFAULT_AUTORECONNECT_FREQUENCY); 1911 this.setAutoReconnectAttemptLimit(net.user1.orbiter.ConnectionMonitor.DEFAULT_AUTORECONNECT_ATTEMPT_LIMIT); 1912 this.setConnectionTimeout(net.user1.orbiter.ConnectionMonitor.DEFAULT_CONNECTION_TIMEOUT); 1913 this.setHeartbeatFrequency(net.user1.orbiter.ConnectionMonitor.DEFAULT_HEARTBEAT_FREQUENCY); 1914 } 1915 1916 net.user1.orbiter.ConnectionMonitor.prototype.setHeartbeatFrequency = function (milliseconds) { 1917 if (milliseconds >= net.user1.orbiter.ConnectionMonitor.MIN_HEARTBEAT_FREQUENCY) { 1918 this.heartBeatFrequency = milliseconds; 1919 this.log.info("[CONNECTION_MONITOR] Heartbeat frequency set to " 1920 + milliseconds + " ms."); 1921 // Log a warning for low heartbeat frequencies... 1922 if (milliseconds >= net.user1.orbiter.ConnectionMonitor.MIN_HEARTBEAT_FREQUENCY && milliseconds < 1000) { 1923 this.log.info("[CONNECTION_MONITOR] HEARTBEAT FREQUENCY WARNING: " 1924 + milliseconds + " ms. Current frequency will generate " 1925 + (Math.floor((1000/milliseconds)*10)/10) 1926 + " messages per second per connected client."); 1927 } 1928 1929 // If the connection is ready, then restart 1930 // the heartbeat when the heartbeat frequency changes. 1931 if (this.orbiter.isReady()) { 1932 this.startHeartbeat(); 1933 } 1934 } else { 1935 this.log.warn("[CONNECTION_MONITOR] Invalid heartbeat frequency specified: " 1936 + milliseconds + ". Frequency must be " 1937 + net.user1.orbiter.ConnectionMonitor.MIN_HEARTBEAT_FREQUENCY + " or greater."); 1938 } 1939 } 1940 1941 net.user1.orbiter.ConnectionMonitor.prototype.getHeartbeatFrequency = function () { 1942 return this.heartBeatFrequency; 1943 } 1944 1945 net.user1.orbiter.ConnectionMonitor.prototype.setAutoReconnectFrequency = function (minMS, maxMS, delayFirstAttempt) { 1946 maxMS = (typeof maxMS == "undefined") ? -1 : maxMS; 1947 delayFirstAttempt = (typeof delayFirstAttempt == "undefined") ? false : delayFirstAttempt; 1948 1949 if (minMS == 0 || minMS < -1) { 1950 this.log.warn("[CONNECTION_MONITOR] Invalid auto-reconnect minMS specified: [" 1951 + minMS + "]. Value must not be zero or less than -1. Value adjusted" 1952 + " to [-1] (no reconnect)."); 1953 minMS = -1; 1954 } 1955 if (minMS == -1) { 1956 this.stopReconnect(); 1957 } else { 1958 if (maxMS == -1) { 1959 maxMS = minMS; 1960 } 1961 if (maxMS < minMS) { 1962 this.log.warn("[CONNECTION_MONITOR] Invalid auto-reconnect maxMS specified: [" 1963 + maxMS + "]." + " Value of maxMS must be greater than or equal " 1964 + "to minMS. Value adjusted to [" + minMS + "]."); 1965 maxMS = minMS; 1966 } 1967 } 1968 1969 this.autoReconnectDelayFirstAttempt = delayFirstAttempt; 1970 this.autoReconnectMinMS = minMS; 1971 this.autoReconnectMaxMS = maxMS; 1972 1973 this.log.info("[CONNECTION_MONITOR] Assigning auto-reconnect frequency settings: [minMS: " 1974 + minMS + ", maxMS: " + maxMS + ", delayFirstAttempt: " 1975 + delayFirstAttempt.toString() + "]."); 1976 if (minMS > 0 && minMS < 1000) { 1977 this.log.info("[CONNECTION_MONITOR] RECONNECT FREQUENCY WARNING: " 1978 + minMS + " minMS specified. Current frequency will cause " 1979 + (Math.floor((1000/minMS)*10)/10).toString() 1980 + " reconnection attempts per second."); 1981 } 1982 this.selectReconnectFrequency(); 1983 } 1984 1985 net.user1.orbiter.ConnectionMonitor.prototype.getAutoReconnectFrequency = function () { 1986 return this.autoReconnectFrequency; 1987 } 1988 1989 net.user1.orbiter.ConnectionMonitor.prototype.setAutoReconnectAttemptLimit = function (attempts) { 1990 if (attempts < -1 || attempts == 0) { 1991 this.log.warn("[CONNECTION_MONITOR] Invalid Auto-reconnect attempt limit specified: " 1992 + attempts + ". Limit must -1 or greater than 1."); 1993 return; 1994 } 1995 1996 this.autoReconnectAttemptLimit = attempts; 1997 1998 if (attempts == -1) { 1999 this.log.info("[CONNECTION_MONITOR] Auto-reconnect attempt limit set to none."); 2000 } else { 2001 this.log.info("[CONNECTION_MONITOR] Auto-reconnect attempt limit set to " 2002 + attempts + " attempt(s)."); 2003 } 2004 }; 2005 2006 net.user1.orbiter.ConnectionMonitor.prototype.getAutoReconnectAttemptLimit = function () { 2007 return this.autoReconnectAttemptLimit; 2008 } 2009 2010 net.user1.orbiter.ConnectionMonitor.prototype.setConnectionTimeout = function (milliseconds) { 2011 if (milliseconds > 0) { 2012 this.connectionTimeout = milliseconds; 2013 this.log.info("[CONNECTION_MONITOR] Connection timeout set to " 2014 + milliseconds + " ms."); 2015 } else { 2016 this.log.warn("[CONNECTION_MONITOR] Invalid connection timeout specified: " 2017 + milliseconds + ". Frequency must be greater " 2018 + "than zero."); 2019 } 2020 } 2021 2022 net.user1.orbiter.ConnectionMonitor.prototype.getConnectionTimeout = function () { 2023 return this.connectionTimeout; 2024 } 2025 2026 net.user1.orbiter.ConnectionMonitor.prototype.sharePing = function (share) { 2027 this.sharedPing = share; 2028 } 2029 2030 net.user1.orbiter.ConnectionMonitor.prototype.isPingShared = function () { 2031 return this.sharedPing; 2032 } 2033 2034 net.user1.orbiter.ConnectionMonitor.prototype.disableHeartbeatLogging = function () { 2035 this.log.addSuppressionTerm("<A>CLIENT_HEARTBEAT</A>"); 2036 this.log.addSuppressionTerm("<A>_PING</A>"); 2037 this.log.addSuppressionTerm("[_PING]"); 2038 this.log.addSuppressionTerm("<![CDATA[_PING]]>"); 2039 } 2040 2041 net.user1.orbiter.ConnectionMonitor.prototype.enableHeartbeatLogging = function () { 2042 this.log.removeSuppressionTerm("<A>CLIENT_HEARTBEAT</A>"); 2043 this.log.removeSuppressionTerm("<A>_PING</A>"); 2044 this.log.removeSuppressionTerm("[_PING]"); 2045 this.log.removeSuppressionTerm("<![CDATA[_PING]]>"); 2046 } 2047 2048 // ============================================================================= 2049 // DISPOSAL 2050 // ============================================================================= 2051 2052 net.user1.orbiter.ConnectionMonitor.prototype.dispose = function () { 2053 this.disposed = true; 2054 2055 this.stopHeartbeat(); 2056 this.stopReconnect(); 2057 2058 this.heartbeats = null; 2059 2060 this.orbiter.removeEventListener(net.user1.orbiter.OrbiterEvent.READY, this.connectReadyListener, this); 2061 this.orbiter.removeEventListener(net.user1.orbiter.OrbiterEvent.CLOSE, this.connectCloseListener, this); 2062 this.orbiter = null; 2063 this.msgManager.removeMessageListener("u7", this.u7); 2064 this.msgManager(null); 2065 this.log = null; 2066 }; 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 //============================================================================== 2085 // CLASS DECLARATION 2086 //============================================================================== 2087 /** @class 2088 @private */ 2089 net.user1.orbiter.CoreMessageListener = function (orbiter) { 2090 this.orbiter = orbiter; 2091 this.log = orbiter.getLog(); 2092 this.registerCoreListeners(); 2093 this.orbiter.getConnectionManager().addEventListener(net.user1.orbiter.ConnectionManagerEvent.SELECT_CONNECTION, 2094 this.selectConnectionListener, this); 2095 }; 2096 2097 net.user1.orbiter.CoreMessageListener.prototype.registerCoreListeners = function () { 2098 var msgMan = this.orbiter.getMessageManager(); 2099 msgMan.addMessageListener(net.user1.orbiter.UPC.RECEIVE_MESSAGE, this.u7, this); 2100 msgMan.addMessageListener(net.user1.orbiter.UPC.SERVER_HELLO, this.u66, this); 2101 msgMan.addMessageListener(net.user1.orbiter.UPC.SESSION_TERMINATED, this.u84, this); 2102 }; 2103 2104 net.user1.orbiter.CoreMessageListener.prototype.selectConnectionListener = function (e) { 2105 var msgMan = this.orbiter.getMessageManager(); 2106 if (msgMan.removeListenersOnDisconnect) { 2107 this.registerCoreListeners(); 2108 } 2109 }; 2110 2111 net.user1.orbiter.CoreMessageListener.prototype.u7 = function (message, 2112 broadcastType, 2113 fromClientID, 2114 toRoomID) { 2115 var msgMan = this.orbiter.getMessageManager(); 2116 var args; 2117 var userDefinedArgs = Array.prototype.slice.call(arguments).slice(4); 2118 2119 // ===== To Clients, or To Server ===== 2120 if (broadcastType != net.user1.orbiter.ReceiveMessageBroadcastType.TO_ROOMS) { 2121 args = [fromClientID].concat(userDefinedArgs); 2122 msgMan.notifyMessageListeners(message, args); 2123 return; 2124 } 2125 2126 // ===== To Rooms ===== 2127 var listeners = msgMan.getMessageListeners(message); 2128 2129 // Split the recipient room ID into two parts 2130 var toRoomSimpleID = net.user1.orbiter.RoomIDParser.getSimpleRoomID(toRoomID); 2131 var toRoomQualifier = net.user1.orbiter.RoomIDParser.getQualifier(toRoomID); 2132 var listenerFound; 2133 var listenerIgnoredMessage; 2134 var messageListener; 2135 2136 // ===== Run once for each message listener ===== 2137 for (var i = 0; i < listeners.length; i++) { 2138 messageListener = listeners[i]; 2139 listenerIgnoredMessage = true; 2140 2141 // --- Has no "forRoomIDs" filter --- 2142 if (messageListener.getForRoomIDs() == null) { 2143 args = [fromClientID, toRoomID].concat(userDefinedArgs); 2144 messageListener.getListenerFunction().apply(messageListener.getThisArg(), args); 2145 listenerFound = true; 2146 listenerIgnoredMessage = false; 2147 continue; // Done with this listener. On to the next. 2148 } 2149 2150 // --- Has a "forRoomIDs" filter --- 2151 var listenerRoomIDs = messageListener.getForRoomIDs(); 2152 var listenerRoomQualifier; 2153 var listenerRoomSimpleID; 2154 var listenerRoomIDString; 2155 // ===== Run once for each room id ===== 2156 for (var i = 0; i < listenerRoomIDs.length; i++) { 2157 listenerRoomIDString = listenerRoomIDs[i]; 2158 listenerRoomQualifier = net.user1.orbiter.RoomIDParser.getQualifier(listenerRoomIDString); 2159 listenerRoomSimpleID = net.user1.orbiter.RoomIDParser.getSimpleRoomID(listenerRoomIDString); 2160 2161 // Check if the listener is interested in the recipient room... 2162 if (listenerRoomQualifier == toRoomQualifier 2163 && 2164 (listenerRoomSimpleID == toRoomSimpleID 2165 || listenerRoomSimpleID == "*")) { 2166 // Found a match. Notify the listener... 2167 2168 // Prepare args. 2169 if (listenerRoomIDs.length == 1) { 2170 // The listener is interested in messages sent to a 2171 // specific room only, so omit the "toRoom" arg. 2172 args = [fromClientID].concat(userDefinedArgs); 2173 } else { 2174 // The listener is interested in messages sent to 2175 // multiple rooms, so include the "toRoomID" arg so the listener 2176 // knows which room received the message. 2177 args = [fromClientID, toRoomID].concat(userDefinedArgs); 2178 } 2179 2180 messageListener.getListenerFunction().apply(messageListener.getThisArg(), args); 2181 listenerFound = true; 2182 listenerIgnoredMessage = false; 2183 break; // Stop looking at this listener's room ids 2184 } 2185 } // Done looking at this listener's room ids 2186 if (listenerIgnoredMessage) { 2187 this.log.debug("Message listener ignored message: " + message + ". " 2188 + "Listener registered to receive " 2189 + "messages sent to: " + messageListener.getForRoomIDs() 2190 + ", but message was sent to: " + toRoomID); 2191 } 2192 } // Done looking at listeners for the incoming message 2193 if (!listenerFound) { 2194 this.log.warn("No message listener handled incoming message: " 2195 + message + ", sent to: " + toRoomID); 2196 } 2197 }; 2198 2199 net.user1.orbiter.CoreMessageListener.prototype.u66 = function (serverVersion, 2200 sessionID, 2201 serverUPCVersionString, 2202 protocolCompatible, 2203 affinityAddress, 2204 affinityDuration) { 2205 this.log.info("[ORBITER] Server version: " + serverVersion); 2206 this.log.info("[ORBITER] Server UPC version: " + serverUPCVersionString); 2207 2208 var inProgressConnection = this.orbiter.getConnectionManager().getInProgressConnection(); 2209 var inProgressConnectionHost = inProgressConnection.getHost(); 2210 if (affinityAddress != "" 2211 && typeof affinityAddress !== "undefined" 2212 && affinityAddress != inProgressConnectionHost) { 2213 this.orbiter.getConnectionManager().setAffinity(inProgressConnectionHost, 2214 affinityAddress, 2215 parseFloat(affinityDuration)); 2216 inProgressConnection.applyAffinity(); 2217 } 2218 }; 2219 2220 /** 2221 * SESSION_TERMINATED 2222 */ 2223 net.user1.orbiter.CoreMessageListener.prototype.u84 = function () { 2224 this.orbiter.getConnectionManager().dispatchSessionTerminated(); 2225 }; 2226 //============================================================================== 2227 // CLASS DECLARATION 2228 //============================================================================== 2229 /** @class 2230 @extends net.user1.events.Event 2231 */ 2232 net.user1.orbiter.OrbiterEvent = function (type, 2233 serverUPCVersion, 2234 connectionRefusal) { 2235 net.user1.events.Event.call(this, type); 2236 2237 this.serverUPCVersion = serverUPCVersion; 2238 this.connectionRefusal = connectionRefusal; 2239 }; 2240 2241 //============================================================================== 2242 // INHERITANCE 2243 //============================================================================== 2244 net.user1.utils.extend(net.user1.orbiter.OrbiterEvent, net.user1.events.Event); 2245 2246 //============================================================================== 2247 // STATIC VARIABLES 2248 //============================================================================== 2249 /** @constant */ 2250 net.user1.orbiter.OrbiterEvent.READY = "READY"; 2251 /** @constant */ 2252 net.user1.orbiter.OrbiterEvent.CLOSE = "CLOSE"; 2253 /** @constant */ 2254 net.user1.orbiter.OrbiterEvent.PROTOCOL_INCOMPATIBLE = "PROTOCOL_INCOMPATIBLE"; 2255 /** @constant */ 2256 net.user1.orbiter.OrbiterEvent.CONNECT_REFUSED = "CONNECT_REFUSED"; 2257 2258 //============================================================================== 2259 // INSTANCE METHODS 2260 //============================================================================== 2261 net.user1.orbiter.OrbiterEvent.prototype.getServerUPCVersion = function () { 2262 return this.serverUPCVersion; 2263 } 2264 2265 net.user1.orbiter.OrbiterEvent.prototype.getConnectionRefusal = function () { 2266 return this.connectionRefusal; 2267 } 2268 2269 net.user1.orbiter.OrbiterEvent.prototype.toString = function () { 2270 return "[object OrbiterEvent]"; 2271 } 2272 2273 //============================================================================== 2274 // CLASS DECLARATION 2275 //============================================================================== 2276 /** @class 2277 * The Orbiter class is the root class of every OrbiterMicro application. 2278 * It provides basic tools for connecting to Union server, and gives 2279 * the application access to the core Orbiter system modules. 2280 * The Orbiter class dispatches the following events: 2281 2282 <ul class="summary"> 2283 <li class="fixedFont">{@link net.user1.orbiter.OrbiterEvent.READY}</li> 2284 <li class="fixedFont">{@link net.user1.orbiter.OrbiterEvent.CLOSE}</li> 2285 <li class="fixedFont">{@link net.user1.orbiter.OrbiterEvent.PROTOCOL_INCOMPATIBLE}</li> 2286 </ul> 2287 2288 To register for events, use {@link net.user1.events.EventDispatcher#addEventListener}. 2289 2290 @extends net.user1.events.EventDispatcher 2291 */ 2292 net.user1.orbiter.Orbiter = function () { 2293 // Invoke superclass constructor 2294 net.user1.events.EventDispatcher.call(this); 2295 2296 // Initialization. For non-browser environments, set window to null. 2297 this.window = typeof window == "undefined" ? null : window; 2298 2299 // Initialize system versions. 2300 this.system = new net.user1.orbiter.System(this.window); 2301 this.log = new net.user1.logger.Logger(); 2302 2303 // Output host version information. 2304 if (typeof navigator != "undefined") { 2305 this.log.info("User Agent: " + navigator.userAgent + " " + navigator.platform); 2306 } 2307 this.log.info("Union Client Version: " + this.system.getClientType() + " " + this.system.getClientVersion().toStringVerbose()); 2308 this.log.info("Client UPC Protocol Version: " + this.system.getUPCVersion().toString()); 2309 this.consoleLogger = null; 2310 2311 if (!this.system.isJavaScriptCompatible()) { 2312 // Missing required JavaScript capabilities, so abort. 2313 this.log.fatal("[ORBITERMICRO] JavaScript version incompatibility detected." 2314 + " Quitting."); 2315 return; 2316 } 2317 2318 // Set up the connection manager. 2319 this.connectionMan = new net.user1.orbiter.ConnectionManager(this); 2320 2321 // Set up the message manager. 2322 this.messageManager = new net.user1.orbiter.MessageManager(this.log, this.connectionMan); 2323 2324 2325 // Set up the core message listener 2326 this.coreMsgListener = new net.user1.orbiter.CoreMessageListener(this); 2327 2328 // Register for ConnectionManager events. 2329 this.connectionMan.addEventListener(net.user1.orbiter.ConnectionManagerEvent.READY, 2330 this._readyListener, this); 2331 this.connectionMan.addEventListener(net.user1.orbiter.ConnectionManagerEvent.CONNECT_FAILURE, 2332 this._connectFailureListener, this); 2333 this.connectionMan.addEventListener(net.user1.orbiter.ConnectionManagerEvent.DISCONNECT, 2334 this._disconnectListener, this); 2335 2336 // Set up the connection monitor 2337 this.connectionMonitor = new net.user1.orbiter.ConnectionMonitor(this); 2338 this.connectionMonitor.restoreDefaults(); 2339 2340 this.clientID = ""; 2341 this.sessionID = ""; 2342 2343 // Self-client shim 2344 this.selfClient = {}; 2345 this.selfClient.ping = -1; 2346 this.selfClient.getPing = function () { 2347 return this.ping; 2348 }; 2349 2350 // Register to be notified when a new connection is about to be opened 2351 this.connectionMan.addEventListener(net.user1.orbiter.ConnectionManagerEvent.SELECT_CONNECTION, 2352 this._selectConnectionListener, this); 2353 2354 // Enable HTTP failover connections 2355 this.httpFailoverEnabled = true; 2356 2357 this.log.info("[ORBITERMICRO] Initialization complete."); 2358 }; 2359 2360 //============================================================================== 2361 // INHERITANCE 2362 //============================================================================== 2363 net.user1.utils.extend(net.user1.orbiter.Orbiter, net.user1.events.EventDispatcher); 2364 2365 //============================================================================== 2366 // CLIENT INFO 2367 //============================================================================== 2368 net.user1.orbiter.Orbiter.prototype.getClientID = function () { 2369 return this.clientID; 2370 }; 2371 2372 net.user1.orbiter.Orbiter.prototype.self = function () { 2373 return this.selfClient; 2374 } 2375 2376 //============================================================================== 2377 // SESSION ID 2378 //============================================================================== 2379 /** @private */ 2380 net.user1.orbiter.Orbiter.prototype.setSessionID = function (id) { 2381 this.sessionID = id; 2382 }; 2383 2384 net.user1.orbiter.Orbiter.prototype.getSessionID = function () { 2385 return this.sessionID == null ? "" : this.sessionID; 2386 }; 2387 2388 //============================================================================== 2389 // UPC LISTENERS 2390 //============================================================================== 2391 /** @private */ 2392 net.user1.orbiter.Orbiter.prototype.u29 = function (clientID) { 2393 this.clientID = clientID; 2394 }; 2395 2396 /** @private */ 2397 net.user1.orbiter.Orbiter.prototype.u66 = function (clientID, 2398 sessionID, 2399 serverUPCVersionString, 2400 protocolCompatible, 2401 affinityAddress, 2402 affinityDuration) { 2403 var serverUPCVersion = new net.user1.orbiter.VersionNumber(); 2404 serverUPCVersion.fromVersionString(serverUPCVersionString); 2405 if (protocolCompatible == "false") { 2406 this.dispatchProtocolIncompatible(serverUPCVersion); 2407 } 2408 }; 2409 2410 /** @private */ 2411 net.user1.orbiter.Orbiter.prototype.u164 = function (reason, description) { 2412 this.connectionMonitor.setAutoReconnectFrequency(-1); 2413 this.dispatchConnectRefused(new net.user1.orbiter.ConnectionRefusal(reason, description)); 2414 }; 2415 2416 //============================================================================== 2417 // CONNECTION 2418 //============================================================================== 2419 /** @private */ 2420 net.user1.orbiter.Orbiter.prototype.buildConnection = function (host, port, type, sendDelay) { 2421 var connection; 2422 2423 switch (type) { 2424 case net.user1.orbiter.ConnectionType.HTTP: 2425 if (this.system.hasHTTPDirectConnection()) { 2426 connection = new net.user1.orbiter.HTTPDirectConnection(); 2427 } else { 2428 connection = new net.user1.orbiter.HTTPIFrameConnection(); 2429 } 2430 break; 2431 2432 case net.user1.orbiter.ConnectionType.SECURE_HTTP: 2433 if (this.system.hasHTTPDirectConnection()) { 2434 connection = new net.user1.orbiter.SecureHTTPDirectConnection(); 2435 } else { 2436 connection = new net.user1.orbiter.SecureHTTPIFrameConnection(); 2437 } 2438 break; 2439 2440 case net.user1.orbiter.ConnectionType.WEBSOCKET: 2441 connection = new net.user1.orbiter.WebSocketConnection(); 2442 break; 2443 2444 case net.user1.orbiter.ConnectionType.SECURE_WEBSOCKET: 2445 connection = new net.user1.orbiter.SecureWebSocketConnection(); 2446 break; 2447 2448 default: 2449 throw new Error("[ORBITER] Error at buildConnection(). Invalid type specified: [" + type + "]"); 2450 } 2451 2452 try { 2453 connection.setServer(host, port); 2454 } catch (e) { 2455 this.log.error("[CONNECTION] " + connection.toString() + " " + e); 2456 } finally { 2457 this.connectionMan.addConnection(connection); 2458 if (connection instanceof net.user1.orbiter.HTTPConnection) { 2459 // Set delay after adding connection so the connection object has 2460 // access to this Orbiter object 2461 connection.setSendDelay(sendDelay); 2462 } 2463 } 2464 }; 2465 2466 /** 2467 * Connects to Union Server using the specified host and port(s). 2468 * 2469 * @param host 2470 * @param port1 2471 * @param port2 2472 * @param ... 2473 * @param portn 2474 */ 2475 net.user1.orbiter.Orbiter.prototype.connect = function (host) { 2476 this.useSecureConnect = false; 2477 this.doConnect.apply(this, arguments); 2478 }; 2479 2480 /** 2481 * <p> 2482 * The secureConnect() method is identical to the connect() method, except that 2483 * it uses an encrypted connection (TLS or SSL) rather than an 2484 * unencrypted connection. Before secureConnect() can be used, Union Server 2485 * must be configured to accept client communications over a secure gateway, 2486 * which includes the installation of a server-side security certificate. For 2487 * instructions on configuring Union Server for secure communications, see 2488 * Union Server's documentation at http://unionplatform.com. 2489 * </p> 2490 * 2491 * @see net.user1.orbiter.Orbiter#connect 2492 */ 2493 net.user1.orbiter.Orbiter.prototype.secureConnect = function (host) { 2494 this.useSecureConnect = true; 2495 this.doConnect.apply(this, arguments); 2496 }; 2497 2498 /** 2499 * @private 2500 */ 2501 net.user1.orbiter.Orbiter.prototype.doConnect = function (host) { 2502 var ports = Array.prototype.slice.call(arguments).slice(1); 2503 if (host != null) { 2504 this.setServer.apply(this, [host].concat(ports)); 2505 } 2506 this.log.info("[ORBITER] Connecting to Union..."); 2507 this.connectionMan.connect(); 2508 }; 2509 2510 net.user1.orbiter.Orbiter.prototype.disconnect = function () { 2511 this.connectionMan.disconnect(); 2512 }; 2513 2514 /** 2515 * Assigns the host and port(s) Orbiter should use when attempting to connect to 2516 * Union Server. The first argument is the host address (e.g., "example.com"), 2517 * Subsequent arguments list the integer ports for the connection (e.g., 80). 2518 * Orbiter will attempt to connect over the ports in the order specified. For 2519 * example, given the code setServer("tryunion.com", 9100, 80, 443, Orbiter 2520 * will first attempt to connect to Union Server over port 9100; if the 2521 * connection fails, Orbiter will automatically next attempt to connect over 2522 * port 80, if that fails, Orbiter will attempt to connect to port 443. To add 2523 * multiple hosts (not just multiple ports) to Orbiter's list of failover 2524 * connections, use ConnectionManager's addConnection() method. 2525 * 2526 * To reduce network latency and bandwidth consumption, Orbiter automatically 2527 * attempts to connect via WebSocket wherever WebSocket is supported. Where 2528 * WebSocket is not supported, Orbiter automatically fails over to HTTP 2529 * communications. 2530 * 2531 * Wherever possible, to allow maximum connection success by Union clients, 2532 * Union Server should be run on port 80. 2533 * 2534 * @param host 2535 * @param port1 2536 * @param port2 2537 * @param ... 2538 * @param portn 2539 */ 2540 net.user1.orbiter.Orbiter.prototype.setServer = function (host) { 2541 if (host != null && arguments.length > 1) { 2542 if (this.connectionMan.getConnections().length > 0) { 2543 this.connectionMan.removeAllConnections(); 2544 } 2545 // Where possible, create regular WebSocket connections for the specified 2546 // host and its ports. 2547 var connectionType; 2548 if (this.system.hasWebSocket()) { 2549 for (var i = 1; i < arguments.length; i++) { 2550 connectionType = this.useSecureConnect 2551 ? net.user1.orbiter.ConnectionType.SECURE_WEBSOCKET 2552 : net.user1.orbiter.ConnectionType.WEBSOCKET; 2553 this.buildConnection(host, arguments[i], connectionType, -1); 2554 } 2555 } else { 2556 this.log.info("[ORBITERMICRO] WebSocket not found in host environment. Trying HTTP."); 2557 } 2558 // Next, if failover is enabled or WebSocket is not supported, create HTTPConnections 2559 if (this.isHTTPFailoverEnabled() || !this.system.hasWebSocket()) { 2560 for (i = 1; i < arguments.length; i++) { 2561 connectionType = this.useSecureConnect 2562 ? net.user1.orbiter.ConnectionType.SECURE_HTTP 2563 : net.user1.orbiter.ConnectionType.HTTP; 2564 this.buildConnection(host, 2565 arguments[i], 2566 connectionType, 2567 net.user1.orbiter.HTTPConnection.DEFAULT_SEND_DELAY); 2568 } 2569 } 2570 } else { 2571 this.log.error("[ORBITERMICRO] setServer() failed. Invalid host or port supplied [" + arguments + "]."); 2572 } 2573 }; 2574 2575 net.user1.orbiter.Orbiter.prototype.isReady = function () { 2576 return this.connectionMan.isReady(); 2577 }; 2578 2579 2580 //============================================================================== 2581 // HTTP FAILOVER CONFIGURATION 2582 //============================================================================== 2583 net.user1.orbiter.Orbiter.prototype.enableHTTPFailover = function () { 2584 this.httpFailoverEnabled = true; 2585 }; 2586 2587 net.user1.orbiter.Orbiter.prototype.disableHTTPFailover = function () { 2588 this.httpFailoverEnabled = false; 2589 }; 2590 2591 net.user1.orbiter.Orbiter.prototype.isHTTPFailoverEnabled = function () { 2592 return this.httpFailoverEnabled; 2593 }; 2594 2595 //============================================================================== 2596 // CONNECTION EVENT LISTENERS 2597 //============================================================================== 2598 /** @private */ 2599 net.user1.orbiter.Orbiter.prototype._disconnectListener = function (e) { 2600 this.clientID = ""; 2601 this.dispatchClose(); 2602 }; 2603 2604 /** @private */ 2605 net.user1.orbiter.Orbiter.prototype._connectFailureListener = function (e) { 2606 this.clientID = ""; 2607 this.dispatchClose(); 2608 }; 2609 2610 /** @private */ 2611 net.user1.orbiter.Orbiter.prototype._readyListener = function (e) { 2612 this.log.info("[ORBITER] Orbiter now connected and ready."); 2613 this.dispatchReady(); 2614 }; 2615 2616 /** @private */ 2617 net.user1.orbiter.Orbiter.prototype._selectConnectionListener = function (e) { 2618 // Register to be notified when the client's ID is received 2619 this.messageManager.addMessageListener("u29", this.u29, this); 2620 // Register to be notified when the server's "hello" message is received 2621 this.messageManager.addMessageListener("u66", this.u66, this); 2622 this.messageManager.addMessageListener(net.user1.orbiter.UPC.CONNECTION_REFUSED, this.u164, this); 2623 }; 2624 2625 //============================================================================== 2626 // INSTANCE RETRIEVAL 2627 //============================================================================== 2628 net.user1.orbiter.Orbiter.prototype.getLog = function () { 2629 return this.log; 2630 }; 2631 2632 net.user1.orbiter.Orbiter.prototype.getMessageManager = function () { 2633 return this.messageManager; 2634 }; 2635 2636 net.user1.orbiter.Orbiter.prototype.getConnectionManager = function () { 2637 return this.connectionMan; 2638 }; 2639 2640 net.user1.orbiter.Orbiter.prototype.getConnectionMonitor = function () { 2641 return this.connectionMonitor; 2642 }; 2643 2644 net.user1.orbiter.Orbiter.prototype.getSystem = function () { 2645 return this.system; 2646 }; 2647 2648 //============================================================================== 2649 // LOGGING 2650 //============================================================================== 2651 net.user1.orbiter.Orbiter.prototype.enableConsole = function () { 2652 if (this.consoleLogger == null) { 2653 this.consoleLogger = new net.user1.logger.ConsoleLogger(this.log); 2654 } 2655 } 2656 2657 net.user1.orbiter.Orbiter.prototype.disableConsole = function() { 2658 if (this.consoleLogger != null) { 2659 this.consoleLogger.dispose(); 2660 this.consoleLogger = null; 2661 } 2662 }; 2663 2664 //============================================================================== 2665 // EVENT DISPATCH 2666 //============================================================================== 2667 /** @private */ 2668 net.user1.orbiter.Orbiter.prototype.dispatchReady = function () { 2669 this.dispatchEvent(new net.user1.orbiter.OrbiterEvent(net.user1.orbiter.OrbiterEvent.READY)); 2670 } 2671 2672 /** @private */ 2673 net.user1.orbiter.Orbiter.prototype.dispatchClose = function () { 2674 this.dispatchEvent(new net.user1.orbiter.OrbiterEvent(net.user1.orbiter.OrbiterEvent.CLOSE)); 2675 } 2676 2677 /** @private */ 2678 net.user1.orbiter.Orbiter.prototype.dispatchProtocolIncompatible = function () { 2679 this.dispatchEvent(new net.user1.orbiter.OrbiterEvent(net.user1.orbiter.OrbiterEvent.PROTOCOL_INCOMPATIBLE)); 2680 } 2681 2682 /** @private */ 2683 net.user1.orbiter.Orbiter.prototype.dispatchConnectRefused = function (refusal) { 2684 this.dispatchEvent(new net.user1.orbiter.OrbiterEvent(net.user1.orbiter.OrbiterEvent.CONNECT_REFUSED, 2685 null, refusal)); 2686 }; 2687 2688 //============================================================================== 2689 // TOSTRING 2690 //============================================================================== 2691 net.user1.orbiter.Orbiter.prototype.toString = function () { 2692 return "[object Orbiter]"; 2693 } 2694 2695 //============================================================================== 2696 // CONNECTION STATE CONSTANTS 2697 //============================================================================== 2698 /** @class */ 2699 net.user1.orbiter.ConnectionState = new Object(); 2700 /** @constant */ 2701 net.user1.orbiter.ConnectionState.UNKNOWN = -1; 2702 /** @constant */ 2703 net.user1.orbiter.ConnectionState.NOT_CONNECTED = 0; 2704 /** @constant */ 2705 net.user1.orbiter.ConnectionState.READY = 1; 2706 /** @constant */ 2707 net.user1.orbiter.ConnectionState.CONNECTION_IN_PROGRESS = 2; 2708 /** @constant */ 2709 net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS = 3; 2710 /** @constant */ 2711 net.user1.orbiter.ConnectionState.LOGGED_IN = 4; 2712 //============================================================================== 2713 // CLASS DECLARATION 2714 //============================================================================== 2715 /** @class 2716 @extends net.user1.events.Event 2717 */ 2718 net.user1.orbiter.ConnectionEvent = function (type, upc, data, connection, status) { 2719 net.user1.events.Event.call(this, type); 2720 2721 this.upc = upc; 2722 this.data = data; 2723 this.connection = connection 2724 this.status = status; 2725 }; 2726 2727 //============================================================================== 2728 // INHERITANCE 2729 //============================================================================== 2730 net.user1.utils.extend(net.user1.orbiter.ConnectionEvent, net.user1.events.Event); 2731 2732 //============================================================================== 2733 // STATIC VARIABLES 2734 //============================================================================== 2735 2736 /** @constant */ 2737 net.user1.orbiter.ConnectionEvent.BEGIN_CONNECT = "BEGIN_CONNECT"; 2738 /** @constant */ 2739 net.user1.orbiter.ConnectionEvent.BEGIN_HANDSHAKE = "BEGIN_HANDSHAKE"; 2740 /** @constant */ 2741 net.user1.orbiter.ConnectionEvent.READY = "READY"; 2742 /** @constant */ 2743 net.user1.orbiter.ConnectionEvent.CONNECT_FAILURE = "CONNECT_FAILURE"; 2744 /** @constant */ 2745 net.user1.orbiter.ConnectionEvent.CLIENT_KILL_CONNECT = "CLIENT_KILL_CONNECT"; 2746 /** @constant */ 2747 net.user1.orbiter.ConnectionEvent.SERVER_KILL_CONNECT = "SERVER_KILL_CONNECT"; 2748 /** @constant */ 2749 net.user1.orbiter.ConnectionEvent.DISCONNECT = "DISCONNECT"; 2750 /** @constant */ 2751 net.user1.orbiter.ConnectionEvent.RECEIVE_UPC = "RECEIVE_UPC"; 2752 /** @constant */ 2753 net.user1.orbiter.ConnectionEvent.SEND_DATA = "SEND_DATA"; 2754 /** @constant */ 2755 net.user1.orbiter.ConnectionEvent.RECEIVE_DATA = "RECEIVE_DATA"; 2756 /** @constant */ 2757 net.user1.orbiter.ConnectionEvent.SESSION_TERMINATED = "SESSION_TERMINATED"; 2758 /** @constant */ 2759 net.user1.orbiter.ConnectionEvent.SESSION_NOT_FOUND = "SESSION_NOT_FOUND"; 2760 2761 //============================================================================== 2762 // INSTANCE METHODS 2763 //============================================================================== 2764 2765 net.user1.orbiter.ConnectionEvent.prototype.getUPC = function () { 2766 return this.upc; 2767 } 2768 2769 net.user1.orbiter.ConnectionEvent.prototype.getData = function () { 2770 return this.data; 2771 } 2772 2773 net.user1.orbiter.ConnectionEvent.prototype.getStatus = function () { 2774 return this.status; 2775 } 2776 2777 net.user1.orbiter.ConnectionEvent.prototype.toString = function () { 2778 return "[object ConnectionEvent]"; 2779 } 2780 2781 //============================================================================== 2782 // HTTP REQUEST MODE CONSTANTS 2783 //============================================================================== 2784 /** @class */ 2785 net.user1.orbiter.ConnectionType = new Object(); 2786 /** @constant */ 2787 net.user1.orbiter.ConnectionType.HTTP = "HTTP"; 2788 /** @constant */ 2789 net.user1.orbiter.ConnectionType.SECURE_HTTP = "SECURE_HTTP"; 2790 /** @constant */ 2791 net.user1.orbiter.ConnectionType.WEBSOCKET = "WEBSOCKET"; 2792 /** @constant */ 2793 net.user1.orbiter.ConnectionType.SECURE_WEBSOCKET = "SECURE_WEBSOCKET"; 2794 //============================================================================== 2795 // CLASS DECLARATION 2796 //============================================================================== 2797 /** @class 2798 * Connection is the abstract superclass of HTTPConnection and WebSocketConnection; 2799 * it is used internally by Orbiter, and is not intended for direct use by Orbiter 2800 * developers. For information on communication with Union Server, see 2801 * Orbiter's connect() method, the WebSocketConnection class and the 2802 * HTTPDirectConnection and HTTPIFrameConnection classes. 2803 * 2804 * The Connection class dispatches the following events: 2805 2806 <ul class="summary"> 2807 <li class="fixedFont">{@link net.user1.orbiter.ConnectionEvent.BEGIN_CONNECT}</li> 2808 <li class="fixedFont">{@link net.user1.orbiter.ConnectionEvent.BEGIN_HANDSHAKE}</li> 2809 <li class="fixedFont">{@link net.user1.orbiter.ConnectionEvent.READY}</li> 2810 <li class="fixedFont">{@link net.user1.orbiter.ConnectionEvent.CONNECT_FAILURE}</li> 2811 <li class="fixedFont">{@link net.user1.orbiter.ConnectionEvent.CLIENT_KILL_CONNECT}</li> 2812 <li class="fixedFont">{@link net.user1.orbiter.ConnectionEvent.SERVER_KILL_CONNECT}</li> 2813 <li class="fixedFont">{@link net.user1.orbiter.ConnectionEvent.DISCONNECT}</li> 2814 <li class="fixedFont">{@link net.user1.orbiter.ConnectionEvent.RECEIVE_UPC}</li> 2815 <li class="fixedFont">{@link net.user1.orbiter.ConnectionEvent.SEND_DATA}</li> 2816 <li class="fixedFont">{@link net.user1.orbiter.ConnectionEvent.RECEIVE_DATA}</li> 2817 <li class="fixedFont">{@link net.user1.orbiter.ConnectionEvent.SESSION_TERMINATED}</li> 2818 <li class="fixedFont">{@link net.user1.orbiter.ConnectionEvent.SESSION_NOT_FOUND}</li> 2819 </ul> 2820 2821 To register for events, use {@link net.user1.events.EventDispatcher#addEventListener}. 2822 2823 @extends net.user1.events.EventDispatcher 2824 2825 * 2826 * @see net.user1.orbiter.Orbiter#connect 2827 * @see net.user1.orbiter.Orbiter#secureConnect 2828 * @see net.user1.orbiter.HTTPDirectConnection 2829 * @see net.user1.orbiter.HTTPIFrameConnection 2830 * @see net.user1.orbiter.WebSocketConnection 2831 * @see net.user1.orbiter.SecureHTTPDirectConnection 2832 * @see net.user1.orbiter.SecureHTTPIFrameConnection 2833 * @see net.user1.orbiter.SecureWebSocketConnection 2834 */ 2835 net.user1.orbiter.Connection = function (host, port, type) { 2836 // Call superconstructor 2837 net.user1.events.EventDispatcher.call(this); 2838 2839 // Variables 2840 this.mostRecentConnectAchievedReady = false; 2841 this.mostRecentConnectTimedOut = false; 2842 this.readyCount = 0; 2843 this.connectAttemptCount = 0; 2844 this.connectAbortCount = 0; 2845 this.readyTimeoutID = 0; 2846 this.readyTimeout = 0; 2847 this.orbiter = null; 2848 this.disposed = false; 2849 this.requestedHost = null; 2850 2851 // Initialization 2852 this.setServer(host, port); 2853 this.connectionType = type; 2854 this.connectionState = net.user1.orbiter.ConnectionState.NOT_CONNECTED; 2855 }; 2856 2857 //============================================================================== 2858 // INHERITANCE 2859 //============================================================================== 2860 net.user1.utils.extend(net.user1.orbiter.Connection, net.user1.events.EventDispatcher); 2861 2862 //============================================================================== 2863 // DEPENDENCIES 2864 //============================================================================== 2865 /** @private */ 2866 net.user1.orbiter.Connection.prototype.setOrbiter = function (orbiter) { 2867 if (this.orbiter != null) { 2868 this.orbiter.getMessageManager().removeMessageListener("u63", this.u63); 2869 this.orbiter.getMessageManager().removeMessageListener("u66", this.u66); 2870 this.orbiter.getMessageManager().removeMessageListener("u84", this.u84); 2871 this.orbiter.getMessageManager().removeMessageListener("u85", this.u85); 2872 } 2873 this.orbiter = orbiter; 2874 } 2875 2876 //============================================================================== 2877 // CONNECT/DISCONNECT 2878 //============================================================================== 2879 net.user1.orbiter.Connection.prototype.connect = function () { 2880 this.disconnect(); 2881 this.applyAffinity(); 2882 this.orbiter.getLog().info(this.toString() + " Attempting connection..."); 2883 this.connectAttemptCount++; 2884 this.mostRecentConnectAchievedReady = false; 2885 this.mostRecentConnectTimedOut = false; 2886 this.connectionState = net.user1.orbiter.ConnectionState.CONNECTION_IN_PROGRESS; 2887 // Start the ready timer. Ready state must be achieved before the timer 2888 // completes or the connection will auto-disconnect. 2889 this.startReadyTimer(); 2890 this.dispatchBeginConnect(); 2891 } 2892 2893 net.user1.orbiter.Connection.prototype.disconnect = function () { 2894 var state = this.connectionState; 2895 2896 if (state != net.user1.orbiter.ConnectionState.NOT_CONNECTED) { 2897 this.deactivateConnection(); 2898 2899 if (state == net.user1.orbiter.ConnectionState.CONNECTION_IN_PROGRESS) { 2900 this.connectAbortCount++; 2901 this.dispatchConnectFailure("Client closed connection before READY state was achieved."); 2902 } else { 2903 this.dispatchClientKillConnect(); 2904 } 2905 } 2906 } 2907 2908 /** @private */ 2909 net.user1.orbiter.Connection.prototype.deactivateConnection = function () { 2910 this.connectionState = net.user1.orbiter.ConnectionState.NOT_CONNECTED; 2911 this.stopReadyTimer(); 2912 this.orbiter.setSessionID(""); 2913 } 2914 2915 //============================================================================== 2916 // CONNECTION CONFIGURATION 2917 //============================================================================== 2918 net.user1.orbiter.Connection.prototype.setServer = function (host, 2919 port) { 2920 this.requestedHost = host; 2921 2922 // Check for valid ports 2923 if (port < 1 || port > 65536) { 2924 throw new Error("Illegal port specified [" + port + "]. Must be greater than 0 and less than 65537."); 2925 } 2926 this.port = port; 2927 } 2928 2929 net.user1.orbiter.Connection.prototype.getRequestedHost = function () { 2930 return this.requestedHost; 2931 }; 2932 2933 net.user1.orbiter.Connection.prototype.getHost = function () { 2934 if (this.host == null) { 2935 return this.getRequestedHost(); 2936 } else { 2937 return this.host; 2938 } 2939 }; 2940 2941 net.user1.orbiter.Connection.prototype.getPort = function () { 2942 return this.port; 2943 }; 2944 2945 net.user1.orbiter.Connection.prototype.getType = function () { 2946 return this.connectionType; 2947 }; 2948 2949 //============================================================================== 2950 // READY HANDSHAKE 2951 //============================================================================== 2952 /** @private */ 2953 net.user1.orbiter.Connection.prototype.beginReadyHandshake = function () { 2954 this.dispatchBeginHandshake(); 2955 2956 if (!this.orbiter.getMessageManager().hasMessageListener("u63", this.u63)) { 2957 this.orbiter.getMessageManager().addMessageListener("u63", this.u63, this); 2958 this.orbiter.getMessageManager().addMessageListener("u66", this.u66, this); 2959 this.orbiter.getMessageManager().addMessageListener("u84", this.u84, this); 2960 this.orbiter.getMessageManager().addMessageListener("u85", this.u85, this); 2961 } 2962 2963 this.sendHello(); 2964 } 2965 2966 /** @private */ 2967 net.user1.orbiter.Connection.prototype.sendHello = function() { 2968 var helloString = this.buildHelloMessage(); 2969 this.orbiter.getLog().debug(this.toString() + " Sending CLIENT_HELLO: " + helloString); 2970 this.transmitHelloMessage(helloString); 2971 } 2972 2973 /** @private */ 2974 net.user1.orbiter.Connection.prototype.buildHelloMessage = function () { 2975 var helloString = "<U><M>u65</M>" 2976 + "<L>" 2977 + "<A>" + this.orbiter.getSystem().getClientType() + "</A>" 2978 + "<A>" + (typeof navigator != "undefined" ? navigator.userAgent + ";" : "") 2979 + this.orbiter.getSystem().getClientVersion().toStringVerbose() + "</A>" 2980 + "<A>" + this.orbiter.getSystem().getUPCVersion().toString() + "</A></L></U>"; 2981 return helloString; 2982 } 2983 2984 /** @private */ 2985 net.user1.orbiter.Connection.prototype.transmitHelloMessage = function (helloString) { 2986 this.send(helloString); 2987 } 2988 2989 //============================================================================== 2990 // READY TIMER 2991 //============================================================================== 2992 /** @private */ 2993 net.user1.orbiter.Connection.prototype.readyTimerListener = function () { 2994 this.stopReadyTimer(); 2995 if (this.connectionState == net.user1.orbiter.ConnectionState.CONNECTION_IN_PROGRESS) { 2996 this.orbiter.getLog().warn("[CONNECTION] " + this.toString() + " Failed to achieve" + 2997 " ready state after " + this.readyTimeout + "ms. Aborting connection..."); 2998 this.mostRecentConnectTimedOut = true; 2999 this.disconnect(); 3000 } 3001 } 3002 3003 /** @private */ 3004 net.user1.orbiter.Connection.prototype.stopReadyTimer = function () { 3005 if (this.readyTimeoutID != -1) { 3006 clearTimeout(this.readyTimeoutID); 3007 } 3008 } 3009 3010 /** @private */ 3011 net.user1.orbiter.Connection.prototype.startReadyTimer = function () { 3012 var currentObj = this; 3013 var callback = this.readyTimerListener; 3014 this.stopReadyTimer(); 3015 this.readyTimeout = this.orbiter.getConnectionManager().getReadyTimeout(); 3016 var self = this; 3017 this.readyTimeoutID = setTimeout (function () { 3018 callback.call(currentObj); 3019 }, self.readyTimeout); 3020 } 3021 3022 //============================================================================== 3023 // READY STATE ACCESS 3024 //============================================================================== 3025 /** @private */ 3026 net.user1.orbiter.Connection.prototype.getReadyCount = function () { 3027 return this.readyCount; 3028 } 3029 3030 net.user1.orbiter.Connection.prototype.isReady = function () { 3031 return this.connectionState == net.user1.orbiter.ConnectionState.READY; 3032 } 3033 3034 /** @private */ 3035 net.user1.orbiter.Connection.prototype.isValid = function () { 3036 if (this.mostRecentConnectAchievedReady) { 3037 this.orbiter.getLog().debug(this.toString() + " Connection is" 3038 + " valid because its last connection attempt succeeded."); 3039 return true; 3040 } 3041 3042 if (this.connectAttemptCount == 0) { 3043 this.orbiter.getLog().debug(this.toString() + " Connection is" 3044 + " valid because it has either never attempted to connect, or has not" 3045 + " attempted to connect since its last successful connection."); 3046 return true; 3047 } 3048 3049 if ((this.connectAttemptCount > 0) && 3050 (this.connectAttemptCount == this.connectAbortCount) 3051 && !this.mostRecentConnectTimedOut) { 3052 this.orbiter.getLog().debug(this.toString() + " Connection is" 3053 + " valid because either all connection attempts ever or all" 3054 + " connection attempts since its last successful connection were" 3055 + " aborted before the ready timeout was reached."); 3056 return true; 3057 } 3058 3059 this.orbiter.getLog().debug(this.toString() + " Connection is not" 3060 + " valid; its most recent connection failed to achieve a ready state."); 3061 return false; 3062 } 3063 3064 3065 //============================================================================== 3066 // UPC LISTENERS 3067 //============================================================================== 3068 /** @private */ 3069 net.user1.orbiter.Connection.prototype.u63 = function () { 3070 this.stopReadyTimer(); 3071 this.connectionState = net.user1.orbiter.ConnectionState.READY; 3072 this.mostRecentConnectAchievedReady = true; 3073 this.readyCount++; 3074 this.connectAttemptCount = 0; 3075 this.connectAbortCount = 0; 3076 this.dispatchReady(); 3077 } 3078 3079 /** @private */ 3080 net.user1.orbiter.Connection.prototype.u66 = function (serverVersion, 3081 sessionID, 3082 upcVersion, 3083 protocolCompatible, 3084 affinityAddress, 3085 affinityDuration) { 3086 this.orbiter.setSessionID(sessionID); 3087 }; 3088 3089 /** @private */ 3090 net.user1.orbiter.Connection.prototype.u84 = function () { 3091 this.dispatchSessionTerminated(); 3092 } 3093 3094 /** @private */ 3095 net.user1.orbiter.Connection.prototype.u85 = function () { 3096 this.dispatchSessionNotFound(); 3097 } 3098 3099 //============================================================================== 3100 // SERVER AFFINITY 3101 //============================================================================== 3102 /** @private */ 3103 net.user1.orbiter.Connection.prototype.applyAffinity = function () { 3104 var affinityAddress = this.orbiter.getConnectionManager().getAffinity(this.requestedHost); 3105 if (affinityAddress == this.requestedHost) { 3106 this.orbiter.getLog().info(this.toString() + " No affinity address found for requested host [" 3107 + this.requestedHost + "]. Using requested host for next connection attempt."); 3108 } else { 3109 this.orbiter.getLog().info(this.toString() + " Applying affinity address [" + affinityAddress + "] for supplied host [" + this.requestedHost + "]."); 3110 } 3111 this.host = affinityAddress; 3112 } 3113 3114 //============================================================================== 3115 // TOSTRING 3116 //============================================================================== 3117 net.user1.orbiter.Connection.prototype.toString = function () { 3118 var s = "[" + this.connectionType + ", requested host: " + this.requestedHost 3119 + ", host: " + (this.host == null ? "" : this.host) 3120 + ", port: " + this.port + "]"; 3121 return s; 3122 } 3123 3124 //============================================================================== 3125 // EVENT DISPATCHING 3126 //============================================================================== 3127 /** @private */ 3128 net.user1.orbiter.Connection.prototype.dispatchSendData = function (data) { 3129 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.SEND_DATA, 3130 null, data, this)); 3131 } 3132 3133 /** @private */ 3134 net.user1.orbiter.Connection.prototype.dispatchReceiveData = function (data) { 3135 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.RECEIVE_DATA, 3136 null, data, this)); 3137 } 3138 3139 /** @private */ 3140 net.user1.orbiter.Connection.prototype.dispatchConnectFailure = function (status) { 3141 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.CONNECT_FAILURE, 3142 null, null, this, status)); 3143 } 3144 3145 /** @private */ 3146 net.user1.orbiter.Connection.prototype.dispatchBeginConnect = function () { 3147 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.BEGIN_CONNECT, 3148 null, null, this)); 3149 } 3150 3151 /** @private */ 3152 net.user1.orbiter.Connection.prototype.dispatchBeginHandshake = function () { 3153 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.BEGIN_HANDSHAKE, 3154 null, null, this)); 3155 } 3156 3157 /** @private */ 3158 net.user1.orbiter.Connection.prototype.dispatchReady = function () { 3159 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.READY, 3160 null, null, this)); 3161 } 3162 3163 /** @private */ 3164 net.user1.orbiter.Connection.prototype.dispatchServerKillConnect = function () { 3165 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.SERVER_KILL_CONNECT, 3166 null, null, this)); 3167 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.DISCONNECT, 3168 null, null, this)); 3169 } 3170 3171 /** @private */ 3172 net.user1.orbiter.Connection.prototype.dispatchClientKillConnect = function () { 3173 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.CLIENT_KILL_CONNECT, 3174 null, null, this)); 3175 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.DISCONNECT, 3176 null, null, this)); 3177 } 3178 3179 /** @private */ 3180 net.user1.orbiter.Connection.prototype.dispatchSessionTerminated = function () { 3181 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.SESSION_TERMINATED, 3182 null, null, this)); 3183 } 3184 3185 /** @private */ 3186 net.user1.orbiter.Connection.prototype.dispatchSessionNotFound = function () { 3187 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.SESSION_NOT_FOUND, 3188 null, null, this)); 3189 } 3190 3191 //============================================================================== 3192 // DISPOSAL 3193 //============================================================================== 3194 /** @private */ 3195 net.user1.orbiter.Connection.prototype.dispose = function () { 3196 this.disposed = true; 3197 this.messageManager.removeMessageListener("u63", this.u63); 3198 this.messageManager.removeMessageListener("u66", this.u66); 3199 this.messageManager.removeMessageListener("u84", this.u84); 3200 this.messageManager.removeMessageListener("u85", this.u85); 3201 this.stopReadyTimer(); 3202 this.readyTimer = null; 3203 this.orbiter = null; 3204 } 3205 //============================================================================== 3206 // CLASS DECLARATION 3207 //============================================================================== 3208 /** 3209 * @class 3210 * 3211 * <p> 3212 * The WebSocketConnection class is used by Orbiter to communicate with 3213 * Union Server over a persistent TCP/IP socket. Normally, developers need not 3214 * use the WebSocketConnection class directly, and should instead make connections 3215 * via the Orbiter class's connect() method. However, the 3216 * WebSocketConnection class is required for fine-grained connection configuration, 3217 * such as defining failover socket connections for multiple Union Servers 3218 * running at different host addresses. 3219 * </p> 3220 * 3221 * <p> 3222 * By default, Orbiter uses WebSocketConnection connections to communicate 3223 * with Union Server. WebSocketConnection connections offer faster response times than 3224 * HTTP connections, but occupy an operating-system-level socket continuously 3225 * for the duration of the connection. If a WebSocketConnection connection 3226 * cannot be established (due to, say, a restrictive firewall), Orbiter 3227 * automatically attempts to communicate using HTTP requests sent via an 3228 * HTTPDirectConnection or HTTPIFrameConnection. Developers can override 3229 * Orbiter's default connection failover system by manually configuring 3230 * connections using the ConnectionManager class and Orbiter's 3231 * disableHTTPFailover() method.</p> 3232 * 3233 * <p> 3234 * For secure WebSocket and HTTP communications, see SecureWebSocketConnection, 3235 * SecureHTTPDirectConnection, and SecureHTTPIFrameConnection. 3236 * </p> 3237 * 3238 * For a list of events dispatched by WebSocketConnection, see 3239 * WebSocketConnection's superclass, {@link net.user1.orbiter.Connection}. 3240 * 3241 * @extends net.user1.orbiter.Connection 3242 * 3243 * @see net.user1.orbiter.Orbiter#connect 3244 * @see net.user1.orbiter.Orbiter#secureConnect 3245 * @see net.user1.orbiter.SecureWebSocketConnection 3246 * @see net.user1.orbiter.SecureHTTPDirectConnection 3247 * @see net.user1.orbiter.SecureHTTPIFrameConnection 3248 */ 3249 net.user1.orbiter.WebSocketConnection = function (host, port, type) { 3250 // Invoke superclass constructor 3251 net.user1.orbiter.Connection.call(this, host, port, type || net.user1.orbiter.ConnectionType.WEBSOCKET); 3252 3253 this.socket = null; 3254 }; 3255 3256 //============================================================================== 3257 // INHERITANCE 3258 //============================================================================== 3259 net.user1.utils.extend(net.user1.orbiter.WebSocketConnection, net.user1.orbiter.Connection); 3260 3261 //============================================================================== 3262 // SOCKET OBJECT MANAGEMENT 3263 //============================================================================== 3264 /** @private */ 3265 net.user1.orbiter.WebSocketConnection.prototype.getNewSocket = function () { 3266 // Deactivate the old socket 3267 this.deactivateSocket(this.socket); 3268 3269 // Create the new socket 3270 if (typeof MozWebSocket != "undefined") { 3271 // Firefox 6 3272 this.socket = new MozWebSocket(this.buildURL()); 3273 } else { 3274 // Other browsers 3275 this.socket = new WebSocket(this.buildURL()); 3276 } 3277 3278 // Register for socket events 3279 var self = this; 3280 this.socket.onopen = function (e) {self.connectListener(e)}; 3281 this.socket.onmessage = function (e) {self.dataListener(e)}; 3282 this.socket.onclose = function (e) {self.closeListener(e)}; 3283 this.socket.onerror = function (e) {self.ioErrorListener(e)}; 3284 }; 3285 3286 /** @private */ 3287 net.user1.orbiter.WebSocketConnection.prototype.buildURL = function () { 3288 return "ws://" + this.host + ":" + this.port; 3289 }; 3290 3291 /** @private */ 3292 net.user1.orbiter.WebSocketConnection.prototype.deactivateSocket = function (oldSocket) { 3293 if (oldSocket == null) { 3294 return; 3295 } 3296 3297 this.socket.onopen = null; 3298 this.socket.onmessage = null; 3299 this.socket.onclose = null; 3300 this.socket.onerror = null; 3301 3302 try { 3303 oldSocket.close() 3304 } catch (e) { 3305 // Do nothing 3306 } 3307 3308 this.socket = null; 3309 }; 3310 3311 //============================================================================== 3312 // CONNECTION AND DISCONNECTION 3313 //============================================================================== 3314 3315 net.user1.orbiter.WebSocketConnection.prototype.connect = function () { 3316 net.user1.orbiter.Connection.prototype.connect.call(this); 3317 3318 // Attempt to connect 3319 try { 3320 this.getNewSocket(); 3321 } catch (e) { 3322 // Socket could not be opened 3323 this.deactivateConnection(); 3324 this.dispatchConnectFailure(e.toString()); 3325 } 3326 }; 3327 3328 /** @private */ 3329 net.user1.orbiter.WebSocketConnection.prototype.deactivateConnection = function () { 3330 this.orbiter.getLog().debug("[CONNECTION] " + this.toString() + " Deactivating..."); 3331 this.connectionState = net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS; 3332 this.deactivateSocket(this.socket); 3333 net.user1.orbiter.Connection.prototype.deactivateConnection.call(this); 3334 }; 3335 3336 //============================================================================== 3337 // SOCKET CONNECTION LISTENERS 3338 //============================================================================== 3339 /** @private */ 3340 net.user1.orbiter.WebSocketConnection.prototype.connectListener = function (e) { 3341 if (this.disposed) return; 3342 3343 this.orbiter.getLog().debug(this.toString() + " Socket connected."); 3344 this.beginReadyHandshake(); 3345 } 3346 3347 /** @private */ 3348 net.user1.orbiter.WebSocketConnection.prototype.closeListener = function (e) { 3349 if (this.disposed) return; 3350 3351 var state = this.connectionState; 3352 this.deactivateConnection(); 3353 3354 if (state == net.user1.orbiter.ConnectionState.CONNECTION_IN_PROGRESS) { 3355 this.dispatchConnectFailure("WebSocket onclose: Server closed connection before READY state was achieved."); 3356 } else { 3357 this.dispatchServerKillConnect(); 3358 } 3359 }; 3360 3361 /** @private */ 3362 net.user1.orbiter.WebSocketConnection.prototype.ioErrorListener = function (e) { 3363 if (this.disposed) return; 3364 3365 var state = this.connectionState; 3366 this.deactivateConnection(); 3367 3368 // Note: when Union closes the connection, Firefox 7 dispatches onerror, not 3369 // onclose, so treat onerror like an onclose event 3370 if (state == net.user1.orbiter.ConnectionState.CONNECTION_IN_PROGRESS) { 3371 this.dispatchConnectFailure("WebSocket onerror: Server closed connection before READY state was achieved."); 3372 } else { 3373 this.dispatchServerKillConnect(); 3374 } 3375 }; 3376 3377 //============================================================================== 3378 // DATA RECEIVING AND SENDING 3379 //============================================================================== 3380 /** @private */ 3381 net.user1.orbiter.WebSocketConnection.prototype.dataListener = function (dataEvent) { 3382 if (this.disposed) return; 3383 3384 var data = dataEvent.data; 3385 this.dispatchReceiveData(data); 3386 3387 if (data.indexOf("<U>") == 0) { 3388 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent( 3389 net.user1.orbiter.ConnectionEvent.RECEIVE_UPC, 3390 data)); 3391 } else { 3392 // The message isn't UPC. Must be an error... 3393 this.orbiter.getLog().error(this.toString() + " Received invalid message" 3394 + " (not UPC or malformed UPC): " + data); 3395 } 3396 }; 3397 3398 /** @private */ 3399 net.user1.orbiter.WebSocketConnection.prototype.send = function (data) { 3400 this.dispatchSendData(data); 3401 this.socket.send(data); 3402 }; 3403 3404 // ============================================================================= 3405 // DISPOSAL 3406 // ============================================================================= 3407 /** @private */ 3408 net.user1.orbiter.WebSocketConnection.prototype.dispose = function () { 3409 net.user1.orbiter.Connection.prototype.dispose.call(this); 3410 this.deactivateSocket(this.socket); 3411 }; 3412 //============================================================================== 3413 // CLASS DECLARATION 3414 //============================================================================== 3415 /** @class 3416 * 3417 * <p> 3418 * The SecureWebSocketConnection class is identical to WebSocketConnection 3419 * except that it performs communications over WSS (i.e., an encrypted TLS or 3420 * SSL socket connection) rather than plain WebSocket.</p> 3421 * 3422 * For a list of events dispatched by SecureWebSocketConnection, see 3423 * {@link net.user1.orbiter.Connection}. 3424 * 3425 * @extends net.user1.orbiter.WebSocketConnection 3426 * 3427 * @see net.user1.orbiter.WebSocketConnection 3428 */ 3429 net.user1.orbiter.SecureWebSocketConnection = function (host, port) { 3430 // Invoke superclass constructor 3431 net.user1.orbiter.WebSocketConnection.call(this, host, port, net.user1.orbiter.ConnectionType.SECURE_WEBSOCKET); 3432 }; 3433 3434 //============================================================================== 3435 // INHERITANCE 3436 //============================================================================== 3437 net.user1.utils.extend(net.user1.orbiter.SecureWebSocketConnection, net.user1.orbiter.WebSocketConnection); 3438 3439 /** @private */ 3440 net.user1.orbiter.SecureWebSocketConnection.prototype.buildURL = function () { 3441 return "wss://" + this.host + ":" + this.port; 3442 }; 3443 //============================================================================== 3444 // CLASS DECLARATION 3445 //============================================================================== 3446 /** 3447 * @class 3448 * 3449 * HTTPConnection is the abstract superclass of HTTPDirectConnection and 3450 * HTTPIFrameConnection; it is used internally by Orbiter, and is not intended 3451 * for direct use by Orbiter developers. For information on HTTP communication 3452 * with Union Server, see the HTTPDirectConnection and HTTPIFrameConnection classes. 3453 * 3454 * For a list of events dispatched by HTTPConnection, see HTTPConnection's 3455 * superclass, {@link net.user1.orbiter.Connection}. 3456 * 3457 * @extends net.user1.orbiter.Connection 3458 * 3459 * 3460 * @see net.user1.orbiter.HTTPDirectConnection 3461 * @see net.user1.orbiter.HTTPIFrameConnection 3462 */ 3463 net.user1.orbiter.HTTPConnection = function (host, port, type) { 3464 // Invoke superclass constructor 3465 net.user1.orbiter.Connection.call(this, host, port, type || net.user1.orbiter.ConnectionType.HTTP); 3466 3467 // Instance variables 3468 this.url = ""; 3469 this.sendDelayTimerEnabled = true; 3470 this.sendDelayTimeoutID = -1; 3471 this.sendDelayTimerRunning = false; 3472 this.sendDelay = net.user1.orbiter.HTTPConnection.DEFAULT_SEND_DELAY; 3473 3474 this.messageQueue = new Array(); 3475 3476 this.retryDelay = 500; 3477 this.retryHelloTimeoutID = -1; 3478 this.retryIncomingTimeoutID = -1; 3479 this.retryOutgoingTimeoutID = -1; 3480 3481 this.helloResponsePending = false; 3482 this.outgoingResponsePending = false; 3483 3484 // Initialization 3485 this.addEventListener(net.user1.orbiter.ConnectionEvent.SESSION_TERMINATED, this.sessionTerminatedListener, this); 3486 this.addEventListener(net.user1.orbiter.ConnectionEvent.SESSION_NOT_FOUND, this.sessionNotFoundListener, this); 3487 }; 3488 3489 //============================================================================== 3490 // INHERITANCE 3491 //============================================================================== 3492 net.user1.utils.extend(net.user1.orbiter.HTTPConnection, net.user1.orbiter.Connection); 3493 3494 //============================================================================== 3495 // STATIC VARIABLES 3496 //============================================================================== 3497 /** @constant */ 3498 net.user1.orbiter.HTTPConnection.DEFAULT_SEND_DELAY = 300; 3499 3500 //============================================================================== 3501 // ABSTRACT METHODS (MUST BE IMPLEMENTED BY SUBCLASSES) 3502 //============================================================================== 3503 3504 net.user1.orbiter.HTTPConnection.prototype.doRequestDeactivation = net.user1.utils.abstractError; 3505 net.user1.orbiter.HTTPConnection.prototype.doSendHello = net.user1.utils.abstractError; 3506 net.user1.orbiter.HTTPConnection.prototype.doRetryHello = net.user1.utils.abstractError; 3507 net.user1.orbiter.HTTPConnection.prototype.doSendOutgoing = net.user1.utils.abstractError; 3508 net.user1.orbiter.HTTPConnection.prototype.doRetryOutgoing = net.user1.utils.abstractError; 3509 net.user1.orbiter.HTTPConnection.prototype.doSendIncoming = net.user1.utils.abstractError; 3510 net.user1.orbiter.HTTPConnection.prototype.doRetryIncoming = net.user1.utils.abstractError; 3511 net.user1.orbiter.HTTPConnection.prototype.doDispose = net.user1.utils.abstractError; 3512 3513 //============================================================================== 3514 // CONNECTION AND DISCONNECTION 3515 //============================================================================== 3516 net.user1.orbiter.HTTPConnection.prototype.connect = function () { 3517 net.user1.orbiter.Connection.prototype.connect.call(this); 3518 }; 3519 3520 /** @private */ 3521 net.user1.orbiter.HTTPConnection.prototype.deactivateConnection = function () { 3522 this.orbiter.getLog().debug("[CONNECTION] " + this.toString() + " Deactivating..."); 3523 this.connectionState = net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS; 3524 this.stopSendDelayTimer(); 3525 if (this.retryHelloTimeoutID != -1) { 3526 this.orbiter.getLog().debug("[CONNECTION] " + this.toString() + " Cancelling scheduled hello-request retry."); 3527 clearTimeout(this.retryHelloTimeoutID); 3528 this.retryHelloTimeoutID = -1 3529 } 3530 if (this.retryIncomingTimeoutID != -1) { 3531 this.orbiter.getLog().debug("[CONNECTION] " + this.toString() + " Cancelling scheduled incoming-request retry."); 3532 clearTimeout(this.retryIncomingTimeoutID); 3533 this.retryIncomingTimeoutID = -1 3534 } 3535 if (this.retryOutgoingTimeoutID != -1) { 3536 this.orbiter.getLog().debug("[CONNECTION] " + this.toString() + " Cancelling scheduled outgoing-request retry."); 3537 clearTimeout(this.retryOutgoingTimeoutID); 3538 this.retryOutgoingTimeoutID = -1 3539 } 3540 this.deactivateHTTPRequests(); 3541 net.user1.orbiter.Connection.prototype.deactivateConnection.call(this); 3542 }; 3543 3544 /** @private */ 3545 net.user1.orbiter.HTTPConnection.prototype.deactivateHTTPRequests = function () { 3546 this.orbiter.getLog().debug("[CONNECTION] " + this.toString() + " Closing all pending HTTP requests."); 3547 this.doRequestDeactivation(); 3548 this.helloResponsePending = false; 3549 this.outgoingResponsePending = false; 3550 }; 3551 3552 //============================================================================== 3553 // SESSION MANAGEMENT 3554 //============================================================================== 3555 3556 /** @private */ 3557 net.user1.orbiter.HTTPConnection.prototype.sessionTerminatedListener = function (e) { 3558 var state = this.connectionState; 3559 this.deactivateConnection(); 3560 3561 if (state == net.user1.orbiter.ConnectionState.CONNECTION_IN_PROGRESS) { 3562 this.dispatchConnectFailure("Server terminated session before READY state was achieved."); 3563 } else { 3564 this.dispatchServerKillConnect(); 3565 } 3566 }; 3567 3568 /** @private */ 3569 net.user1.orbiter.HTTPConnection.prototype.sessionNotFoundListener = function (e) { 3570 var state = this.connectionState; 3571 3572 this.deactivateConnection(); 3573 3574 if (state == net.user1.orbiter.ConnectionState.CONNECTION_IN_PROGRESS) { 3575 this.dispatchConnectFailure("Client attempted to reestablish an expired session" 3576 + " or establish an unknown session."); 3577 } else { 3578 this.dispatchServerKillConnect(); 3579 } 3580 } 3581 3582 3583 //============================================================================== 3584 // SERVER ASSIGNMENT 3585 //============================================================================== 3586 /** @private */ 3587 net.user1.orbiter.HTTPConnection.prototype.setServer = function (host, port) { 3588 try { 3589 net.user1.orbiter.Connection.prototype.setServer.call(this, host, port); 3590 } finally { 3591 this.buildURL(); 3592 } 3593 } 3594 3595 /** @private */ 3596 net.user1.orbiter.HTTPConnection.prototype.buildURL = function () { 3597 this.url = "http://" + this.host + ":" + this.port; 3598 } 3599 3600 //============================================================================== 3601 // OUTGOING DELAY TIMER 3602 //============================================================================== 3603 /** @private */ 3604 net.user1.orbiter.HTTPConnection.prototype.sendDelayTimerListener = function () { 3605 this.sendDelayTimerRunning = false; 3606 if (this.messageQueue.length > 0) { 3607 this.flushMessageQueue(); 3608 } else { 3609 // No messages in queue, so take no action. 3610 } 3611 } 3612 3613 /** @private */ 3614 net.user1.orbiter.HTTPConnection.prototype.stopSendDelayTimer = function () { 3615 this.sendDelayTimerRunning = false; 3616 if (this.sendDelayTimeoutID != -1) { 3617 clearTimeout(this.sendDelayTimeoutID); 3618 } 3619 this.sendDelayTimeoutID = -1; 3620 } 3621 3622 /** @private */ 3623 net.user1.orbiter.HTTPConnection.prototype.startSendDelayTimer = function () { 3624 this.stopSendDelayTimer(); 3625 var currentObj = this; 3626 var callback = this.sendDelayTimerListener; 3627 this.sendDelayTimerRunning = true; 3628 this.sendDelayTimeoutID = setTimeout(function () { 3629 callback.call(currentObj); 3630 }, this.sendDelay); 3631 } 3632 3633 net.user1.orbiter.HTTPConnection.prototype.setSendDelay = function (milliseconds) { 3634 if (milliseconds > 0) { 3635 if ((milliseconds != this.sendDelay)) { 3636 this.sendDelay = milliseconds; 3637 this.orbiter.getLog().debug("[CONNECTION] " + this.toString() + " Send delay set to: [" 3638 + milliseconds + "]."); 3639 } 3640 this.sendDelayTimerEnabled = true; 3641 } else if (milliseconds == -1) { 3642 this.orbiter.getLog().debug("[CONNECTION] " + toString() + " Send delay disabled."); 3643 this.sendDelayTimerEnabled = false; 3644 this.stopSendDelayTimer(); 3645 } else { 3646 throw new Error("[CONNECTION]" + this.toString() + " Invalid send-delay specified: [" 3647 + milliseconds + "]."); 3648 } 3649 } 3650 3651 net.user1.orbiter.HTTPConnection.prototype.getSendDelay = function () { 3652 return this.sendDelay; 3653 } 3654 3655 //============================================================================== 3656 // RETRY DELAY 3657 //============================================================================== 3658 net.user1.orbiter.HTTPConnection.prototype.setRetryDelay = function (milliseconds) { 3659 if (milliseconds > -1) { 3660 if (milliseconds != this.retryDelay) { 3661 this.retryDelay = milliseconds; 3662 this.orbiter.getLog().debug("[CONNECTION] " + this.toString() + " Retry delay set to: [" 3663 + milliseconds + "]."); 3664 } 3665 } else { 3666 throw new Error("[CONNECTION]" + this.toString() + " Invalid retry delay specified: [" 3667 + milliseconds + "]."); 3668 } 3669 } 3670 3671 //============================================================================== 3672 // DATA SENDING AND QUEUING 3673 //============================================================================== 3674 3675 net.user1.orbiter.HTTPConnection.prototype.send = function (data) { 3676 // If the timer isn't running... 3677 if (!this.sendDelayTimerRunning) { 3678 // ...it is either disabled or expired. Either way, it's time to 3679 // attempt to flush the queue. 3680 this.messageQueue.push(data); 3681 this.flushMessageQueue(); 3682 } else { 3683 // The send-delay timer is running, so we can't send yet. Just queue the message. 3684 this.messageQueue.push(data); 3685 } 3686 } 3687 3688 /** @private */ 3689 net.user1.orbiter.HTTPConnection.prototype.flushMessageQueue = function () { 3690 if (!this.outgoingResponsePending) { 3691 this.openNewOutgoingRequest(this.messageQueue.join("")); 3692 this.messageQueue = new Array(); 3693 } else { 3694 // AN OUTGOING RESPONSE IS STILL PENDING, SO DON'T SEND A NEW ONE 3695 } 3696 } 3697 3698 //============================================================================== 3699 // HELLO REQUEST MANAGEMENT 3700 //============================================================================== 3701 3702 /** @private */ 3703 net.user1.orbiter.HTTPConnection.prototype.transmitHelloMessage = function (helloString) { 3704 this.dispatchSendData(helloString); 3705 this.helloResponsePending = true; 3706 this.doSendHello(helloString); 3707 } 3708 3709 /** @private */ 3710 net.user1.orbiter.HTTPConnection.prototype.helloCompleteListener = function (data) { 3711 if (this.disposed) return; 3712 3713 if (this.helloResponsePending) { 3714 this.helloResponsePending = false; 3715 this.processIncomingData(data); 3716 3717 // Don't immediately open a request in the complete handler due to Win IE bug 3718 var self = this; 3719 setTimeout(function () { 3720 self.openNewIncomingRequest(); 3721 }, 0); 3722 } else { 3723 if (this.connectionState == net.user1.orbiter.ConnectionState.NOT_CONNECTED) { 3724 this.orbiter.getLog().error("[CONNECTION]" + toString() + " u66 (SERVER_HELLO) received, but client is not connected. Ignoring."); 3725 } else { 3726 this.orbiter.getLog().error("[CONNECTION]" + toString() + " Redundant u66 (SERVER_HELLO) received. Ignoring."); 3727 } 3728 } 3729 } 3730 3731 /** @private */ 3732 net.user1.orbiter.HTTPConnection.prototype.helloErrorListener = function () { 3733 if (this.disposed) return; 3734 // There's already a retry scheduled 3735 if (this.retryHelloTimeoutID != -1) return; 3736 // The connection attempt has been aborted 3737 if (this.connectionState != net.user1.orbiter.ConnectionState.CONNECTION_IN_PROGRESS) { 3738 this.orbiter.getLog().error("[CONNECTION]" + this.toString() + " u65 (CLIENT_HELLO) request failed." 3739 + " Connection is no longer in progress, so no retry scheduled."); 3740 return; 3741 } 3742 3743 this.orbiter.getLog().error("[CONNECTION]" + this.toString() + " u65 (CLIENT_HELLO) request failed." 3744 + " Retrying in " + this.retryDelay + "ms."); 3745 3746 // Retry 3747 var self = this; 3748 this.retryHelloTimeoutID = setTimeout(function () { 3749 self.retryHelloTimeoutID = -1; 3750 self.doRetryHello(); 3751 }, this.retryDelay); 3752 } 3753 3754 //============================================================================== 3755 // OUTGOING REQUEST MANAGEMENT 3756 //============================================================================== 3757 3758 /** @private */ 3759 net.user1.orbiter.HTTPConnection.prototype.openNewOutgoingRequest = function (data) { 3760 this.dispatchSendData(data); 3761 this.outgoingResponsePending = true; 3762 this.doSendOutgoing(data); 3763 if (this.sendDelayTimerEnabled == true) { 3764 this.startSendDelayTimer(); 3765 } 3766 } 3767 3768 /** @private */ 3769 net.user1.orbiter.HTTPConnection.prototype.outgoingCompleteListener = function () { 3770 if (this.disposed) return; 3771 3772 this.outgoingResponsePending = false; 3773 3774 if (!this.sendDelayTimerRunning && this.messageQueue.length > 0) { 3775 // Don't immediately open a request in the complete handler due to Win IE bug 3776 var self = this; 3777 setTimeout(function () { 3778 self.flushMessageQueue(); 3779 }, 0); 3780 } 3781 } 3782 3783 /** @private */ 3784 net.user1.orbiter.HTTPConnection.prototype.outgoingErrorListener = function () { 3785 if (this.disposed) return; 3786 // There's already a retry scheduled 3787 if (this.retryOutgoingTimeoutID != -1) return; 3788 // The connection has been closed 3789 if (this.connectionState == net.user1.orbiter.ConnectionState.NOT_CONNECTED 3790 || this.connectionState == net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS) { 3791 this.orbiter.getLog().error("[CONNECTION]" + this.toString() + " Outgoing request failed." 3792 + " Connection is closed, so no retry scheduled."); 3793 return; 3794 } 3795 3796 this.orbiter.getLog().error("[CONNECTION]" + this.toString() + " Outgoing request failed." 3797 + " Retrying in " + this.retryDelay + "ms."); 3798 3799 // Retry 3800 var self = this; 3801 this.retryOutgoingTimeoutID = setTimeout(function () { 3802 self.retryOutgoingTimeoutID = -1; 3803 if (self.disposed 3804 || self.connectionState == net.user1.orbiter.ConnectionState.NOT_CONNECTED 3805 || self.connectionState == net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS) { 3806 return; 3807 } 3808 self.doRetryOutgoing(); 3809 }, this.retryDelay); 3810 } 3811 3812 //============================================================================== 3813 // INCOMING REQUEST MANAGEMENT 3814 //============================================================================== 3815 3816 /** @private */ 3817 net.user1.orbiter.HTTPConnection.prototype.openNewIncomingRequest = function () { 3818 this.doSendIncoming(); 3819 } 3820 3821 /** @private */ 3822 net.user1.orbiter.HTTPConnection.prototype.incomingCompleteListener = function (data) { 3823 if (this.disposed 3824 || this.connectionState == net.user1.orbiter.ConnectionState.NOT_CONNECTED 3825 || this.connectionState == net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS) { 3826 // Incoming request complete, but connection is closed. Ignore content. 3827 return; 3828 } 3829 3830 // Don't immediately open a request in the complete handler due to Win IE bug 3831 var self = this; 3832 setTimeout(function () { 3833 self.processIncomingData(data); 3834 // A message listener might have closed this connection in response to an incoming 3835 // message. Do not open a new incoming request unless the connection is still open. 3836 if (self.disposed 3837 || self.connectionState == net.user1.orbiter.ConnectionState.NOT_CONNECTED 3838 || self.connectionState == net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS) { 3839 return; 3840 } 3841 self.openNewIncomingRequest(); 3842 }, 0); 3843 } 3844 3845 /** @private */ 3846 net.user1.orbiter.HTTPConnection.prototype.incomingErrorListener = function () { 3847 if (this.disposed) return; 3848 // There's already a retry scheduled 3849 if (this.retryIncomingTimeoutID != -1) return; 3850 // The connection has been closed 3851 if (this.connectionState == net.user1.orbiter.ConnectionState.NOT_CONNECTED 3852 || this.connectionState == net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS) { 3853 this.orbiter.getLog().error("[CONNECTION]" + this.toString() + " Incoming request failed." 3854 + " Connection is closed, so no retry scheduled."); 3855 return; 3856 } 3857 3858 this.orbiter.getLog().error("[CONNECTION]" + this.toString() + " Incoming request failed." 3859 + " Retrying in " + this.retryDelay + "ms."); 3860 3861 // Retry 3862 var self = this; 3863 this.retryIncomingTimeoutID = setTimeout(function () { 3864 self.retryIncomingTimeoutID = -1; 3865 if (self.disposed 3866 || self.connectionState == net.user1.orbiter.ConnectionState.NOT_CONNECTED 3867 || self.connectionState == net.user1.orbiter.ConnectionState.DISCONNECTION_IN_PROGRESS) { 3868 return; 3869 } 3870 self.doRetryIncoming(); 3871 }, this.retryDelay); 3872 } 3873 3874 //============================================================================== 3875 // PROCESS DATA FROM THE SERVER 3876 //============================================================================== 3877 3878 /** @private */ 3879 net.user1.orbiter.HTTPConnection.prototype.processIncomingData = function (data) { 3880 if (this.disposed) return; 3881 var listeners; 3882 3883 this.dispatchReceiveData(data); 3884 3885 var upcs = new Array(); 3886 var upcEndTagIndex = data.indexOf("</U>"); 3887 // Empty responses are valid. 3888 if (upcEndTagIndex == -1 && data.length > 0) { 3889 this.orbiter.getLog().error("Invalid message received. No UPC found: [" + data + "]"); 3890 if (!this.isReady()) { 3891 // If invalid XML is received prior to achieving ready, then this 3892 // probably isn't a Union server, so disconnect. 3893 this.disconnect(); 3894 return; 3895 } 3896 } 3897 3898 while (upcEndTagIndex != -1) { 3899 upcs.push(data.substring(0, upcEndTagIndex+4)); 3900 data = data.substring(upcEndTagIndex+4); 3901 upcEndTagIndex = data.indexOf("</U>"); 3902 } 3903 for (var i = 0; i < upcs.length; i++) { 3904 this.dispatchEvent(new net.user1.orbiter.ConnectionEvent(net.user1.orbiter.ConnectionEvent.RECEIVE_UPC, upcs[i])); 3905 } 3906 } 3907 3908 //============================================================================== 3909 // SERVER AFFINITY 3910 //============================================================================== 3911 /** @private */ 3912 net.user1.orbiter.HTTPConnection.prototype.applyAffinity = function (data) { 3913 net.user1.orbiter.Connection.prototype.applyAffinity.call(this); 3914 this.buildURL(); 3915 }; 3916 3917 //============================================================================== 3918 // TOSTRING 3919 //============================================================================== 3920 net.user1.orbiter.HTTPConnection.prototype.toString = function () { 3921 var s = "[" + this.connectionType + ", requested host: " + this.requestedHost 3922 + ", host: " + (this.host == null ? "" : this.host) 3923 + ", port: " + this.port 3924 + ", send-delay: " + this.getSendDelay() + "]"; 3925 return s; 3926 } 3927 3928 // ============================================================================= 3929 // DISPOSAL 3930 // ============================================================================= 3931 /** @private */ 3932 net.user1.orbiter.HTTPConnection.prototype.dispose = function () { 3933 this.doDispose(); 3934 this.stopSendDelayTimer(); 3935 net.user1.orbiter.Connection.prototype.dispose.call(this); 3936 } 3937 //============================================================================== 3938 // CLASS DECLARATION 3939 //============================================================================== 3940 /** 3941 * @class 3942 * 3943 * The HTTPIFrameConnection class is used by Orbiter to communicate with 3944 * Union Server over HTTP in browsers that do not support CORS. 3945 * Rather than using CORS, HTTPIFrameConnection bypasses cross-origin restrictions 3946 * by proxying communications through a hidden HTML iframe. 3947 * 3948 * For a list of events dispatched by HTTPDirectConnection, 3949 * {@link net.user1.orbiter.Connection}. 3950 * 3951 * For more information on HTTP communication with Union Server, see 3952 * the HTTPDirectConnection class. 3953 * 3954 * @extends net.user1.orbiter.HTTPConnection 3955 * 3956 * @see net.user1.orbiter.HTTPDirectConnection 3957 * @see net.user1.orbiter.WebSocketConnection 3958 * @see net.user1.orbiter.SecureHTTPDirectConnection 3959 * @see net.user1.orbiter.SecureHTTPIFrameConnection 3960 * @see net.user1.orbiter.SecureWebSocketConnection 3961 */ 3962 net.user1.orbiter.HTTPIFrameConnection = function (host, port, type) { 3963 // Invoke superclass constructor 3964 net.user1.orbiter.HTTPConnection.call(this, host, port, type || net.user1.orbiter.ConnectionType.HTTP); 3965 this.postMessageInited = false; 3966 this.iFrameReady = false; 3967 }; 3968 3969 //============================================================================== 3970 // INHERITANCE 3971 //============================================================================== 3972 net.user1.utils.extend(net.user1.orbiter.HTTPIFrameConnection, net.user1.orbiter.HTTPConnection); 3973 3974 //============================================================================== 3975 // POSTMESSAGE INITIALIZATION 3976 //============================================================================== 3977 /** @private */ 3978 net.user1.orbiter.HTTPIFrameConnection.prototype.initPostMessage = function () { 3979 if (this.postMessageInited) { 3980 throw new Error("[HTTPIFrameConnection] Illegal duplicate initialization attempt."); 3981 } 3982 var self = this; 3983 var win = this.orbiter.window; 3984 var errorMsg = null; 3985 3986 if (win == null) { 3987 errorMsg = "[HTTPIFrameConnection] Unable to create connection." 3988 + " No window object found."; 3989 } else { 3990 if (typeof win.addEventListener != "undefined") { 3991 // ...the standard way 3992 win.addEventListener("message", postMessageListener, false); 3993 } else if (typeof win.attachEvent != "undefined") { 3994 // ...the IE-specific way 3995 win.attachEvent("onmessage", postMessageListener); 3996 } else { 3997 errorMsg = "[HTTPIFrameConnection] Unable to create connection." 3998 + " No event listener registration method found on window object."; 3999 } 4000 } 4001 4002 if (errorMsg != null) { 4003 this.orbiter.getLog().error(errorMsg); 4004 throw new Error(errorMsg); 4005 } 4006 4007 /** @private */ 4008 function postMessageListener (e) { 4009 // The connection's host might have been reassigned (normally to an ip) due 4010 // to server affinity in a clustered deployment, so allow for posts from both the 4011 // requestedHost and the host. 4012 if (e.origin.indexOf("//" + self.host + (self.port == 80 ? "" : (":" + self.port))) == -1 4013 && e.origin.indexOf("//" + self.requestedHost + (self.port == 80 ? "" : (":" + self.port))) == -1) { 4014 self.orbiter.getLog().error("[CONNECTION] " + self.toString() 4015 + " Ignored message from unknown origin: " + e.origin); 4016 return; 4017 } 4018 4019 self.processPostMessage(e.data); 4020 } 4021 4022 this.postMessageInited = true; 4023 }; 4024 4025 //============================================================================== 4026 // IFRAME MANAGEMENT 4027 //============================================================================== 4028 /** @private */ 4029 net.user1.orbiter.HTTPIFrameConnection.prototype.makeIFrame = function () { 4030 if (typeof this.orbiter.window.document == "undefined") { 4031 var errorMsg = "[HTTPIFrameConnection] Unable to create connection." 4032 + " No document object found."; 4033 this.orbiter.getLog().error(errorMsg); 4034 throw new Error(errorMsg); 4035 } 4036 var doc = this.orbiter.window.document; 4037 4038 this.iFrameReady = false; 4039 if (this.iframe != null) { 4040 this.postToIFrame("dispose"); 4041 doc.body.removeChild(this.iframe); 4042 } 4043 this.iframe = doc.createElement('iframe'); 4044 this.iframe.width = "0px"; 4045 this.iframe.height = "0px"; 4046 this.iframe.border = "0px"; 4047 this.iframe.frameBorder = "0"; 4048 this.iframe.style.visibility = "hidden"; 4049 this.iframe.style.display = "none"; 4050 this.iframe.src = this.url + "/orbiter"; 4051 doc.body.appendChild(this.iframe); 4052 } 4053 4054 /** @private */ 4055 net.user1.orbiter.HTTPIFrameConnection.prototype.onIFrameReady = function () { 4056 this.beginReadyHandshake(); 4057 } 4058 4059 /** @private */ 4060 net.user1.orbiter.HTTPIFrameConnection.prototype.postToIFrame = function (cmd, data) { 4061 if (this.iframe && this.iFrameReady) { 4062 data = data == undefined ? "" : data; 4063 // In order to post to the iframe, the targetOrigin must match the iframe's origin 4064 this.iframe.contentWindow.postMessage(cmd + "," + data, this.iframe.contentWindow.location.href); 4065 } 4066 } 4067 4068 /** @private */ 4069 net.user1.orbiter.HTTPIFrameConnection.prototype.processPostMessage = function (postedData) { 4070 var delimiterIndex = postedData.indexOf(","); 4071 var cmd = postedData.substring(0, delimiterIndex); 4072 var data = postedData.substring(delimiterIndex+1); 4073 4074 switch (cmd) { 4075 case"ready": 4076 this.iFrameReady = true; 4077 this.onIFrameReady(); 4078 break; 4079 4080 case "hellocomplete": 4081 this.helloCompleteListener(data); 4082 break; 4083 4084 case "helloerror": 4085 this.helloErrorListener(); 4086 break; 4087 4088 case "outgoingcomplete": 4089 this.outgoingCompleteListener(); 4090 break; 4091 4092 case "outgoingerror": 4093 this.outgoingErrorListener(); 4094 break; 4095 4096 case "incomingcomplete": 4097 this.incomingCompleteListener(data); 4098 break; 4099 4100 case "incomingerror": 4101 this.incomingErrorListener(); 4102 break; 4103 } 4104 } 4105 4106 //============================================================================== 4107 // CONNECTION AND DISCONNECTION 4108 //============================================================================== 4109 net.user1.orbiter.HTTPIFrameConnection.prototype.connect = function () { 4110 if (!this.postMessageInited) { 4111 this.initPostMessage(); 4112 } 4113 4114 net.user1.orbiter.HTTPConnection.prototype.connect.call(this); 4115 this.makeIFrame(); 4116 }; 4117 4118 /** @private */ 4119 net.user1.orbiter.HTTPIFrameConnection.prototype.doRequestDeactivation = function() { 4120 this.postToIFrame("deactivate"); 4121 }; 4122 4123 //============================================================================== 4124 // UPC LISTENERS (IFRAME-SPECIFIC IMPLEMENTATION) 4125 //============================================================================== 4126 4127 /** @private */ 4128 net.user1.orbiter.HTTPIFrameConnection.prototype.u66 = function (serverVersion, 4129 sessionID, 4130 upcVersion, 4131 protocolCompatible) { 4132 net.user1.orbiter.Connection.prototype.u66.call(this, 4133 serverVersion, 4134 sessionID, 4135 upcVersion, 4136 protocolCompatible); 4137 if (this.iframe != null) { 4138 this.postToIFrame("sessionid", sessionID); 4139 } 4140 } 4141 4142 //============================================================================== 4143 // HELLO REQUEST MANAGEMENT 4144 //============================================================================== 4145 4146 /** @private */ 4147 net.user1.orbiter.HTTPIFrameConnection.prototype.doSendHello = function (helloString) { 4148 this.postToIFrame("sendhello", helloString); 4149 }; 4150 4151 /** @private */ 4152 net.user1.orbiter.HTTPIFrameConnection.prototype.doRetryHello = function () { 4153 this.postToIFrame("retryhello"); 4154 } 4155 4156 //============================================================================== 4157 // OUTGOING REQUEST MANAGEMENT 4158 //============================================================================== 4159 4160 /** @private */ 4161 net.user1.orbiter.HTTPIFrameConnection.prototype.doSendOutgoing = function (data) { 4162 this.postToIFrame("sendoutgoing", data); 4163 }; 4164 4165 /** @private */ 4166 net.user1.orbiter.HTTPIFrameConnection.prototype.doRetryOutgoing = function () { 4167 this.postToIFrame("retryoutgoing"); 4168 }; 4169 4170 //============================================================================== 4171 // INCOMING REQUEST MANAGEMENT 4172 //============================================================================== 4173 4174 /** @private */ 4175 net.user1.orbiter.HTTPIFrameConnection.prototype.doSendIncoming = function () { 4176 this.postToIFrame("sendincoming"); 4177 }; 4178 4179 /** @private */ 4180 net.user1.orbiter.HTTPIFrameConnection.prototype.doRetryIncoming = function () { 4181 this.postToIFrame("retryincoming"); 4182 }; 4183 4184 //============================================================================== 4185 // TOSTRING 4186 //============================================================================== 4187 net.user1.orbiter.HTTPIFrameConnection.prototype.toString = function () { 4188 var s = "[HTTPIFrameConnection, requested host: " + this.requestedHost 4189 + ", host: " + (this.host == null ? "" : this.host) 4190 + ", port: " + this.port 4191 + ", send-delay: " + this.getSendDelay() + "]"; 4192 return s; 4193 }; 4194 4195 //============================================================================== 4196 // DISPOSAL 4197 //============================================================================== 4198 /** @private */ 4199 net.user1.orbiter.HTTPIFrameConnection.prototype.doDispose = function () { 4200 this.postToIFrame("dispose"); 4201 }; 4202 //============================================================================== 4203 // CLASS DECLARATION 4204 //============================================================================== 4205 /** 4206 * @class 4207 * <p> 4208 * The HTTPDirectConnection class is used by Orbiter to communicate with 4209 * Union Server over HTTP; it uses CORS to bypass cross-origin restrictions 4210 * when Union Server is hosted on a domain that does not match the domain at 4211 * which the Orbiter client is hosted. Normally, developers need not use the 4212 * HTTPDirectConnection class directly, and should instead make connections 4213 * via the Orbiter class's connect() method. However, the 4214 * HTTPDirectConnection class is required for fine-grained connection configuration, 4215 * such as defining failover connections for multiple Union Servers 4216 * running at different host addresses. 4217 * </p> 4218 * 4219 * <p> 4220 * By default, Orbiter uses the WebSocketConnection class, not the 4221 * HTTPDirectConnection class, to communicate with Union Server. The 4222 * HTTPDirectConnection class is used as a backup connection 4223 * when the primary WebSocketConnection connection is blocked by a firewall. 4224 * However, on a very heavily loaded server with limited persistent socket 4225 * connections available, communicating with Union Server over HTTP--which uses 4226 * short-lived socket connections--can improve performance at the 4227 * expense of realtime responsiveness. To reduce server load when communicating 4228 * over HTTP, use HTTPDirectConnection's setSendDelay() method to decrease the 4229 * frequency of Orbiter's requests for updates from Union Server. Developers 4230 * that wish to use HTTP connections as the primary form of communication with 4231 * Union Server must do so by manually configuring connections via the 4232 * ConnectionManager class's addConnection() method.</p> 4233 * 4234 * <p> 4235 * In environments that do not support CORS (such as IE8 on Windows), Orbiter 4236 * conducts HTTP communications using HTTPIFrameConnection instead of HTTPDirectConnection. 4237 * </p> 4238 * 4239 * <p> 4240 * For secure HTTP and WebSocket communications, see SecureHTTPDirectConnection, 4241 * SecureHTTPIFrameConnection, and SecureWebSocketConnection. 4242 * </p> 4243 * 4244 * 4245 * For a list of events dispatched by HTTPDirectConnection, 4246 * {@link net.user1.orbiter.Connection}. 4247 * 4248 * @extends net.user1.orbiter.HTTPConnection 4249 * 4250 * @see net.user1.orbiter.Orbiter#connect 4251 * @see net.user1.orbiter.Orbiter#secureConnect 4252 * 4253 * @see net.user1.orbiter.SecureHTTPDirectConnection 4254 * @see net.user1.orbiter.SecureHTTPIFrameConnection 4255 * @see net.user1.orbiter.SecureWebSocketConnection 4256 */ 4257 net.user1.orbiter.HTTPDirectConnection = function (host, port, type) { 4258 // Invoke superclass constructor 4259 net.user1.orbiter.HTTPConnection.call(this, host, port, type || net.user1.orbiter.ConnectionType.HTTP); 4260 4261 this.outgoingRequestID = 0; 4262 this.incomingRequestID = 0; 4263 4264 this.lastOutgoingPostData = null; 4265 this.lastIncomingPostData = null; 4266 this.lastHelloPostData = null; 4267 4268 this.pendingRequests = []; 4269 }; 4270 4271 //============================================================================== 4272 // INHERITANCE 4273 //============================================================================== 4274 net.user1.utils.extend(net.user1.orbiter.HTTPDirectConnection, net.user1.orbiter.HTTPConnection); 4275 4276 4277 //============================================================================== 4278 // CONNECTION AND DISCONNECTION 4279 //============================================================================== 4280 net.user1.orbiter.HTTPDirectConnection.prototype.connect = function () { 4281 net.user1.orbiter.HTTPConnection.prototype.connect.call(this); 4282 this.beginReadyHandshake(); 4283 }; 4284 4285 //============================================================================== 4286 // HELLO REQUEST MANAGEMENT 4287 //============================================================================== 4288 4289 /** @private Abstract method implementation */ 4290 net.user1.orbiter.HTTPDirectConnection.prototype.doSendHello = function (helloString) { 4291 this.newHelloRequest(helloString); 4292 }; 4293 4294 /** @private Abstract method implementation */ 4295 net.user1.orbiter.HTTPDirectConnection.prototype.doRetryHello = function () { 4296 this.retryHello(); 4297 } 4298 4299 /** @private */ 4300 net.user1.orbiter.HTTPDirectConnection.prototype.newHelloRequest = function (data) { 4301 this.lastHelloPostData = this.createHelloPostData(encodeURIComponent(data)); 4302 this.transmitRequest(this.lastHelloPostData, 4303 net.user1.orbiter.HTTPDirectConnection.helloRequestReadystatechangeListener, 4304 net.user1.orbiter.HTTPDirectConnection.helloRequestErrorListener); 4305 } 4306 4307 /** @private */ 4308 net.user1.orbiter.HTTPDirectConnection.prototype.createHelloPostData = function (data) { 4309 return "mode=d" + "&data=" + data; 4310 } 4311 4312 /** @private */ 4313 net.user1.orbiter.HTTPDirectConnection.prototype.retryHello = function () { 4314 this.transmitRequest(this.lastHelloPostData, 4315 net.user1.orbiter.HTTPDirectConnection.helloRequestReadystatechangeListener, 4316 net.user1.orbiter.HTTPDirectConnection.helloRequestErrorListener); 4317 } 4318 4319 /** @private */ 4320 net.user1.orbiter.HTTPDirectConnection.helloRequestReadystatechangeListener = function (xhr, connection) { 4321 if (xhr.readyState == 4) { 4322 connection.removePendingRequest(xhr); 4323 if (xhr.status >= 200 && xhr.status <= 299) { 4324 connection.helloCompleteListener(xhr.responseText); 4325 } else { 4326 connection.helloErrorListener(); 4327 } 4328 } 4329 } 4330 4331 /** @private */ 4332 net.user1.orbiter.HTTPDirectConnection.helloRequestErrorListener = function (xhr, connection) { 4333 connection.removePendingRequest(xhr); 4334 connection.helloErrorListener(); 4335 } 4336 4337 //============================================================================== 4338 // OUTGOING REQUEST MANAGEMENT 4339 //============================================================================== 4340 4341 /** @private Abstract method implementation */ 4342 net.user1.orbiter.HTTPDirectConnection.prototype.doSendOutgoing = function (data) { 4343 this.newOutgoingRequest(data); 4344 }; 4345 4346 /** @private Abstract method implementation */ 4347 net.user1.orbiter.HTTPDirectConnection.prototype.doRetryOutgoing = function () { 4348 this.retryOutgoing(); 4349 }; 4350 4351 /** @private */ 4352 net.user1.orbiter.HTTPDirectConnection.prototype.newOutgoingRequest = function (data) { 4353 this.lastOutgoingPostData = this.createOutgoingPostData(encodeURIComponent(data)); 4354 this.transmitRequest(this.lastOutgoingPostData, 4355 net.user1.orbiter.HTTPDirectConnection.outgoingRequestReadystatechangeListener, 4356 net.user1.orbiter.HTTPDirectConnection.outgoingRequestErrorListener); 4357 } 4358 4359 /** @private */ 4360 net.user1.orbiter.HTTPDirectConnection.prototype.createOutgoingPostData = function (data) { 4361 this.outgoingRequestID++; 4362 return "rid=" + this.outgoingRequestID + "&sid=" + this.orbiter.getSessionID() + "&mode=s" + "&data=" + data; 4363 } 4364 4365 /** @private */ 4366 net.user1.orbiter.HTTPDirectConnection.prototype.retryOutgoing = function () { 4367 this.transmitRequest(this.lastOutgoingPostData, 4368 net.user1.orbiter.HTTPDirectConnection.outgoingRequestReadystatechangeListener, 4369 net.user1.orbiter.HTTPDirectConnection.outgoingRequestErrorListener); 4370 } 4371 4372 /** @private */ 4373 net.user1.orbiter.HTTPDirectConnection.outgoingRequestReadystatechangeListener = function (xhr, connection) { 4374 if (xhr.readyState == 4) { 4375 connection.removePendingRequest(xhr); 4376 if (xhr.status >= 200 && xhr.status <= 299) { 4377 connection.outgoingCompleteListener(); 4378 } else { 4379 connection.outgoingErrorListener(); 4380 } 4381 } 4382 } 4383 4384 /** @private */ 4385 net.user1.orbiter.HTTPDirectConnection.outgoingRequestErrorListener = function (xhr, connection) { 4386 connection.removePendingRequest(xhr); 4387 connection.outgoingErrorListener(); 4388 } 4389 4390 //============================================================================== 4391 // INCOMING REQUEST MANAGEMENT 4392 //============================================================================== 4393 4394 /** @private Abstract method implementation */ 4395 net.user1.orbiter.HTTPDirectConnection.prototype.doSendIncoming = function () { 4396 this.newIncomingRequest(); 4397 }; 4398 4399 /** @private Abstract method implementation */ 4400 net.user1.orbiter.HTTPDirectConnection.prototype.doRetryIncoming = function () { 4401 this.retryIncoming(); 4402 }; 4403 4404 /** @private */ 4405 net.user1.orbiter.HTTPDirectConnection.prototype.newIncomingRequest = function () { 4406 this.lastIncomingPostData = this.createIncomingPostData(); 4407 this.transmitRequest(this.lastIncomingPostData, 4408 net.user1.orbiter.HTTPDirectConnection.incomingRequestReadystatechangeListener, 4409 net.user1.orbiter.HTTPDirectConnection.incomingRequestErrorListener); 4410 } 4411 4412 /** @private */ 4413 net.user1.orbiter.HTTPDirectConnection.prototype.createIncomingPostData = function () { 4414 this.incomingRequestID++; 4415 return "rid=" + this.incomingRequestID + "&sid=" + this.orbiter.getSessionID() + "&mode=c"; 4416 } 4417 4418 /** @private */ 4419 net.user1.orbiter.HTTPDirectConnection.prototype.retryIncoming = function () { 4420 this.transmitRequest(this.lastIncomingPostData, 4421 net.user1.orbiter.HTTPDirectConnection.incomingRequestReadystatechangeListener, 4422 net.user1.orbiter.HTTPDirectConnection.incomingRequestErrorListener); 4423 } 4424 4425 /** @private */ 4426 net.user1.orbiter.HTTPDirectConnection.incomingRequestReadystatechangeListener = function (xhr, connection) { 4427 if (xhr.readyState == 4) { 4428 connection.removePendingRequest(xhr); 4429 if (xhr.status >= 200 && xhr.status <= 299) { 4430 connection.incomingCompleteListener(xhr.responseText); 4431 } else { 4432 connection.incomingErrorListener(); 4433 } 4434 } 4435 } 4436 4437 /** @private */ 4438 net.user1.orbiter.HTTPDirectConnection.incomingRequestErrorListener = function (xhr, connection) { 4439 connection.removePendingRequest(xhr); 4440 connection.incomingErrorListener(); 4441 } 4442 4443 //============================================================================== 4444 // XHR MANAGEMENT 4445 //============================================================================== 4446 /** @private */ 4447 net.user1.orbiter.HTTPDirectConnection.prototype.transmitRequest = function (data, 4448 readystatechangeListener, 4449 errorListener) { 4450 var self = this; 4451 var request; 4452 4453 if (typeof XDomainRequest != "undefined") { 4454 // IE 4455 request = new XDomainRequest(); 4456 request.onload = function () { 4457 request.readyState = 4; // Emulate standards-based API 4458 request.status = 200; 4459 readystatechangeListener(this, self) 4460 }; 4461 request.onerror = function () { 4462 errorListener(this, self); 4463 }; 4464 request.ontimeout = function () { 4465 errorListener(this, self); 4466 }; 4467 request.onprogress = function () {}; // Do nothing (required) 4468 } else { 4469 // All other standards-based browsers 4470 var request = new XMLHttpRequest(); 4471 this.pendingRequests.push(request); 4472 request.onreadystatechange = function () { 4473 readystatechangeListener(this, self); 4474 }; 4475 request.onerror = function () { 4476 errorListener(this, self); 4477 }; 4478 } 4479 // Call open before setting header 4480 request.open("POST", this.url); 4481 // Standards-based browsers (IE doesn't allow the setting of headers) 4482 if (typeof request.setRequestHeader != "undefined") { 4483 request.setRequestHeader("Content-Type", "text/plain;charset=UTF-8"); 4484 } 4485 request.send(data); 4486 } 4487 4488 /** @private */ 4489 net.user1.orbiter.HTTPDirectConnection.prototype.removePendingRequest = function (request) { 4490 for (var i = this.pendingRequests.length; --i >= 0; ) { 4491 if (this.pendingRequests[i] === request) { 4492 this.pendingRequests.splice(i, 1); 4493 } 4494 } 4495 } 4496 4497 /** @private Abstract method implementation */ 4498 net.user1.orbiter.HTTPDirectConnection.prototype.doRequestDeactivation = function () { 4499 for (var i = this.pendingRequests.length; --i >= 0;) { 4500 try { 4501 this.pendingRequests[i].abort(); 4502 } catch (e) { 4503 // Do nothing 4504 } 4505 } 4506 this.pendingRequests = []; 4507 } 4508 4509 //============================================================================== 4510 // TOSTRING 4511 //============================================================================== 4512 net.user1.orbiter.HTTPDirectConnection.prototype.toString = function () { 4513 var s = "[HTTPDirectConnection, requested host: " + this.requestedHost 4514 + ", host: " + (this.host == null ? "" : this.host) 4515 + ", port: " + this.port 4516 + ", send-delay: " + this.getSendDelay() + "]"; 4517 return s; 4518 }; 4519 4520 //============================================================================== 4521 // DISPOSAL 4522 //============================================================================== 4523 /** @private */ 4524 net.user1.orbiter.HTTPDirectConnection.prototype.doDispose = function () { 4525 this.deactivateHTTPRequests(); 4526 }; 4527 //============================================================================== 4528 // CLASS DECLARATION 4529 //============================================================================== 4530 /** @class 4531 * <p> 4532 * The SecureHTTPDirectConnection class is identical to HTTPDirectConnection 4533 * except that it performs communications over HTTPS (i.e., an encrypted TLS or 4534 * SSL connection) rather than plain HTTP.</p> 4535 * 4536 * For a list of events dispatched by SecureHTTPDirectConnection, see 4537 * {@link net.user1.orbiter.Connection}. 4538 * 4539 * @extends net.user1.orbiter.HTTPDirectConnection 4540 * 4541 * @see net.user1.orbiter.HTTPDirectConnection 4542 */ 4543 net.user1.orbiter.SecureHTTPDirectConnection = function (host, port) { 4544 // Invoke superclass constructor 4545 net.user1.orbiter.HTTPDirectConnection.call(this, host, port, net.user1.orbiter.ConnectionType.SECURE_HTTP); 4546 }; 4547 4548 //============================================================================== 4549 // INHERITANCE 4550 //============================================================================== 4551 net.user1.utils.extend(net.user1.orbiter.SecureHTTPDirectConnection, net.user1.orbiter.HTTPDirectConnection); 4552 4553 /** @private */ 4554 net.user1.orbiter.SecureHTTPDirectConnection.prototype.buildURL = function () { 4555 this.url = "https://" + this.host + ":" + this.port; 4556 }; 4557 4558 //============================================================================== 4559 // TOSTRING 4560 //============================================================================== 4561 net.user1.orbiter.SecureHTTPDirectConnection.prototype.toString = function () { 4562 var s = "[SecureHTTPDirectConnection, requested host: " + this.requestedHost 4563 + ", host: " + (this.host == null ? "" : this.host) 4564 + ", port: " + this.port 4565 + ", send-delay: " + this.getSendDelay() + "]"; 4566 return s; 4567 }; 4568 //============================================================================== 4569 // CLASS DECLARATION 4570 //============================================================================== 4571 /** 4572 * @class 4573 * 4574 * <p> 4575 * The SecureHTTPIFrameConnection class is identical to HTTPIFrameConnection 4576 * except that it performs communications over HTTPS (i.e., an encrypted TLS or 4577 * SSL connection) rather than plain HTTP.</p> 4578 * 4579 * For a list of events dispatched by SecureHTTPIFrameConnection, 4580 * see {@link net.user1.orbiter.Connection}. 4581 * 4582 * @extends net.user1.orbiter.HTTPIFrameConnection 4583 * 4584 * @see net.user1.orbiter.HTTPIFrameConnection 4585 */ 4586 net.user1.orbiter.SecureHTTPIFrameConnection = function (host, port) { 4587 // Invoke superclass constructor 4588 net.user1.orbiter.HTTPIFrameConnection.call(this, host, port, net.user1.orbiter.ConnectionType.SECURE_HTTP); 4589 }; 4590 4591 //============================================================================== 4592 // INHERITANCE 4593 //============================================================================== 4594 net.user1.utils.extend(net.user1.orbiter.SecureHTTPIFrameConnection, net.user1.orbiter.HTTPIFrameConnection); 4595 4596 /** @private */ 4597 net.user1.orbiter.SecureHTTPIFrameConnection.prototype.buildURL = function () { 4598 this.url = "https://" + this.host + ":" + this.port; 4599 }; 4600 4601 //============================================================================== 4602 // TOSTRING 4603 //============================================================================== 4604 net.user1.orbiter.SecureHTTPIFrameConnection.prototype.toString = function () { 4605 var s = "[SecureHTTPIFrameConnection, requested host: " + this.requestedHost 4606 + ", host: " + (this.host == null ? "" : this.host) 4607 + ", port: " + this.port 4608 + ", send-delay: " + this.getSendDelay() + "]"; 4609 return s; 4610 }; 4611 //============================================================================== 4612 // CLASS DECLARATION 4613 //============================================================================== 4614 /** @private */ 4615 net.user1.orbiter.MessageListener = function (listener, 4616 forRoomIDs, 4617 thisArg) { 4618 this.listener = listener; 4619 this.forRoomIDs = forRoomIDs; 4620 this.thisArg = thisArg; 4621 }; 4622 4623 //============================================================================== 4624 // INSTANCE METHODS 4625 //============================================================================== 4626 /** @private */ 4627 net.user1.orbiter.MessageListener.prototype.getListenerFunction = function () { 4628 return this.listener; 4629 }; 4630 4631 /** @private */ 4632 net.user1.orbiter.MessageListener.prototype.getForRoomIDs = function () { 4633 return this.forRoomIDs; 4634 }; 4635 4636 /** @private */ 4637 net.user1.orbiter.MessageListener.prototype.getThisArg = function () { 4638 return this.thisArg; 4639 }; 4640 4641 /** @private */ 4642 net.user1.orbiter.MessageListener.prototype.toString = function () { 4643 return "[object MessageListener]"; 4644 }; 4645 //============================================================================== 4646 // CLASS DECLARATION 4647 //============================================================================== 4648 /** @class */ 4649 net.user1.orbiter.MessageManager = function (log, connectionManager) { 4650 this.log = log; 4651 this.messageListeners = new Object(); 4652 this.removeListenersOnDisconnect = true; 4653 this.numMessagesSent = 0; 4654 this.numMessagesReceived = 0; 4655 this.currentConnection = null; 4656 this.connectionManager = connectionManager; 4657 this.connectionManager.addEventListener(net.user1.orbiter.ConnectionManagerEvent.SELECT_CONNECTION, 4658 this.selectConnectionListener, this); 4659 }; 4660 4661 //============================================================================== 4662 // INSTANCE METHODS 4663 //============================================================================== 4664 net.user1.orbiter.MessageManager.prototype.getNumMessagesReceived = function () { 4665 return this.numMessagesReceived; 4666 } 4667 4668 net.user1.orbiter.MessageManager.prototype.getNumMessagesSent = function () { 4669 return this.numMessagesSent; 4670 } 4671 4672 net.user1.orbiter.MessageManager.prototype.getTotalMessages = function () { 4673 return this.numMessagesSent + this.numMessagesReceived; 4674 } 4675 4676 /** @private */ 4677 net.user1.orbiter.MessageManager.prototype.selectConnectionListener = function (e) { 4678 if (this.currentConnection != null) { 4679 this.currentConnection.removeEventListener(net.user1.orbiter.ConnectionEvent.RECEIVE_UPC, 4680 this.upcReceivedListener, this); 4681 this.currentConnection.removeEventListener(net.user1.orbiter.ConnectionEvent.DISCONNECT, 4682 this.disconnectListener, this); 4683 this.currentConnection.removeEventListener(net.user1.orbiter.ConnectionEvent.CONNECT_FAILURE, 4684 this.connectFailureListener, this); 4685 } 4686 4687 this.currentConnection = e.getConnection(); 4688 4689 this.currentConnection.addEventListener(net.user1.orbiter.ConnectionEvent.RECEIVE_UPC, 4690 this.upcReceivedListener, this); 4691 this.currentConnection.addEventListener(net.user1.orbiter.ConnectionEvent.DISCONNECT, 4692 this.disconnectListener, this); 4693 this.currentConnection.addEventListener(net.user1.orbiter.ConnectionEvent.CONNECT_FAILURE, 4694 this.connectFailureListener, this); 4695 } 4696 4697 /** @private */ 4698 net.user1.orbiter.MessageManager.prototype.disconnectListener = function (e) { 4699 this.cleanupAfterClosedConnection(e.target); 4700 } 4701 4702 /** @private */ 4703 net.user1.orbiter.MessageManager.prototype.connectFailureListener = function (e) { 4704 this.cleanupAfterClosedConnection(e.target); 4705 } 4706 4707 /** @private */ 4708 net.user1.orbiter.MessageManager.prototype.cleanupAfterClosedConnection = function (connection) { 4709 var listenerList; 4710 if (this.removeListenersOnDisconnect) { 4711 this.log.info("[MESSAGE_MANAGER] Removing registered message listeners."); 4712 for (var message in this.messageListeners) { 4713 listenerList = this.messageListeners[message]; 4714 for (var p in listenerList) { 4715 this.removeMessageListener(message, listenerList[p].getListenerFunction()); 4716 } 4717 } 4718 } else { 4719 this.log.warn("[MESSAGE_MANAGER] Leaving message listeners registered. \n" 4720 + "Be sure to remove any unwanted message listeners manually."); 4721 } 4722 4723 this.numMessagesReceived = 0; 4724 this.numMessagesSent = 0; 4725 } 4726 4727 net.user1.orbiter.MessageManager.prototype.sendUPC = function (message) { 4728 // Quit if the connection isn't ready... 4729 if (!this.connectionManager.isReady()) { 4730 this.log.warn("[MESSAGE_MANAGER] Connection not ready. UPC not sent. Message: " 4731 + message); 4732 return; 4733 } 4734 4735 // Build the UPC to send. 4736 var theUPC = "<U><M>" + message + "</M>"; 4737 var a; 4738 4739 if (arguments.length > 1) { 4740 theUPC += "<L>"; 4741 for (var i = 1; i < arguments.length; i++) { 4742 a = arguments[i]; 4743 a = a == undefined ? "" : a.toString(); 4744 // Wrap any non-filter argument that contains a start tag ("<") in CDATA 4745 if (a.indexOf("<") != -1) { 4746 if (a.indexOf('<f t=') != 0) { 4747 a = "<![CDATA[" + a + "]]>"; 4748 } 4749 } 4750 theUPC += "<A>" + a + "</A>"; 4751 } 4752 theUPC += "</L>"; 4753 } 4754 theUPC += "</U>"; 4755 4756 // Count the message 4757 this.numMessagesSent++; 4758 4759 // Send the UPC to the server 4760 this.log.debug("[MESSAGE_MANAGER] UPC sent: " + theUPC); 4761 this.connectionManager.getActiveConnection().send(theUPC); 4762 }; 4763 4764 /** @private */ 4765 net.user1.orbiter.MessageManager.prototype.sendUPCObject = function (upc) { 4766 var args = upc.args.slice(); 4767 args.unshift(upc.method); 4768 this.sendUPC.apply(this, args); 4769 }; 4770 4771 /** @private */ 4772 net.user1.orbiter.MessageManager.prototype.upcReceivedListener = function (e) { 4773 this.numMessagesReceived++; 4774 4775 var upc = e.getUPC(); 4776 this.log.debug("[MESSAGE_MANAGER] UPC received: " + upc ); 4777 4778 var method; 4779 var upcArgs = new Array(); 4780 4781 var closeMTagIndex = upc.indexOf("</M>"); 4782 method = upc.substring(6, closeMTagIndex); 4783 4784 var searchBeginIndex = upc.indexOf("<A>", closeMTagIndex); 4785 var closeATagIndex; 4786 var arg; 4787 while (searchBeginIndex != -1) { 4788 closeATagIndex = upc.indexOf("</A>", searchBeginIndex); 4789 arg = upc.substring(searchBeginIndex+3, closeATagIndex); 4790 if (arg.indexOf("<![CDATA[") == 0) { 4791 arg = arg.substr(9, arg.length-12); 4792 } 4793 upcArgs.push(arg); 4794 searchBeginIndex = upc.indexOf("<A>", closeATagIndex); 4795 } 4796 4797 this.notifyMessageListeners(method, upcArgs); 4798 }; 4799 4800 net.user1.orbiter.MessageManager.prototype.addMessageListener = function (message, 4801 listener, 4802 thisArg, 4803 forRoomIDs) { 4804 if (forRoomIDs != null) { 4805 var typeString = Object.prototype.toString.call(forRoomIDs); 4806 if (typeString != "[object Array]") { 4807 throw new Error("[MESSAGE_MANAGER] Illegal argument type " + typeString 4808 + " supplied for addMessageListener()'s forRoomIDs" 4809 + " parameter. Value must be an Array."); 4810 } 4811 } 4812 4813 // Each message gets a list of MessageListener objects. 4814 // If this message has no such list, make one. 4815 if (this.messageListeners[message] === undefined) { 4816 this.messageListeners[message] = new Array(); 4817 } 4818 var listenerArray = this.messageListeners[message]; 4819 4820 // Quit if the listener is already registered 4821 if (this.hasMessageListener(message, listener)) { 4822 return false; 4823 } 4824 4825 // Add the listener 4826 var newListener = new net.user1.orbiter.MessageListener(listener, 4827 forRoomIDs === undefined ? null : forRoomIDs, 4828 thisArg); 4829 listenerArray.push(newListener); 4830 return true; 4831 }; 4832 4833 net.user1.orbiter.MessageManager.prototype.removeMessageListener = function (message, 4834 listener) { 4835 // Quit if the message has no listeners 4836 var listenerArray = this.messageListeners[message]; 4837 if (listenerArray == null) { 4838 return false; 4839 } 4840 4841 // Remove the listener 4842 var foundListener; 4843 for (var i = 0; i < listenerArray.length; i++) { 4844 if (listenerArray[i].getListenerFunction() == listener) { 4845 foundListener = true; 4846 listenerArray.splice(i, 1); 4847 break; 4848 } 4849 } 4850 4851 // Delete the listeners array if it's now empty 4852 if (listenerArray.length == 0) { 4853 delete this.messageListeners[message]; 4854 } 4855 4856 return foundListener; 4857 }; 4858 4859 net.user1.orbiter.MessageManager.prototype.hasMessageListener = function (message, 4860 listener) { 4861 // Quit if the message has no listeners 4862 var listenerArray = this.messageListeners[message]; 4863 if (listenerArray == null) { 4864 return false; 4865 } 4866 4867 // Check for the listener 4868 for (var i = 0; i < listenerArray.length; i++) { 4869 if (listenerArray[i].getListenerFunction() 4870 == listener) { 4871 return true; 4872 } 4873 } 4874 return false; 4875 }; 4876 4877 net.user1.orbiter.MessageManager.prototype.getMessageListeners = function (message) { 4878 return this.messageListeners[message] != undefined ? this.messageListeners[message] : []; 4879 }; 4880 4881 /** @private */ 4882 net.user1.orbiter.MessageManager.prototype.notifyMessageListeners = function (message, args) { 4883 // Retrieve the list of listeners for this message. 4884 var listeners = this.messageListeners[message]; 4885 // If there are no listeners registered, then quit 4886 if (listeners === undefined) { 4887 // Log a warning if it's not a UPC 4888 if (!(message.charAt(0) == "u" && parseInt(message.substring(1)) > 1)) { 4889 this.log.warn("Message delivery failed. No listeners found. Message: " + 4890 message + ". Arguments: " + args.join()); 4891 } 4892 return; 4893 } else { 4894 listeners = listeners.slice(0); 4895 } 4896 var numListeners = listeners.length; 4897 for (var i = 0; i < numListeners; i++) { 4898 listeners[i].getListenerFunction().apply(listeners[i].getThisArg(), args); 4899 } 4900 }; 4901 4902 net.user1.orbiter.MessageManager.prototype.dispose = function () { 4903 this.log.info("[MESSAGE_MANAGER] Disposing resources."); 4904 this.log = null; 4905 this.orbiter = null; 4906 this.messageListeners = null; 4907 this.numMessagesSent = 0; 4908 this.numMessagesReceived = 0; 4909 this.currentConnection = null; 4910 } 4911 4912 net.user1.orbiter.MessageManager.prototype.toString = function () { 4913 return "[object MessageManager]"; 4914 }; 4915 4916 //============================================================================== 4917 // UPC CONSTANTS 4918 //============================================================================== 4919 /** @class */ 4920 net.user1.orbiter.UPC = new Object(); 4921 4922 // CLIENT TO SERVER 4923 /** @constant */ 4924 net.user1.orbiter.UPC.SEND_MESSAGE_TO_ROOMS = "u1"; 4925 /** @constant */ 4926 net.user1.orbiter.UPC.SEND_MESSAGE_TO_CLIENTS = "u2"; 4927 /** @constant */ 4928 net.user1.orbiter.UPC.SET_CLIENT_ATTR = "u3"; 4929 /** @constant */ 4930 net.user1.orbiter.UPC.JOIN_ROOM = "u4"; 4931 /** @constant */ 4932 net.user1.orbiter.UPC.SET_ROOM_ATTR = "u5"; 4933 /** @constant */ 4934 net.user1.orbiter.UPC.LEAVE_ROOM = "u10"; 4935 /** @constant */ 4936 net.user1.orbiter.UPC.CREATE_ACCOUNT = "u11"; 4937 /** @constant */ 4938 net.user1.orbiter.UPC.REMOVE_ACCOUNT = "u12"; 4939 /** @constant */ 4940 net.user1.orbiter.UPC.CHANGE_ACCOUNT_PASSWORD = "u13"; 4941 /** @constant */ 4942 net.user1.orbiter.UPC.LOGIN = "u14"; 4943 /** @constant */ 4944 net.user1.orbiter.UPC.GET_CLIENTCOUNT_SNAPSHOT = "u18"; 4945 /** @constant */ 4946 net.user1.orbiter.UPC.SYNC_TIME = "u19"; 4947 /** @constant */ 4948 net.user1.orbiter.UPC.GET_ROOMLIST_SNAPSHOT = "u21"; 4949 /** @constant */ 4950 net.user1.orbiter.UPC.CREATE_ROOM = "u24"; 4951 /** @constant */ 4952 net.user1.orbiter.UPC.REMOVE_ROOM = "u25"; 4953 /** @constant */ 4954 net.user1.orbiter.UPC.WATCH_FOR_ROOMS = "u26"; 4955 /** @constant */ 4956 net.user1.orbiter.UPC.STOP_WATCHING_FOR_ROOMS = "u27"; 4957 /** @constant */ 4958 net.user1.orbiter.UPC.GET_ROOM_SNAPSHOT = "u55"; 4959 /** @constant */ 4960 net.user1.orbiter.UPC.SEND_MESSAGE_TO_SERVER = "u57"; 4961 /** @constant */ 4962 net.user1.orbiter.UPC.OBSERVE_ROOM = "u58"; 4963 /** @constant */ 4964 net.user1.orbiter.UPC.STOP_OBSERVING_ROOM = "u61"; 4965 /** @constant */ 4966 net.user1.orbiter.UPC.SET_ROOM_UPDATE_LEVELS = "u64"; 4967 /** @constant */ 4968 net.user1.orbiter.UPC.CLIENT_HELLO = "u65"; 4969 /** @constant */ 4970 net.user1.orbiter.UPC.REMOVE_ROOM_ATTR = "u67"; 4971 /** @constant */ 4972 net.user1.orbiter.UPC.REMOVE_CLIENT_ATTR = "u69"; 4973 /** @constant */ 4974 net.user1.orbiter.UPC.SEND_ROOMMODULE_MESSAGE = "u70"; 4975 /** @constant */ 4976 net.user1.orbiter.UPC.SEND_SERVERMODULE_MESSAGE = "u71"; 4977 /** @constant */ 4978 net.user1.orbiter.UPC.TERMINATE_SESSION = "u83"; 4979 /** @constant */ 4980 net.user1.orbiter.UPC.LOGOFF = "u86"; 4981 /** @constant */ 4982 net.user1.orbiter.UPC.GET_CLIENTLIST_SNAPSHOT = "u91"; 4983 /** @constant */ 4984 net.user1.orbiter.UPC.WATCH_FOR_CLIENTS = "u92"; 4985 /** @constant */ 4986 net.user1.orbiter.UPC.STOP_WATCHING_FOR_CLIENTS = "u93"; 4987 /** @constant */ 4988 net.user1.orbiter.UPC.GET_CLIENT_SNAPSHOT = "u94"; 4989 /** @constant */ 4990 net.user1.orbiter.UPC.OBSERVE_CLIENT = "u95"; 4991 /** @constant */ 4992 net.user1.orbiter.UPC.STOP_OBSERVING_CLIENT = "u96"; 4993 /** @constant */ 4994 net.user1.orbiter.UPC.GET_ACCOUNTLIST_SNAPSHOT = "u97"; 4995 /** @constant */ 4996 net.user1.orbiter.UPC.WATCH_FOR_ACCOUNTS = "u98"; 4997 /** @constant */ 4998 net.user1.orbiter.UPC.STOP_WATCHING_FOR_ACCOUNTS = "u99"; 4999 /** @constant */ 5000 net.user1.orbiter.UPC.GET_ACCOUNT_SNAPSHOT = "u100"; 5001 /** @constant */ 5002 net.user1.orbiter.UPC.OBSERVE_ACCOUNT = "u121"; 5003 /** @constant */ 5004 net.user1.orbiter.UPC.STOP_OBSERVING_ACCOUNT = "u122"; 5005 /** @constant */ 5006 net.user1.orbiter.UPC.ADD_ROLE = "u133"; 5007 /** @constant */ 5008 net.user1.orbiter.UPC.REMOVE_ROLE = "u135"; 5009 /** @constant */ 5010 net.user1.orbiter.UPC.KICK_CLIENT = "u149"; 5011 /** @constant */ 5012 net.user1.orbiter.UPC.BAN = "u137"; 5013 /** @constant */ 5014 net.user1.orbiter.UPC.UNBAN = "u139"; 5015 /** @constant */ 5016 net.user1.orbiter.UPC.GET_BANNED_LIST_SNAPSHOT = "u141"; 5017 /** @constant */ 5018 net.user1.orbiter.UPC.WATCH_FOR_BANNED_ADDRESSES = "u143"; 5019 /** @constant */ 5020 net.user1.orbiter.UPC.STOP_WATCHING_FOR_BANNED_ADDRESSES = "u145"; 5021 /** @constant */ 5022 net.user1.orbiter.UPC.GET_NODELIST_SNAPSHOT = "u165"; 5023 /** @constant */ 5024 net.user1.orbiter.UPC.GET_GATEWAYS_SNAPSHOT = "u167"; 5025 5026 // SERVER TO CLIENT 5027 /** @constant */ 5028 net.user1.orbiter.UPC.JOINED_ROOM = "u6"; 5029 /** @constant */ 5030 net.user1.orbiter.UPC.RECEIVE_MESSAGE = "u7"; 5031 /** @constant */ 5032 net.user1.orbiter.UPC.CLIENT_ATTR_UPDATE = "u8"; 5033 /** @constant */ 5034 net.user1.orbiter.UPC.ROOM_ATTR_UPDATE = "u9"; 5035 /** @constant */ 5036 net.user1.orbiter.UPC.CLIENT_METADATA = "u29"; 5037 /** @constant */ 5038 net.user1.orbiter.UPC.CREATE_ROOM_RESULT = "u32"; 5039 /** @constant */ 5040 net.user1.orbiter.UPC.REMOVE_ROOM_RESULT = "u33"; 5041 /** @constant */ 5042 net.user1.orbiter.UPC.CLIENTCOUNT_SNAPSHOT = "u34"; 5043 /** @constant */ 5044 net.user1.orbiter.UPC.CLIENT_ADDED_TO_ROOM = "u36"; 5045 /** @constant */ 5046 net.user1.orbiter.UPC.CLIENT_REMOVED_FROM_ROOM = "u37"; 5047 /** @constant */ 5048 net.user1.orbiter.UPC.ROOMLIST_SNAPSHOT = "u38"; 5049 /** @constant */ 5050 net.user1.orbiter.UPC.ROOM_ADDED = "u39"; 5051 /** @constant */ 5052 net.user1.orbiter.UPC.ROOM_REMOVED = "u40"; 5053 /** @constant */ 5054 net.user1.orbiter.UPC.WATCH_FOR_ROOMS_RESULT = "u42"; 5055 /** @constant */ 5056 net.user1.orbiter.UPC.STOP_WATCHING_FOR_ROOMS_RESULT = "u43"; 5057 /** @constant */ 5058 net.user1.orbiter.UPC.LEFT_ROOM = "u44"; 5059 /** @constant */ 5060 net.user1.orbiter.UPC.CHANGE_ACCOUNT_PASSWORD_RESULT = "u46"; 5061 /** @constant */ 5062 net.user1.orbiter.UPC.CREATE_ACCOUNT_RESULT = "u47"; 5063 /** @constant */ 5064 net.user1.orbiter.UPC.REMOVE_ACCOUNT_RESULT = "u48"; 5065 /** @constant */ 5066 net.user1.orbiter.UPC.LOGIN_RESULT = "u49"; 5067 /** @constant */ 5068 net.user1.orbiter.UPC.SERVER_TIME_UPDATE = "u50"; 5069 /** @constant */ 5070 net.user1.orbiter.UPC.ROOM_SNAPSHOT = "u54"; 5071 /** @constant */ 5072 net.user1.orbiter.UPC.OBSERVED_ROOM = "u59"; 5073 /** @constant */ 5074 net.user1.orbiter.UPC.GET_ROOM_SNAPSHOT_RESULT = "u60"; 5075 /** @constant */ 5076 net.user1.orbiter.UPC.STOPPED_OBSERVING_ROOM = "u62"; 5077 /** @constant */ 5078 net.user1.orbiter.UPC.CLIENT_READY = "u63"; 5079 /** @constant */ 5080 net.user1.orbiter.UPC.SERVER_HELLO = "u66"; 5081 /** @constant */ 5082 net.user1.orbiter.UPC.JOIN_ROOM_RESULT = "u72"; 5083 /** @constant */ 5084 net.user1.orbiter.UPC.SET_CLIENT_ATTR_RESULT = "u73"; 5085 /** @constant */ 5086 net.user1.orbiter.UPC.SET_ROOM_ATTR_RESULT = "u74"; 5087 /** @constant */ 5088 net.user1.orbiter.UPC.GET_CLIENTCOUNT_SNAPSHOT_RESULT = "u75"; 5089 /** @constant */ 5090 net.user1.orbiter.UPC.LEAVE_ROOM_RESULT = "u76"; 5091 /** @constant */ 5092 net.user1.orbiter.UPC.OBSERVE_ROOM_RESULT = "u77"; 5093 /** @constant */ 5094 net.user1.orbiter.UPC.STOP_OBSERVING_ROOM_RESULT = "u78"; 5095 /** @constant */ 5096 net.user1.orbiter.UPC.ROOM_ATTR_REMOVED = "u79"; 5097 /** @constant */ 5098 net.user1.orbiter.UPC.REMOVE_ROOM_ATTR_RESULT = "u80"; 5099 /** @constant */ 5100 net.user1.orbiter.UPC.CLIENT_ATTR_REMOVED = "u81"; 5101 /** @constant */ 5102 net.user1.orbiter.UPC.REMOVE_CLIENT_ATTR_RESULT = "u82"; 5103 /** @constant */ 5104 net.user1.orbiter.UPC.SESSION_TERMINATED = "u84"; 5105 /** @constant */ 5106 net.user1.orbiter.UPC.SESSION_NOT_FOUND = "u85"; 5107 /** @constant */ 5108 net.user1.orbiter.UPC.LOGOFF_RESULT = "u87"; 5109 /** @constant */ 5110 net.user1.orbiter.UPC.LOGGED_IN = "u88"; 5111 /** @constant */ 5112 net.user1.orbiter.UPC.LOGGED_OFF = "u89"; 5113 /** @constant */ 5114 net.user1.orbiter.UPC.ACCOUNT_PASSWORD_CHANGED = "u90"; 5115 /** @constant */ 5116 net.user1.orbiter.UPC.CLIENTLIST_SNAPSHOT = "u101"; 5117 /** @constant */ 5118 net.user1.orbiter.UPC.CLIENT_ADDED_TO_SERVER = "u102"; 5119 /** @constant */ 5120 net.user1.orbiter.UPC.CLIENT_REMOVED_FROM_SERVER = "u103"; 5121 /** @constant */ 5122 net.user1.orbiter.UPC.CLIENT_SNAPSHOT = "u104"; 5123 /** @constant */ 5124 net.user1.orbiter.UPC.OBSERVE_CLIENT_RESULT = "u105"; 5125 /** @constant */ 5126 net.user1.orbiter.UPC.STOP_OBSERVING_CLIENT_RESULT = "u106"; 5127 /** @constant */ 5128 net.user1.orbiter.UPC.WATCH_FOR_CLIENTS_RESULT = "u107"; 5129 /** @constant */ 5130 net.user1.orbiter.UPC.STOP_WATCHING_FOR_CLIENTS_RESULT = "u108"; 5131 /** @constant */ 5132 net.user1.orbiter.UPC.WATCH_FOR_ACCOUNTS_RESULT = "u109"; 5133 /** @constant */ 5134 net.user1.orbiter.UPC.STOP_WATCHING_FOR_ACCOUNTS_RESULT = "u110"; 5135 /** @constant */ 5136 net.user1.orbiter.UPC.ACCOUNT_ADDED = "u111"; 5137 /** @constant */ 5138 net.user1.orbiter.UPC.ACCOUNT_REMOVED = "u112"; 5139 /** @constant */ 5140 net.user1.orbiter.UPC.JOINED_ROOM_ADDED_TO_CLIENT = "u113"; 5141 /** @constant */ 5142 net.user1.orbiter.UPC.JOINED_ROOM_REMOVED_FROM_CLIENT = "u114"; 5143 /** @constant */ 5144 net.user1.orbiter.UPC.GET_CLIENT_SNAPSHOT_RESULT = "u115"; 5145 /** @constant */ 5146 net.user1.orbiter.UPC.GET_ACCOUNT_SNAPSHOT_RESULT = "u116"; 5147 /** @constant */ 5148 net.user1.orbiter.UPC.OBSERVED_ROOM_ADDED_TO_CLIENT = "u117"; 5149 /** @constant */ 5150 net.user1.orbiter.UPC.OBSERVED_ROOM_REMOVED_FROM_CLIENT = "u118"; 5151 /** @constant */ 5152 net.user1.orbiter.UPC.CLIENT_OBSERVED = "u119"; 5153 /** @constant */ 5154 net.user1.orbiter.UPC.STOPPED_OBSERVING_CLIENT = "u120"; 5155 /** @constant */ 5156 net.user1.orbiter.UPC.OBSERVE_ACCOUNT_RESULT = "u123"; 5157 /** @constant */ 5158 net.user1.orbiter.UPC.ACCOUNT_OBSERVED = "u124"; 5159 /** @constant */ 5160 net.user1.orbiter.UPC.STOP_OBSERVING_ACCOUNT_RESULT = "u125"; 5161 /** @constant */ 5162 net.user1.orbiter.UPC.STOPPED_OBSERVING_ACCOUNT = "u126"; 5163 /** @constant */ 5164 net.user1.orbiter.UPC.ACCOUNT_LIST_UPDATE = "u127"; 5165 /** @constant */ 5166 net.user1.orbiter.UPC.UPDATE_LEVELS_UPDATE = "u128"; 5167 /** @constant */ 5168 net.user1.orbiter.UPC.CLIENT_OBSERVED_ROOM = "u129"; 5169 /** @constant */ 5170 net.user1.orbiter.UPC.CLIENT_STOPPED_OBSERVING_ROOM = "u130"; 5171 /** @constant */ 5172 net.user1.orbiter.UPC.ROOM_OCCUPANTCOUNT_UPDATE = "u131"; 5173 /** @constant */ 5174 net.user1.orbiter.UPC.ROOM_OBSERVERCOUNT_UPDATE = "u132"; 5175 /** @constant */ 5176 net.user1.orbiter.UPC.ADD_ROLE_RESULT = "u134"; 5177 /** @constant */ 5178 net.user1.orbiter.UPC.REMOVE_ROLE_RESULT = "u136"; 5179 /** @constant */ 5180 net.user1.orbiter.UPC.BAN_RESULT = "u138"; 5181 /** @constant */ 5182 net.user1.orbiter.UPC.UNBAN_RESULT = "u140"; 5183 /** @constant */ 5184 net.user1.orbiter.UPC.BANNED_LIST_SNAPSHOT = "u142"; 5185 /** @constant */ 5186 net.user1.orbiter.UPC.WATCH_FOR_BANNED_ADDRESSES_RESULT = "u144"; 5187 /** @constant */ 5188 net.user1.orbiter.UPC.STOP_WATCHING_FOR_BANNED_ADDRESSES_RESULT = "u146"; 5189 /** @constant */ 5190 net.user1.orbiter.UPC.BANNED_ADDRESS_ADDED = "u147"; 5191 /** @constant */ 5192 net.user1.orbiter.UPC.BANNED_ADDRESS_REMOVED = "u148"; 5193 /** @constant */ 5194 net.user1.orbiter.UPC.KICK_CLIENT_RESULT = "u150"; 5195 /** @constant */ 5196 net.user1.orbiter.UPC.SERVERMODULELIST_SNAPSHOT = "u152"; 5197 /** @constant */ 5198 net.user1.orbiter.UPC.GET_UPC_STATS_SNAPSHOT_RESULT = "u155"; 5199 /** @constant */ 5200 net.user1.orbiter.UPC.UPC_STATS_SNAPSHOT = "u156"; 5201 /** @constant */ 5202 net.user1.orbiter.UPC.RESET_UPC_STATS_RESULT = "u158"; 5203 /** @constant */ 5204 net.user1.orbiter.UPC.WATCH_FOR_PROCESSED_UPCS_RESULT = "u160"; 5205 /** @constant */ 5206 net.user1.orbiter.UPC.PROCESSED_UPC_ADDED = "u161"; 5207 /** @constant */ 5208 net.user1.orbiter.UPC.STOP_WATCHING_FOR_PROCESSED_UPCS_RESULT = "u163"; 5209 /** @constant */ 5210 net.user1.orbiter.UPC.CONNECTION_REFUSED = "u164"; 5211 /** @constant */ 5212 net.user1.orbiter.UPC.NODELIST_SNAPSHOT = "u166"; 5213 /** @constant */ 5214 net.user1.orbiter.UPC.GATEWAYS_SNAPSHOT = "u168"; 5215 //============================================================================== 5216 // LOADED FLAG 5217 //============================================================================== 5218 /** 5219 * @constant 5220 * 5221 * Indicates that Orbiter has finished loading. 5222 */ 5223 net.user1.orbiter.LOADED = true; 5224 5225 })((typeof window == "undefined") ? this : window); 5226