var rpcRequestID = 1,
rpcSessionID = L.env.sessionid || '00000000000000000000000000000000',
- rpcBaseURL = L.url('admin/ubus');
+ rpcBaseURL = L.url('admin/ubus'),
+ rpcInterceptorFns = [];
return L.Class.extend({
- call: function(req, cb) {
+ call: function(req, cb, nobatch) {
var q = '';
if (Array.isArray(req)) {
return Promise.resolve([]);
for (var i = 0; i < req.length; i++)
- q += '%s%s.%s'.format(
- q ? ';' : '/',
- req[i].params[1],
- req[i].params[2]
- );
+ if (req[i].params)
+ q += '%s%s.%s'.format(
+ q ? ';' : '/',
+ req[i].params[1],
+ req[i].params[2]
+ );
}
- else {
+ else if (req.params) {
q += '/%s.%s'.format(req.params[1], req.params[2]);
}
return L.Request.post(rpcBaseURL + q, req, {
- timeout: (L.env.rpctimeout || 5) * 1000,
+ timeout: (L.env.rpctimeout || 20) * 1000,
+ nobatch: nobatch,
credentials: true
- }).then(cb);
+ }).then(cb, cb);
},
- handleListReply: function(req, msg) {
- var list = msg.result;
+ parseCallReply: function(req, res) {
+ var msg = null;
- /* verify message frame */
- if (typeof(msg) != 'object' || msg.jsonrpc != '2.0' || !msg.id || !Array.isArray(list))
- list = [ ];
+ if (res instanceof Error)
+ return req.reject(res);
- req.resolve(list);
- },
-
- handleCallReply: function(req, res) {
- var type = Object.prototype.toString,
- msg = null;
+ try {
+ if (!res.ok)
+ L.raise('RPCError', 'RPC call to %s/%s failed with HTTP error %d: %s',
+ req.object, req.method, res.status, res.statusText || '?');
- if (!res.ok)
- L.error('RPCError', 'RPC call failed with HTTP error %d: %s',
- res.status, res.statusText || '?');
+ msg = res.json();
+ }
+ catch (e) {
+ return req.reject(e);
+ }
- msg = res.json();
+ /*
+ * The interceptor args are intentionally swapped.
+ * Response is passed as first arg to align with Request class interceptors
+ */
+ Promise.all(rpcInterceptorFns.map(function(fn) { return fn(msg, req) }))
+ .then(this.handleCallReply.bind(this, req, msg))
+ .catch(req.reject);
+ },
- /* fetch response attribute and verify returned type */
- var ret = undefined;
+ handleCallReply: function(req, msg) {
+ var type = Object.prototype.toString,
+ ret = null;
+
+ try {
+ /* verify message frame */
+ if (!L.isObject(msg) || msg.jsonrpc != '2.0')
+ L.raise('RPCError', 'RPC call to %s/%s returned invalid message frame',
+ req.object, req.method);
+
+ /* check error condition */
+ if (L.isObject(msg.error) && msg.error.code && msg.error.message)
+ L.raise('RPCError', 'RPC call to %s/%s failed with error %d: %s',
+ req.object, req.method, msg.error.code, msg.error.message || '?');
+ }
+ catch (e) {
+ return req.reject(e);
+ }
- /* verify message frame */
- if (typeof(msg) == 'object' && msg.jsonrpc == '2.0') {
- if (typeof(msg.error) == 'object' && msg.error.code && msg.error.message)
- req.reject(new Error('RPC call failed with error %d: %s'
- .format(msg.error.code, msg.error.message || '?')));
- else if (Array.isArray(msg.result) && msg.result[0] == 0)
- ret = (msg.result.length > 1) ? msg.result[1] : msg.result[0];
+ if (!req.object && !req.method) {
+ ret = msg.result;
}
- else {
- req.reject(new Error('Invalid message frame received'));
+ else if (Array.isArray(msg.result)) {
+ ret = (msg.result.length > 1) ? msg.result[1] : msg.result[0];
}
if (req.expect) {
params: arguments.length ? this.varargs(arguments) : undefined
};
- return this.call(msg, this.handleListReply);
+ return new Promise(L.bind(function(resolveFn, rejectFn) {
+ /* store request info */
+ var req = {
+ resolve: resolveFn,
+ reject: rejectFn
+ };
+
+ /* call rpc */
+ this.call(msg, this.parseCallReply.bind(this, req));
+ }, this));
},
declare: function(options) {
resolve: resolveFn,
reject: rejectFn,
params: params,
- priv: priv
+ priv: priv,
+ object: options.object,
+ method: options.method
};
/* build message object */
};
/* call rpc */
- rpc.call(msg, rpc.handleCallReply.bind(rpc, req));
+ rpc.call(msg, rpc.parseCallReply.bind(rpc, req), options.nobatch);
});
}, this, this, options);
},
setBaseURL: function(url) {
rpcBaseURL = url;
+ },
+
+ getStatusText: function(statusCode) {
+ switch (statusCode) {
+ case 0: return _('Command OK');
+ case 1: return _('Invalid command');
+ case 2: return _('Invalid argument');
+ case 3: return _('Method not found');
+ case 4: return _('Resource not found');
+ case 5: return _('No data received');
+ case 6: return _('Permission denied');
+ case 7: return _('Request timeout');
+ case 8: return _('Not supported');
+ case 9: return _('Unspecified error');
+ case 10: return _('Connection lost');
+ default: return _('Unknown error code');
+ }
+ },
+
+ addInterceptor: function(interceptorFn) {
+ if (typeof(interceptorFn) == 'function')
+ rpcInterceptorFns.push(interceptorFn);
+ return interceptorFn;
+ },
+
+ removeInterceptor: function(interceptorFn) {
+ var oldlen = rpcInterceptorFns.length, i = oldlen;
+ while (i--)
+ if (rpcInterceptorFns[i] === interceptorFn)
+ rpcInterceptorFns.splice(i, 1);
+ return (rpcInterceptorFns.length < oldlen);
}
});