LP#1268619: websockets: auto-upgrade to shared workers; SSL-always
authorBill Erickson <berick@esilibrary.com>
Mon, 7 Apr 2014 16:04:33 +0000 (12:04 -0400)
committerGalen Charlton <gmc@esilibrary.com>
Tue, 19 Aug 2014 22:54:46 +0000 (15:54 -0700)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Signed-off-by: Galen Charlton <gmc@esilibrary.com>

src/javascript/opensrf.js
src/javascript/opensrf_ws.js [new file with mode: 0644]
src/javascript/opensrf_ws_shared.js

index db6e49d..997b33f 100644 (file)
@@ -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 (file)
index 0000000..352bd2a
--- /dev/null
@@ -0,0 +1,95 @@
+/* -----------------------------------------------------------------------
+ * Copyright (C) 2014  Equinox Software, Inc.
+ * Bill Erickson <berick@esilibrary.com>
+ *  
+ * 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;
+    }
+}
index 4b90dc8..ff0b586 100644 (file)
@@ -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); 
     }
 
     /**