http://git-wip-us.apache.org/repos/asf/incubator-guacamole-website/blob/eff0fb8d/content/doc/0.9.13-incubating/guacamole-common-js/StringReader.js.html ---------------------------------------------------------------------- diff --git a/content/doc/0.9.13-incubating/guacamole-common-js/StringReader.js.html b/content/doc/0.9.13-incubating/guacamole-common-js/StringReader.js.html new file mode 100644 index 0000000..554defc --- /dev/null +++ b/content/doc/0.9.13-incubating/guacamole-common-js/StringReader.js.html @@ -0,0 +1,227 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>JSDoc: Source: StringReader.js</title> + + <script src="scripts/prettify/prettify.js"> </script> + <script src="scripts/prettify/lang-css.js"> </script> + <!--[if lt IE 9]> + <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> + <![endif]--> + <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> + <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> +</head> + +<body> + +<div id="main"> + + <h1 class="page-title">Source: StringReader.js</h1> + + + + + + + <section> + <article> + <pre class="prettyprint source linenums"><code>/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var Guacamole = Guacamole || {}; + +/** + * A reader which automatically handles the given input stream, returning + * strictly text data. Note that this object will overwrite any installed event + * handlers on the given Guacamole.InputStream. + * + * @constructor + * @param {Guacamole.InputStream} stream The stream that data will be read + * from. + */ +Guacamole.StringReader = function(stream) { + + /** + * Reference to this Guacamole.InputStream. + * @private + */ + var guac_reader = this; + + /** + * Wrapped Guacamole.ArrayBufferReader. + * @private + * @type {Guacamole.ArrayBufferReader} + */ + var array_reader = new Guacamole.ArrayBufferReader(stream); + + /** + * The number of bytes remaining for the current codepoint. + * + * @private + * @type {Number} + */ + var bytes_remaining = 0; + + /** + * The current codepoint value, as calculated from bytes read so far. + * + * @private + * @type {Number} + */ + var codepoint = 0; + + /** + * Decodes the given UTF-8 data into a Unicode string. The data may end in + * the middle of a multibyte character. + * + * @private + * @param {ArrayBuffer} buffer Arbitrary UTF-8 data. + * @return {String} A decoded Unicode string. + */ + function __decode_utf8(buffer) { + + var text = ""; + + var bytes = new Uint8Array(buffer); + for (var i=0; i<bytes.length; i++) { + + // Get current byte + var value = bytes[i]; + + // Start new codepoint if nothing yet read + if (bytes_remaining === 0) { + + // 1 byte (0xxxxxxx) + if ((value | 0x7F) === 0x7F) + text += String.fromCharCode(value); + + // 2 byte (110xxxxx) + else if ((value | 0x1F) === 0xDF) { + codepoint = value & 0x1F; + bytes_remaining = 1; + } + + // 3 byte (1110xxxx) + else if ((value | 0x0F )=== 0xEF) { + codepoint = value & 0x0F; + bytes_remaining = 2; + } + + // 4 byte (11110xxx) + else if ((value | 0x07) === 0xF7) { + codepoint = value & 0x07; + bytes_remaining = 3; + } + + // Invalid byte + else + text += "\uFFFD"; + + } + + // Continue existing codepoint (10xxxxxx) + else if ((value | 0x3F) === 0xBF) { + + codepoint = (codepoint << 6) | (value & 0x3F); + bytes_remaining--; + + // Write codepoint if finished + if (bytes_remaining === 0) + text += String.fromCharCode(codepoint); + + } + + // Invalid byte + else { + bytes_remaining = 0; + text += "\uFFFD"; + } + + } + + return text; + + } + + // Receive blobs as strings + array_reader.ondata = function(buffer) { + + // Decode UTF-8 + var text = __decode_utf8(buffer); + + // Call handler, if present + if (guac_reader.ontext) + guac_reader.ontext(text); + + }; + + // Simply call onend when end received + array_reader.onend = function() { + if (guac_reader.onend) + guac_reader.onend(); + }; + + /** + * Fired once for every blob of text data received. + * + * @event + * @param {String} text The data packet received. + */ + this.ontext = null; + + /** + * Fired once this stream is finished and no further data will be written. + * @event + */ + this.onend = null; + +};</code></pre> + </article> + </section> + + + + +</div> + +<nav> + <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Guacamole.ArrayBufferReader.html">ArrayBufferReader</a></li><li><a href="Guacamole.ArrayBufferWriter.html">ArrayBufferWriter</a></li><li><a href="Guacamole.AudioPlayer.html">AudioPlayer</a></li><li><a href="Guacamole.AudioRecorder.html">AudioRecorder</a></li><li><a href="Guacamole.BlobReader.html">BlobReader</a></li><li><a href="Guacamole.BlobWriter.html">BlobWriter</a></li><li><a href="Guacamole.ChainedTunnel.html">ChainedTunnel</a></li><li><a href="Guacamole.Client.html">Client</a></li><li><a href="Guacamole.DataURIReader.html">DataURIReader</a></li><li><a href="Guacamole.Display.html">Display</a></li><li><a href="Guacamole.Display.VisibleLayer.html">VisibleLayer</a></li><li><a href="Guacamole.HTTPTunnel.html">HTTPTunnel</a></li><li><a href="Guacamole.InputStream.html">InputStream</a></li><li><a href="Guacamole.IntegerPool.html">IntegerPool</a></li><li><a href="Guacamole.JSONReader.html">JSONReader</a></li> <li><a href="Guacamole.Keyboard.html">Keyboard</a></li><li><a href="Guacamole.Keyboard.ModifierState.html">ModifierState</a></li><li><a href="Guacamole.Layer.html">Layer</a></li><li><a href="Guacamole.Layer.Pixel.html">Pixel</a></li><li><a href="Guacamole.Mouse.html">Mouse</a></li><li><a href="Guacamole.Mouse.State.html">State</a></li><li><a href="Guacamole.Mouse.Touchpad.html">Touchpad</a></li><li><a href="Guacamole.Mouse.Touchscreen.html">Touchscreen</a></li><li><a href="Guacamole.Object.html">Object</a></li><li><a href="Guacamole.OnScreenKeyboard.html">OnScreenKeyboard</a></li><li><a href="Guacamole.OnScreenKeyboard.Key.html">Key</a></li><li><a href="Guacamole.OnScreenKeyboard.Layout.html">Layout</a></li><li><a href="Guacamole.OutputStream.html">OutputStream</a></li><li><a href="Guacamole.Parser.html">Parser</a></li><li><a href="Guacamole.RawAudioFormat.html">RawAudioFormat</a></li><li><a href="Guacamole.RawAudioPlayer.html">RawAudioPlayer</a></li><li><a href="Guacamole.RawAudioR ecorder.html">RawAudioRecorder</a></li><li><a href="Guacamole.SessionRecording.html">SessionRecording</a></li><li><a href="Guacamole.StaticHTTPTunnel.html">StaticHTTPTunnel</a></li><li><a href="Guacamole.Status.html">Status</a></li><li><a href="Guacamole.StringReader.html">StringReader</a></li><li><a href="Guacamole.StringWriter.html">StringWriter</a></li><li><a href="Guacamole.Tunnel.html">Tunnel</a></li><li><a href="Guacamole.VideoPlayer.html">VideoPlayer</a></li><li><a href="Guacamole.WebSocketTunnel.html">WebSocketTunnel</a></li></ul><h3>Events</h3><ul><li><a href="Guacamole.ArrayBufferReader.html#event:ondata">ondata</a></li><li><a href="Guacamole.ArrayBufferReader.html#event:onend">onend</a></li><li><a href="Guacamole.ArrayBufferWriter.html#event:onack">onack</a></li><li><a href="Guacamole.AudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.AudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobReader.html#event:onend">onend</a></li>< li><a href="Guacamole.BlobReader.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.BlobWriter.html#event:onack">onack</a></li><li><a href="Guacamole.BlobWriter.html#event:oncomplete">oncomplete</a></li><li><a href="Guacamole.BlobWriter.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobWriter.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.ChainedTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onaudio">onaudio</a></li><li><a href="Guacamole.Client.html#event:onclipboard">onclipboard</a></li><li><a href="Guacamole.Client.html#event:onerror">onerror</a></li><li><a href="Guacamole.Client.html#event:onfile">onfile</a></li><li><a href="Guacamole.Client.html#event:onfilesystem">onfilesystem</a></li><li><a href="Guacamole.Client.html#event:onna me">onname</a></li><li><a href="Guacamole.Client.html#event:onpipe">onpipe</a></li><li><a href="Guacamole.Client.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onsync">onsync</a></li><li><a href="Guacamole.Client.html#event:onvideo">onvideo</a></li><li><a href="Guacamole.DataURIReader.html#event:onend">onend</a></li><li><a href="Guacamole.Display.html#event:oncursor">oncursor</a></li><li><a href="Guacamole.Display.html#event:onresize">onresize</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.HTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.InputStream.html#event:onblob">onblob</a></li><li><a href="Guacamole.InputStream.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onprogress">onprog ress</a></li><li><a href="Guacamole.Keyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.Keyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.html#event:onmouseout">onmouseout</a></li><li><a href="Guacamole.Mouse.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Object.html#event:onbody">onbody</a></li>< li><a href="Guacamole.Object.html#event:onundefine">onundefine</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.OutputStream.html#event:onack">onack</a></li><li><a href="Guacamole.Parser.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.SessionRecording.html#event:onpause">onpause</a></li><li><a href="Guacamole.SessionRecording.html#event:onplay">onplay</a></li><li><a href="Guacamole .SessionRecording.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.SessionRecording.html#event:onseek">onseek</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.StringReader.html#event:onend">onend</a></li><li><a href="Guacamole.StringReader.html#event:ontext">ontext</a></li><li><a href="Guacamole.StringWriter.html#event:onack">onack</a></li><li><a href="Guacamole.Tunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.Tunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.Tunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamol e.WebSocketTunnel.html#event:onstatechange">onstatechange</a></li></ul><h3>Namespaces</h3><ul><li><a href="Guacamole.html">Guacamole</a></li><li><a href="Guacamole.AudioContextFactory.html">AudioContextFactory</a></li></ul> +</nav> + +<br class="clear"> + +<footer> + Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun Jul 02 2017 12:09:53 GMT-0700 (PDT) +</footer> + +<script> prettyPrint(); </script> +<script src="scripts/linenumber.js"> </script> + <!-- Google Analytics --> + <script type="text/javascript"> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-75289145-1', 'auto'); + ga('send', 'pageview'); + </script> +</body> +</html>
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-website/blob/eff0fb8d/content/doc/0.9.13-incubating/guacamole-common-js/StringWriter.js.html ---------------------------------------------------------------------- diff --git a/content/doc/0.9.13-incubating/guacamole-common-js/StringWriter.js.html b/content/doc/0.9.13-incubating/guacamole-common-js/StringWriter.js.html new file mode 100644 index 0000000..3823af8 --- /dev/null +++ b/content/doc/0.9.13-incubating/guacamole-common-js/StringWriter.js.html @@ -0,0 +1,252 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>JSDoc: Source: StringWriter.js</title> + + <script src="scripts/prettify/prettify.js"> </script> + <script src="scripts/prettify/lang-css.js"> </script> + <!--[if lt IE 9]> + <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> + <![endif]--> + <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> + <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> +</head> + +<body> + +<div id="main"> + + <h1 class="page-title">Source: StringWriter.js</h1> + + + + + + + <section> + <article> + <pre class="prettyprint source linenums"><code>/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var Guacamole = Guacamole || {}; + +/** + * A writer which automatically writes to the given output stream with text + * data. + * + * @constructor + * @param {Guacamole.OutputStream} stream The stream that data will be written + * to. + */ +Guacamole.StringWriter = function(stream) { + + /** + * Reference to this Guacamole.StringWriter. + * @private + */ + var guac_writer = this; + + /** + * Wrapped Guacamole.ArrayBufferWriter. + * @private + * @type {Guacamole.ArrayBufferWriter} + */ + var array_writer = new Guacamole.ArrayBufferWriter(stream); + + /** + * Internal buffer for UTF-8 output. + * @private + */ + var buffer = new Uint8Array(8192); + + /** + * The number of bytes currently in the buffer. + * @private + */ + var length = 0; + + // Simply call onack for acknowledgements + array_writer.onack = function(status) { + if (guac_writer.onack) + guac_writer.onack(status); + }; + + /** + * Expands the size of the underlying buffer by the given number of bytes, + * updating the length appropriately. + * + * @private + * @param {Number} bytes The number of bytes to add to the underlying + * buffer. + */ + function __expand(bytes) { + + // Resize buffer if more space needed + if (length+bytes >= buffer.length) { + var new_buffer = new Uint8Array((length+bytes)*2); + new_buffer.set(buffer); + buffer = new_buffer; + } + + length += bytes; + + } + + /** + * Appends a single Unicode character to the current buffer, resizing the + * buffer if necessary. The character will be encoded as UTF-8. + * + * @private + * @param {Number} codepoint The codepoint of the Unicode character to + * append. + */ + function __append_utf8(codepoint) { + + var mask; + var bytes; + + // 1 byte + if (codepoint <= 0x7F) { + mask = 0x00; + bytes = 1; + } + + // 2 byte + else if (codepoint <= 0x7FF) { + mask = 0xC0; + bytes = 2; + } + + // 3 byte + else if (codepoint <= 0xFFFF) { + mask = 0xE0; + bytes = 3; + } + + // 4 byte + else if (codepoint <= 0x1FFFFF) { + mask = 0xF0; + bytes = 4; + } + + // If invalid codepoint, append replacement character + else { + __append_utf8(0xFFFD); + return; + } + + // Offset buffer by size + __expand(bytes); + var offset = length - 1; + + // Add trailing bytes, if any + for (var i=1; i<bytes; i++) { + buffer[offset--] = 0x80 | (codepoint & 0x3F); + codepoint >>= 6; + } + + // Set initial byte + buffer[offset] = mask | codepoint; + + } + + /** + * Encodes the given string as UTF-8, returning an ArrayBuffer containing + * the resulting bytes. + * + * @private + * @param {String} text The string to encode as UTF-8. + * @return {Uint8Array} The encoded UTF-8 data. + */ + function __encode_utf8(text) { + + // Fill buffer with UTF-8 + for (var i=0; i<text.length; i++) { + var codepoint = text.charCodeAt(i); + __append_utf8(codepoint); + } + + // Flush buffer + if (length > 0) { + var out_buffer = buffer.subarray(0, length); + length = 0; + return out_buffer; + } + + } + + /** + * Sends the given text. + * + * @param {String} text The text to send. + */ + this.sendText = function(text) { + if (text.length) + array_writer.sendData(__encode_utf8(text)); + }; + + /** + * Signals that no further text will be sent, effectively closing the + * stream. + */ + this.sendEnd = function() { + array_writer.sendEnd(); + }; + + /** + * Fired for received data, if acknowledged by the server. + * @event + * @param {Guacamole.Status} status The status of the operation. + */ + this.onack = null; + +};</code></pre> + </article> + </section> + + + + +</div> + +<nav> + <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Guacamole.ArrayBufferReader.html">ArrayBufferReader</a></li><li><a href="Guacamole.ArrayBufferWriter.html">ArrayBufferWriter</a></li><li><a href="Guacamole.AudioPlayer.html">AudioPlayer</a></li><li><a href="Guacamole.AudioRecorder.html">AudioRecorder</a></li><li><a href="Guacamole.BlobReader.html">BlobReader</a></li><li><a href="Guacamole.BlobWriter.html">BlobWriter</a></li><li><a href="Guacamole.ChainedTunnel.html">ChainedTunnel</a></li><li><a href="Guacamole.Client.html">Client</a></li><li><a href="Guacamole.DataURIReader.html">DataURIReader</a></li><li><a href="Guacamole.Display.html">Display</a></li><li><a href="Guacamole.Display.VisibleLayer.html">VisibleLayer</a></li><li><a href="Guacamole.HTTPTunnel.html">HTTPTunnel</a></li><li><a href="Guacamole.InputStream.html">InputStream</a></li><li><a href="Guacamole.IntegerPool.html">IntegerPool</a></li><li><a href="Guacamole.JSONReader.html">JSONReader</a></li> <li><a href="Guacamole.Keyboard.html">Keyboard</a></li><li><a href="Guacamole.Keyboard.ModifierState.html">ModifierState</a></li><li><a href="Guacamole.Layer.html">Layer</a></li><li><a href="Guacamole.Layer.Pixel.html">Pixel</a></li><li><a href="Guacamole.Mouse.html">Mouse</a></li><li><a href="Guacamole.Mouse.State.html">State</a></li><li><a href="Guacamole.Mouse.Touchpad.html">Touchpad</a></li><li><a href="Guacamole.Mouse.Touchscreen.html">Touchscreen</a></li><li><a href="Guacamole.Object.html">Object</a></li><li><a href="Guacamole.OnScreenKeyboard.html">OnScreenKeyboard</a></li><li><a href="Guacamole.OnScreenKeyboard.Key.html">Key</a></li><li><a href="Guacamole.OnScreenKeyboard.Layout.html">Layout</a></li><li><a href="Guacamole.OutputStream.html">OutputStream</a></li><li><a href="Guacamole.Parser.html">Parser</a></li><li><a href="Guacamole.RawAudioFormat.html">RawAudioFormat</a></li><li><a href="Guacamole.RawAudioPlayer.html">RawAudioPlayer</a></li><li><a href="Guacamole.RawAudioR ecorder.html">RawAudioRecorder</a></li><li><a href="Guacamole.SessionRecording.html">SessionRecording</a></li><li><a href="Guacamole.StaticHTTPTunnel.html">StaticHTTPTunnel</a></li><li><a href="Guacamole.Status.html">Status</a></li><li><a href="Guacamole.StringReader.html">StringReader</a></li><li><a href="Guacamole.StringWriter.html">StringWriter</a></li><li><a href="Guacamole.Tunnel.html">Tunnel</a></li><li><a href="Guacamole.VideoPlayer.html">VideoPlayer</a></li><li><a href="Guacamole.WebSocketTunnel.html">WebSocketTunnel</a></li></ul><h3>Events</h3><ul><li><a href="Guacamole.ArrayBufferReader.html#event:ondata">ondata</a></li><li><a href="Guacamole.ArrayBufferReader.html#event:onend">onend</a></li><li><a href="Guacamole.ArrayBufferWriter.html#event:onack">onack</a></li><li><a href="Guacamole.AudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.AudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobReader.html#event:onend">onend</a></li>< li><a href="Guacamole.BlobReader.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.BlobWriter.html#event:onack">onack</a></li><li><a href="Guacamole.BlobWriter.html#event:oncomplete">oncomplete</a></li><li><a href="Guacamole.BlobWriter.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobWriter.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.ChainedTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onaudio">onaudio</a></li><li><a href="Guacamole.Client.html#event:onclipboard">onclipboard</a></li><li><a href="Guacamole.Client.html#event:onerror">onerror</a></li><li><a href="Guacamole.Client.html#event:onfile">onfile</a></li><li><a href="Guacamole.Client.html#event:onfilesystem">onfilesystem</a></li><li><a href="Guacamole.Client.html#event:onna me">onname</a></li><li><a href="Guacamole.Client.html#event:onpipe">onpipe</a></li><li><a href="Guacamole.Client.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onsync">onsync</a></li><li><a href="Guacamole.Client.html#event:onvideo">onvideo</a></li><li><a href="Guacamole.DataURIReader.html#event:onend">onend</a></li><li><a href="Guacamole.Display.html#event:oncursor">oncursor</a></li><li><a href="Guacamole.Display.html#event:onresize">onresize</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.HTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.InputStream.html#event:onblob">onblob</a></li><li><a href="Guacamole.InputStream.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onprogress">onprog ress</a></li><li><a href="Guacamole.Keyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.Keyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.html#event:onmouseout">onmouseout</a></li><li><a href="Guacamole.Mouse.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Object.html#event:onbody">onbody</a></li>< li><a href="Guacamole.Object.html#event:onundefine">onundefine</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.OutputStream.html#event:onack">onack</a></li><li><a href="Guacamole.Parser.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.SessionRecording.html#event:onpause">onpause</a></li><li><a href="Guacamole.SessionRecording.html#event:onplay">onplay</a></li><li><a href="Guacamole .SessionRecording.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.SessionRecording.html#event:onseek">onseek</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.StringReader.html#event:onend">onend</a></li><li><a href="Guacamole.StringReader.html#event:ontext">ontext</a></li><li><a href="Guacamole.StringWriter.html#event:onack">onack</a></li><li><a href="Guacamole.Tunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.Tunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.Tunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamol e.WebSocketTunnel.html#event:onstatechange">onstatechange</a></li></ul><h3>Namespaces</h3><ul><li><a href="Guacamole.html">Guacamole</a></li><li><a href="Guacamole.AudioContextFactory.html">AudioContextFactory</a></li></ul> +</nav> + +<br class="clear"> + +<footer> + Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun Jul 02 2017 12:09:53 GMT-0700 (PDT) +</footer> + +<script> prettyPrint(); </script> +<script src="scripts/linenumber.js"> </script> + <!-- Google Analytics --> + <script type="text/javascript"> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-75289145-1', 'auto'); + ga('send', 'pageview'); + </script> +</body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-guacamole-website/blob/eff0fb8d/content/doc/0.9.13-incubating/guacamole-common-js/Tunnel.js.html ---------------------------------------------------------------------- diff --git a/content/doc/0.9.13-incubating/guacamole-common-js/Tunnel.js.html b/content/doc/0.9.13-incubating/guacamole-common-js/Tunnel.js.html new file mode 100644 index 0000000..bd5f5a5 --- /dev/null +++ b/content/doc/0.9.13-incubating/guacamole-common-js/Tunnel.js.html @@ -0,0 +1,1270 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>JSDoc: Source: Tunnel.js</title> + + <script src="scripts/prettify/prettify.js"> </script> + <script src="scripts/prettify/lang-css.js"> </script> + <!--[if lt IE 9]> + <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> + <![endif]--> + <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> + <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> +</head> + +<body> + +<div id="main"> + + <h1 class="page-title">Source: Tunnel.js</h1> + + + + + + + <section> + <article> + <pre class="prettyprint source linenums"><code>/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var Guacamole = Guacamole || {}; + +/** + * Core object providing abstract communication for Guacamole. This object + * is a null implementation whose functions do nothing. Guacamole applications + * should use {@link Guacamole.HTTPTunnel} instead, or implement their own tunnel based + * on this one. + * + * @constructor + * @see Guacamole.HTTPTunnel + */ +Guacamole.Tunnel = function() { + + /** + * Connect to the tunnel with the given optional data. This data is + * typically used for authentication. The format of data accepted is + * up to the tunnel implementation. + * + * @param {String} data The data to send to the tunnel when connecting. + */ + this.connect = function(data) {}; + + /** + * Disconnect from the tunnel. + */ + this.disconnect = function() {}; + + /** + * Send the given message through the tunnel to the service on the other + * side. All messages are guaranteed to be received in the order sent. + * + * @param {...*} elements + * The elements of the message to send to the service on the other side + * of the tunnel. + */ + this.sendMessage = function(elements) {}; + + /** + * The current state of this tunnel. + * + * @type {Number} + */ + this.state = Guacamole.Tunnel.State.CONNECTING; + + /** + * The maximum amount of time to wait for data to be received, in + * milliseconds. If data is not received within this amount of time, + * the tunnel is closed with an error. The default value is 15000. + * + * @type {Number} + */ + this.receiveTimeout = 15000; + + /** + * The UUID uniquely identifying this tunnel. If not yet known, this will + * be null. + * + * @type {String} + */ + this.uuid = null; + + /** + * Fired whenever an error is encountered by the tunnel. + * + * @event + * @param {Guacamole.Status} status A status object which describes the + * error. + */ + this.onerror = null; + + /** + * Fired whenever the state of the tunnel changes. + * + * @event + * @param {Number} state The new state of the client. + */ + this.onstatechange = null; + + /** + * Fired once for every complete Guacamole instruction received, in order. + * + * @event + * @param {String} opcode The Guacamole instruction opcode. + * @param {Array} parameters The parameters provided for the instruction, + * if any. + */ + this.oninstruction = null; + +}; + +/** + * The Guacamole protocol instruction opcode reserved for arbitrary internal + * use by tunnel implementations. The value of this opcode is guaranteed to be + * the empty string (""). Tunnel implementations may use this opcode for any + * purpose. It is currently used by the HTTP tunnel to mark the end of the HTTP + * response, and by the WebSocket tunnel to transmit the tunnel UUID. + * + * @constant + * @type {String} + */ +Guacamole.Tunnel.INTERNAL_DATA_OPCODE = ''; + +/** + * All possible tunnel states. + */ +Guacamole.Tunnel.State = { + + /** + * A connection is in pending. It is not yet known whether connection was + * successful. + * + * @type {Number} + */ + "CONNECTING": 0, + + /** + * Connection was successful, and data is being received. + * + * @type {Number} + */ + "OPEN": 1, + + /** + * The connection is closed. Connection may not have been successful, the + * tunnel may have been explicitly closed by either side, or an error may + * have occurred. + * + * @type {Number} + */ + "CLOSED": 2 + +}; + +/** + * Guacamole Tunnel implemented over HTTP via XMLHttpRequest. + * + * @constructor + * @augments Guacamole.Tunnel + * + * @param {String} tunnelURL + * The URL of the HTTP tunneling service. + * + * @param {Boolean} [crossDomain=false] + * Whether tunnel requests will be cross-domain, and thus must use CORS + * mechanisms and headers. By default, it is assumed that tunnel requests + * will be made to the same domain. + */ +Guacamole.HTTPTunnel = function(tunnelURL, crossDomain) { + + /** + * Reference to this HTTP tunnel. + * @private + */ + var tunnel = this; + + var TUNNEL_CONNECT = tunnelURL + "?connect"; + var TUNNEL_READ = tunnelURL + "?read:"; + var TUNNEL_WRITE = tunnelURL + "?write:"; + + var POLLING_ENABLED = 1; + var POLLING_DISABLED = 0; + + // Default to polling - will be turned off automatically if not needed + var pollingMode = POLLING_ENABLED; + + var sendingMessages = false; + var outputMessageBuffer = ""; + + // If requests are expected to be cross-domain, the cookie that the HTTP + // tunnel depends on will only be sent if withCredentials is true + var withCredentials = !!crossDomain; + + /** + * The current receive timeout ID, if any. + * @private + */ + var receive_timeout = null; + + /** + * Initiates a timeout which, if data is not received, causes the tunnel + * to close with an error. + * + * @private + */ + function reset_timeout() { + + // Get rid of old timeout (if any) + window.clearTimeout(receive_timeout); + + // Set new timeout + receive_timeout = window.setTimeout(function () { + close_tunnel(new Guacamole.Status(Guacamole.Status.Code.UPSTREAM_TIMEOUT, "Server timeout.")); + }, tunnel.receiveTimeout); + + } + + /** + * Closes this tunnel, signaling the given status and corresponding + * message, which will be sent to the onerror handler if the status is + * an error status. + * + * @private + * @param {Guacamole.Status} status The status causing the connection to + * close; + */ + function close_tunnel(status) { + + // Ignore if already closed + if (tunnel.state === Guacamole.Tunnel.State.CLOSED) + return; + + // If connection closed abnormally, signal error. + if (status.code !== Guacamole.Status.Code.SUCCESS && tunnel.onerror) { + + // Ignore RESOURCE_NOT_FOUND if we've already connected, as that + // only signals end-of-stream for the HTTP tunnel. + if (tunnel.state === Guacamole.Tunnel.State.CONNECTING + || status.code !== Guacamole.Status.Code.RESOURCE_NOT_FOUND) + tunnel.onerror(status); + + } + + // Mark as closed + tunnel.state = Guacamole.Tunnel.State.CLOSED; + + // Reset output message buffer + sendingMessages = false; + + if (tunnel.onstatechange) + tunnel.onstatechange(tunnel.state); + + } + + + this.sendMessage = function() { + + // Do not attempt to send messages if not connected + if (tunnel.state !== Guacamole.Tunnel.State.OPEN) + return; + + // Do not attempt to send empty messages + if (arguments.length === 0) + return; + + /** + * Converts the given value to a length/string pair for use as an + * element in a Guacamole instruction. + * + * @private + * @param value The value to convert. + * @return {String} The converted value. + */ + function getElement(value) { + var string = new String(value); + return string.length + "." + string; + } + + // Initialized message with first element + var message = getElement(arguments[0]); + + // Append remaining elements + for (var i=1; i<arguments.length; i++) + message += "," + getElement(arguments[i]); + + // Final terminator + message += ";"; + + // Add message to buffer + outputMessageBuffer += message; + + // Send if not currently sending + if (!sendingMessages) + sendPendingMessages(); + + }; + + function sendPendingMessages() { + + // Do not attempt to send messages if not connected + if (tunnel.state !== Guacamole.Tunnel.State.OPEN) + return; + + if (outputMessageBuffer.length > 0) { + + sendingMessages = true; + + var message_xmlhttprequest = new XMLHttpRequest(); + message_xmlhttprequest.open("POST", TUNNEL_WRITE + tunnel.uuid); + message_xmlhttprequest.withCredentials = withCredentials; + message_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8"); + + // Once response received, send next queued event. + message_xmlhttprequest.onreadystatechange = function() { + if (message_xmlhttprequest.readyState === 4) { + + // If an error occurs during send, handle it + if (message_xmlhttprequest.status !== 200) + handleHTTPTunnelError(message_xmlhttprequest); + + // Otherwise, continue the send loop + else + sendPendingMessages(); + + } + }; + + message_xmlhttprequest.send(outputMessageBuffer); + outputMessageBuffer = ""; // Clear buffer + + } + else + sendingMessages = false; + + } + + function handleHTTPTunnelError(xmlhttprequest) { + + var code = parseInt(xmlhttprequest.getResponseHeader("Guacamole-Status-Code")); + var message = xmlhttprequest.getResponseHeader("Guacamole-Error-Message"); + + close_tunnel(new Guacamole.Status(code, message)); + + } + + function handleResponse(xmlhttprequest) { + + var interval = null; + var nextRequest = null; + + var dataUpdateEvents = 0; + + // The location of the last element's terminator + var elementEnd = -1; + + // Where to start the next length search or the next element + var startIndex = 0; + + // Parsed elements + var elements = new Array(); + + function parseResponse() { + + // Do not handle responses if not connected + if (tunnel.state !== Guacamole.Tunnel.State.OPEN) { + + // Clean up interval if polling + if (interval !== null) + clearInterval(interval); + + return; + } + + // Do not parse response yet if not ready + if (xmlhttprequest.readyState < 2) return; + + // Attempt to read status + var status; + try { status = xmlhttprequest.status; } + + // If status could not be read, assume successful. + catch (e) { status = 200; } + + // Start next request as soon as possible IF request was successful + if (!nextRequest && status === 200) + nextRequest = makeRequest(); + + // Parse stream when data is received and when complete. + if (xmlhttprequest.readyState === 3 || + xmlhttprequest.readyState === 4) { + + reset_timeout(); + + // Also poll every 30ms (some browsers don't repeatedly call onreadystatechange for new data) + if (pollingMode === POLLING_ENABLED) { + if (xmlhttprequest.readyState === 3 && !interval) + interval = setInterval(parseResponse, 30); + else if (xmlhttprequest.readyState === 4 && interval) + clearInterval(interval); + } + + // If canceled, stop transfer + if (xmlhttprequest.status === 0) { + tunnel.disconnect(); + return; + } + + // Halt on error during request + else if (xmlhttprequest.status !== 200) { + handleHTTPTunnelError(xmlhttprequest); + return; + } + + // Attempt to read in-progress data + var current; + try { current = xmlhttprequest.responseText; } + + // Do not attempt to parse if data could not be read + catch (e) { return; } + + // While search is within currently received data + while (elementEnd < current.length) { + + // If we are waiting for element data + if (elementEnd >= startIndex) { + + // We now have enough data for the element. Parse. + var element = current.substring(startIndex, elementEnd); + var terminator = current.substring(elementEnd, elementEnd+1); + + // Add element to array + elements.push(element); + + // If last element, handle instruction + if (terminator === ";") { + + // Get opcode + var opcode = elements.shift(); + + // Call instruction handler. + if (tunnel.oninstruction) + tunnel.oninstruction(opcode, elements); + + // Clear elements + elements.length = 0; + + } + + // Start searching for length at character after + // element terminator + startIndex = elementEnd + 1; + + } + + // Search for end of length + var lengthEnd = current.indexOf(".", startIndex); + if (lengthEnd !== -1) { + + // Parse length + var length = parseInt(current.substring(elementEnd+1, lengthEnd)); + + // If we're done parsing, handle the next response. + if (length === 0) { + + // Clean up interval if polling + if (interval) + clearInterval(interval); + + // Clean up object + xmlhttprequest.onreadystatechange = null; + xmlhttprequest.abort(); + + // Start handling next request + if (nextRequest) + handleResponse(nextRequest); + + // Done parsing + break; + + } + + // Calculate start of element + startIndex = lengthEnd + 1; + + // Calculate location of element terminator + elementEnd = startIndex + length; + + } + + // If no period yet, continue search when more data + // is received + else { + startIndex = current.length; + break; + } + + } // end parse loop + + } + + } + + // If response polling enabled, attempt to detect if still + // necessary (via wrapping parseResponse()) + if (pollingMode === POLLING_ENABLED) { + xmlhttprequest.onreadystatechange = function() { + + // If we receive two or more readyState==3 events, + // there is no need to poll. + if (xmlhttprequest.readyState === 3) { + dataUpdateEvents++; + if (dataUpdateEvents >= 2) { + pollingMode = POLLING_DISABLED; + xmlhttprequest.onreadystatechange = parseResponse; + } + } + + parseResponse(); + }; + } + + // Otherwise, just parse + else + xmlhttprequest.onreadystatechange = parseResponse; + + parseResponse(); + + } + + /** + * Arbitrary integer, unique for each tunnel read request. + * @private + */ + var request_id = 0; + + function makeRequest() { + + // Make request, increment request ID + var xmlhttprequest = new XMLHttpRequest(); + xmlhttprequest.open("GET", TUNNEL_READ + tunnel.uuid + ":" + (request_id++)); + xmlhttprequest.withCredentials = withCredentials; + xmlhttprequest.send(null); + + return xmlhttprequest; + + } + + this.connect = function(data) { + + // Start waiting for connect + reset_timeout(); + + // Start tunnel and connect + var connect_xmlhttprequest = new XMLHttpRequest(); + connect_xmlhttprequest.onreadystatechange = function() { + + if (connect_xmlhttprequest.readyState !== 4) + return; + + // If failure, throw error + if (connect_xmlhttprequest.status !== 200) { + handleHTTPTunnelError(connect_xmlhttprequest); + return; + } + + reset_timeout(); + + // Get UUID from response + tunnel.uuid = connect_xmlhttprequest.responseText; + + tunnel.state = Guacamole.Tunnel.State.OPEN; + if (tunnel.onstatechange) + tunnel.onstatechange(tunnel.state); + + // Start reading data + handleResponse(makeRequest()); + + }; + + connect_xmlhttprequest.open("POST", TUNNEL_CONNECT, true); + connect_xmlhttprequest.withCredentials = withCredentials; + connect_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8"); + connect_xmlhttprequest.send(data); + + }; + + this.disconnect = function() { + close_tunnel(new Guacamole.Status(Guacamole.Status.Code.SUCCESS, "Manually closed.")); + }; + +}; + +Guacamole.HTTPTunnel.prototype = new Guacamole.Tunnel(); + +/** + * Guacamole Tunnel implemented over WebSocket via XMLHttpRequest. + * + * @constructor + * @augments Guacamole.Tunnel + * @param {String} tunnelURL The URL of the WebSocket tunneling service. + */ +Guacamole.WebSocketTunnel = function(tunnelURL) { + + /** + * Reference to this WebSocket tunnel. + * @private + */ + var tunnel = this; + + /** + * The WebSocket used by this tunnel. + * @private + */ + var socket = null; + + /** + * The current receive timeout ID, if any. + * @private + */ + var receive_timeout = null; + + /** + * The WebSocket protocol corresponding to the protocol used for the current + * location. + * @private + */ + var ws_protocol = { + "http:": "ws:", + "https:": "wss:" + }; + + // Transform current URL to WebSocket URL + + // If not already a websocket URL + if ( tunnelURL.substring(0, 3) !== "ws:" + && tunnelURL.substring(0, 4) !== "wss:") { + + var protocol = ws_protocol[window.location.protocol]; + + // If absolute URL, convert to absolute WS URL + if (tunnelURL.substring(0, 1) === "/") + tunnelURL = + protocol + + "//" + window.location.host + + tunnelURL; + + // Otherwise, construct absolute from relative URL + else { + + // Get path from pathname + var slash = window.location.pathname.lastIndexOf("/"); + var path = window.location.pathname.substring(0, slash + 1); + + // Construct absolute URL + tunnelURL = + protocol + + "//" + window.location.host + + path + + tunnelURL; + + } + + } + + /** + * Initiates a timeout which, if data is not received, causes the tunnel + * to close with an error. + * + * @private + */ + function reset_timeout() { + + // Get rid of old timeout (if any) + window.clearTimeout(receive_timeout); + + // Set new timeout + receive_timeout = window.setTimeout(function () { + close_tunnel(new Guacamole.Status(Guacamole.Status.Code.UPSTREAM_TIMEOUT, "Server timeout.")); + }, tunnel.receiveTimeout); + + } + + /** + * Closes this tunnel, signaling the given status and corresponding + * message, which will be sent to the onerror handler if the status is + * an error status. + * + * @private + * @param {Guacamole.Status} status The status causing the connection to + * close; + */ + function close_tunnel(status) { + + // Ignore if already closed + if (tunnel.state === Guacamole.Tunnel.State.CLOSED) + return; + + // If connection closed abnormally, signal error. + if (status.code !== Guacamole.Status.Code.SUCCESS && tunnel.onerror) + tunnel.onerror(status); + + // Mark as closed + tunnel.state = Guacamole.Tunnel.State.CLOSED; + if (tunnel.onstatechange) + tunnel.onstatechange(tunnel.state); + + socket.close(); + + } + + this.sendMessage = function(elements) { + + // Do not attempt to send messages if not connected + if (tunnel.state !== Guacamole.Tunnel.State.OPEN) + return; + + // Do not attempt to send empty messages + if (arguments.length === 0) + return; + + /** + * Converts the given value to a length/string pair for use as an + * element in a Guacamole instruction. + * + * @private + * @param value The value to convert. + * @return {String} The converted value. + */ + function getElement(value) { + var string = new String(value); + return string.length + "." + string; + } + + // Initialized message with first element + var message = getElement(arguments[0]); + + // Append remaining elements + for (var i=1; i<arguments.length; i++) + message += "," + getElement(arguments[i]); + + // Final terminator + message += ";"; + + socket.send(message); + + }; + + this.connect = function(data) { + + reset_timeout(); + + // Connect socket + socket = new WebSocket(tunnelURL + "?" + data, "guacamole"); + + socket.onopen = function(event) { + reset_timeout(); + }; + + socket.onclose = function(event) { + close_tunnel(new Guacamole.Status(parseInt(event.reason), event.reason)); + }; + + socket.onerror = function(event) { + close_tunnel(new Guacamole.Status(Guacamole.Status.Code.SERVER_ERROR, event.data)); + }; + + socket.onmessage = function(event) { + + reset_timeout(); + + var message = event.data; + var startIndex = 0; + var elementEnd; + + var elements = []; + + do { + + // Search for end of length + var lengthEnd = message.indexOf(".", startIndex); + if (lengthEnd !== -1) { + + // Parse length + var length = parseInt(message.substring(elementEnd+1, lengthEnd)); + + // Calculate start of element + startIndex = lengthEnd + 1; + + // Calculate location of element terminator + elementEnd = startIndex + length; + + } + + // If no period, incomplete instruction. + else + close_tunnel(new Guacamole.Status(Guacamole.Status.Code.SERVER_ERROR, "Incomplete instruction.")); + + // We now have enough data for the element. Parse. + var element = message.substring(startIndex, elementEnd); + var terminator = message.substring(elementEnd, elementEnd+1); + + // Add element to array + elements.push(element); + + // If last element, handle instruction + if (terminator === ";") { + + // Get opcode + var opcode = elements.shift(); + + // Update state and UUID when first instruction received + if (tunnel.state !== Guacamole.Tunnel.State.OPEN) { + + // Associate tunnel UUID if received + if (opcode === Guacamole.Tunnel.INTERNAL_DATA_OPCODE) + tunnel.uuid = elements[0]; + + // Tunnel is now open and UUID is available + tunnel.state = Guacamole.Tunnel.State.OPEN; + if (tunnel.onstatechange) + tunnel.onstatechange(tunnel.state); + + } + + // Call instruction handler. + if (opcode !== Guacamole.Tunnel.INTERNAL_DATA_OPCODE && tunnel.oninstruction) + tunnel.oninstruction(opcode, elements); + + // Clear elements + elements.length = 0; + + } + + // Start searching for length at character after + // element terminator + startIndex = elementEnd + 1; + + } while (startIndex < message.length); + + }; + + }; + + this.disconnect = function() { + close_tunnel(new Guacamole.Status(Guacamole.Status.Code.SUCCESS, "Manually closed.")); + }; + +}; + +Guacamole.WebSocketTunnel.prototype = new Guacamole.Tunnel(); + +/** + * Guacamole Tunnel which cycles between all specified tunnels until + * no tunnels are left. Another tunnel is used if an error occurs but + * no instructions have been received. If an instruction has been + * received, or no tunnels remain, the error is passed directly out + * through the onerror handler (if defined). + * + * @constructor + * @augments Guacamole.Tunnel + * @param {...*} tunnelChain + * The tunnels to use, in order of priority. + */ +Guacamole.ChainedTunnel = function(tunnelChain) { + + /** + * Reference to this chained tunnel. + * @private + */ + var chained_tunnel = this; + + /** + * Data passed in via connect(), to be used for + * wrapped calls to other tunnels' connect() functions. + * @private + */ + var connect_data; + + /** + * Array of all tunnels passed to this ChainedTunnel through the + * constructor arguments. + * @private + */ + var tunnels = []; + + /** + * The tunnel committed via commit_tunnel(), if any, or null if no tunnel + * has yet been committed. + * + * @private + * @type {Guacamole.Tunnel} + */ + var committedTunnel = null; + + // Load all tunnels into array + for (var i=0; i<arguments.length; i++) + tunnels.push(arguments[i]); + + /** + * Sets the current tunnel. + * + * @private + * @param {Guacamole.Tunnel} tunnel The tunnel to set as the current tunnel. + */ + function attach(tunnel) { + + // Set own functions to tunnel's functions + chained_tunnel.disconnect = tunnel.disconnect; + chained_tunnel.sendMessage = tunnel.sendMessage; + + /** + * Fails the currently-attached tunnel, attaching a new tunnel if + * possible. + * + * @private + * @param {Guacamole.Status} [status] + * An object representing the failure that occured in the + * currently-attached tunnel, if known. + * + * @return {Guacamole.Tunnel} + * The next tunnel, or null if there are no more tunnels to try or + * if no more tunnels should be tried. + */ + var failTunnel = function failTunnel(status) { + + // Do not attempt to continue using next tunnel on server timeout + if (status && status.code === Guacamole.Status.Code.UPSTREAM_TIMEOUT) { + tunnels = []; + return null; + } + + // Get next tunnel + var next_tunnel = tunnels.shift(); + + // If there IS a next tunnel, try using it. + if (next_tunnel) { + tunnel.onerror = null; + tunnel.oninstruction = null; + tunnel.onstatechange = null; + attach(next_tunnel); + } + + return next_tunnel; + + }; + + /** + * Use the current tunnel from this point forward. Do not try any more + * tunnels, even if the current tunnel fails. + * + * @private + */ + function commit_tunnel() { + tunnel.onstatechange = chained_tunnel.onstatechange; + tunnel.oninstruction = chained_tunnel.oninstruction; + tunnel.onerror = chained_tunnel.onerror; + chained_tunnel.uuid = tunnel.uuid; + committedTunnel = tunnel; + } + + // Wrap own onstatechange within current tunnel + tunnel.onstatechange = function(state) { + + switch (state) { + + // If open, use this tunnel from this point forward. + case Guacamole.Tunnel.State.OPEN: + commit_tunnel(); + if (chained_tunnel.onstatechange) + chained_tunnel.onstatechange(state); + break; + + // If closed, mark failure, attempt next tunnel + case Guacamole.Tunnel.State.CLOSED: + if (!failTunnel() && chained_tunnel.onstatechange) + chained_tunnel.onstatechange(state); + break; + + } + + }; + + // Wrap own oninstruction within current tunnel + tunnel.oninstruction = function(opcode, elements) { + + // Accept current tunnel + commit_tunnel(); + + // Invoke handler + if (chained_tunnel.oninstruction) + chained_tunnel.oninstruction(opcode, elements); + + }; + + // Attach next tunnel on error + tunnel.onerror = function(status) { + + // Mark failure, attempt next tunnel + if (!failTunnel(status) && chained_tunnel.onerror) + chained_tunnel.onerror(status); + + }; + + // Attempt connection + tunnel.connect(connect_data); + + } + + this.connect = function(data) { + + // Remember connect data + connect_data = data; + + // Get committed tunnel if exists or the first tunnel on the list + var next_tunnel = committedTunnel ? committedTunnel : tunnels.shift(); + + // Attach first tunnel + if (next_tunnel) + attach(next_tunnel); + + // If there IS no first tunnel, error + else if (chained_tunnel.onerror) + chained_tunnel.onerror(Guacamole.Status.Code.SERVER_ERROR, "No tunnels to try."); + + }; + +}; + +Guacamole.ChainedTunnel.prototype = new Guacamole.Tunnel(); + +/** + * Guacamole Tunnel which replays a Guacamole protocol dump from a static file + * received via HTTP. Instructions within the file are parsed and handled as + * quickly as possible, while the file is being downloaded. + * + * @constructor + * @augments Guacamole.Tunnel + * @param {String} url + * The URL of a Guacamole protocol dump. + * + * @param {Boolean} [crossDomain=false] + * Whether tunnel requests will be cross-domain, and thus must use CORS + * mechanisms and headers. By default, it is assumed that tunnel requests + * will be made to the same domain. + */ +Guacamole.StaticHTTPTunnel = function StaticHTTPTunnel(url, crossDomain) { + + /** + * Reference to this Guacamole.StaticHTTPTunnel. + * + * @private + */ + var tunnel = this; + + /** + * The current, in-progress HTTP request. If no request is currently in + * progress, this will be null. + * + * @private + * @type {XMLHttpRequest} + */ + var xhr = null; + + /** + * Changes the stored numeric state of this tunnel, firing the onstatechange + * event if the new state is different and a handler has been defined. + * + * @private + * @param {Number} state + * The new state of this tunnel. + */ + var setState = function setState(state) { + + // Notify only if state changes + if (state !== tunnel.state) { + tunnel.state = state; + if (tunnel.onstatechange) + tunnel.onstatechange(state); + } + + }; + + /** + * Returns the Guacamole protocol status code which most closely + * represents the given HTTP status code. + * + * @private + * @param {Number} httpStatus + * The HTTP status code to translate into a Guacamole protocol status + * code. + * + * @returns {Number} + * The Guacamole protocol status code which most closely represents the + * given HTTP status code. + */ + var getGuacamoleStatusCode = function getGuacamoleStatusCode(httpStatus) { + + // Translate status codes with known equivalents + switch (httpStatus) { + + // HTTP 400 - Bad request + case 400: + return Guacamole.Status.Code.CLIENT_BAD_REQUEST; + + // HTTP 403 - Forbidden + case 403: + return Guacamole.Status.Code.CLIENT_FORBIDDEN; + + // HTTP 404 - Resource not found + case 404: + return Guacamole.Status.Code.RESOURCE_NOT_FOUND; + + // HTTP 429 - Too many requests + case 429: + return Guacamole.Status.Code.CLIENT_TOO_MANY; + + // HTTP 503 - Server unavailable + case 503: + return Guacamole.Status.Code.SERVER_BUSY; + + } + + // Default all other codes to generic internal error + return Guacamole.Status.Code.SERVER_ERROR; + + }; + + this.sendMessage = function sendMessage(elements) { + // Do nothing + }; + + this.connect = function connect(data) { + + // Ensure any existing connection is killed + tunnel.disconnect(); + + // Connection is now starting + setState(Guacamole.Tunnel.State.CONNECTING); + + // Start a new connection + xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.withCredentials = !!crossDomain; + xhr.responseType = 'text'; + xhr.send(null); + + var offset = 0; + + // Create Guacamole protocol parser specifically for this connection + var parser = new Guacamole.Parser(); + + // Invoke tunnel's oninstruction handler for each parsed instruction + parser.oninstruction = function instructionReceived(opcode, args) { + if (tunnel.oninstruction) + tunnel.oninstruction(opcode, args); + }; + + // Continuously parse received data + xhr.onreadystatechange = function readyStateChanged() { + + // Parse while data is being received + if (xhr.readyState === 3 || xhr.readyState === 4) { + + // Connection is open + setState(Guacamole.Tunnel.State.OPEN); + + var buffer = xhr.responseText; + var length = buffer.length; + + // Parse only the portion of data which is newly received + if (offset < length) { + parser.receive(buffer.substring(offset)); + offset = length; + } + + } + + // Clean up and close when done + if (xhr.readyState === 4) + tunnel.disconnect(); + + }; + + // Reset state and close upon error + xhr.onerror = function httpError() { + + // Fail if file could not be downloaded via HTTP + if (tunnel.onerror) + tunnel.onerror(new Guacamole.Status(getGuacamoleStatusCode(xhr.status), xhr.statusText)); + + tunnel.disconnect(); + }; + + }; + + this.disconnect = function disconnect() { + + // Abort and dispose of XHR if a request is in progress + if (xhr) { + xhr.abort(); + xhr = null; + } + + // Connection is now closed + setState(Guacamole.Tunnel.State.CLOSED); + + }; + +}; + +Guacamole.StaticHTTPTunnel.prototype = new Guacamole.Tunnel(); +</code></pre> + </article> + </section> + + + + +</div> + +<nav> + <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Guacamole.ArrayBufferReader.html">ArrayBufferReader</a></li><li><a href="Guacamole.ArrayBufferWriter.html">ArrayBufferWriter</a></li><li><a href="Guacamole.AudioPlayer.html">AudioPlayer</a></li><li><a href="Guacamole.AudioRecorder.html">AudioRecorder</a></li><li><a href="Guacamole.BlobReader.html">BlobReader</a></li><li><a href="Guacamole.BlobWriter.html">BlobWriter</a></li><li><a href="Guacamole.ChainedTunnel.html">ChainedTunnel</a></li><li><a href="Guacamole.Client.html">Client</a></li><li><a href="Guacamole.DataURIReader.html">DataURIReader</a></li><li><a href="Guacamole.Display.html">Display</a></li><li><a href="Guacamole.Display.VisibleLayer.html">VisibleLayer</a></li><li><a href="Guacamole.HTTPTunnel.html">HTTPTunnel</a></li><li><a href="Guacamole.InputStream.html">InputStream</a></li><li><a href="Guacamole.IntegerPool.html">IntegerPool</a></li><li><a href="Guacamole.JSONReader.html">JSONReader</a></li> <li><a href="Guacamole.Keyboard.html">Keyboard</a></li><li><a href="Guacamole.Keyboard.ModifierState.html">ModifierState</a></li><li><a href="Guacamole.Layer.html">Layer</a></li><li><a href="Guacamole.Layer.Pixel.html">Pixel</a></li><li><a href="Guacamole.Mouse.html">Mouse</a></li><li><a href="Guacamole.Mouse.State.html">State</a></li><li><a href="Guacamole.Mouse.Touchpad.html">Touchpad</a></li><li><a href="Guacamole.Mouse.Touchscreen.html">Touchscreen</a></li><li><a href="Guacamole.Object.html">Object</a></li><li><a href="Guacamole.OnScreenKeyboard.html">OnScreenKeyboard</a></li><li><a href="Guacamole.OnScreenKeyboard.Key.html">Key</a></li><li><a href="Guacamole.OnScreenKeyboard.Layout.html">Layout</a></li><li><a href="Guacamole.OutputStream.html">OutputStream</a></li><li><a href="Guacamole.Parser.html">Parser</a></li><li><a href="Guacamole.RawAudioFormat.html">RawAudioFormat</a></li><li><a href="Guacamole.RawAudioPlayer.html">RawAudioPlayer</a></li><li><a href="Guacamole.RawAudioR ecorder.html">RawAudioRecorder</a></li><li><a href="Guacamole.SessionRecording.html">SessionRecording</a></li><li><a href="Guacamole.StaticHTTPTunnel.html">StaticHTTPTunnel</a></li><li><a href="Guacamole.Status.html">Status</a></li><li><a href="Guacamole.StringReader.html">StringReader</a></li><li><a href="Guacamole.StringWriter.html">StringWriter</a></li><li><a href="Guacamole.Tunnel.html">Tunnel</a></li><li><a href="Guacamole.VideoPlayer.html">VideoPlayer</a></li><li><a href="Guacamole.WebSocketTunnel.html">WebSocketTunnel</a></li></ul><h3>Events</h3><ul><li><a href="Guacamole.ArrayBufferReader.html#event:ondata">ondata</a></li><li><a href="Guacamole.ArrayBufferReader.html#event:onend">onend</a></li><li><a href="Guacamole.ArrayBufferWriter.html#event:onack">onack</a></li><li><a href="Guacamole.AudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.AudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobReader.html#event:onend">onend</a></li>< li><a href="Guacamole.BlobReader.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.BlobWriter.html#event:onack">onack</a></li><li><a href="Guacamole.BlobWriter.html#event:oncomplete">oncomplete</a></li><li><a href="Guacamole.BlobWriter.html#event:onerror">onerror</a></li><li><a href="Guacamole.BlobWriter.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.ChainedTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.ChainedTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onaudio">onaudio</a></li><li><a href="Guacamole.Client.html#event:onclipboard">onclipboard</a></li><li><a href="Guacamole.Client.html#event:onerror">onerror</a></li><li><a href="Guacamole.Client.html#event:onfile">onfile</a></li><li><a href="Guacamole.Client.html#event:onfilesystem">onfilesystem</a></li><li><a href="Guacamole.Client.html#event:onna me">onname</a></li><li><a href="Guacamole.Client.html#event:onpipe">onpipe</a></li><li><a href="Guacamole.Client.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.Client.html#event:onsync">onsync</a></li><li><a href="Guacamole.Client.html#event:onvideo">onvideo</a></li><li><a href="Guacamole.DataURIReader.html#event:onend">onend</a></li><li><a href="Guacamole.Display.html#event:oncursor">oncursor</a></li><li><a href="Guacamole.Display.html#event:onresize">onresize</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.HTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.HTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.InputStream.html#event:onblob">onblob</a></li><li><a href="Guacamole.InputStream.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onend">onend</a></li><li><a href="Guacamole.JSONReader.html#event:onprogress">onprog ress</a></li><li><a href="Guacamole.Keyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.Keyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchpad.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.Touchscreen.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Mouse.html#event:onmousedown">onmousedown</a></li><li><a href="Guacamole.Mouse.html#event:onmousemove">onmousemove</a></li><li><a href="Guacamole.Mouse.html#event:onmouseout">onmouseout</a></li><li><a href="Guacamole.Mouse.html#event:onmouseup">onmouseup</a></li><li><a href="Guacamole.Object.html#event:onbody">onbody</a></li>< li><a href="Guacamole.Object.html#event:onundefine">onundefine</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeydown">onkeydown</a></li><li><a href="Guacamole.OnScreenKeyboard.html#event:onkeyup">onkeyup</a></li><li><a href="Guacamole.OutputStream.html#event:onack">onack</a></li><li><a href="Guacamole.Parser.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onclose">onclose</a></li><li><a href="Guacamole.RawAudioRecorder.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.SessionRecording._PlaybackTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.SessionRecording.html#event:onpause">onpause</a></li><li><a href="Guacamole.SessionRecording.html#event:onplay">onplay</a></li><li><a href="Guacamole .SessionRecording.html#event:onprogress">onprogress</a></li><li><a href="Guacamole.SessionRecording.html#event:onseek">onseek</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.StaticHTTPTunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.StringReader.html#event:onend">onend</a></li><li><a href="Guacamole.StringReader.html#event:ontext">ontext</a></li><li><a href="Guacamole.StringWriter.html#event:onack">onack</a></li><li><a href="Guacamole.Tunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.Tunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamole.Tunnel.html#event:onstatechange">onstatechange</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:onerror">onerror</a></li><li><a href="Guacamole.WebSocketTunnel.html#event:oninstruction">oninstruction</a></li><li><a href="Guacamol e.WebSocketTunnel.html#event:onstatechange">onstatechange</a></li></ul><h3>Namespaces</h3><ul><li><a href="Guacamole.html">Guacamole</a></li><li><a href="Guacamole.AudioContextFactory.html">AudioContextFactory</a></li></ul> +</nav> + +<br class="clear"> + +<footer> + Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun Jul 02 2017 12:09:53 GMT-0700 (PDT) +</footer> + +<script> prettyPrint(); </script> +<script src="scripts/linenumber.js"> </script> + <!-- Google Analytics --> + <script type="text/javascript"> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-75289145-1', 'auto'); + ga('send', 'pageview'); + </script> +</body> +</html>
