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