]>
git.proxmox.com Git - mirror_novnc.git/blob - include/websock.js
2 * Websock: high-performance binary WebSockets
3 * Copyright (C) 2012 Joel Martin
4 * Licensed under MPL 2.0 (see LICENSE.txt)
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.
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.
17 /*jslint browser: true, bitwise: false, plusplus: false */
18 /*global Util, Base64 */
21 // Load Flash WebSocket emulator if needed
23 // To force WebSocket emulator even when native WebSocket available
24 //window.WEB_SOCKET_FORCE_FLASH = true;
25 // To enable WebSocket emulator debug:
26 //window.WEB_SOCKET_DEBUG=1;
28 if (window
.WebSocket
&& !window
.WEB_SOCKET_FORCE_FLASH
) {
29 Websock_native
= true;
30 } else if (window
.MozWebSocket
&& !window
.WEB_SOCKET_FORCE_FLASH
) {
31 Websock_native
= true;
32 window
.WebSocket
= window
.MozWebSocket
;
34 /* no builtin WebSocket so load web_socket.js */
36 Websock_native
= false;
38 function get_INCLUDE_URI() {
39 return (typeof INCLUDE_URI
!== "undefined") ?
40 INCLUDE_URI
: "include/";
43 var start
= "<script src='" + get_INCLUDE_URI(),
44 end
= "'><\/script>", extra
= "";
46 window
.WEB_SOCKET_SWF_LOCATION
= get_INCLUDE_URI() +
47 "web-socket-js/WebSocketMain.swf";
48 if (Util
.Engine
.trident
) {
49 Util
.Debug("Forcing uncached load of WebSocketMain.swf");
50 window
.WEB_SOCKET_SWF_LOCATION
+= "?" + Math
.random();
52 extra
+= start
+ "web-socket-js/swfobject.js" + end
;
53 extra
+= start
+ "web-socket-js/web_socket.js" + end
;
54 document
.write(extra
);
62 var api
= {}, // Public API
63 websocket
= null, // WebSocket object
64 mode
= 'base64', // Current WebSocket mode: 'binary', 'base64'
65 rQ
= [], // Receive queue
66 rQi
= 0, // Receive queue index
67 rQmax
= 10000, // Max receive queue size before compacting
68 sQ
= [], // Send queue
71 'message' : function() {},
72 'open' : function() {},
73 'close' : function() {},
74 'error' : function() {}
81 // Queue public functions
94 function set_rQi(val
) {
99 return rQ
.length
- rQi
;
105 function rQshift8() {
108 function rQunshift8(num
) {
117 function rQshift16() {
118 return (rQ
[rQi
++] << 8) +
121 function rQshift32() {
122 return (rQ
[rQi
++] << 24) +
127 function rQshiftStr(len
) {
128 if (typeof(len
) === 'undefined') { len
= rQlen(); }
129 var arr
= rQ
.slice(rQi
, rQi
+ len
);
131 return String
.fromCharCode
.apply(null, arr
);
133 function rQshiftBytes(len
) {
134 if (typeof(len
) === 'undefined') { len
= rQlen(); }
136 return rQ
.slice(rQi
-len
, rQi
);
139 function rQslice(start
, end
) {
141 return rQ
.slice(rQi
+ start
, rQi
+ end
);
143 return rQ
.slice(rQi
+ start
);
147 // Check to see if we must wait for 'num' bytes (default to FBU.bytes)
148 // to be available in the receive queue. Return true if we need to
149 // wait (and possibly print a debug message), otherwise false.
150 function rQwait(msg
, num
, goback
) {
151 var rQlen
= rQ
.length
- rQi
; // Skip rQlen() function call
155 throw("rQwait cannot backup " + goback
+ " bytes");
159 //Util.Debug(" waiting for " + (num-rQlen) +
160 // " " + msg + " byte(s)");
161 return true; // true means need more data
167 // Private utility routines
170 function encode_message() {
171 if (mode
=== 'binary') {
172 // Put in a binary arraybuffer
173 return (new Uint8Array(sQ
)).buffer
;
176 return Base64
.encode(sQ
);
180 function decode_message(data
) {
181 //Util.Debug(">> decode_message: " + data);
182 if (mode
=== 'binary') {
183 // push arraybuffer values onto the end
184 rQ
.push
.apply(rQ
, (new Uint8Array(data
)));
186 // base64 decode and concat to the end
187 rQ
= rQ
.concat(Base64
.decode(data
, 0));
189 //Util.Debug(">> decode_message, rQ: " + rQ);
194 // Public Send functions
198 if (websocket
.bufferedAmount
!== 0) {
199 Util
.Debug("bufferedAmount: " + websocket
.bufferedAmount
);
201 if (websocket
.bufferedAmount
< api
.maxBufferedAmount
) {
202 //Util.Debug("arr: " + arr);
203 //Util.Debug("sQ: " + sQ);
205 websocket
.send(encode_message(sQ
));
210 Util
.Info("Delaying send, bufferedAmount: " +
211 websocket
.bufferedAmount
);
216 // overridable for testing
218 //Util.Debug(">> send_array: " + arr);
223 function send_string(str
) {
224 //Util.Debug(">> send_string: " + str);
225 api
.send(str
.split('').map(
226 function (chr
) { return chr
.charCodeAt(0); } ) );
230 // Other public functions
232 function recv_message(e
) {
233 //Util.Debug(">> recv_message: " + e.data.length);
236 decode_message(e
.data
);
238 eventHandlers
.message();
239 // Compact the receive queue
240 if (rQ
.length
> rQmax
) {
241 //Util.Debug("Compacting receive queue");
246 Util
.Debug("Ignoring empty message");
249 if (typeof exc
.stack
!== 'undefined') {
250 Util
.Warn("recv_message, caught exception: " + exc
.stack
);
251 } else if (typeof exc
.description
!== 'undefined') {
252 Util
.Warn("recv_message, caught exception: " + exc
.description
);
254 Util
.Warn("recv_message, caught exception:" + exc
);
256 if (typeof exc
.name
!== 'undefined') {
257 eventHandlers
.error(exc
.name
+ ": " + exc
.message
);
259 eventHandlers
.error(exc
);
262 //Util.Debug("<< recv_message");
266 // Set event handlers
267 function on(evt
, handler
) {
268 eventHandlers
[evt
] = handler
;
271 function init(protocols
) {
281 // Check for full typed array support
282 if (('Uint8Array' in window
) &&
283 ('set' in Uint8Array
.prototype)) {
287 // Check for full binary type support in WebSockets
288 // TODO: this sucks, the property should exist on the prototype
291 if (bt
&& ('binaryType' in (new WebSocket("ws://localhost:17523")))) {
292 Util
.Info("Detected binaryType support in WebSockets");
296 // Just ignore failed test localhost connections
299 // Default protocols if not specified
300 if (typeof(protocols
) === "undefined") {
302 protocols
= ['binary', 'base64'];
304 protocols
= 'base64';
308 // If no binary support, make sure it was not requested
310 if (protocols
=== 'binary') {
311 throw("WebSocket binary sub-protocol requested but not supported");
313 if (typeof(protocols
) === "object") {
314 var new_protocols
= [];
315 for (var i
= 0; i
< protocols
.length
; i
++) {
316 if (protocols
[i
] === 'binary') {
317 Util
.Error("Skipping unsupported WebSocket binary sub-protocol");
319 new_protocols
.push(protocols
[i
]);
322 if (new_protocols
.length
> 0) {
323 protocols
= new_protocols
;
325 throw("Only WebSocket binary sub-protocol was requested and not supported.");
333 function open(uri
, protocols
) {
334 protocols
= init(protocols
);
339 websocket
= new WebSocket(uri
, protocols
);
342 websocket
.onmessage
= recv_message
;
343 websocket
.onopen = function() {
344 Util
.Debug(">> WebSock.onopen");
345 if (websocket
.protocol
) {
346 mode
= websocket
.protocol
;
347 Util
.Info("Server chose sub-protocol: " + websocket
.protocol
);
350 Util
.Error("Server select no sub-protocol!: " + websocket
.protocol
);
352 if (mode
=== 'binary') {
353 websocket
.binaryType
= 'arraybuffer';
355 eventHandlers
.open();
356 Util
.Debug("<< WebSock.onopen");
358 websocket
.onclose = function(e
) {
359 Util
.Debug(">> WebSock.onclose");
360 eventHandlers
.close(e
);
361 Util
.Debug("<< WebSock.onclose");
363 websocket
.onerror = function(e
) {
364 Util
.Debug(">> WebSock.onerror: " + e
);
365 eventHandlers
.error(e
);
366 Util
.Debug("<< WebSock.onerror");
372 if ((websocket
.readyState
=== WebSocket
.OPEN
) ||
373 (websocket
.readyState
=== WebSocket
.CONNECTING
)) {
374 Util
.Info("Closing WebSocket connection");
377 websocket
.onmessage = function (e
) { return; };
381 // Override internal functions for testing
382 // Takes a send function, returns reference to recv function
383 function testMode(override_send
) {
385 api
.send
= override_send
;
386 api
.close = function () {};
390 function constructor() {
391 // Configuration settings
392 api
.maxBufferedAmount
= 200;
394 // Direct access to send and receive queues
397 api
.get_rQi
= get_rQi
;
398 api
.set_rQi
= set_rQi
;
400 // Routines to read from the receive queue
402 api
.rQpeek8
= rQpeek8
;
403 api
.rQshift8
= rQshift8
;
404 api
.rQunshift8
= rQunshift8
;
405 api
.rQshift16
= rQshift16
;
406 api
.rQshift32
= rQshift32
;
407 api
.rQshiftStr
= rQshiftStr
;
408 api
.rQshiftBytes
= rQshiftBytes
;
409 api
.rQslice
= rQslice
;
414 api
.send_string
= send_string
;
420 api
.testMode
= testMode
;
425 return constructor();