Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 3107e63

Browse files
authored
Mark Request/Response bodys with Content-Lengths, closes #522 (#545)
Previously, using a `Request`/`Response` `body` in another `fetch` request, without copying over headers would send the request with `Transfer-Encoding: chunked`. The Workers runtime will remember the `Content-Length` here, so we now attach it to the stream.
1 parent e49b9b3 commit 3107e63

File tree

2 files changed

+36
-11
lines changed

2 files changed

+36
-11
lines changed

packages/core/src/standards/http.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,17 @@ export class Body<Inner extends BaseRequest | BaseResponse> {
270270
// TODO: maybe set { highWaterMark: 0 } as a strategy here?
271271
bodyStream = new ReadableStream(source);
272272
Object.defineProperty(bodyStream, kBodyStreamBrand, { value: true });
273+
274+
// If this body has a known content length, attach it to the body
275+
// https://github.com/cloudflare/miniflare/issues/522
276+
// (note `parseInt(null)` is `NaN`)
277+
const maybeContentLength = parseInt(this.headers.get("Content-Length")!);
278+
if (!isNaN(maybeContentLength)) {
279+
Object.defineProperty(bodyStream, kContentLength, {
280+
value: maybeContentLength,
281+
});
282+
}
283+
273284
return (this[kBodyStream] = bodyStream);
274285
}
275286
get bodyUsed(): boolean {
@@ -835,17 +846,8 @@ export async function fetch(
835846

836847
await waitForOpenOutputGate();
837848

838-
// Don't pass our strange hybrid Request to undici
839-
if (input instanceof Request) input = input[_kInner];
840-
if (init instanceof Request) init = init[_kInner] as RequestInit;
841-
if (!(init instanceof BaseRequest) && init?.body instanceof ReadableStream) {
842-
// We mutate `init` here, make sure this isn't visible to the caller
843-
init = cloneRequestInit(init);
844-
init.duplex = "half";
845-
}
846-
847849
// Set the headers guard to "none" so we can delete the "Host" header
848-
const req = new BaseRequest(input, init);
850+
const req = new Request(input, init);
849851
// @ts-expect-error internal kGuard isn't included in type definitions
850852
req.headers[fetchSymbols.kGuard] = "none";
851853
// Delete the "Host" header, the correct value will be added by undici
@@ -877,7 +879,8 @@ export async function fetch(
877879
this instanceof Dispatcher ? this : getGlobalDispatcher(),
878880
removeHeaders
879881
);
880-
const baseRes = await baseFetch(req, { dispatcher });
882+
// Don't pass our strange hybrid Request to undici
883+
const baseRes = await baseFetch(req[_kInner], { dispatcher });
881884

882885
// Increment the subrequest count by the number of redirects
883886
// TODO (someday): technically we should check the subrequest count before

packages/core/test/standards/http.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,28 @@ test("fetch: accepts stream body", async (t) => {
12481248
res = await fetch(upstream, { method: "POST", body: stream });
12491249
t.is(await res.text(), "chunk2");
12501250
});
1251+
test("fetch: uses known content length if possible", async (t) => {
1252+
// https://github.com/cloudflare/miniflare/issues/522
1253+
const upstream = (
1254+
await useServer(t, (req, res) => {
1255+
res.end(String(req.headers["transfer-encoding"]));
1256+
})
1257+
).http;
1258+
1259+
const body = new ReadableStream({
1260+
start(controller) {
1261+
controller.enqueue(utf8Encode("chunk"));
1262+
controller.close();
1263+
},
1264+
});
1265+
const request = new Request("http://localhost", {
1266+
method: "POST",
1267+
headers: { "Content-Length": "5" },
1268+
body,
1269+
});
1270+
const res = await fetch(upstream, { method: "POST", body: request.body });
1271+
t.is(await res.text(), "undefined");
1272+
});
12511273
test('fetch: returns full Response for "manual" redirect', async (t) => {
12521274
const upstream = (await useServer(t, redirectingServerListener)).http;
12531275
const url = new URL("/?n=3", upstream);

0 commit comments

Comments
 (0)