LP#1268619: OpenSRF JS websockets plugin
authorBill Erickson <berick@esilibrary.com>
Thu, 20 Sep 2012 19:55:04 +0000 (15:55 -0400)
committerGalen Charlton <gmc@esilibrary.com>
Tue, 19 Aug 2014 22:34:59 +0000 (15:34 -0700)
Adds an opensrf_ws.js handler.  Requries some small modifications to
opensrf.js.  Load opensrf_ws.js in DojoSRF.

For now, to use, add to script:

OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_WS;

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Signed-off-by: Galen Charlton <gmc@esilibrary.com>
src/javascript/DojoSRF.js
src/javascript/opensrf.js
src/javascript/opensrf_ws.js [new file with mode: 0644]

index 42578f8..7e3e5c8 100644 (file)
@@ -10,6 +10,7 @@ if(!dojo._hasResource['DojoSRF']){
     dojo.require('opensrf.JSON_v1', true);
     dojo.require('opensrf.opensrf', true);
     dojo.require('opensrf.opensrf_xhr', true);
+    dojo.require('opensrf.opensrf_ws', true);
 
     OpenSRF.session_cache = {};
     OpenSRF.CachedClientSession = function ( app ) {
index c0e454c..408f0af 100644 (file)
@@ -21,6 +21,7 @@ var OSRF_APP_SESSION_DISCONNECTED = 2;
 /* types of transport layers */
 var OSRF_TRANSPORT_TYPE_XHR = 1;
 var OSRF_TRANSPORT_TYPE_XMPP = 2;
+var OSRF_TRANSPORT_TYPE_WS = 3;
 
 /* message types */
 var OSRF_MESSAGE_TYPE_REQUEST = 'REQUEST';
@@ -205,7 +206,10 @@ OpenSRF.Session = function() {
     this.state = OSRF_APP_SESSION_DISCONNECTED;
 };
 
-OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_XHR; /* default to XHR */
+OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_WS;
+if (true || typeof WebSocket == 'undefined')
+    OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_XHR;
+
 OpenSRF.Session.cache = {};
 OpenSRF.Session.find_session = function(thread_trace) {
     return OpenSRF.Session.cache[thread_trace];
@@ -217,6 +221,8 @@ OpenSRF.Session.prototype.cleanup = function() {
 OpenSRF.Session.prototype.send = function(osrf_msg, args) {
     args = (args) ? args : {};
     switch(OpenSRF.Session.transport) {
+        case OSRF_TRANSPORT_TYPE_WS:
+            return this.send_ws(osrf_msg, args);
         case OSRF_TRANSPORT_TYPE_XHR:
             return this.send_xhr(osrf_msg, args);
         case OSRF_TRANSPORT_TYPE_XMPP:
@@ -231,8 +237,22 @@ OpenSRF.Session.prototype.send_xhr = function(osrf_msg, args) {
     new OpenSRF.XHRequest(osrf_msg, args).send();
 };
 
+OpenSRF.Session.prototype.send_ws = function(osrf_msg, args) {
+    args.session = this;
+    if (this.websocket) {
+        this.websocket.args = args; // callbacks
+        this.websocket.send(osrf_msg);
+    } else {
+        this.websocket = new OpenSRF.WSRequest(
+            this, args, function(wsreq) {
+                wsreq.send(osrf_msg);
+            }
+        );
+    }
+};
+
 OpenSRF.Session.prototype.send_xmpp = function(osrf_msg, args) {
-    alert('xmpp transport not yet implemented');
+    alert('xmpp transport not implemented');
 };
 
 
@@ -254,15 +274,21 @@ OpenSRF.ClientSession.prototype.connect = function(args) {
     args = (args) ? args : {};
     this.remote_id = null;
 
-    if(args.onconnect)
+    if (this.state == OSRF_APP_SESSION_CONNECTED) {
+        if (args.onconnect) args.onconnect();
+        return true;
+    }
+
+    if(args.onconnect) {
         this.onconnect = args.onconnect;
 
-    /* if no handler is provided, make this a synchronous call */
-    if(!this.onconnect) 
+    } else {
+        /* if no handler is provided, make this a synchronous call */
         this.timeout = (args.timeout) ? args.timeout : 5;
+    }
 
     message = new osrfMessage({
-        'threadTrace' : this.reqid
+        'threadTrace' : this.last_id++
         'type' : OSRF_MESSAGE_TYPE_CONNECT
     });
 
@@ -270,17 +296,28 @@ OpenSRF.ClientSession.prototype.connect = function(args) {
 
     if(this.onconnect || this.state == OSRF_APP_SESSION_CONNECTED)
         return true;
+
     return false;
 };
 
 OpenSRF.ClientSession.prototype.disconnect = function(args) {
-    this.send(
-        new osrfMessage({
-            'threadTrace' : this.reqid, 
-            'type' : OSRF_MESSAGE_TYPE_DISCONNECT
-        })
-    );
+
+    if (this.state == OSRF_APP_SESSION_CONNECTED) {
+        this.send(
+            new osrfMessage({
+                'threadTrace' : this.last_id++,
+                'type' : OSRF_MESSAGE_TYPE_DISCONNECT
+            })
+        );
+    }
+
     this.remote_id = null;
+    this.state = OSRF_APP_SESSION_DISCONNECTED;
+
+    if (this.websocket) {
+        this.websocket.close();
+        delete this.websocket;
+    }
 };
 
 
@@ -397,9 +434,10 @@ function log(msg) {
     }
 }
 
-OpenSRF.Stack.push = function(net_msg, callbacks) {
-    var ses = OpenSRF.Session.find_session(net_msg.thread); 
-    if(!ses) return;
+// ses may be passed to us by the network handler
+OpenSRF.Stack.push = function(net_msg, callbacks, ses) {
+    if (!ses) ses = OpenSRF.Session.find_session(net_msg.thread); 
+    if (!ses) return;
     ses.remote_id = net_msg.from;
     osrf_msgs = [];
 
diff --git a/src/javascript/opensrf_ws.js b/src/javascript/opensrf_ws.js
new file mode 100644 (file)
index 0000000..d522834
--- /dev/null
@@ -0,0 +1,104 @@
+/* -----------------------------------------------------------------------
+ * Copyright (C) 2012  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 WS_PATH = '/osrf-websocket';
+
+/**
+ * onopen is required. no data can be sent 
+ * until the async connection dance completes.
+ */
+OpenSRF.WSRequest = function(session, args, onopen) {
+    this.session = session;
+    this.args = args;
+
+    var proto = location.protocol == 'https' ? 'wss' : 'ws';
+
+    var path = proto + '://' + location.host + 
+        WS_PATH + '?service=' + this.session.service;
+
+    try {
+        this.ws = new WebSocket(path);
+    } catch(e) {
+        throw new Error("WebSocket() not supported in this browser " + e);
+    }
+
+    var self = this;
+
+    this.ws.onopen = function(evt) {
+        onopen(self);
+    }
+
+    this.ws.onmessage = function(evt) {
+        self.core_handler(evt.data);
+    }
+
+    this.ws.onerror = function(evt) {
+        self.transport_error_handler(evt.data);
+    }
+
+    this.ws.onclose = function(evt) {
+    }
+};
+
+OpenSRF.WSRequest.prototype.send = function(message) {
+    //console.log('sending: ' + js2JSON([message.serialize()]));
+    this.last_message = message;
+    this.ws.send(js2JSON([message.serialize()]));
+    return this;
+};
+
+OpenSRF.WSRequest.prototype.close = function() {
+    try { this.ws.close(); } catch(e) {}
+}
+
+OpenSRF.WSRequest.prototype.core_handler = function(json) {
+    //console.log('received: ' + json);
+
+    OpenSRF.Stack.push(
+        new OpenSRF.NetMessage(null, null, '', json),
+        {
+            onresponse : this.args.onresponse,
+            oncomplete : this.args.oncomplete,
+            onerror : this.args.onerror,
+            onmethoderror : this.method_error_handler()
+        },
+        this.args.session
+    );
+};
+
+
+OpenSRF.WSRequest.prototype.method_error_handler = function() {
+    var self = this;
+    return function(req, status, status_text) {
+        if(self.args.onmethoderror) 
+            self.args.onmethoderror(req, status, status_text);
+
+        if(self.args.onerror)  {
+            self.args.onerror(
+                self.last_message, self.session.service, '');
+        }
+    };
+};
+
+OpenSRF.WSRequest.prototype.transport_error_handler = function(msg) {
+    if(this.args.ontransporterror) {
+        this.args.ontransporterror(msg);
+    }
+    if(this.args.onerror) {
+        this.args.onerror(msg, this.session.service, '');
+    }
+};
+
+