]> git.proxmox.com Git - mirror_novnc.git/blame - include/websock.js
timers: fix over repeated connects.
[mirror_novnc.git] / include / websock.js
CommitLineData
72f1348b
JM
1/*
2 * Websock: high-performance binary WebSockets
d58f8b51 3 * Copyright (C) 2012 Joel Martin
72f1348b
JM
4 * Licensed under LGPL-3 (see LICENSE.txt)
5 *
6 * Websock is similar to the standard WebSocket object but Websock
7 * enables communication with raw TCP sockets (i.e. the binary stream)
8 * via websockify. This is accomplished by base64 encoding the data
9 * stream between Websock and websockify.
10 *
11 * Websock has built-in receive queue buffering; the message event
12 * does not contain actual data but is simply a notification that
13 * there is new data available. Several rQ* methods are available to
14 * read binary data off of the receive queue.
15 */
16
ff4bfcb7
JM
17/*jslint browser: true, bitwise: false, plusplus: false */
18/*global Util, Base64 */
19
72f1348b
JM
20
21// Load Flash WebSocket emulator if needed
22
bee36506 23// To force WebSocket emulator even when native WebSocket available
e5d5a7d3 24//window.WEB_SOCKET_FORCE_FLASH = true;
bee36506 25// To enable WebSocket emulator debug:
e5d5a7d3 26//window.WEB_SOCKET_DEBUG=1;
bee36506 27
f2d85676 28if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
72f1348b 29 Websock_native = true;
f2d85676 30} else if (window.MozWebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
ce3bdbcc
JM
31 Websock_native = true;
32 window.WebSocket = window.MozWebSocket;
72f1348b
JM
33} else {
34 /* no builtin WebSocket so load web_socket.js */
b688a909 35
72f1348b
JM
36 Websock_native = false;
37 (function () {
38 function get_INCLUDE_URI() {
39 return (typeof INCLUDE_URI !== "undefined") ?
40 INCLUDE_URI : "include/";
41 }
42
43 var start = "<script src='" + get_INCLUDE_URI(),
44 end = "'><\/script>", extra = "";
45
ff4bfcb7 46 window.WEB_SOCKET_SWF_LOCATION = get_INCLUDE_URI() +
61fc1f22 47 "web-socket-js/WebSocketMain.swf";
0981845e
JM
48 if (Util.Engine.trident) {
49 Util.Debug("Forcing uncached load of WebSocketMain.swf");
ff4bfcb7 50 window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
0981845e 51 }
72f1348b 52 extra += start + "web-socket-js/swfobject.js" + end;
72f1348b
JM
53 extra += start + "web-socket-js/web_socket.js" + end;
54 document.write(extra);
55 }());
56}
57
58
59function Websock() {
d890e864 60"use strict";
72f1348b
JM
61
62var api = {}, // Public API
63 websocket = null, // WebSocket object
64 rQ = [], // Receive queue
65 rQi = 0, // Receive queue index
66 rQmax = 10000, // Max receive queue size before compacting
67 sQ = [], // Send queue
68
69 eventHandlers = {
70 'message' : function() {},
71 'open' : function() {},
72 'close' : function() {},
73 'error' : function() {}
fa8f14d5
JM
74 },
75
76 test_mode = false;
72f1348b
JM
77
78
79//
80// Queue public functions
81//
82
83function get_sQ() {
84 return sQ;
85}
86
87function get_rQ() {
88 return rQ;
89}
90function get_rQi() {
91 return rQi;
92}
d890e864 93function set_rQi(val) {
72f1348b 94 rQi = val;
ff4bfcb7 95}
72f1348b
JM
96
97function rQlen() {
98 return rQ.length - rQi;
99}
100
101function rQpeek8() {
102 return (rQ[rQi] );
103}
104function rQshift8() {
105 return (rQ[rQi++] );
106}
107function rQunshift8(num) {
108 if (rQi === 0) {
109 rQ.unshift(num);
110 } else {
111 rQi -= 1;
112 rQ[rQi] = num;
113 }
114
115}
116function rQshift16() {
117 return (rQ[rQi++] << 8) +
118 (rQ[rQi++] );
119}
120function rQshift32() {
121 return (rQ[rQi++] << 24) +
122 (rQ[rQi++] << 16) +
123 (rQ[rQi++] << 8) +
124 (rQ[rQi++] );
125}
126function rQshiftStr(len) {
f2d85676 127 if (typeof(len) === 'undefined') { len = rQlen(); }
72f1348b
JM
128 var arr = rQ.slice(rQi, rQi + len);
129 rQi += len;
130 return arr.map(function (num) {
131 return String.fromCharCode(num); } ).join('');
132
133}
134function rQshiftBytes(len) {
f2d85676 135 if (typeof(len) === 'undefined') { len = rQlen(); }
72f1348b
JM
136 rQi += len;
137 return rQ.slice(rQi-len, rQi);
138}
139
140function rQslice(start, end) {
141 if (end) {
142 return rQ.slice(rQi + start, rQi + end);
143 } else {
144 return rQ.slice(rQi + start);
145 }
146}
147
148// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
149// to be available in the receive queue. Return true if we need to
150// wait (and possibly print a debug message), otherwise false.
151function rQwait(msg, num, goback) {
152 var rQlen = rQ.length - rQi; // Skip rQlen() function call
153 if (rQlen < num) {
154 if (goback) {
155 if (rQi < goback) {
156 throw("rQwait cannot backup " + goback + " bytes");
157 }
158 rQi -= goback;
159 }
160 //Util.Debug(" waiting for " + (num-rQlen) +
161 // " " + msg + " byte(s)");
162 return true; // true means need more data
163 }
164 return false;
165}
166
167//
168// Private utility routines
169//
170
171function encode_message() {
172 /* base64 encode */
173 return Base64.encode(sQ);
174}
175
176function decode_message(data) {
177 //Util.Debug(">> decode_message: " + data);
178 /* base64 decode */
179 rQ = rQ.concat(Base64.decode(data, 0));
180 //Util.Debug(">> decode_message, rQ: " + rQ);
181}
182
183
184//
185// Public Send functions
186//
187
188function flush() {
8b502df2
JM
189 if (websocket.bufferedAmount !== 0) {
190 Util.Debug("bufferedAmount: " + websocket.bufferedAmount);
191 }
7cc5fbc5 192 if (websocket.bufferedAmount < api.maxBufferedAmount) {
72f1348b
JM
193 //Util.Debug("arr: " + arr);
194 //Util.Debug("sQ: " + sQ);
f31eeaa8 195 if (sQ.length > 0) {
72f1348b
JM
196 websocket.send(encode_message(sQ));
197 sQ = [];
198 }
199 return true;
200 } else {
8b502df2
JM
201 Util.Info("Delaying send, bufferedAmount: " +
202 websocket.bufferedAmount);
72f1348b
JM
203 return false;
204 }
205}
206
207// overridable for testing
208function send(arr) {
209 //Util.Debug(">> send_array: " + arr);
210 sQ = sQ.concat(arr);
1756a30a 211 return flush();
43cf7bd8 212}
72f1348b
JM
213
214function send_string(str) {
215 //Util.Debug(">> send_string: " + str);
216 api.send(str.split('').map(
217 function (chr) { return chr.charCodeAt(0); } ) );
218}
219
220//
221// Other public functions
222
223function recv_message(e) {
224 //Util.Debug(">> recv_message: " + e.data.length);
225
226 try {
227 decode_message(e.data);
228 if (rQlen() > 0) {
43cf7bd8 229 eventHandlers.message();
72f1348b
JM
230 // Compact the receive queue
231 if (rQ.length > rQmax) {
232 //Util.Debug("Compacting receive queue");
233 rQ = rQ.slice(rQi);
234 rQi = 0;
235 }
236 } else {
237 Util.Debug("Ignoring empty message");
238 }
239 } catch (exc) {
240 if (typeof exc.stack !== 'undefined') {
241 Util.Warn("recv_message, caught exception: " + exc.stack);
242 } else if (typeof exc.description !== 'undefined') {
243 Util.Warn("recv_message, caught exception: " + exc.description);
244 } else {
245 Util.Warn("recv_message, caught exception:" + exc);
246 }
247 if (typeof exc.name !== 'undefined') {
43cf7bd8 248 eventHandlers.error(exc.name + ": " + exc.message);
72f1348b 249 } else {
43cf7bd8 250 eventHandlers.error(exc);
72f1348b
JM
251 }
252 }
253 //Util.Debug("<< recv_message");
43cf7bd8 254}
72f1348b
JM
255
256
257// Set event handlers
258function on(evt, handler) {
259 eventHandlers[evt] = handler;
260}
261
262function init() {
263 rQ = [];
264 rQi = 0;
265 sQ = [];
266 websocket = null;
267}
268
269function open(uri) {
270 init();
271
fa8f14d5
JM
272 if (test_mode) {
273 websocket = {};
274 } else {
275 websocket = new WebSocket(uri, 'base64');
276 // TODO: future native binary support
277 //websocket = new WebSocket(uri, ['binary', 'base64']);
278 }
72f1348b
JM
279
280 websocket.onmessage = recv_message;
d890e864 281 websocket.onopen = function() {
72f1348b 282 Util.Debug(">> WebSock.onopen");
d890e864
JM
283 if (websocket.protocol) {
284 Util.Info("Server chose sub-protocol: " + websocket.protocol);
bee36506
JM
285 } else {
286 Util.Error("Server select no sub-protocol!: " + websocket.protocol);
d890e864 287 }
43cf7bd8 288 eventHandlers.open();
72f1348b 289 Util.Debug("<< WebSock.onopen");
43cf7bd8 290 };
72f1348b
JM
291 websocket.onclose = function(e) {
292 Util.Debug(">> WebSock.onclose");
d890e864 293 eventHandlers.close(e);
72f1348b 294 Util.Debug("<< WebSock.onclose");
43cf7bd8 295 };
72f1348b 296 websocket.onerror = function(e) {
d890e864 297 Util.Debug(">> WebSock.onerror: " + e);
43cf7bd8 298 eventHandlers.error(e);
d890e864 299 Util.Debug("<< WebSock.onerror");
43cf7bd8 300 };
72f1348b
JM
301}
302
303function close() {
304 if (websocket) {
305 if ((websocket.readyState === WebSocket.OPEN) ||
306 (websocket.readyState === WebSocket.CONNECTING)) {
307 Util.Info("Closing WebSocket connection");
308 websocket.close();
309 }
310 websocket.onmessage = function (e) { return; };
311 }
312}
313
fa8f14d5
JM
314// Override internal functions for testing
315// Takes a send function, returns reference to recv function
316function testMode(override_send) {
317 test_mode = true;
318 api.send = override_send;
319 api.close = function () {};
320 return recv_message;
321}
322
72f1348b 323function constructor() {
7cc5fbc5
JM
324 // Configuration settings
325 api.maxBufferedAmount = 200;
326
72f1348b
JM
327 // Direct access to send and receive queues
328 api.get_sQ = get_sQ;
329 api.get_rQ = get_rQ;
330 api.get_rQi = get_rQi;
331 api.set_rQi = set_rQi;
332
333 // Routines to read from the receive queue
334 api.rQlen = rQlen;
335 api.rQpeek8 = rQpeek8;
336 api.rQshift8 = rQshift8;
337 api.rQunshift8 = rQunshift8;
338 api.rQshift16 = rQshift16;
339 api.rQshift32 = rQshift32;
340 api.rQshiftStr = rQshiftStr;
341 api.rQshiftBytes = rQshiftBytes;
342 api.rQslice = rQslice;
343 api.rQwait = rQwait;
344
345 api.flush = flush;
346 api.send = send;
347 api.send_string = send_string;
348
72f1348b
JM
349 api.on = on;
350 api.init = init;
351 api.open = open;
352 api.close = close;
fa8f14d5 353 api.testMode = testMode;
72f1348b
JM
354
355 return api;
356}
357
358return constructor();
359
360}