This commit provides the javascript complement to [WebSocket-Resources](https://github.com/WhisperSystems/WebSocket-Resources), allowing us to use a bi-directional request-response framework over websockets. See websocket-resources.js and websocket-resources_test.js for usage details. Along the way I also factored the websocket keepalive and reconnect logic into its own file/wrapper object.
		
			
				
	
	
		
			134 lines
		
	
	
	
		
			4.9 KiB
			
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			134 lines
		
	
	
	
		
			4.9 KiB
			
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* vim: ts=4:sw=4:expandtab
 | 
						|
 *
 | 
						|
 * This program is free software: you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU Lesser General Public License as published by
 | 
						|
 * the Free Software Foundation, either version 3 of the License, or
 | 
						|
 * (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU Lesser General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Lesser General Public License
 | 
						|
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
;(function(){
 | 
						|
    'use strict';
 | 
						|
 | 
						|
    /*
 | 
						|
     * WebSocket-Resources
 | 
						|
     *
 | 
						|
     * Create a request-response interface over websockets using the
 | 
						|
     * WebSocket-Resources sub-protocol[1].
 | 
						|
     *
 | 
						|
     * var client = new WebSocketResource(socket, function(request) {
 | 
						|
     *    request.respond(200, 'OK');
 | 
						|
     * });
 | 
						|
     *
 | 
						|
     * client.sendRequest({
 | 
						|
     *    verb: 'PUT',
 | 
						|
     *    path: '/v1/messages',
 | 
						|
     *    body: '{ some: "json" }',
 | 
						|
     *    success: function(message, status, request) {...},
 | 
						|
     *    error: function(message, status, request) {...}
 | 
						|
     * });
 | 
						|
     *
 | 
						|
     * 1. https://github.com/WhisperSystems/WebSocket-Resources
 | 
						|
     *
 | 
						|
     */
 | 
						|
 | 
						|
    var Request = function(options) {
 | 
						|
        this.verb    = options.verb || options.type;
 | 
						|
        this.path    = options.path || options.url;
 | 
						|
        this.body    = options.body || options.data;
 | 
						|
        this.success = options.success
 | 
						|
        this.error   = options.error
 | 
						|
        this.id      = options.id;
 | 
						|
 | 
						|
        if (this.id === undefined) {
 | 
						|
            var bits = new Uint32Array(2);
 | 
						|
            window.crypto.getRandomValues(bits);
 | 
						|
            this.id = dcodeIO.Long.fromBits(bits[0], bits[1], true);
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
    var IncomingWebSocketRequest = function(options) {
 | 
						|
        var request = new Request(options);
 | 
						|
        var socket = options.socket;
 | 
						|
 | 
						|
        this.verb = request.verb;
 | 
						|
        this.path = request.path;
 | 
						|
        this.body = request.body;
 | 
						|
 | 
						|
        this.respond = function(status, message) {
 | 
						|
            socket.send(
 | 
						|
                new textsecure.protobuf.WebSocketMessage({
 | 
						|
                    type: textsecure.protobuf.WebSocketMessage.Type.RESPONSE,
 | 
						|
                    response: { id: request.id, message: message, status: status }
 | 
						|
                }).encode().toArrayBuffer()
 | 
						|
            );
 | 
						|
        };
 | 
						|
    };
 | 
						|
 | 
						|
    var outgoing = {};
 | 
						|
    var OutgoingWebSocketRequest = function(options, socket) {
 | 
						|
        var request = new Request(options);
 | 
						|
        outgoing[request.id] = request;
 | 
						|
        socket.send(
 | 
						|
            new textsecure.protobuf.WebSocketMessage({
 | 
						|
                type: textsecure.protobuf.WebSocketMessage.Type.REQUEST,
 | 
						|
                request: {
 | 
						|
                    verb : request.verb,
 | 
						|
                    path : request.path,
 | 
						|
                    body : request.body,
 | 
						|
                    id   : request.id
 | 
						|
                }
 | 
						|
            }).encode().toArrayBuffer()
 | 
						|
        );
 | 
						|
    };
 | 
						|
 | 
						|
    window.WebSocketResource = function(socket, handleRequest) {
 | 
						|
        this.sendRequest = function(options) {
 | 
						|
            return new OutgoingWebSocketRequest(options, socket);
 | 
						|
        };
 | 
						|
 | 
						|
        socket.onmessage = function(socketMessage) {
 | 
						|
            var blob = socketMessage.data;
 | 
						|
            var reader = new FileReader();
 | 
						|
            reader.onload = function() {
 | 
						|
                var message = textsecure.protobuf.WebSocketMessage.decode(reader.result);
 | 
						|
                if (message.type === textsecure.protobuf.WebSocketMessage.Type.REQUEST ) {
 | 
						|
                    handleRequest(
 | 
						|
                        new IncomingWebSocketRequest({
 | 
						|
                            verb   : message.request.verb,
 | 
						|
                            path   : message.request.path,
 | 
						|
                            body   : message.request.body,
 | 
						|
                            id     : message.request.id,
 | 
						|
                            socket : socket
 | 
						|
                        })
 | 
						|
                    );
 | 
						|
                }
 | 
						|
                else if (message.type === textsecure.protobuf.WebSocketMessage.Type.RESPONSE ) {
 | 
						|
                    var response = message.response;
 | 
						|
                    var request = outgoing[response.id];
 | 
						|
                    if (request) {
 | 
						|
                        request.response = response;
 | 
						|
                        var callback = request.error;
 | 
						|
                        if (response.status >= 200 && response.status < 300) {
 | 
						|
                            callback = request.success;
 | 
						|
                        }
 | 
						|
 | 
						|
                        if (typeof callback === 'function') {
 | 
						|
                            callback(response.message, response.status, request);
 | 
						|
                        }
 | 
						|
                    } else {
 | 
						|
                        throw 'Received response for unknown request ' + message.response.id;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            };
 | 
						|
            reader.readAsArrayBuffer(blob);
 | 
						|
        };
 | 
						|
    };
 | 
						|
 | 
						|
}());
 |