From: Jo-Philipp Wich Date: Wed, 11 Sep 2019 07:25:11 +0000 (+0200) Subject: luci-base: rpc.js: revamp error handling, add interceptor support X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=f141433f5e38153e9dfbb8b320dfa70db1a47cf2;p=oweals%2Fluci.git luci-base: rpc.js: revamp error handling, add interceptor support Add two new functions L.rpc.addInterceptor() and L.rpc.removeInterceptor() which allow to register and remove interceptor functions which are invoked before the rpc reply result promise is fulfilled. Signed-off-by: Jo-Philipp Wich --- diff --git a/modules/luci-base/htdocs/luci-static/resources/rpc.js b/modules/luci-base/htdocs/luci-static/resources/rpc.js index e6a1bb24d..9a0f0164a 100644 --- a/modules/luci-base/htdocs/luci-static/resources/rpc.js +++ b/modules/luci-base/htdocs/luci-static/resources/rpc.js @@ -2,7 +2,8 @@ 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) { @@ -39,29 +40,50 @@ return L.Class.extend({ req.resolve(list); }, - handleCallReply: function(req, res) { - var type = Object.prototype.toString, - msg = null; + parseCallReply: function(req, res) { + var msg = null; - if (!res.ok) - L.error('RPCError', 'RPC call to %s/%s failed with HTTP error %d: %s', - req.object, req.method, res.status, res.statusText || '?'); + 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 || '?'); - msg = res.json(); + msg = res.json(); + } + catch (e) { + return req.reject(e); + } - /* fetch response attribute and verify returned type */ - var ret = undefined; + /* + * 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); + }, - /* 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 to %s/%s failed with error %d: %s' - .format(req.object, req.method, msg.error.code, msg.error.message || '?'))); - else if (Array.isArray(msg.result)) - ret = (msg.result.length > 1) ? msg.result[1] : msg.result[0]; + 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 || '?'); } - else { - req.reject(new Error('Invalid message frame received')); + catch (e) { + return req.reject(e); + } + + if (Array.isArray(msg.result)) { + ret = (msg.result.length > 1) ? msg.result[1] : msg.result[0]; } if (req.expect) { @@ -139,7 +161,7 @@ return L.Class.extend({ }; /* call rpc */ - rpc.call(msg, rpc.handleCallReply.bind(rpc, req)); + rpc.call(msg, rpc.parseCallReply.bind(rpc, req)); }); }, this, this, options); }, @@ -175,5 +197,19 @@ return L.Class.extend({ 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); } });