~ubuntu-branches/ubuntu/trusty/websockify/trusty-updates

« back to all changes in this revision

Viewing changes to include/websock.js

  • Committer: Package Import Robot
  • Author(s): Thomas Goirand
  • Date: 2013-02-23 01:22:51 UTC
  • Revision ID: package-import@ubuntu.com-20130223012251-3qkk1n1p93kb3j87
Tags: upstream-0.3.0+dfsg1
ImportĀ upstreamĀ versionĀ 0.3.0+dfsg1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Websock: high-performance binary WebSockets
 
3
 * Copyright (C) 2012 Joel Martin
 
4
 * Licensed under MPL 2.0 (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
/*jslint browser: true, bitwise: false, plusplus: false */
 
18
/*global Util, Base64 */
 
19
 
 
20
 
 
21
// Load Flash WebSocket emulator if needed
 
22
 
 
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;
 
27
 
 
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;
 
33
} else {
 
34
    /* no builtin WebSocket so load web_socket.js */
 
35
 
 
36
    Websock_native = false;
 
37
    (function () {
 
38
        window.WEB_SOCKET_SWF_LOCATION = Util.get_include_uri() +
 
39
                    "web-socket-js/WebSocketMain.swf";
 
40
        if (Util.Engine.trident) {
 
41
            Util.Debug("Forcing uncached load of WebSocketMain.swf");
 
42
            window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
 
43
        }
 
44
        Util.load_scripts(["web-socket-js/swfobject.js",
 
45
                           "web-socket-js/web_socket.js"]);
 
46
    }());
 
47
}
 
48
 
 
49
 
 
50
function Websock() {
 
51
"use strict";
 
52
 
 
53
var api = {},         // Public API
 
54
    websocket = null, // WebSocket object
 
55
    mode = 'base64',  // Current WebSocket mode: 'binary', 'base64'
 
56
    rQ = [],          // Receive queue
 
57
    rQi = 0,          // Receive queue index
 
58
    rQmax = 10000,    // Max receive queue size before compacting
 
59
    sQ = [],          // Send queue
 
60
 
 
61
    eventHandlers = {
 
62
        'message' : function() {},
 
63
        'open'    : function() {},
 
64
        'close'   : function() {},
 
65
        'error'   : function() {}
 
66
    },
 
67
 
 
68
    test_mode = false;
 
69
 
 
70
 
 
71
//
 
72
// Queue public functions
 
73
//
 
74
 
 
75
function get_sQ() {
 
76
    return sQ;
 
77
}
 
78
 
 
79
function get_rQ() {
 
80
    return rQ;
 
81
}
 
82
function get_rQi() {
 
83
    return rQi;
 
84
}
 
85
function set_rQi(val) {
 
86
    rQi = val;
 
87
}
 
88
 
 
89
function rQlen() {
 
90
    return rQ.length - rQi;
 
91
}
 
92
 
 
93
function rQpeek8() {
 
94
    return (rQ[rQi]      );
 
95
}
 
96
function rQshift8() {
 
97
    return (rQ[rQi++]      );
 
98
}
 
99
function rQunshift8(num) {
 
100
    if (rQi === 0) {
 
101
        rQ.unshift(num);
 
102
    } else {
 
103
        rQi -= 1;
 
104
        rQ[rQi] = num;
 
105
    }
 
106
 
 
107
}
 
108
function rQshift16() {
 
109
    return (rQ[rQi++] <<  8) +
 
110
           (rQ[rQi++]      );
 
111
}
 
112
function rQshift32() {
 
113
    return (rQ[rQi++] << 24) +
 
114
           (rQ[rQi++] << 16) +
 
115
           (rQ[rQi++] <<  8) +
 
116
           (rQ[rQi++]      );
 
117
}
 
118
function rQshiftStr(len) {
 
119
    if (typeof(len) === 'undefined') { len = rQlen(); }
 
120
    var arr = rQ.slice(rQi, rQi + len);
 
121
    rQi += len;
 
122
    return String.fromCharCode.apply(null, arr);
 
123
}
 
124
function rQshiftBytes(len) {
 
125
    if (typeof(len) === 'undefined') { len = rQlen(); }
 
126
    rQi += len;
 
127
    return rQ.slice(rQi-len, rQi);
 
128
}
 
129
 
 
130
function rQslice(start, end) {
 
131
    if (end) {
 
132
        return rQ.slice(rQi + start, rQi + end);
 
133
    } else {
 
134
        return rQ.slice(rQi + start);
 
135
    }
 
136
}
 
137
 
 
138
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
 
139
// to be available in the receive queue. Return true if we need to
 
140
// wait (and possibly print a debug message), otherwise false.
 
141
function rQwait(msg, num, goback) {
 
142
    var rQlen = rQ.length - rQi; // Skip rQlen() function call
 
143
    if (rQlen < num) {
 
144
        if (goback) {
 
145
            if (rQi < goback) {
 
146
                throw("rQwait cannot backup " + goback + " bytes");
 
147
            }
 
148
            rQi -= goback;
 
149
        }
 
150
        //Util.Debug("   waiting for " + (num-rQlen) +
 
151
        //           " " + msg + " byte(s)");
 
152
        return true;  // true means need more data
 
153
    }
 
154
    return false;
 
155
}
 
156
 
 
157
//
 
158
// Private utility routines
 
159
//
 
160
 
 
161
function encode_message() {
 
162
    if (mode === 'binary') {
 
163
        // Put in a binary arraybuffer
 
164
        return (new Uint8Array(sQ)).buffer;
 
165
    } else {
 
166
        // base64 encode
 
167
        return Base64.encode(sQ);
 
168
    }
 
169
}
 
170
 
 
171
function decode_message(data) {
 
172
    //Util.Debug(">> decode_message: " + data);
 
173
    if (mode === 'binary') {
 
174
        // push arraybuffer values onto the end
 
175
        var u8 = new Uint8Array(data);
 
176
        for (var i = 0; i < u8.length; i++) {
 
177
            rQ.push(u8[i]);
 
178
        }
 
179
    } else {
 
180
        // base64 decode and concat to the end
 
181
        rQ = rQ.concat(Base64.decode(data, 0));
 
182
    }
 
183
    //Util.Debug(">> decode_message, rQ: " + rQ);
 
184
}
 
185
 
 
186
 
 
187
//
 
188
// Public Send functions
 
189
//
 
190
 
 
191
function flush() {
 
192
    if (websocket.bufferedAmount !== 0) {
 
193
        Util.Debug("bufferedAmount: " + websocket.bufferedAmount);
 
194
    }
 
195
    if (websocket.bufferedAmount < api.maxBufferedAmount) {
 
196
        //Util.Debug("arr: " + arr);
 
197
        //Util.Debug("sQ: " + sQ);
 
198
        if (sQ.length > 0) {
 
199
            websocket.send(encode_message(sQ));
 
200
            sQ = [];
 
201
        }
 
202
        return true;
 
203
    } else {
 
204
        Util.Info("Delaying send, bufferedAmount: " +
 
205
                websocket.bufferedAmount);
 
206
        return false;
 
207
    }
 
208
}
 
209
 
 
210
// overridable for testing
 
211
function send(arr) {
 
212
    //Util.Debug(">> send_array: " + arr);
 
213
    sQ = sQ.concat(arr);
 
214
    return flush();
 
215
}
 
216
 
 
217
function send_string(str) {
 
218
    //Util.Debug(">> send_string: " + str);
 
219
    api.send(str.split('').map(
 
220
        function (chr) { return chr.charCodeAt(0); } ) );
 
221
}
 
222
 
 
223
//
 
224
// Other public functions
 
225
 
 
226
function recv_message(e) {
 
227
    //Util.Debug(">> recv_message: " + e.data.length);
 
228
 
 
229
    try {
 
230
        decode_message(e.data);
 
231
        if (rQlen() > 0) {
 
232
            eventHandlers.message();
 
233
            // Compact the receive queue
 
234
            if (rQ.length > rQmax) {
 
235
                //Util.Debug("Compacting receive queue");
 
236
                rQ = rQ.slice(rQi);
 
237
                rQi = 0;
 
238
            }
 
239
        } else {
 
240
            Util.Debug("Ignoring empty message");
 
241
        }
 
242
    } catch (exc) {
 
243
        if (typeof exc.stack !== 'undefined') {
 
244
            Util.Warn("recv_message, caught exception: " + exc.stack);
 
245
        } else if (typeof exc.description !== 'undefined') {
 
246
            Util.Warn("recv_message, caught exception: " + exc.description);
 
247
        } else {
 
248
            Util.Warn("recv_message, caught exception:" + exc);
 
249
        }
 
250
        if (typeof exc.name !== 'undefined') {
 
251
            eventHandlers.error(exc.name + ": " + exc.message);
 
252
        } else {
 
253
            eventHandlers.error(exc);
 
254
        }
 
255
    }
 
256
    //Util.Debug("<< recv_message");
 
257
}
 
258
 
 
259
 
 
260
// Set event handlers
 
261
function on(evt, handler) { 
 
262
    eventHandlers[evt] = handler;
 
263
}
 
264
 
 
265
function init(protocols) {
 
266
    rQ         = [];
 
267
    rQi        = 0;
 
268
    sQ         = [];
 
269
    websocket  = null;
 
270
 
 
271
    var bt = false,
 
272
        wsbt = false,
 
273
        try_binary = false;
 
274
 
 
275
    // Check for full typed array support
 
276
    if (('Uint8Array' in window) &&
 
277
        ('set' in Uint8Array.prototype)) {
 
278
        bt = true;
 
279
    }
 
280
 
 
281
    // Check for full binary type support in WebSockets
 
282
    // TODO: this sucks, the property should exist on the prototype
 
283
    // but it does not.
 
284
    try {
 
285
        if (bt && ('binaryType' in (new WebSocket("ws://localhost:17523")))) {
 
286
            Util.Info("Detected binaryType support in WebSockets");
 
287
            wsbt = true;
 
288
        }
 
289
    } catch (exc) {
 
290
        // Just ignore failed test localhost connections
 
291
    }
 
292
 
 
293
    // Default protocols if not specified
 
294
    if (typeof(protocols) === "undefined") {
 
295
        if (wsbt) {
 
296
            protocols = ['binary', 'base64'];
 
297
        } else {
 
298
            protocols = 'base64';
 
299
        }
 
300
    }
 
301
 
 
302
    // If no binary support, make sure it was not requested
 
303
    if (!wsbt) {
 
304
        if (protocols === 'binary') {
 
305
            throw("WebSocket binary sub-protocol requested but not supported");
 
306
        }
 
307
        if (typeof(protocols) === "object") {
 
308
            var new_protocols = [];
 
309
            for (var i = 0; i < protocols.length; i++) {
 
310
                if (protocols[i] === 'binary') {
 
311
                    Util.Error("Skipping unsupported WebSocket binary sub-protocol");
 
312
                } else {
 
313
                    new_protocols.push(protocols[i]);
 
314
                }
 
315
            }
 
316
            if (new_protocols.length > 0) {
 
317
                protocols = new_protocols;
 
318
            } else {
 
319
                throw("Only WebSocket binary sub-protocol was requested and not supported.");
 
320
            }
 
321
        }
 
322
    }
 
323
 
 
324
    return protocols;
 
325
}
 
326
 
 
327
function open(uri, protocols) {
 
328
    protocols = init(protocols);
 
329
 
 
330
    if (test_mode) {
 
331
        websocket = {};
 
332
    } else {
 
333
        websocket = new WebSocket(uri, protocols);
 
334
        if (protocols.indexOf('binary') >= 0) {
 
335
            websocket.binaryType = 'arraybuffer';
 
336
        }
 
337
    }
 
338
 
 
339
    websocket.onmessage = recv_message;
 
340
    websocket.onopen = function() {
 
341
        Util.Debug(">> WebSock.onopen");
 
342
        if (websocket.protocol) {
 
343
            mode = websocket.protocol;
 
344
            Util.Info("Server chose sub-protocol: " + websocket.protocol);
 
345
        } else {
 
346
            mode = 'base64';
 
347
            Util.Error("Server select no sub-protocol!: " + websocket.protocol);
 
348
        }
 
349
        eventHandlers.open();
 
350
        Util.Debug("<< WebSock.onopen");
 
351
    };
 
352
    websocket.onclose = function(e) {
 
353
        Util.Debug(">> WebSock.onclose");
 
354
        eventHandlers.close(e);
 
355
        Util.Debug("<< WebSock.onclose");
 
356
    };
 
357
    websocket.onerror = function(e) {
 
358
        Util.Debug(">> WebSock.onerror: " + e);
 
359
        eventHandlers.error(e);
 
360
        Util.Debug("<< WebSock.onerror");
 
361
    };
 
362
}
 
363
 
 
364
function close() {
 
365
    if (websocket) {
 
366
        if ((websocket.readyState === WebSocket.OPEN) ||
 
367
            (websocket.readyState === WebSocket.CONNECTING)) {
 
368
            Util.Info("Closing WebSocket connection");
 
369
            websocket.close();
 
370
        }
 
371
        websocket.onmessage = function (e) { return; };
 
372
    }
 
373
}
 
374
 
 
375
// Override internal functions for testing
 
376
// Takes a send function, returns reference to recv function
 
377
function testMode(override_send, data_mode) {
 
378
    test_mode = true;
 
379
    mode = data_mode;
 
380
    api.send = override_send;
 
381
    api.close = function () {};
 
382
    return recv_message;
 
383
}
 
384
 
 
385
function constructor() {
 
386
    // Configuration settings
 
387
    api.maxBufferedAmount = 200;
 
388
 
 
389
    // Direct access to send and receive queues
 
390
    api.get_sQ       = get_sQ;
 
391
    api.get_rQ       = get_rQ;
 
392
    api.get_rQi      = get_rQi;
 
393
    api.set_rQi      = set_rQi;
 
394
 
 
395
    // Routines to read from the receive queue
 
396
    api.rQlen        = rQlen;
 
397
    api.rQpeek8      = rQpeek8;
 
398
    api.rQshift8     = rQshift8;
 
399
    api.rQunshift8   = rQunshift8;
 
400
    api.rQshift16    = rQshift16;
 
401
    api.rQshift32    = rQshift32;
 
402
    api.rQshiftStr   = rQshiftStr;
 
403
    api.rQshiftBytes = rQshiftBytes;
 
404
    api.rQslice      = rQslice;
 
405
    api.rQwait       = rQwait;
 
406
 
 
407
    api.flush        = flush;
 
408
    api.send         = send;
 
409
    api.send_string  = send_string;
 
410
 
 
411
    api.on           = on;
 
412
    api.init         = init;
 
413
    api.open         = open;
 
414
    api.close        = close;
 
415
    api.testMode     = testMode;
 
416
 
 
417
    return api;
 
418
}
 
419
 
 
420
return constructor();
 
421
 
 
422
}