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