mirror of
https://github.com/denoland/deno.git
synced 2025-09-27 04:39:10 +00:00
fix(runtime/js/http): cancel body on response failure (#10225)
This commit is contained in:
parent
2d722832c4
commit
8a416a5ba2
2 changed files with 82 additions and 27 deletions
|
@ -228,3 +228,47 @@ unitTest(
|
||||||
await promise;
|
await promise;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
unitTest(
|
||||||
|
{ perms: { net: true } },
|
||||||
|
async function httpServerCancelBodyOnResponseFailure() {
|
||||||
|
const promise = (async () => {
|
||||||
|
const listener = Deno.listen({ port: 4501 });
|
||||||
|
const conn = await listener.accept();
|
||||||
|
const httpConn = Deno.serveHttp(conn);
|
||||||
|
const event = await httpConn.nextRequest();
|
||||||
|
assert(event);
|
||||||
|
const { respondWith } = event;
|
||||||
|
let cancelReason = null;
|
||||||
|
const responseError = await assertThrowsAsync(
|
||||||
|
async () => {
|
||||||
|
let interval = 0;
|
||||||
|
await respondWith(
|
||||||
|
new Response(
|
||||||
|
new ReadableStream({
|
||||||
|
start(controller) {
|
||||||
|
interval = setInterval(() => {
|
||||||
|
const message = `data: ${Date.now()}\n\n`;
|
||||||
|
controller.enqueue(new TextEncoder().encode(message));
|
||||||
|
}, 200);
|
||||||
|
},
|
||||||
|
cancel(reason) {
|
||||||
|
cancelReason = reason;
|
||||||
|
clearInterval(interval);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Deno.errors.Http,
|
||||||
|
);
|
||||||
|
assertEquals(cancelReason, responseError);
|
||||||
|
httpConn.close();
|
||||||
|
listener.close();
|
||||||
|
})();
|
||||||
|
|
||||||
|
const resp = await fetch("http://127.0.0.1:4501/");
|
||||||
|
await resp.body!.cancel();
|
||||||
|
await promise;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
|
@ -136,40 +136,51 @@
|
||||||
respBody = new Uint8Array(0);
|
respBody = new Uint8Array(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseBodyRid = await Deno.core.opAsync("op_http_response", [
|
let responseBodyRid;
|
||||||
responseSenderRid,
|
try {
|
||||||
innerResp.status ?? 200,
|
responseBodyRid = await Deno.core.opAsync("op_http_response", [
|
||||||
innerResp.headerList,
|
responseSenderRid,
|
||||||
], respBody instanceof Uint8Array ? respBody : null);
|
innerResp.status ?? 200,
|
||||||
|
innerResp.headerList,
|
||||||
|
], respBody instanceof Uint8Array ? respBody : null);
|
||||||
|
} catch (error) {
|
||||||
|
if (respBody !== null && respBody instanceof ReadableStream) {
|
||||||
|
await respBody.cancel(error);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
// If `respond` returns a responseBodyRid, we should stream the body
|
// If `respond` returns a responseBodyRid, we should stream the body
|
||||||
// to that resource.
|
// to that resource.
|
||||||
if (responseBodyRid !== null) {
|
if (responseBodyRid !== null) {
|
||||||
if (respBody === null || !(respBody instanceof ReadableStream)) {
|
try {
|
||||||
throw new TypeError("Unreachable");
|
if (respBody === null || !(respBody instanceof ReadableStream)) {
|
||||||
}
|
throw new TypeError("Unreachable");
|
||||||
const reader = respBody.getReader();
|
|
||||||
while (true) {
|
|
||||||
const { value, done } = await reader.read();
|
|
||||||
if (done) break;
|
|
||||||
if (!(value instanceof Uint8Array)) {
|
|
||||||
await reader.cancel("value not a Uint8Array");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
try {
|
const reader = respBody.getReader();
|
||||||
await Deno.core.opAsync(
|
while (true) {
|
||||||
"op_http_response_write",
|
const { value, done } = await reader.read();
|
||||||
responseBodyRid,
|
if (done) break;
|
||||||
value,
|
if (!(value instanceof Uint8Array)) {
|
||||||
);
|
await reader.cancel(new TypeError("Value not a Uint8Array"));
|
||||||
} catch (err) {
|
break;
|
||||||
await reader.cancel(err);
|
}
|
||||||
break;
|
try {
|
||||||
|
await Deno.core.opAsync(
|
||||||
|
"op_http_response_write",
|
||||||
|
responseBodyRid,
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
await reader.cancel(error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
// Once all chunks are sent, and the request body is closed, we can
|
||||||
|
// close the response body.
|
||||||
|
await Deno.core.opAsync("op_http_response_close", responseBodyRid);
|
||||||
}
|
}
|
||||||
// Once all chunks are sent, and the request body is closed, we can close
|
|
||||||
// the response body.
|
|
||||||
await Deno.core.opAsync("op_http_response_close", responseBodyRid);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue