summaryrefslogtreecommitdiff
path: root/js/src/Ice/browser
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/Ice/browser')
-rw-r--r--js/src/Ice/browser/.gitignore2
-rw-r--r--js/src/Ice/browser/Buffer.js419
-rw-r--r--js/src/Ice/browser/Debug.js41
-rw-r--r--js/src/Ice/browser/EndpointFactory.js51
-rw-r--r--js/src/Ice/browser/EndpointI.js543
-rw-r--r--js/src/Ice/browser/Transceiver.js418
6 files changed, 1474 insertions, 0 deletions
diff --git a/js/src/Ice/browser/.gitignore b/js/src/Ice/browser/.gitignore
new file mode 100644
index 00000000000..9c5c0a3f6d2
--- /dev/null
+++ b/js/src/Ice/browser/.gitignore
@@ -0,0 +1,2 @@
+ConnectionInfo.js
+EndpointInfo.js
diff --git a/js/src/Ice/browser/Buffer.js b/js/src/Ice/browser/Buffer.js
new file mode 100644
index 00000000000..5d6c33393fe
--- /dev/null
+++ b/js/src/Ice/browser/Buffer.js
@@ -0,0 +1,419 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+(function(global){
+ //
+ // IE 10 doesn't implement ArrayBuffer.slice
+ //
+
+ if(!ArrayBuffer.prototype.slice)
+ {
+ ArrayBuffer.prototype.slice = function (start, end)
+ {
+ var b = new Uint8Array(this);
+ end = end === undefined ? b.length : end;
+ var result = new Uint8Array(new ArrayBuffer(end - start));
+ for(var i = 0, length = result.length; i < length; i++)
+ {
+ result[i] = b[i + start];
+ }
+ return result.buffer;
+ };
+ }
+
+ var __BufferOverflowException__ = "BufferOverflowException";
+ var __BufferUnderflowException__ = "BufferUnderflowException";
+ var __IndexOutOfBoundsException__ = "IndexOutOfBoundsException";
+
+ //
+ // Buffer implementation to be used by web browsers, it uses ArrayBuffer as
+ // the store.
+ //
+
+ require("Ice/Class");
+ require("Ice/Long");
+
+ var Ice = global.Ice || Ice;
+ var Long = Ice.Long;
+
+ var Buffer = Ice.Class({
+ __init__: function(buffer)
+ {
+ if(buffer !== undefined)
+ {
+ this.b = buffer;
+ this.v = new DataView(this.b);
+ }
+ else
+ {
+ this.b = null; // ArrayBuffer
+ this.v = null; // DataView
+ }
+ this._position = 0;
+ this._limit = 0;
+ this._shrinkCounter = 0;
+ },
+ empty: function()
+ {
+ return this._limit === 0;
+ },
+ resize: function(n)
+ {
+ if(n === 0)
+ {
+ this.clear();
+ }
+ else if(n > this.capacity)
+ {
+ this.reserve(n);
+ }
+ this._limit = n;
+ },
+ clear: function()
+ {
+ this.b = null;
+ this.v = null;
+ this._position = 0;
+ this._limit = 0;
+ },
+ //
+ // Call expand(n) to add room for n additional bytes. Note that expand()
+ // examines the current position of the buffer first; we don't want to
+ // expand the buffer if the caller is writing to a location that is
+ // already in the buffer.
+ //
+ expand: function(n)
+ {
+ var sz = this.capacity === 0 ? n : this._position + n;
+ if(sz > this._limit)
+ {
+ this.resize(sz);
+ }
+ },
+ reset: function()
+ {
+ if(this._limit > 0 && this._limit * 2 < this.capacity)
+ {
+ //
+ // If the current buffer size is smaller than the
+ // buffer capacity, we shrink the buffer memory to the
+ // current size. This is to avoid holding on to too much
+ // memory if it's not needed anymore.
+ //
+ if(++this._shrinkCounter > 2)
+ {
+ this.reserve(this._limit);
+ this._shrinkCounter = 0;
+ }
+ }
+ else
+ {
+ this._shrinkCounter = 0;
+ }
+ this._limit = 0;
+ this._position = 0;
+ },
+ reserve: function(n)
+ {
+ if(n > this.capacity)
+ {
+ var capacity = Math.max(n, 2 * this.capacity);
+ capacity = Math.max(1024, capacity);
+ if(!this.b)
+ {
+ this.b = new ArrayBuffer(capacity);
+ }
+ else
+ {
+ var b = new Uint8Array(capacity);
+ b.set(new Uint8Array(this.b));
+ this.b = b.buffer;
+ }
+ this.v = new DataView(this.b);
+ }
+ else if(n < this.capacity)
+ {
+ this.b = this.b.slice(0, this.capacity);
+ this.v = new DataView(this.b);
+ }
+ else
+ {
+ return;
+ }
+ },
+ put: function(v)
+ {
+ if(this._position === this._limit)
+ {
+ throw new Error(__BufferOverflowException__);
+ }
+ this.v.setUint8(this._position, v);
+ this._position++;
+ },
+ putAt: function(i, v)
+ {
+ if(i >= this._limit)
+ {
+ throw new Error(__IndexOutOfBoundsException__);
+ }
+ this.v.setUint8(i, v);
+ },
+ putArray: function(v)
+ {
+ if(v.byteLength > 0)
+ {
+ //Expects an Uint8Array
+ if(this._position + v.length > this._limit)
+ {
+ throw new Error(__BufferOverflowException__);
+ }
+ new Uint8Array(this.b, 0, this.b.byteLength).set(v, this._position);
+ this._position += v.byteLength;
+ }
+ },
+ putShort: function(v)
+ {
+ if(this._position + 2 > this._limit)
+ {
+ throw new Error(__BufferOverflowException__);
+ }
+ this.v.setInt16(this._position, v, true);
+ this._position += 2;
+ },
+ putInt: function(v)
+ {
+ if(this._position + 4 > this._limit)
+ {
+ throw new Error(__BufferOverflowException__);
+ }
+ this.v.setInt32(this._position, v, true);
+ this._position += 4;
+ },
+ putIntAt: function(i, v)
+ {
+ if(i + 4 > this._limit || i < 0)
+ {
+ throw new Error(__IndexOutOfBoundsException__);
+ }
+ this.v.setInt32(i, v, true);
+ },
+ putFloat: function(v)
+ {
+ if(this._position + 4 > this._limit)
+ {
+ throw new Error(__BufferOverflowException__);
+ }
+ this.v.setFloat32(this._position, v, true);
+ this._position += 4;
+ },
+ putDouble: function(v)
+ {
+ if(this._position + 8 > this._limit)
+ {
+ throw new Error(__BufferOverflowException__);
+ }
+ this.v.setFloat64(this._position, v, true);
+ this._position += 8;
+ },
+ putLong: function(v)
+ {
+ if(this._position + 8 > this._limit)
+ {
+ throw new Error(__BufferOverflowException__);
+ }
+ this.v.setInt32(this._position, v.low, true);
+ this._position += 4;
+ this.v.setInt32(this._position, v.high, true);
+ this._position += 4;
+ },
+ writeString: function(stream, v)
+ {
+ //
+ // Encode the string as utf8
+ //
+ var encoded = unescape(encodeURIComponent(v));
+
+ stream.writeSize(encoded.length);
+ stream.expand(encoded.length);
+ this.putString(encoded, encoded.length);
+ },
+ putString: function(v, sz)
+ {
+ if(this._position + sz > this._limit)
+ {
+ throw new Error(__BufferOverflowException__);
+ }
+ for(var i = 0; i < sz; ++i)
+ {
+ this.v.setUint8(this._position, v.charCodeAt(i));
+ this._position++;
+ }
+ },
+ get: function()
+ {
+ if(this._position >= this._limit)
+ {
+ throw new Error(__BufferUnderflowException__);
+ }
+ var v = this.v.getUint8(this._position);
+ this._position++;
+ return v;
+ },
+ getAt: function(i)
+ {
+ if(i < 0 || i >= this._limit)
+ {
+ throw new Error(__IndexOutOfBoundsException__);
+ }
+ return this.v.getUint8(i);
+ },
+ getArray: function(length)
+ {
+ if(this._position + length > this._limit)
+ {
+ throw new Error(__BufferUnderflowException__);
+ }
+ var buffer = this.b.slice(this._position, this._position + length);
+ this._position += length;
+ return new Uint8Array(buffer);
+ },
+ getArrayAt: function(position, length)
+ {
+ if(position + length > this._limit)
+ {
+ throw new Error(__BufferUnderflowException__);
+ }
+ length = length === undefined ? (this.b.byteLength - position) : length;
+ return new Uint8Array(this.b.slice(position, position + length));
+ },
+ getShort: function()
+ {
+ if(this._limit - this._position < 2)
+ {
+ throw new Error(__BufferUnderflowException__);
+ }
+ var v = this.v.getInt16(this._position, true);
+ this._position += 2;
+ return v;
+ },
+ getInt: function()
+ {
+ if(this._limit - this._position < 4)
+ {
+ throw new Error(__BufferUnderflowException__);
+ }
+ var v = this.v.getInt32(this._position, true);
+ this._position += 4;
+ return v;
+ },
+ getFloat: function()
+ {
+ if(this._limit - this._position < 4)
+ {
+ throw new Error(__BufferUnderflowException__);
+ }
+ var v = this.v.getFloat32(this._position, true);
+ this._position += 4;
+ return v;
+ },
+ getDouble: function()
+ {
+ if(this._limit - this._position < 8)
+ {
+ throw new Error(__BufferUnderflowException__);
+ }
+ var v = this.v.getFloat64(this._position, true);
+ this._position += 8;
+ return v;
+ },
+ getLong: function()
+ {
+ if(this._limit - this._position < 8)
+ {
+ throw new Error(__BufferUnderflowException__);
+ }
+ var v = new Long();
+ v.low = this.v.getInt32(this._position, true);
+ this._position += 4;
+ v.high = this.v.getInt32(this._position, true);
+ this._position += 4;
+ return v;
+ },
+ getString: function(length)
+ {
+ if(this._position + length > this._limit)
+ {
+ throw new Error(__BufferUnderflowException__);
+ }
+
+ var data = new DataView(this.b, this._position, length);
+ var s = "";
+
+ for(var i = 0; i < length; ++i)
+ {
+ s += String.fromCharCode(data.getUint8(i));
+ }
+ this._position += length;
+ s = decodeURIComponent(escape(s));
+ return s;
+ }
+ });
+
+ var prototype = Buffer.prototype;
+
+ Object.defineProperty(prototype, "position", {
+ get: function() { return this._position; },
+ set: function(position){
+ if(position >= 0 && position <= this._limit)
+ {
+ this._position = position;
+ }
+ }
+ });
+
+ Object.defineProperty(prototype, "limit", {
+ get: function() { return this._limit; },
+ set: function(limit){
+ if(limit <= this.capacity)
+ {
+ this._limit = limit;
+ if(this._position > limit)
+ {
+ this._position = limit;
+ }
+ }
+ }
+ });
+
+ Object.defineProperty(prototype, "capacity", {
+ get: function() { return this.b === null ? 0 : this.b.byteLength; }
+ });
+
+ Object.defineProperty(prototype, "remaining", {
+ get: function() { return this._limit - this._position; }
+ });
+
+ //
+ // Create a native buffer from an array of bytes.
+ //
+ Buffer.createNative = function(data)
+ {
+ if(data === undefined)
+ {
+ return new Uint8Array(0);
+ }
+ else
+ {
+ return new Uint8Array(data);
+ }
+ };
+
+ Ice.Buffer = Buffer;
+ global.Ice = Ice;
+}(typeof (global) === "undefined" ? window : global));
diff --git a/js/src/Ice/browser/Debug.js b/js/src/Ice/browser/Debug.js
new file mode 100644
index 00000000000..c6739e40ebd
--- /dev/null
+++ b/js/src/Ice/browser/Debug.js
@@ -0,0 +1,41 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+(function(global){
+ require("Ice/Class");
+ require("Ice/Exception");
+
+ var Ice = global.Ice || {};
+
+ var Exception = Ice.Exception;
+
+ var AssertionFailedException = Ice.Class(Error, {
+ __init__: function(message)
+ {
+ Error.call(this);
+ Exception.captureStackTrace(this);
+ this.message = message;
+ }
+ });
+ var Debug = {};
+
+ Debug.AssertionFailedException = AssertionFailedException;
+
+ Debug.assert = function(b, msg)
+ {
+ if(!b)
+ {
+ console.log(msg === undefined ? "assertion failed" : msg);
+ console.log(Error().stack);
+ throw new AssertionFailedException(msg === undefined ? "assertion failed" : msg);
+ }
+ };
+ Ice.Debug = Debug;
+ global.Ice = Ice;
+}(typeof (global) === "undefined" ? window : global));
diff --git a/js/src/Ice/browser/EndpointFactory.js b/js/src/Ice/browser/EndpointFactory.js
new file mode 100644
index 00000000000..c8481c654b5
--- /dev/null
+++ b/js/src/Ice/browser/EndpointFactory.js
@@ -0,0 +1,51 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+(function(global){
+ require("Ice/Class");
+ require("Ice/Endpoint");
+
+ require("Ice/browser/EndpointInfo");
+ require("Ice/browser/EndpointI");
+
+ var Ice = global.Ice || {};
+ var IceWS = global.IceWS || {};
+
+ var EndpointI = IceWS.EndpointI;
+
+ var EndpointFactory = Ice.Class({
+ __init__:function(instance, secure)
+ {
+ this._instance = instance;
+ this._secure = secure;
+ },
+ type: function()
+ {
+ return this._secure ? IceWS.WSSEndpointType : IceWS.WSEndpointType;
+ },
+ protocol: function()
+ {
+ return this._secure ? "wss" : "ws";
+ },
+ create: function(str, oaEndpoint)
+ {
+ return EndpointI.fromString(this._instance, this._secure, str, oaEndpoint);
+ },
+ read: function(s)
+ {
+ return EndpointI.fromStream(s, this._secure);
+ },
+ destroy: function()
+ {
+ this._instance = null;
+ }
+ });
+ IceWS.EndpointFactory = EndpointFactory;
+ global.IceWS = IceWS;
+}(typeof (global) === "undefined" ? window : global));
diff --git a/js/src/Ice/browser/EndpointI.js b/js/src/Ice/browser/EndpointI.js
new file mode 100644
index 00000000000..f364a087922
--- /dev/null
+++ b/js/src/Ice/browser/EndpointI.js
@@ -0,0 +1,543 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+(function(global){
+ require("Ice/Class");
+ require("Ice/Address");
+ require("Ice/HashUtil");
+ require("Ice/StringUtil");
+ require("Ice/Endpoint");
+ require("Ice/LocalException");
+
+ require("Ice/browser/Transceiver");
+ require("Ice/browser/EndpointInfo");
+
+ var Ice = global.Ice || {};
+ var IceWS = global.IceWS || {};
+
+ var Address = Ice.Address;
+ var HashUtil = Ice.HashUtil;
+ var StringUtil = Ice.StringUtil;
+ var Transceiver = IceWS.Transceiver;
+
+ var Class = Ice.Class;
+
+ var EndpointI = Class(Ice.Endpoint, {
+ __init__: function(instance, secure, ho, po, ti, conId, co, re)
+ {
+ this._instance = instance;
+ this._secure = secure;
+ this._host = ho;
+ this._port = po;
+ this._timeout = ti;
+ this._connectionId = conId;
+ this._compress = co;
+ this._resource = re;
+ this.calcHashValue();
+ },
+ //
+ // Convert the endpoint to its string form
+ //
+ toString: function()
+ {
+ //
+ // WARNING: Certain features, such as proxy validation in Glacier2,
+ // depend on the format of proxy strings. Changes to toString() and
+ // methods called to generate parts of the reference string could break
+ // these features. Please review for all features that depend on the
+ // format of proxyToString() before changing this and related code.
+ //
+ var s = (this._secure ? "wss" : "ws");
+
+ if(this._host !== null && this._host.length > 0)
+ {
+ s += " -h ";
+ s += (this._host.indexOf(':') !== -1) ? ("\"" + this._host + "\"") : this._host;
+ }
+
+ s += " -p " + this._port;
+
+ if(this._timeout != -1)
+ {
+ s += " -t " + this._timeout;
+ }
+ if(this._compress)
+ {
+ s += " -z";
+ }
+ if(this._resource !== null && this._resource.length > 0)
+ {
+ s += " -r ";
+ s += (this._resource.indexOf(':') !== -1) ? ("\"" + this._resource + "\"") : this._resource;
+ }
+ return s;
+ },
+ //
+ // Return the endpoint information.
+ //
+ getInfo: function()
+ {
+ return new EndpointInfoI(this._secure, this._timeout, this._compress, this._host, this._port, this._resource);
+ },
+ //
+ // Marshal the endpoint
+ //
+ streamWrite: function(s)
+ {
+ s.writeShort(this._secure ? IceWS.WSSEndpointType : IceWS.WSEndpointType);
+ s.startWriteEncaps();
+ s.writeString(this._host);
+ s.writeInt(this._port);
+ s.writeInt(this._timeout);
+ s.writeBool(this._compress);
+ s.writeString(this._resource);
+ s.endWriteEncaps();
+ },
+ //
+ // Return the endpoint type
+ //
+ type: function()
+ {
+ return this._secure ? IceWS.WSSEndpointType : IceWS.WSEndpointType;
+ },
+ //
+ // Return the timeout for the endpoint in milliseconds. 0 means
+ // non-blocking, -1 means no timeout.
+ //
+ timeout: function()
+ {
+ return this._timeout;
+ },
+ //
+ // Return a new endpoint with a different timeout value, provided
+ // that timeouts are supported by the endpoint. Otherwise the same
+ // endpoint is returned.
+ //
+ changeTimeout: function(timeout)
+ {
+ if(timeout === this._timeout)
+ {
+ return this;
+ }
+ else
+ {
+ return new EndpointI(this._instance, this._secure, this._host, this._port, timeout, this._connectionId, this._compress, this._resource);
+ }
+ },
+ //
+ // Return a new endpoint with a different connection id.
+ //
+ changeConnectionId: function(connectionId)
+ {
+ if(connectionId === this._connectionId)
+ {
+ return this;
+ }
+ else
+ {
+ return new EndpointI(this._instance, this._secure, this._host, this._port, this._timeout, connectionId, this._compress, this._resource);
+ }
+ },
+ //
+ // Return true if the endpoints support bzip2 compress, or false
+ // otherwise.
+ //
+ compress: function()
+ {
+ return this._compress;
+ },
+ //
+ // Return a new endpoint with a different compression value,
+ // provided that compression is supported by the
+ // endpoint. Otherwise the same endpoint is returned.
+ //
+ changeCompress: function(compress)
+ {
+ if(compress === this._compress)
+ {
+ return this;
+ }
+ else
+ {
+ return new EndpointI(this._instance, this._secure, this._host, this._port, this._timeout, this._connectionId, compress, this._resource);
+ }
+ },
+ //
+ // Return true if the endpoint is datagram-based.
+ //
+ datagram: function()
+ {
+ return false;
+ },
+ //
+ // Return true if the endpoint is secure.
+ //
+ secure: function()
+ {
+ return this._secure;
+ },
+ //
+ // Return a server side transceiver for this endpoint, or null if a
+ // transceiver can only be created by an acceptor. In case a
+ // transceiver is created, this operation also returns a new
+ // "effective" endpoint, which might differ from this endpoint,
+ // for example, if a dynamic port number is assigned.
+ //
+ transceiver: function(endpoint)
+ {
+ return null;
+ },
+ //
+ // Return an acceptor for this endpoint, or null if no acceptors
+ // is available. In case an acceptor is created, this operation
+ // also returns a new "effective" endpoint, which might differ
+ // from this endpoint, for example, if a dynamic port number is
+ // assigned.
+ //
+ acceptor: function(endpoint, adapterName)
+ {
+ return null;
+ },
+ connect: function()
+ {
+ if(this._instance.traceLevels().network >= 2)
+ {
+ this._instance.initializationData().logger.trace(this._instance.traceLevels().networkCat,
+ "trying to establish " + (this._secure ? "wss" : "ws") + " connection to " + this._host + ":" +
+ this._port);
+ }
+
+ return Transceiver.createOutgoing(this._instance, this._secure, new Address(this._host, this._port),
+ this._resource);
+ },
+ hashCode: function()
+ {
+ return this._hashCode;
+ },
+ //
+ // Compare endpoints for sorting purposes
+ //
+ equals: function(p)
+ {
+ if(!(p instanceof EndpointI))
+ {
+ return false;
+ }
+
+ if(this === p)
+ {
+ return true;
+ }
+
+ if(this._host !== p._host)
+ {
+ return false;
+ }
+
+ if(this._port !== p._port)
+ {
+ return false;
+ }
+
+ if(this._timeout !== p._timeout)
+ {
+ return false;
+ }
+
+ if(this._connectionId !== p._connectionId)
+ {
+ return false;
+ }
+
+ if(this._compress !== p._compress)
+ {
+ return false;
+ }
+
+ if(this._resource !== p._resource)
+ {
+ return false;
+ }
+
+ return true;
+ },
+ compareTo: function(p)
+ {
+ if(this === p)
+ {
+ return 0;
+ }
+
+ if(p === null)
+ {
+ return 1;
+ }
+
+ if(!(p instanceof EndpointI))
+ {
+ return this.type() < p.type() ? -1 : 1;
+ }
+
+ if(this._port < p._port)
+ {
+ return -1;
+ }
+ else if(p._port < this._port)
+ {
+ return 1;
+ }
+
+ if(this._timeout < p._timeout)
+ {
+ return -1;
+ }
+ else if(p._timeout < this._timeout)
+ {
+ return 1;
+ }
+
+ if(this._connectionId != p._connectionId)
+ {
+ return this._connectionId < p._connectionId ? -1 : 1;
+ }
+
+ if(!this._compress && p._compress)
+ {
+ return -1;
+ }
+ else if(!p._compress && this._compress)
+ {
+ return 1;
+ }
+
+ if(this._host < p._host)
+ {
+ return -1;
+ }
+ else if(p._host < this._host)
+ {
+ return 1;
+ }
+
+ if(this._resource == p._resource)
+ {
+ return 0;
+ }
+ else
+ {
+ return this._resource < p._resource ? -1 : 1;
+ }
+ },
+ calcHashValue: function()
+ {
+ var h = 5381;
+ h = HashUtil.addNumber(h, this._secure ? IceWS.WSSEndpointType : IceWS.WSEndpointType);
+ h = HashUtil.addString(h, this._host);
+ h = HashUtil.addNumber(h, this._port);
+ h = HashUtil.addNumber(h, this._timeout);
+ h = HashUtil.addString(h, this._connectionId);
+ h = HashUtil.addBoolean(h, this._compress);
+ h = HashUtil.addString(h, this._resource);
+ this._hashCode = h;
+ }
+ });
+
+ EndpointI.fromString = function(instance, secure, str, oaEndpoint)
+ {
+ var host = null;
+ var port = 0;
+ var timeout = -1;
+ var compress = false;
+ var resource = "";
+
+ var protocol = secure ? "wss" : "ws";
+
+ var arr = str.split(/[ \t\n\r]+/);
+
+ var i = 0;
+ while(i < arr.length)
+ {
+ if(arr[i].length === 0)
+ {
+ i++;
+ continue;
+ }
+
+ var option = arr[i++];
+ if(option.length != 2 && option.charAt(0) != '-')
+ {
+ throw new Ice.EndpointParseException("expected an endpoint option but found `" + option +
+ "' in endpoint `" + protocol + " " + str + "'");
+ }
+
+ var argument = null;
+ if(i < arr.length && arr[i].charAt(0) != '-')
+ {
+ argument = arr[i++];
+ if(argument.charAt(0) == '\"' && argument.charAt(argument.length - 1) == '\"')
+ {
+ argument = argument.substring(1, argument.length - 1);
+ }
+ }
+
+ switch(option.charAt(1))
+ {
+ case 'h':
+ {
+ if(argument === null)
+ {
+ throw new Ice.EndpointParseException("no argument provided for -h option in endpoint `" +
+ protocol + " " + str + "'");
+ }
+
+ host = argument;
+ break;
+ }
+
+ case 'p':
+ {
+ if(argument === null)
+ {
+ throw new Ice.EndpointParseException("no argument provided for -p option in endpoint `" +
+ protocol + " " + str + "'");
+ }
+
+ try
+ {
+ port = StringUtil.toInt(argument);
+ }
+ catch(ex)
+ {
+ throw new Ice.EndpointParseException("invalid port value `" + argument +
+ "' in endpoint `" + protocol + " " + str + "'");
+ }
+
+ if(port < 0 || port > 65535)
+ {
+ throw new Ice.EndpointParseException("port value `" + argument +
+ "' out of range in endpoint `" + protocol + " " + str +
+ "'");
+ }
+
+ break;
+ }
+
+ case 'r':
+ {
+ if(argument === null)
+ {
+ throw new Ice.EndpointParseException("no argument provided for -r option in endpoint `" +
+ protocol + " " + str + "'");
+ }
+
+ resource = argument;
+ break;
+ }
+
+ case 't':
+ {
+ if(argument === null)
+ {
+ throw new Ice.EndpointParseException("no argument provided for -t option in endpoint `" +
+ protocol + " " + str + "'");
+ }
+
+ try
+ {
+ timeout = StringUtil.toInt(argument);
+ }
+ catch(ex)
+ {
+ throw new Ice.EndpointParseException("invalid timeout value `" + argument +
+ "' in endpoint `" + protocol + " " + str + "'");
+ }
+
+ break;
+ }
+
+ case 'z':
+ {
+ if(argument !== null)
+ {
+ throw new Ice.EndpointParseException("unexpected argument `" + argument +
+ "' provided for -z option in `" + protocol + " " + str +
+ "'");
+ }
+
+ compress = true;
+ break;
+ }
+
+ default:
+ {
+ throw new Ice.EndpointParseException("unknown option `" + option + "' in `" + protocol + " " +
+ str + "'");
+ }
+ }
+ }
+
+ if(host === null)
+ {
+ host = instance.defaultsAndOverrides().defaultHost;
+ }
+ else if(host == "*")
+ {
+ if(oaEndpoint)
+ {
+ host = null;
+ }
+ else
+ {
+ throw new Ice.EndpointParseException("`-h *' not valid for proxy endpoint `" + protocol + " " + str +
+ "'");
+ }
+ }
+
+ if(host === null)
+ {
+ host = "";
+ }
+ return new EndpointI(instance, secure, host, port, timeout, "", compress, resource);
+ };
+
+ EndpointI.fromStream = function(s, secure)
+ {
+ s.startReadEncaps();
+ var host = s.readString();
+ var port = s.readInt();
+ var timeout = s.readInt();
+ var compress = s.readBool();
+ var resource = s.readString();
+ s.endReadEncaps();
+ return new EndpointI(s.instance, secure, host, port, timeout, "", compress, resource);
+ };
+
+ IceWS.EndpointI = EndpointI;
+ global.IceWS = IceWS;
+
+ var EndpointInfoI = Class(IceWS.EndpointInfo, {
+ __init__: function(secure, timeout, compress, host, port, resource)
+ {
+ IceWS.EndpointInfo.call(this, timeout, compress, host, port, resource);
+ this.secure = secure;
+ },
+ type: function()
+ {
+ return this._secure ? IceWS.WSSEndpointType : IceWS.WSEndpointType;
+ },
+ datagram: function()
+ {
+ return false;
+ },
+ secure: function()
+ {
+ return this._secure;
+ }
+ });
+}(typeof (global) === "undefined" ? window : global));
diff --git a/js/src/Ice/browser/Transceiver.js b/js/src/Ice/browser/Transceiver.js
new file mode 100644
index 00000000000..f700dfbfc55
--- /dev/null
+++ b/js/src/Ice/browser/Transceiver.js
@@ -0,0 +1,418 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+(function(global){
+ require("Ice/Class");
+ require("Ice/Debug");
+ require("Ice/ExUtil");
+ require("Ice/Network");
+ require("Ice/SocketOperation");
+ require("Ice/Connection");
+ require("Ice/Exception");
+ require("Ice/LocalException");
+
+ require("Ice/browser/ConnectionInfo");
+
+ var Ice = global.Ice || {};
+ var IceWS = global.IceWS || {};
+
+ var Debug = Ice.Debug;
+ var ExUtil = Ice.ExUtil;
+ var Network = Ice.Network;
+ var SocketOperation = Ice.SocketOperation;
+ var Conn = Ice.Connection;
+ var LocalException = Ice.LocalException;
+ var SocketException = Ice.SocketException;
+
+ var StateNeedConnect = 0;
+ var StateConnectPending = 1;
+ var StateConnected = 2;
+ var StateClosePending = 3;
+ var StateClosed = 4;
+
+ var IsFirefox = navigator.userAgent.indexOf("Firefox") !== -1;
+
+ var Transceiver = Ice.Class({
+ __init__: function(instance)
+ {
+ this._traceLevels = instance.traceLevels();
+ this._logger = instance.initializationData().logger;
+ this._readBuffers = [];
+ this._readPosition = 0;
+ },
+ setCallbacks: function(connectedCallback, bytesAvailableCallback, bytesWrittenCallback)
+ {
+ this._connectedCallback = connectedCallback;
+ this._bytesAvailableCallback = bytesAvailableCallback;
+ this._bytesWrittenCallback = bytesWrittenCallback;
+ },
+ //
+ // Returns SocketOperation.None when initialization is complete.
+ //
+ initialize: function(readBuffer, writeBuffer)
+ {
+ try
+ {
+ if(this._exception)
+ {
+ throw this._exception;
+ }
+
+ if(this._state === StateNeedConnect)
+ {
+ this._state = StateConnectPending;
+ this._fd = new WebSocket(this._url, "ice.zeroc.com");
+ this._fd.binaryType = "arraybuffer";
+ var self = this;
+ this._fd.onopen = function(e) { self.socketConnected(e); };
+ this._fd.onmessage = function(e) { self.socketBytesAvailable(e.data); };
+ this._fd.onclose = function(e) { self.socketClosed(e); };
+ return SocketOperation.Connect; // Waiting for connect to complete.
+ }
+ else if(this._state === StateConnectPending)
+ {
+ //
+ // Socket is connected.
+ //
+ this._desc = fdToString(this._addr);
+ this._state = StateConnected;
+ }
+ }
+ catch(err)
+ {
+ if(!this._exception)
+ {
+ this._exception = translateError(this._state, err);
+ }
+
+ if(this._traceLevels.network >= 2)
+ {
+ var s = [];
+ s.push("failed to establish " + this.type() + " connection\n");
+ s.push(fdToString(this._addr));
+ this._logger.trace(this._traceLevels.networkCat, s.join(""));
+ }
+ throw this._exception;
+ }
+
+ Debug.assert(this._state === StateConnected);
+ if(this._traceLevels.network >= 1)
+ {
+ this._logger.trace(this._traceLevels.networkCat, this.type() +
+ " connection established\n" + this._desc);
+ }
+ return SocketOperation.None;
+ },
+ register: function()
+ {
+ //
+ // Register the socket data listener.
+ //
+ this._registered = true;
+ if(this._hasBytesAvailable || this._exception)
+ {
+ this._bytesAvailableCallback();
+ this._hasBytesAvailable = false;
+ }
+ },
+ unregister: function()
+ {
+ //
+ // Unregister the socket data listener.
+ //
+ this._registered = false;
+ },
+ close: function()
+ {
+ if(this._fd === null)
+ {
+ Debug.assert(this._exception); // Websocket creation failed.
+ return;
+ }
+
+ //
+ // WORKAROUND: With Firefox, calling close() if the websocket isn't connected
+ // yet doesn't close the connection. The server doesn't receive any close frame
+ // and the underlying socket isn't closed causing the server to hang on closing
+ // the connection until the browser exits.
+ //
+ // To workaround this problem, we always wait for the socket to be connected
+ // or closed before closing the socket.
+ //
+ if(this._fd.readyState === WebSocket.CONNECTING && IsFirefox)
+ {
+ this._state = StateClosePending;
+ return;
+ }
+
+ if(this._state == StateConnected && this._traceLevels.network >= 1)
+ {
+ this._logger.trace(this._traceLevels.networkCat, "closing " + this.type() + " connection\n" +
+ this._desc);
+ }
+
+ Debug.assert(this._fd !== null);
+ try
+ {
+ this._state = StateClosed;
+ this._fd.close();
+ }
+ catch(ex)
+ {
+ throw translateError(this._state, ex);
+ }
+ finally
+ {
+ this._fd = null;
+ }
+ },
+ //
+ // Returns true if all of the data was flushed to the kernel buffer.
+ //
+ write: function(byteBuffer)
+ {
+ if(this._exception)
+ {
+ throw this._exception;
+ }
+
+ var remaining = byteBuffer.remaining;
+ Debug.assert(remaining > 0);
+ Debug.assert(this._fd);
+
+ //
+ // Delay the send if more than 1KB is already queued for
+ // sending on the socket. If less, we consider that it's
+ // fine to push more data on the socket.
+ //
+ if(this._fd.bufferedAmount < 1024)
+ {
+ //
+ // Create a slice of the source buffer representing the remaining data to be written.
+ //
+ var slice = byteBuffer.b.slice(byteBuffer.position, byteBuffer.position + remaining);
+
+ //
+ // The socket will accept all of the data.
+ //
+ byteBuffer.position = byteBuffer.position + remaining;
+ this._fd.send(slice);
+ if(remaining > 0 && this._traceLevels.network >= 3)
+ {
+ var msg = "sent " + remaining + " of " + remaining + " bytes via " + this.type() + "\n" +this._desc;
+ this._logger.trace(this._traceLevels.networkCat, msg);
+ }
+ return true;
+ }
+ else
+ {
+ var transceiver = this;
+ var writtenCB = function()
+ {
+ if(transceiver._fd)
+ {
+ if(transceiver._fd.bufferedAmount === 0 || this._exception)
+ {
+ transceiver._bytesWrittenCallback();
+ }
+ else
+ {
+ setTimeout(writtenCB, 50);
+ }
+ }
+ };
+ setTimeout(writtenCB, 50);
+ return false;
+ }
+ },
+ read: function(byteBuffer, moreData)
+ {
+ if(this._exception)
+ {
+ throw this._exception;
+ }
+
+ moreData.value = false;
+
+ if(this._readBuffers.length === 0)
+ {
+ return false; // No data available.
+ }
+
+ var avail = this._readBuffers[0].byteLength - this._readPosition;
+ Debug.assert(avail > 0);
+ var remaining = byteBuffer.remaining;
+
+ while(byteBuffer.remaining > 0)
+ {
+ if(avail > byteBuffer.remaining)
+ {
+ avail = byteBuffer.remaining;
+ }
+
+ new Uint8Array(byteBuffer.b).set(new Uint8Array(this._readBuffers[0], this._readPosition, avail),
+ byteBuffer.position);
+
+ byteBuffer.position += avail;
+ this._readPosition += avail;
+ if(this._readPosition === this._readBuffers[0].byteLength)
+ {
+ //
+ // We've exhausted the current read buffer.
+ //
+ this._readPosition = 0;
+ this._readBuffers.shift();
+ if(this._readBuffers.length === 0)
+ {
+ break; // No more data - we're done.
+ }
+ else
+ {
+ avail = this._readBuffers[0].byteLength;
+ }
+ }
+ }
+
+ var n = remaining - byteBuffer.remaining;
+ if(n > 0 && this._traceLevels.network >= 3)
+ {
+ var msg = "received " + n + " of " + remaining + " bytes via " + this.type() + "\n" + this._desc;
+ this._logger.trace(this._traceLevels.networkCat, msg);
+ }
+
+ moreData.value = this._readBuffers.byteLength > 0;
+
+ return byteBuffer.remaining === 0;
+ },
+ type: function()
+ {
+ return this._secure ? "wss" : "ws";
+ },
+ getInfo: function()
+ {
+ Debug.assert(this._fd !== null);
+ var info = this.createInfo();
+
+ //
+ // The WebSocket API doens't provide this info
+ //
+ info.localAddress = "";
+ info.localPort = -1;
+ info.remoteAddress = this._addr.host;
+ info.remotePort = this._addr.port;
+ return info;
+ },
+ createInfo: function()
+ {
+ return new IceWS.ConnectionInfo();
+ },
+ checkSendSize: function(stream, messageSizeMax)
+ {
+ if(stream.size > messageSizeMax)
+ {
+ ExUtil.throwMemoryLimitException(stream.size, messageSizeMax);
+ }
+ },
+ toString: function()
+ {
+ return this._desc;
+ },
+ socketConnected: function(e)
+ {
+ if(this._state == StateClosePending)
+ {
+ this.close();
+ return;
+ }
+
+ Debug.assert(this._connectedCallback !== null);
+ this._connectedCallback();
+ },
+ socketBytesAvailable: function(buf)
+ {
+ Debug.assert(this._bytesAvailableCallback !== null);
+ if(buf.byteLength > 0)
+ {
+ this._readBuffers.push(buf);
+ if(this._registered)
+ {
+ this._bytesAvailableCallback();
+ }
+ else if(!this._hasBytesAvailable)
+ {
+ this._hasBytesAvailable = true;
+ }
+ }
+ },
+ socketClosed: function(err)
+ {
+ if(this._state == StateClosePending)
+ {
+ this.close();
+ return;
+ }
+
+ this._exception = translateError(this._state, err);
+ if(this._state < StateConnected)
+ {
+ this._connectedCallback();
+ }
+ else if(this._registered)
+ {
+ this._bytesAvailableCallback();
+ }
+ },
+ });
+
+ function fdToString(address)
+ {
+ return "local address = <not available>\nremote address = " + address.host + ":" + address.port;
+ }
+
+ function translateError(state, err)
+ {
+ if(state < StateConnected)
+ {
+ return new Ice.ConnectFailedException(err.code, err);
+ }
+ else
+ {
+ if(err === 1000) // CLOSE_NORMAL
+ {
+ throw new Ice.ConnectionLostException();
+ }
+ return new Ice.SocketException(err.code, err);
+ }
+ }
+
+ Transceiver.createOutgoing = function(instance, secure, addr, resource)
+ {
+ var transceiver = new Transceiver(instance);
+
+ var url = secure ? "wss" : "ws";
+ url += "://" + addr.host;
+ if(addr.port !== 80)
+ {
+ url += ":" + addr.port;
+ }
+ url += resource ? resource : "/";
+ transceiver._url = url;
+ transceiver._fd = null;
+ transceiver._addr = addr;
+ transceiver._desc = "remote address: " + addr.host + ":" + addr.port + " <not connected>";
+ transceiver._state = StateNeedConnect;
+ transceiver._secure = secure;
+ transceiver._exception = null;
+
+ return transceiver;
+ };
+
+ IceWS.Transceiver = Transceiver;
+ global.IceWS = IceWS;
+}(typeof (global) === "undefined" ? window : global));