mirror of
https://github.com/zotero/zotero.git
synced 2026-08-28 05:25:31 +00:00
Add timeout to HiddenBrowser::getPageData()
The queries wait for the document to be ready, so a page that never finished loading would hang the caller -- e.g., full-text indexing -- forever.
This commit is contained in:
parent
9eea7d9a0c
commit
512a2444d8
2 changed files with 61 additions and 2 deletions
|
|
@ -266,12 +266,22 @@ export class HiddenBrowser {
|
|||
|
||||
/**
|
||||
* @param {String[]} props - 'characterSet', 'title', 'bodyText', 'documentHTML', 'cookie', 'channelInfo'
|
||||
* @param {Object} [options]
|
||||
* @param {Number} [options.timeout=30000] - Time to wait for each property in milliseconds.
|
||||
* The queries wait for the document to be ready, so a page that never finishes loading
|
||||
* would otherwise hang the query forever.
|
||||
*/
|
||||
async getPageData(props) {
|
||||
async getPageData(props, { timeout = 30000 } = {}) {
|
||||
var actor = this.browsingContext.currentWindowGlobal.getActor("PageData");
|
||||
var data = {};
|
||||
for (let prop of props) {
|
||||
data[prop] = await actor.sendQuery(prop);
|
||||
let timeoutPromise = new Promise((_, reject) => {
|
||||
setTimeout(
|
||||
() => reject(new Error(`Timed out getting '${prop}' from hidden browser`)),
|
||||
timeout
|
||||
);
|
||||
});
|
||||
data[prop] = await Promise.race([actor.sendQuery(prop), timeoutPromise]);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,6 +182,55 @@ describe("HiddenBrowser", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#getPageData() timeout", function () {
|
||||
var httpd;
|
||||
var port = 16216;
|
||||
var heldResponses = [];
|
||||
|
||||
before(function () {
|
||||
httpd = new HttpServer();
|
||||
httpd.start(port);
|
||||
// Same-origin page whose parser-blocking script never finishes loading, so the
|
||||
// document never reaches 'interactive'
|
||||
httpd.registerPathHandler('/hangpage', {
|
||||
handle(request, response) {
|
||||
response.setHeader('Content-Type', 'text/html', false);
|
||||
response.setStatusLine(null, 200, 'OK');
|
||||
response.write('<html><head><script src="/hang.js"></script></head>'
|
||||
+ '<body>x</body></html>');
|
||||
}
|
||||
});
|
||||
httpd.registerPathHandler('/hang.js', {
|
||||
handle(request, response) {
|
||||
response.processAsync();
|
||||
response.setStatusLine(null, 200, 'OK');
|
||||
response.setHeader('Content-Type', 'text/javascript', false);
|
||||
heldResponses.push(response);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
for (let r of heldResponses) {
|
||||
try { r.finish(); } catch (e) {}
|
||||
}
|
||||
await new Promise(resolve => httpd.stop(resolve));
|
||||
});
|
||||
|
||||
it("getPageData should reject instead of hanging on a never-ready document", async function () {
|
||||
this.timeout(20000);
|
||||
let browser = new HiddenBrowser();
|
||||
await browser.load(`http://127.0.0.1:${port}/hangpage`);
|
||||
let start = Date.now();
|
||||
let e = await getPromiseError(browser.getPageData(['bodyText'], { timeout: 2000 }));
|
||||
let elapsed = Date.now() - start;
|
||||
browser.destroy();
|
||||
assert.ok(e, 'getPageData rejected');
|
||||
assert.include(e.message, 'Timed out');
|
||||
assert.isBelow(elapsed, 10000, 'rejected around the timeout, not after a long hang');
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getDocument()", function () {
|
||||
it("should provide a Document object", async function () {
|
||||
let path = OS.Path.join(getTestDataDirectory().path, 'test-hidden.html');
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue