LP#1268619: websocket gateway: improved memory mgt; logging
[OpenSRF.git] / src / javascript / opensrf_ws.js
1 /* -----------------------------------------------------------------------
2  * Copyright (C) 2012  Equinox Software, Inc.
3  * Bill Erickson <berick@esilibrary.com>
4  *  
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * ----------------------------------------------------------------------- */
15
16 // opensrf defaults
17 var WEBSOCKET_URL_PATH = '/osrf-websocket-translator';
18 var WEBSOCKET_PORT = 7680;
19 var WEBSOCKET_PORT_SSL = 7682;
20
21
22 // Create the websocket and connect to the server
23 // args.onopen is required
24 // if args.default is true, use the default connection
25 OpenSRF.WebSocketConnection = function(args, handlers) {
26     args = args || {};
27     this.handlers = handlers;
28
29     var secure = (args.ssl || location.protocol == 'https');
30     var path = args.path || WEBSOCKET_URL_PATH;
31     var port = args.port || (secure ? WEBSOCKET_PORT_SSL : WEBSOCKET_PORT);
32     var host = args.host || location.host;
33     var proto = (secure) ? 'wss' : 'ws';
34     this.path = proto + '://' + host + ':' + port + path;
35
36     this.setupSocket();
37     OpenSRF.WebSocketConnection.pool[args.name] = this;
38 };
39
40 // global pool of connection objects; name => connection map
41 OpenSRF.WebSocketConnection.pool = {};
42
43 OpenSRF.WebSocketConnection.defaultConnection = function() {
44     return OpenSRF.WebSocketConnection.pool['default'];
45 }
46
47 /**
48  * create a new WebSocket.  useful for new connections or 
49  * applying a new socket to an existing connection (whose 
50  * socket was disconnected)
51  */
52 OpenSRF.WebSocketConnection.prototype.setupSocket = function() {
53
54     try {
55         this.socket = new WebSocket(this.path);
56     } catch(e) {
57         throw new Error("WebSocket() not supported in this browser: " + e);
58     }
59
60     this.socket.onopen = this.handlers.onopen;
61     this.socket.onmessage = this.handlers.onmessage;
62     this.socket.onerror = this.handlers.onerror;
63     this.socket.onclose = this.handlers.onclose;
64 };
65
66 /** default onmessage handler: push the message up the opensrf stack */
67 OpenSRF.WebSocketConnection.default_onmessage = function(evt) {
68     //console.log('receiving: ' + evt.data);
69     var msg = JSON2js(evt.data);
70     OpenSRF.Stack.push(
71         new OpenSRF.NetMessage(
72             null, null, msg.thread, null, msg.osrf_msg)
73     );
74 };
75
76 /** default error handler */
77 OpenSRF.WebSocketConnection.default_onerror = function(evt) {
78     throw new Error("WebSocket Error " + evt + ' : ' + evt.data);
79 };
80
81
82 /** shut it down */
83 OpenSRF.WebSocketConnection.prototype.destroy = function() {
84     this.socket.close();
85     delete OpenSRF.WebSocketConnection.pool[this.name];
86 };
87
88 /**
89  * Creates the request object, but does not connect or send anything
90  * until the first call to send().
91  */
92 OpenSRF.WebSocketRequest = function(session, onopen, connectionArgs) {
93     this.session = session;
94     this.onopen = onopen;
95     this.setupConnection(connectionArgs || {});
96 }
97
98 OpenSRF.WebSocketRequest.prototype.setupConnection = function(args) {
99     var self = this;
100
101     var cname = args.name || 'default';
102     this.wsc = OpenSRF.WebSocketConnection.pool[cname];
103
104     if (this.wsc) { // we have a WebSocketConnection.  
105
106         switch (this.wsc.socket.readyState) {
107
108             case this.wsc.socket.CONNECTING:
109                 // replace the original onopen handler with a new combined handler
110                 var orig_open = this.wsc.socket.onopen;
111                 this.wsc.socket.onopen = function() {
112                     orig_open();
113                     self.onopen(self);
114                 };
115                 break;
116
117             case this.wsc.socket.OPEN:
118                 // user is expecting an onopen event.  socket is 
119                 // already open, so we have to manufacture one.
120                 this.onopen(this);
121                 break;
122
123             default:
124                 console.log('WebSocket is no longer connecting; reconnecting');
125                 this.wsc.setupSocket();
126         }
127
128     } else { // no connection found
129
130         if (cname == 'default' || args.useDefaultHandlers) { // create the default handle 
131
132             this.wsc = new OpenSRF.WebSocketConnection(
133                 {name : cname}, {
134                     onopen : function(evt) {if (self.onopen) self.onopen(self)},
135                     onmessage : OpenSRF.WebSocketConnection.default_onmessage,
136                     onerror : OpenSRF.WebSocketRequest.default_onerror,
137                     onclose : OpenSRF.WebSocketRequest.default_onclose
138                 } 
139             );
140
141         } else {
142             throw new Error("No such WebSocketConnection '" + cname + "'");
143         }
144     }
145 }
146
147
148 OpenSRF.WebSocketRequest.prototype.send = function(message) {
149     var wrapper = {
150         service : this.session.service,
151         thread : this.session.thread,
152         osrf_msg : [message.serialize()]
153     };
154
155     var json = js2JSON(wrapper);
156     //console.log('sending: ' + json);
157
158     // drop it on the wire
159     this.wsc.socket.send(json);
160     return this;
161 };
162
163
164
165