mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
fix(ext/http): correctly consume response body in Deno.serve
(#24811)
Prior to this commit, you could return a `Response` created from a string or Uint8Array multiple times. Now you can't do that anymore.
This commit is contained in:
parent
c79cb339ef
commit
4eda9e64e9
2 changed files with 56 additions and 0 deletions
|
@ -436,6 +436,11 @@ function fastSyncResponseOrStream(
|
||||||
|
|
||||||
const stream = respBody.streamOrStatic;
|
const stream = respBody.streamOrStatic;
|
||||||
const body = stream.body;
|
const body = stream.body;
|
||||||
|
if (body !== undefined) {
|
||||||
|
// We ensure the response has not been consumed yet in the caller of this
|
||||||
|
// function.
|
||||||
|
stream.consumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (TypedArrayPrototypeGetSymbolToStringTag(body) === "Uint8Array") {
|
if (TypedArrayPrototypeGetSymbolToStringTag(body) === "Uint8Array") {
|
||||||
innerRequest?.close();
|
innerRequest?.close();
|
||||||
|
@ -505,6 +510,12 @@ function mapToCallback(context, callback, onError) {
|
||||||
"Return value from serve handler must be a response or a promise resolving to a response",
|
"Return value from serve handler must be a response or a promise resolving to a response",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.bodyUsed) {
|
||||||
|
throw TypeError(
|
||||||
|
"The body of the Response returned from the serve handler has already been consumed.",
|
||||||
|
);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
try {
|
try {
|
||||||
response = await onError(error);
|
response = await onError(error);
|
||||||
|
|
|
@ -631,6 +631,51 @@ Deno.test(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Deno.test(
|
||||||
|
{ permissions: { net: true } },
|
||||||
|
async function httpServerMultipleResponseBodyConsume() {
|
||||||
|
const ac = new AbortController();
|
||||||
|
const { promise, resolve } = Promise.withResolvers<void>();
|
||||||
|
const response = new Response("Hello World");
|
||||||
|
let hadError = false;
|
||||||
|
const server = Deno.serve({
|
||||||
|
handler: () => {
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
port: servePort,
|
||||||
|
signal: ac.signal,
|
||||||
|
onListen: onListen(resolve),
|
||||||
|
onError: () => {
|
||||||
|
hadError = true;
|
||||||
|
return new Response("Internal Server Error", { status: 500 });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await promise;
|
||||||
|
assert(!response.bodyUsed);
|
||||||
|
|
||||||
|
const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
|
||||||
|
headers: { "connection": "close" },
|
||||||
|
});
|
||||||
|
assertEquals(resp.status, 200);
|
||||||
|
const text = await resp.text();
|
||||||
|
assertEquals(text, "Hello World");
|
||||||
|
assert(response.bodyUsed);
|
||||||
|
|
||||||
|
const resp2 = await fetch(`http://127.0.0.1:${servePort}/`, {
|
||||||
|
headers: { "connection": "close" },
|
||||||
|
});
|
||||||
|
assertEquals(resp2.status, 500);
|
||||||
|
const text2 = await resp2.text();
|
||||||
|
assertEquals(text2, "Internal Server Error");
|
||||||
|
assert(hadError);
|
||||||
|
assert(response.bodyUsed);
|
||||||
|
|
||||||
|
ac.abort();
|
||||||
|
await server.finished;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
Deno.test({ permissions: { net: true } }, async function httpServerOverload1() {
|
Deno.test({ permissions: { net: true } }, async function httpServerOverload1() {
|
||||||
const ac = new AbortController();
|
const ac = new AbortController();
|
||||||
const deferred = Promise.withResolvers<void>();
|
const deferred = Promise.withResolvers<void>();
|
||||||
|
|
Loading…
Add table
Reference in a new issue