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