Files
xen-orchestra/packages/xo-lib/index.js

181 lines
3.8 KiB
JavaScript
Raw Normal View History

2014-07-26 10:15:29 +02:00
'use strict';
//====================================================================
var assign = require('lodash.assign');
2015-02-05 11:53:27 +01:00
var Bluebird = require('bluebird');
2014-07-26 10:15:29 +02:00
var forEach = require('lodash.foreach');
2014-07-28 13:21:19 +02:00
var parseUrl = require('url').parse;
2015-02-05 11:53:27 +01:00
var WebSocket = require('ws');
2014-07-26 10:15:29 +02:00
//====================================================================
var notConnected = function () {
throw new Error('not connected');
};
2014-07-28 13:21:19 +02:00
// Fix URL if necessary.
var fixUrl = function (url) {
// Add HTTP protocol if missing.
2014-07-28 13:30:57 +02:00
if (!/^https?:/.test(url)) {
url = 'http:'+ url;
}
url = parseUrl(url);
2014-07-28 13:21:19 +02:00
// Suffix path with /api/ if missing.
2014-07-28 13:30:57 +02:00
var path = url.pathname || '';
2014-07-28 13:21:19 +02:00
if ('/' !== path[path.length - 1]) {
path += '/';
}
if (!/\/api\/$/.test(path)) {
path += 'api/';
}
// Reconstruct the URL.
return [
url.protocol, '//',
url.host,
path,
url.search,
url.hash,
].join('');
};
2014-07-26 10:15:29 +02:00
//====================================================================
var Xo = function (url) {
2014-07-28 13:21:19 +02:00
this._url = fixUrl(url);
2014-07-26 10:15:29 +02:00
// Identifier of the next request.
this._nextId = 0;
// Promises linked to the requests.
this._deferreds = {};
// Current WebSocket.
this._socket = null;
// Current status which may be:
// - disconnected
// - connecting
// - connected
this.status = 'disconnected';
};
assign(Xo.prototype, {
close: function () {
if (this._socket)
{
this._socket.close();
}
},
connect: function () {
if (this.status === 'connected')
{
2015-02-05 11:46:56 +01:00
return Bluebird.cast();
2014-07-26 10:15:29 +02:00
}
2015-02-05 11:46:56 +01:00
var deferred = Bluebird.defer();
2014-07-26 10:15:29 +02:00
this.status = 'connecting';
var opts = {};
if (/^https/.test(this._url)) {
2014-07-26 23:42:04 +02:00
// Due to imperfect TLS implementation in XO-Server.
opts.rejectUnauthorized = false;
}
var socket = this._socket = new WebSocket(this._url, opts);
2014-07-26 10:15:29 +02:00
// When the socket opens, send any queued requests.
socket.on('open', function () {
this.status = 'connected';
// (Re)Opens accesses.
delete this.send;
// Resolves the promise.
deferred.resolve();
}.bind(this));
socket.on('message', function (data) {
// `ws` API is lightly different from standard API.
if (data.data)
{
data = data.data;
}
// TODO: Wraps in a promise to prevent releasing the Zalgo.
var response = JSON.parse(data);
var id = response.id;
var deferred = this._deferreds[id];
if (!deferred)
{
// Response already handled.
return;
}
delete this._deferreds[id];
if ('error' in response)
{
return deferred.reject(response.error);
}
if ('result' in response)
{
return deferred.resolve(response.result);
}
deferred.reject({
message: 'invalid response received',
object: response,
});
}.bind(this));
socket.on('close', function () {
// Closes accesses.
this.send = notConnected;
// Fails all waiting requests.
forEach(this._deferreds, function (deferred) {
deferred.reject('not connected');
});
this._deferreds = {};
}.bind(this));
socket.on('error', function (error) {
// Fails the connect promise if possible.
deferred.reject(error);
});
return deferred.promise;
},
call: function (method, params) {
return this.connect().then(function () {
var socket = this._socket;
var id = this._nextId++;
socket.send(JSON.stringify({
jsonrpc: '2.0',
id: id,
method: method,
params: params || [],
}));
2015-02-05 11:46:56 +01:00
var deferred = this._deferreds[id] = Bluebird.defer();
2014-07-26 10:15:29 +02:00
return deferred.promise;
}.bind(this));
},
});
//====================================================================
2014-07-28 13:21:19 +02:00
exports = module.exports = Xo;
exports.fixUrl = fixUrl;