Skip to content

Commit 02c5ae4

Browse files
committed
Build with server timeout support
1 parent 98c3d4e commit 02c5ae4

File tree

2 files changed

+32
-26
lines changed

2 files changed

+32
-26
lines changed

build/jsroot.js

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,10 @@ settings = {
365365
* @desc Allows to retry files reading if original URL fails
366366
* @private */
367367
FilesRemap: { 'https://root.cern/': 'https://root-eos.web.cern.ch/' },
368+
/** @summary THttpServer read timeout in ms
369+
* @desc Configures timeout for requests to THttpServer
370+
* @default 0 */
371+
ServerTimeout: 0,
368372
/** @summary Configure xhr.withCredentials = true when submitting http requests from JSROOT */
369373
WithCredentials: false,
370374
/** @summary Skip streamer infos from the GUI */
@@ -968,13 +972,21 @@ function findFunction(name) {
968972
/** @summary Method to create http request, without promise can be used only in browser environment
969973
* @private */
970974
function createHttpRequest(url, kind, user_accept_callback, user_reject_callback, use_promise, tmout) {
975+
function handle_error(xhr, message, code, abort_reason) {
976+
if (!xhr.did_abort) {
977+
xhr.did_abort = abort_reason || true;
978+
xhr.abort();
979+
}
980+
if (!xhr.did_error || abort_reason)
981+
console.warn(message);
982+
if (!xhr.did_error) {
983+
xhr.did_error = true;
984+
xhr.error_callback(Error(message), code);
985+
}
986+
}
971987
function configureXhr(xhr) {
972988
xhr.http_callback = isFunc(user_accept_callback) ? user_accept_callback.bind(xhr) : () => {};
973-
xhr.error_callback = isFunc(user_reject_callback) ? user_reject_callback.bind(xhr) : function(err) {
974-
if (err?.message)
975-
console.warn(err.message);
976-
this.http_callback(null);
977-
}.bind(xhr);
989+
xhr.error_callback = isFunc(user_reject_callback) ? user_reject_callback.bind(xhr) : function() { this.http_callback(null); };
978990

979991
if (!kind)
980992
kind = 'buf';
@@ -1010,11 +1022,8 @@ function createHttpRequest(url, kind, user_accept_callback, user_reject_callback
10101022

10111023
if (settings.HandleWrongHttpResponse && (method === 'GET') && isFunc(xhr.addEventListener)) {
10121024
xhr.addEventListener('progress', function(oEvent) {
1013-
if (oEvent.lengthComputable && this.expected_size && (oEvent.loaded > this.expected_size)) {
1014-
this.did_abort = true;
1015-
this.abort();
1016-
this.error_callback(Error(`Server sends more bytes ${oEvent.loaded} than expected ${this.expected_size}. Abort I/O operation`), 598);
1017-
}
1025+
if (oEvent.lengthComputable && this.expected_size && (oEvent.loaded > this.expected_size))
1026+
handle_error(this, `Server sends more bytes ${oEvent.loaded} than expected ${this.expected_size}. Abort I/O operation`, 598);
10181027
}.bind(xhr));
10191028
}
10201029

@@ -1024,11 +1033,8 @@ function createHttpRequest(url, kind, user_accept_callback, user_reject_callback
10241033

10251034
if ((this.readyState === 2) && this.expected_size) {
10261035
const len = parseInt(this.getResponseHeader('Content-Length'));
1027-
if (Number.isInteger(len) && (len > this.expected_size) && !settings.HandleWrongHttpResponse) {
1028-
this.did_abort = 'large';
1029-
this.abort();
1030-
return this.error_callback(Error(`Server response size ${len} larger than expected ${this.expected_size}. Abort I/O operation`), 599);
1031-
}
1036+
if (Number.isInteger(len) && (len > this.expected_size) && !settings.HandleWrongHttpResponse)
1037+
return handle_error(this, `Server response size ${len} larger than expected ${this.expected_size}. Abort I/O operation`, 599, 'large');
10321038
}
10331039

10341040
if (this.readyState !== 4)
@@ -1037,7 +1043,7 @@ function createHttpRequest(url, kind, user_accept_callback, user_reject_callback
10371043
if ((this.status !== 200) && (this.status !== 206) && !browser.qt6 &&
10381044
// in these special cases browsers not always set status
10391045
!((this.status === 0) && ((url.indexOf('file://') === 0) || (url.indexOf('blob:') === 0))))
1040-
return this.error_callback(Error(`Fail to load url ${url}`), this.status);
1046+
return handle_error(this, `Fail to load url ${url}`, this.status);
10411047

10421048
if (this.nodejs_checkzip && (this.getResponseHeader('content-encoding') === 'gzip')) {
10431049
// special handling of gzip JSON objects in Node.js
@@ -1084,11 +1090,7 @@ function createHttpRequest(url, kind, user_accept_callback, user_reject_callback
10841090

10851091
if (tmout && Number.isFinite(tmout)) {
10861092
xhr.timeout = tmout;
1087-
xhr.ontimeout = function() {
1088-
this.did_abort = true;
1089-
this.abort();
1090-
this.error_callback(Error(`Request ${url} timeout`));
1091-
};
1093+
xhr.ontimeout = function() { handle_error(this, `Request ${url} timeout set ${tmout} ms`, 600, 'timeout'); };
10921094
}
10931095

10941096
return xhr;
@@ -124396,15 +124398,12 @@ class TFile {
124396124398
if (file.fAcceptRanges && first_block)
124397124399
totalsz = Math.max(totalsz, 1e5);
124398124400

124399-
return createHttpRequest(fullurl, 'buf', read_callback, undefined, true).then(xhr => {
124401+
return createHttpRequest(fullurl, 'buf', read_callback, undefined, true, file.fTimeout).then(xhr => {
124400124402
if (file.fAcceptRanges) {
124401124403
xhr.setRequestHeader('Range', ranges);
124402124404
xhr.expected_size = Math.max(Math.round(1.1 * totalsz), totalsz + 200); // 200 if offset for the potential gzip
124403124405
}
124404124406

124405-
if (file.fTimeout)
124406-
xhr.timeout = file.fTimeout;
124407-
124408124407
if (isFunc(progress_callback) && isFunc(xhr.addEventListener)) {
124409124408
let sum1 = 0, sum2 = 0, sum_total = 0;
124410124409
for (let n = 1; n < place.length; n += 2) {
@@ -167241,7 +167240,7 @@ class HierarchyPainter extends BasePainter {
167241167240
handleAfterRequest(findFunction(item._after_request)); // v6 support
167242167241
} else
167243167242
handleAfterRequest(draw_handle?.after_request);
167244-
}, undefined, true).then(xhr => {
167243+
}, undefined, true, settings.ServerTimeout).then(xhr => {
167245167244
itemreq = xhr;
167246167245
xhr.send(null);
167247167246
});
@@ -168379,6 +168378,12 @@ function readStyleFromURL(url) {
168379168378
if (d.has('prefer_saved_points'))
168380168379
settings.PreferSavedPoints = true;
168381168380

168381+
if (d.has('tmout'))
168382+
settings.ServerTimeout = parseFloat(d.get('tmout'));
168383+
168384+
if (d.has('ftmout'))
168385+
settings.FilesTimeout = parseFloat(d.get('ftmout'));
168386+
168382168387
const tf1_style = d.get('tf1');
168383168388
if (tf1_style === 'curve')
168384168389
settings.FuncAsCurve = true;

changes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1. Implement padsN draw option for `THStack` and `TMultiGraph`
1111
1. Use `resvg-js` backend for PNG support in node.js #391, thanks to https://github.com/OmarMesqq
1212
1. Remove support for deprectaed `TH1K` class
13+
1. Introduce `settings.ServerTimeout` global timeout for THttpServer operations
1314
1. Fix - paint frame border mode/size from TCanvas
1415
1. Fix - correctly process `TLeafB` arrays in tree draw #384
1516
1. Fix - convert BigInt before `RNtuple` drawing

0 commit comments

Comments
 (0)