diff options
author | Matt Mastracci <matthew@mastracci.com> | 2023-09-25 09:23:55 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-25 17:23:55 +0200 |
commit | a27ee8f368dbac33141fdcb9a17d0e4ea907b8ef (patch) | |
tree | 4bc1bfa1e23b40bfb726cd5b179091393533dec2 /cli/tests/unit/serve_test.ts | |
parent | 83f20007aac0f9ebb0eb59b71a932e7a91d5d9a7 (diff) |
fix(ext/http): ensure that resources are closed when request is cancelled (#20641)
Builds on top of #20622 to fix #10854
Diffstat (limited to 'cli/tests/unit/serve_test.ts')
-rw-r--r-- | cli/tests/unit/serve_test.ts | 50 |
1 files changed, 48 insertions, 2 deletions
diff --git a/cli/tests/unit/serve_test.ts b/cli/tests/unit/serve_test.ts index 2e7836b6a..830817146 100644 --- a/cli/tests/unit/serve_test.ts +++ b/cli/tests/unit/serve_test.ts @@ -2699,12 +2699,14 @@ Deno.test( for (const url of ["text", "file", "stream"]) { // Ensure that we don't panic when the incoming TCP request was dropped - // https://github.com/denoland/deno/issues/20315 + // https://github.com/denoland/deno/issues/20315 and that we correctly + // close/cancel the response Deno.test({ permissions: { read: true, write: true, net: true }, name: `httpServerTcpCancellation_${url}`, fn: async function () { const ac = new AbortController(); + const streamCancelled = url == "stream" ? deferred() : undefined; const listeningPromise = deferred(); const waitForAbort = deferred(); const waitForRequest = deferred(); @@ -2727,7 +2729,9 @@ for (const url of ["text", "file", "stream"]) { start(controller) { _body = null; controller.enqueue(new Uint8Array([1])); - controller.close(); + }, + cancel(reason) { + streamCancelled!.resolve(reason); }, }), ); @@ -2753,15 +2757,57 @@ for (const url of ["text", "file", "stream"]) { // Give it a few milliseconds for the serve machinery to work await new Promise((r) => setTimeout(r, 10)); + // Wait for cancellation before we shut the server down + if (streamCancelled !== undefined) { + await streamCancelled; + } + // Since the handler has a chance of creating resources or running async ops, we need to use a // graceful shutdown here to ensure they have fully drained. await server.shutdown(); + await server.finished; }, }); } Deno.test( + { permissions: { net: true } }, + async function httpServerCancelFetch() { + const request2 = deferred(); + const request2Aborted = deferred(); + const { finished, abort } = await makeServer(async (req) => { + if (req.url.endsWith("/1")) { + const fetchRecursive = await fetch(`http://localhost:${servePort}/2`); + return new Response(fetchRecursive.body); + } else if (req.url.endsWith("/2")) { + request2.resolve(); + return new Response( + new ReadableStream({ + start(_controller) {/* just hang */}, + cancel(reason) { + request2Aborted.resolve(reason); + }, + }), + ); + } + fail(); + }); + const fetchAbort = new AbortController(); + const fetchPromise = await fetch(`http://localhost:${servePort}/1`, { + signal: fetchAbort.signal, + }); + await fetchPromise; + await request2; + fetchAbort.abort(); + assertEquals("resource closed", await request2Aborted); + + abort(); + await finished; + }, +); + +Deno.test( { permissions: { read: true, net: true } }, async function httpServerWithTls() { const ac = new AbortController(); |