summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2019-04-05 07:59:52 +0200
committerJo-Philipp Wich <jo@mein.io>2019-07-07 15:36:24 +0200
commitb21a2813b4c34f19ce3e1075b04806dbe4ffbb1c (patch)
tree64633e48d2494a65d3693fb731b069490f378177
parent90288b2e2ca7e955983eaf0db59762351faa03fe (diff)
luci-base: luci.js: auto-coalesce ubus requests
Extend LuCI.Request to automatically coalesce subsequent requests to ubus resources into single batch requests. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/luci.js105
1 files changed, 98 insertions, 7 deletions
diff --git a/modules/luci-base/htdocs/luci-static/resources/luci.js b/modules/luci-base/htdocs/luci-static/resources/luci.js
index fbbb23054..24af80f69 100644
--- a/modules/luci-base/htdocs/luci-static/resources/luci.js
+++ b/modules/luci-base/htdocs/luci-static/resources/luci.js
@@ -161,33 +161,121 @@
var Response = Class.extend({
__name__: 'LuCI.XHR.Response',
- __init__: function(xhr, url, duration) {
+ __init__: function(xhr, url, duration, headers, content) {
this.ok = (xhr.status >= 200 && xhr.status <= 299);
this.status = xhr.status;
this.statusText = xhr.statusText;
- this.responseText = xhr.responseText;
- this.headers = new Headers(xhr);
+ this.headers = (headers != null) ? headers : new Headers(xhr);
this.duration = duration;
this.url = url;
this.xhr = xhr;
+
+ if (content != null && typeof(content) == 'object') {
+ this.responseJSON = content;
+ this.responseText = null;
+ }
+ else if (content != null) {
+ this.responseJSON = null;
+ this.responseText = String(content);
+ }
+ else {
+ this.responseJSON = null;
+ this.responseText = xhr.responseText;
+ }
+ },
+
+ clone: function(content) {
+ var copy = new Response(this.xhr, this.url, this.duration, this.headers, content);
+
+ copy.ok = this.ok;
+ copy.status = this.status;
+ copy.statusText = this.statusText;
+
+ return copy;
},
json: function() {
- return JSON.parse(this.responseText);
+ if (this.responseJSON == null)
+ this.responseJSON = JSON.parse(this.responseText);
+
+ return this.responseJSON;
},
text: function() {
+ if (this.responseText == null && this.responseJSON != null)
+ this.responseText = JSON.stringify(this.responseJSON);
+
return this.responseText;
}
});
+
+ var requestQueue = [],
+ rpcBaseURL = null;
+
+ function isQueueableRequest(opt) {
+ if (!classes.rpc)
+ return false;
+
+ if (opt.method != 'POST' || typeof(opt.content) != 'object')
+ return false;
+
+ if (opt.nobatch === true)
+ return false;
+
+ if (rpcBaseURL == null)
+ rpcBaseURL = Request.expandURL(classes.rpc.getBaseURL());
+
+ return (rpcBaseURL != null && opt.url.indexOf(rpcBaseURL) == 0);
+ }
+
+ function flushRequestQueue() {
+ if (!requestQueue.length)
+ return;
+
+ var reqopt = Object.assign({}, requestQueue[0][0], { content: [], nobatch: true }),
+ batch = [];
+
+ for (var i = 0; i < requestQueue.length; i++) {
+ batch[i] = requestQueue[i];
+ reqopt.content[i] = batch[i][0].content;
+ }
+
+ requestQueue.length = 0;
+
+ Request.request(rpcBaseURL, reqopt).then(function(reply) {
+ var json = null, req = null;
+
+ try { json = reply.json() }
+ catch(e) { }
+
+ while ((req = batch.shift()) != null)
+ if (Array.isArray(json) && json.length)
+ req[2].call(reqopt, reply.clone(json.shift()));
+ else
+ req[1].call(reqopt, new Error('No related RPC reply'));
+ }).catch(function(error) {
+ var req = null;
+
+ while ((req = batch.shift()) != null)
+ req[1].call(reqopt, error);
+ });
+ }
+
var Request = Class.singleton({
__name__: 'LuCI.Request',
interceptors: [],
+ expandURL: function(url) {
+ if (!/^(?:[^/]+:)?\/\//.test(url))
+ url = location.protocol + '//' + location.host + url;
+
+ return url;
+ },
+
request: function(target, options) {
- var state = { xhr: new XMLHttpRequest(), url: target, start: Date.now() },
+ var state = { xhr: new XMLHttpRequest(), url: this.expandURL(target), start: Date.now() },
opt = Object.assign({}, options, state),
content = null,
contenttype = null,
@@ -231,8 +319,11 @@
if (!opt.cache)
opt.url += ((/\?/).test(opt.url) ? '&' : '?') + (new Date()).getTime();
- if (!/^(?:[^/]+:)?\/\//.test(opt.url))
- opt.url = location.protocol + '//' + location.host + opt.url;
+ if (isQueueableRequest(opt)) {
+ requestQueue.push([opt, rejectFn, resolveFn]);
+ requestAnimationFrame(flushRequestQueue);
+ return;
+ }
if ('username' in opt && 'password' in opt)
opt.xhr.open(opt.method, opt.url, true, opt.username, opt.password);