From 0cf0a8a5fe61228995f75d0f0c6e4f4731888c40 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Mon, 7 Apr 2014 12:04:33 -0400 Subject: [PATCH] LP#1268619: websockets: auto-upgrade to shared workers; SSL-always Signed-off-by: Bill Erickson Signed-off-by: Galen Charlton --- src/javascript/opensrf.js | 46 ++++++++++++++++-- src/javascript/opensrf_ws.js | 95 +++++++++++++++++++++++++++++++++++++ src/javascript/opensrf_ws_shared.js | 8 ++-- 3 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 src/javascript/opensrf_ws.js diff --git a/src/javascript/opensrf.js b/src/javascript/opensrf.js index db6e49d..997b33f 100644 --- a/src/javascript/opensrf.js +++ b/src/javascript/opensrf.js @@ -249,13 +249,51 @@ OpenSRF.Session.prototype.send_xhr = function(osrf_msg, args) { }; OpenSRF.Session.prototype.send_ws = function(osrf_msg) { - new OpenSRF.WebSocketRequest( - this, - function(wsreq) {wsreq.send(osrf_msg)} // onopen - ); + + if (typeof SharedWorker == 'function') { + // vanilla websockets requested, but this browser supports + // shared workers, so use those instead. + return this.send_ws_shared(osrf_msg); + } + + // otherwise, use a per-tab connection + + if (!OpenSRF.websocketConnection) { + this.setup_single_ws(); + } + + var json = js2JSON({ + service : this.service, + thread : this.thread, + osrf_msg : [message.serialize()] + }); + + OpenSRF.websocketConnection.send(json); }; +OpenSRF.Session.prototype.setup_single_ws = function() { + OpenSRF.websocketConnection = new OpenSRF.WebSocket(); + + OpenSRF.websocketConnection.onmessage = function(msg) { + try { + var msg = JSON2js(msg); + } catch(E) { + console.error( + "Error parsing JSON in shared WS response: " + msg); + throw E; + } + OpenSRF.Stack.push( + new OpenSRF.NetMessage( + null, null, msg.thread, null, msg.osrf_msg) + ); + + return; + } +} + OpenSRF.Session.setup_shared_ws = function() { + OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_WS_SHARED; + OpenSRF.sharedWSWorker = new SharedWorker(SHARED_WORKER_LIB); OpenSRF.sharedWSWorker.port.addEventListener('message', function(e) { diff --git a/src/javascript/opensrf_ws.js b/src/javascript/opensrf_ws.js new file mode 100644 index 0000000..352bd2a --- /dev/null +++ b/src/javascript/opensrf_ws.js @@ -0,0 +1,95 @@ +/* ----------------------------------------------------------------------- + * Copyright (C) 2014 Equinox Software, Inc. + * Bill Erickson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * ----------------------------------------------------------------------- */ + + +var WEBSOCKET_URL_PATH = '/osrf-websocket-translator'; +var WEBSOCKET_PORT_SSL = 7682; + +OpenSRF.WebSocket = function() { + this.pending_messages = []; +} + +/** + * If our global socket is already open, use it. Otherwise, queue the + * message for delivery after the socket is open. + */ +OpenSRF.WebSocket.prototype.send = function(message) { + var self = this; + + if (this.socket && this.socket.readyState == this.socket.OPEN) { + // this.socket connection is viable. send our message now. + this.socket.send(message); + return; + } + + // no viable connection. queue our outbound messages for future delivery. + this.pending_messages.push(message); + console.log('pending count ' + this.pending_messages.length); + + if (this.socket && this.socket.readyState == this.socket.CONNECTING) { + // we are already in the middle of a setup call. + // our queued message will be delivered after setup completes. + return; + } + + // we have no websocket or an invalid websocket. build a new one. + + var path = 'wss://' + location.host + ':' + + WEBSOCKET_PORT_SSL + WEBSOCKET_URL_PATH; + + console.debug('connecting websocket to ' + path); + + try { + this.socket = new WebSocket(path); + } catch(E) { + console.log('Error creating WebSocket for path ' + path + ' : ' + E); + throw new Error(E); + } + + this.socket.onopen = function() { + console.debug('websocket.onopen()'); + // deliver any queued messages + var msg; + console.log('pending count ' + self.pending_messages.length); + while ( (msg = self.pending_messages.shift()) ) + self.socket.send(msg); + } + + this.socket.onmessage = function(evt) { + self.onmessage(evt.data); + } + + /** + * Websocket error handler. This type of error indicates a probelem + * with the connection. I.e. it's not port-specific. + * Broadcast to all ports. + */ + this.socket.onerror = function(evt) { + var err = "WebSocket Error " + evt + ' : ' + evt.data; + self.socket.close(); // connection is no good; reset. + throw new Error(err); + } + + /** + * Called when the websocket connection is closed. + * + * Once a websocket is closed, it will be re-opened the next time + * a message delivery attempt is made. Clean up and prepare to reconnect. + */ + this.socket.onclose = function() { + console.debug('closing websocket'); + self.socket = null; + } +} diff --git a/src/javascript/opensrf_ws_shared.js b/src/javascript/opensrf_ws_shared.js index 4b90dc8..ff0b586 100644 --- a/src/javascript/opensrf_ws_shared.js +++ b/src/javascript/opensrf_ws_shared.js @@ -25,7 +25,6 @@ */ var WEBSOCKET_URL_PATH = '/osrf-websocket-translator'; -var WEBSOCKET_PORT = 7680; // TODO: remove. all traffic should use SSL. var WEBSOCKET_PORT_SSL = 7682; var WEBSOCKET_MAX_THREAD_PORT_CACHE_SIZE = 1000; @@ -111,7 +110,8 @@ function send_to_websocket(message) { // assume non-SSL for now. SSL silently dies if the cert is // invalid and has not been added as an exception. need to // explain / document / avoid this better. - var path = 'ws://' + location.host + ':' + WEBSOCKET_PORT + WEBSOCKET_URL_PATH; + var path = 'wss://' + location.host + ':' + + WEBSOCKET_PORT_SSL + WEBSOCKET_URL_PATH; console.debug('connecting websocket to ' + path); @@ -187,10 +187,10 @@ function send_to_websocket(message) { * Broadcast to all ports. */ websocket.onerror = function(evt) { - var err = "WebSocket Error " + evt + ' : ' + evt.data; + var err = "WebSocket Error " + evt; + console.error(err); broadcast({action : 'error', message : err}); websocket.close(); // connection is no good; reset. - throw new Error(err); } /** -- 2.11.0