this.state = OSRF_APP_SESSION_DISCONNECTED;
};
-OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_WS;
-if (true || typeof WebSocket == 'undefined')
- OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_XHR;
+//OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_WS;
+OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_XHR;
OpenSRF.Session.cache = {};
OpenSRF.Session.find_session = function(thread_trace) {
args = (args) ? args : {};
switch(OpenSRF.Session.transport) {
case OSRF_TRANSPORT_TYPE_WS:
- return this.send_ws(osrf_msg, args);
+ return this.send_ws(osrf_msg);
case OSRF_TRANSPORT_TYPE_XHR:
return this.send_xhr(osrf_msg, args);
case OSRF_TRANSPORT_TYPE_XMPP:
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_ws = function(osrf_msg) {
+ new OpenSRF.WebSocketRequest(
+ this,
+ function(wsreq) {wsreq.send(osrf_msg)} // onopen
+ );
};
OpenSRF.Session.prototype.send_xmpp = function(osrf_msg, args) {
this.remote_id = null;
this.locale = OpenSRF.locale || 'en-US';
this.last_id = 0;
- this.thread = Math.random() + '' + new Date().getTime();
this.requests = [];
this.onconnect = null;
+ this.thread = Math.random() + '' + new Date().getTime();
OpenSRF.Session.cache[this.thread] = this;
};
OpenSRF.set_subclass('OpenSRF.ClientSession', 'OpenSRF.Session');
});
};
-OpenSRF.NetMessage = function(to, from, thread, body) {
+OpenSRF.NetMessage = function(to, from, thread, body, osrf_msg) {
this.to = to;
this.from = from;
this.thread = thread;
this.body = body;
+ this.osrf_msg = osrf_msg;
};
OpenSRF.Stack = function() {
}
// 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);
+OpenSRF.Stack.push = function(net_msg, callbacks) {
+ var ses = OpenSRF.Session.find_session(net_msg.thread);
if (!ses) return;
ses.remote_id = net_msg.from;
- osrf_msgs = [];
- try {
- osrf_msgs = JSON2js(net_msg.body);
+ // NetMessage's from websocket connections are parsed before they get here
+ osrf_msgs = net_msg.osrf_msg;
- } catch(E) {
- log('Error parsing OpenSRF message body as JSON: ' + net_msg.body + '\n' + E);
-
- /** UGH
- * For unknown reasons, the Content-Type header will occasionally
- * be included in the XHR.responseText for multipart/mixed messages.
- * When this happens, strip the header and newlines from the message
- * body and re-parse.
- */
- net_msg.body = net_msg.body.replace(/^.*\n\n/, '');
- log('Cleaning up and retrying...');
+ if (!osrf_msgs) {
try {
osrf_msgs = JSON2js(net_msg.body);
- } catch(E2) {
- log('Unable to clean up message, giving up: ' + net_msg.body);
- return;
+
+ if (OpenSRF.Session.transport == OSRF_TRANSPORT_TYPE_WS) {
+ // WebSocketRequests wrap the content
+ osrf_msgs = osrf_msgs.osrf_msg;
+ }
+
+ } catch(E) {
+ log('Error parsing OpenSRF message body as JSON: ' + net_msg.body + '\n' + E);
+
+ /** UGH
+ * For unknown reasons, the Content-Type header will occasionally
+ * be included in the XHR.responseText for multipart/mixed messages.
+ * When this happens, strip the header and newlines from the message
+ * body and re-parse.
+ */
+ net_msg.body = net_msg.body.replace(/^.*\n\n/, '');
+ log('Cleaning up and retrying...');
+
+ try {
+ osrf_msgs = JSON2js(net_msg.body);
+ } catch(E2) {
+ log('Unable to clean up message, giving up: ' + net_msg.body);
+ return;
+ }
}
}
// push the latest responses onto the end of the inbound message queue
for(var i = 0; i < osrf_msgs.length; i++)
- OpenSRF.Stack.queue.push({msg : osrf_msgs[i], callbacks : callbacks, ses : ses});
+ OpenSRF.Stack.queue.push({msg : osrf_msgs[i], ses : ses});
// continue processing responses, oldest to newest
while(OpenSRF.Stack.queue.length) {
var data = OpenSRF.Stack.queue.shift();
- OpenSRF.Stack.handle_message(data.ses, data.msg, data.callbacks);
+ OpenSRF.Stack.handle_message(data.ses, data.msg);
}
};
-OpenSRF.Stack.handle_message = function(ses, osrf_msg, callbacks) {
+OpenSRF.Stack.handle_message = function(ses, osrf_msg) {
- var req = null;
+ var req = ses.find_request(osrf_msg.threadTrace());
if(osrf_msg.type() == OSRF_MESSAGE_TYPE_STATUS) {
var status_text = payload.status();
if(status == OSRF_STATUS_COMPLETE) {
- req = ses.find_request(osrf_msg.threadTrace());
if(req) {
req.complete = true;
- if(callbacks.oncomplete && !req.oncomplete_called) {
+ if(req.oncomplete && !req.oncomplete_called) {
req.oncomplete_called = true;
- return callbacks.oncomplete(req);
+ return req.oncomplete(req);
}
}
}
}
if(status == OSRF_STATUS_NOTFOUND || status == OSRF_STATUS_INTERNALSERVERERROR) {
- req = ses.find_request(osrf_msg.threadTrace());
- if(callbacks.onmethoderror)
- return callbacks.onmethoderror(req, status, status_text);
+ if(req && req.onmethoderror)
+ return req.onmethoderror(req, status, status_text);
}
}
if(osrf_msg.type() == OSRF_MESSAGE_TYPE_RESULT) {
- req = ses.find_request(osrf_msg.threadTrace());
if(req) {
req.response_queue.push(osrf_msg.payload());
- if(callbacks.onresponse)
- return callbacks.onresponse(req);
+ if(req.onresponse) {
+ return req.onresponse(req);
+ }
}
}
};
* GNU General Public License for more details.
* ----------------------------------------------------------------------- */
-var WS_PATH = '/osrf-websocket';
+// opensrf defaults
+var WEBSOCKET_URL_PATH = '/osrf-websocket-translator';
+var WEBSOCKET_PORT = 7680;
+var WEBSOCKET_PORT_SSL = 7682;
+
+
+// Create the websocket and connect to the server
+// args.onopen is required
+// if args.default is true, use the default connection
+OpenSRF.WebSocketConnection = function(args, handlers) {
+ args = args || {};
+ this.handlers = handlers;
+
+ var secure = (args.ssl || location.protocol == 'https');
+ var path = args.path || WEBSOCKET_URL_PATH;
+ var port = args.port || (secure ? WEBSOCKET_PORT_SSL : WEBSOCKET_PORT);
+ var host = args.host || location.host;
+ var proto = (secure) ? 'wss' : 'ws';
+ this.path = proto + '://' + host + ':' + port + path;
+
+ this.setupSocket();
+ OpenSRF.WebSocketConnection.pool[args.name] = this;
+};
-/**
- * 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;
+// global pool of connection objects; name => connection map
+OpenSRF.WebSocketConnection.pool = {};
- var proto = location.protocol == 'https' ? 'wss' : 'ws';
+OpenSRF.WebSocketConnection.defaultConnection = function() {
+ return OpenSRF.WebSocketConnection.pool['default'];
+}
- var path = proto + '://' + location.host +
- WS_PATH + '?service=' + this.session.service;
+/**
+ * create a new WebSocket. useful for new connections or
+ * applying a new socket to an existing connection (whose
+ * socket was disconnected)
+ */
+OpenSRF.WebSocketConnection.prototype.setupSocket = function() {
try {
- this.ws = new WebSocket(path);
+ this.socket = new WebSocket(this.path);
} catch(e) {
- throw new Error("WebSocket() not supported in this browser " + e);
- }
-
- var self = this;
-
- this.ws.onopen = function(evt) {
- onopen(self);
+ throw new Error("WebSocket() not supported in this browser: " + e);
}
- this.ws.onmessage = function(evt) {
- self.core_handler(evt.data);
- }
+ this.socket.onopen = this.handlers.onopen;
+ this.socket.onmessage = this.handlers.onmessage;
+ this.socket.onerror = this.handlers.onerror;
+ this.socket.onclose = this.handlers.onclose;
+};
- this.ws.onerror = function(evt) {
- self.transport_error_handler(evt.data);
- }
+/** default onmessage handler: push the message up the opensrf stack */
+OpenSRF.WebSocketConnection.default_onmessage = function(evt) {
+ console.log('receiving: ' + evt.data);
+ var msg = JSON2js(evt.data);
+ OpenSRF.Stack.push(
+ new OpenSRF.NetMessage(
+ null, null, msg.thread, null, msg.osrf_msg)
+ );
+};
- this.ws.onclose = function(evt) {
- }
+/** default error handler */
+OpenSRF.WebSocketConnection.default_onerror = function(evt) {
+ throw new Error("WebSocket Error " + evt + ' : ' + evt.data);
};
-OpenSRF.WSRequest.prototype.send = function(message) {
- //console.log('sending: ' + js2JSON([message.serialize()]));
- this.last_message = message;
- this.ws.send(js2JSON([message.serialize()]));
- return this;
+
+/** shut it down */
+OpenSRF.WebSocketConnection.prototype.destroy = function() {
+ this.socket.close();
+ delete OpenSRF.WebSocketConnection.pool[this.name];
};
-OpenSRF.WSRequest.prototype.close = function() {
- try { this.ws.close(); } catch(e) {}
+/**
+ * Creates the request object, but does not connect or send anything
+ * until the first call to send().
+ */
+OpenSRF.WebSocketRequest = function(session, onopen, connectionArgs) {
+ this.session = session;
+ this.onopen = onopen;
+ this.setupConnection(connectionArgs || {});
}
-OpenSRF.WSRequest.prototype.core_handler = function(json) {
- //console.log('received: ' + json);
+OpenSRF.WebSocketRequest.prototype.setupConnection = function(args) {
+ var self = this;
- 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
- );
-};
+ var cname = args.name || 'default';
+ this.wsc = OpenSRF.WebSocketConnection.pool[cname];
+ if (this.wsc) { // we have a WebSocketConnection.
-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);
+ switch (this.wsc.socket.readyState) {
+
+ case this.wsc.socket.CONNECTING:
+ // replace the original onopen handler with a new combined handler
+ var orig_open = this.wsc.socket.onopen;
+ this.wsc.socket.onopen = function() {
+ orig_open();
+ self.onopen(self);
+ };
+ break;
- if(self.args.onerror) {
- self.args.onerror(
- self.last_message, self.session.service, '');
+ case this.wsc.socket.OPEN:
+ // user is expecting an onopen event. socket is
+ // already open, so we have to manufacture one.
+ this.onopen(this);
+ break;
+
+ default:
+ console.log('WebSocket is no longer connecting; reconnecting');
+ this.wsc.setupSocket();
}
- };
-};
-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, '');
+ } else { // no connection found
+
+ if (cname == 'default' || args.useDefaultHandlers) { // create the default handle
+
+ this.wsc = new OpenSRF.WebSocketConnection(
+ {name : cname}, {
+ onopen : function(evt) {if (self.onopen) self.onopen(self)},
+ onmessage : OpenSRF.WebSocketConnection.default_onmessage,
+ onerror : OpenSRF.WebSocketRequest.default_onerror,
+ onclose : OpenSRF.WebSocketRequest.default_onclose
+ }
+ );
+
+ } else {
+ throw new Error("No such WebSocketConnection '" + cname + "'");
+ }
}
+}
+
+
+OpenSRF.WebSocketRequest.prototype.send = function(message) {
+ var wrapper = {
+ service : this.session.service,
+ thread : this.session.thread,
+ osrf_msg : [message.serialize()]
+ };
+
+ var json = js2JSON(wrapper);
+ console.log('sending: ' + json);
+
+ // drop it on the wire
+ this.wsc.socket.send(json);
+ return this;
};
+
+