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