diff --git a/src/core/worker_stream.js b/src/core/worker_stream.js index b17d1dabc0b9a..502a6ecde2213 100644 --- a/src/core/worker_stream.js +++ b/src/core/worker_stream.js @@ -55,7 +55,7 @@ class PDFWorkerStreamReader extends BasePDFStreamReader { } cancel(reason) { - this._reader.cancel(reason); + this._reader.cancel(reason).catch(() => {}); } } @@ -81,7 +81,7 @@ class PDFWorkerStreamRangeReader extends BasePDFStreamRangeReader { } cancel(reason) { - this._reader.cancel(reason); + this._reader.cancel(reason).catch(() => {}); } } diff --git a/src/display/fetch_stream.js b/src/display/fetch_stream.js index 2958dca6b38e9..e1d98926d565e 100644 --- a/src/display/fetch_stream.js +++ b/src/display/fetch_stream.js @@ -140,7 +140,7 @@ class PDFFetchStreamReader extends BasePDFStreamReader { } cancel(reason) { - this._reader?.cancel(reason); + this._reader?.cancel(reason).catch(() => {}); this._abortController.abort(); } } @@ -186,7 +186,7 @@ class PDFFetchStreamRangeReader extends BasePDFStreamRangeReader { } cancel(reason) { - this._reader?.cancel(reason); + this._reader?.cancel(reason).catch(() => {}); this._abortController.abort(); } } diff --git a/src/display/node_stream.js b/src/display/node_stream.js index cf363a4ae4668..a2e9a443d7bb6 100644 --- a/src/display/node_stream.js +++ b/src/display/node_stream.js @@ -99,7 +99,7 @@ class PDFNodeStreamReader extends BasePDFStreamReader { } cancel(reason) { - this._reader?.cancel(reason); + this._reader?.cancel(reason).catch(() => {}); } } @@ -138,7 +138,7 @@ class PDFNodeStreamRangeReader extends BasePDFStreamRangeReader { } cancel(reason) { - this._reader?.cancel(reason); + this._reader?.cancel(reason).catch(() => {}); } } diff --git a/test/unit/fetch_stream_spec.js b/test/unit/fetch_stream_spec.js index 3813a34c3aa39..e33be0b9d63a8 100644 --- a/test/unit/fetch_stream_spec.js +++ b/test/unit/fetch_stream_spec.js @@ -118,6 +118,40 @@ describe("fetch_stream", function () { expect(result2.value).toEqual(tailSize); }); + it("cancel after a failed read doesn't cause an unhandled rejection", async function () { + spyOn(globalThis, "fetch").and.callFake( + async () => + new Response( + new ReadableStream({ + pull(controller) { + controller.error(new Error("Network error")); + }, + }), + { status: 200 } + ) + ); + const stream = new PDFFetchStream({ + url: getPdfUrl(), + rangeChunkSize: 32768, + disableStream: false, + disableRange: false, + }); + + const fullReader = stream.getFullReader(); + const rangeReader = stream.getRangeReader(0, 32768); + await expectAsync(fullReader.read()).toBeRejectedWithError("Network error"); + await expectAsync(rangeReader.read()).toBeRejectedWithError( + "Network error" + ); + + fullReader.cancel(new AbortException("Don't need fullReader.")); + rangeReader.cancel(new AbortException("Don't need rangeReader.")); + // Allow any unhandled rejections to be reported. + await new Promise(resolve => { + setTimeout(resolve, 0); + }); + }); + describe("Redirects", function () { it("redirects allowed if all responses are same-origin", async function () { await testCrossOriginRedirects({ diff --git a/test/unit/node_stream_spec.js b/test/unit/node_stream_spec.js index e38929ae7386e..c9c9735074b7f 100644 --- a/test/unit/node_stream_spec.js +++ b/test/unit/node_stream_spec.js @@ -153,4 +153,29 @@ describe("node_stream", function () { expect(isRangeSupported).toBeFalse(); expect(len).toEqual(smallLength); }); + + it("cancel after a failed read doesn't cause an unhandled rejection", async function () { + // We point the stream at a directory instead of at a PDF file on purpose. + // Opening a directory succeeds, but reading from it fails (with EISDIR), + // so both the full reader and the range reader end up with an errored + // stream. + const stream = new PDFNodeStream({ + url: new URL("./test/pdfs/", cwdURL), + rangeChunkSize: 65536, + disableStream: false, + disableRange: false, + }); + + const fullReader = stream.getFullReader(); + const rangeReader = stream.getRangeReader(0, 65536); + await expectAsync(fullReader.read()).toBeRejected(); + await expectAsync(rangeReader.read()).toBeRejected(); + + fullReader.cancel(new AbortException("Don't need fullReader.")); + rangeReader.cancel(new AbortException("Don't need rangeReader.")); + // Allow any unhandled rejections to be reported. + await new Promise(resolve => { + setTimeout(resolve, 0); + }); + }); });