diff --git a/.changeset/quiet-crabs-deny.md b/.changeset/quiet-crabs-deny.md new file mode 100644 index 00000000000..4a2c7acbf32 --- /dev/null +++ b/.changeset/quiet-crabs-deny.md @@ -0,0 +1,5 @@ +--- +"@effect/platform": patch +--- + +Remove the content-length header before sending FetchHttpClient requests. diff --git a/packages/platform/src/internal/fetchHttpClient.ts b/packages/platform/src/internal/fetchHttpClient.ts index 434c143d809..6ba6a095751 100644 --- a/packages/platform/src/internal/fetchHttpClient.ts +++ b/packages/platform/src/internal/fetchHttpClient.ts @@ -16,7 +16,10 @@ const fetch: Client.HttpClient = client.make((request, url, signal, fiber) => { const context = fiber.getFiberRef(FiberRef.currentContext) const fetch: typeof globalThis.fetch = context.unsafeMap.get(fetchTagKey) ?? globalThis.fetch const options: RequestInit = context.unsafeMap.get(requestInitTagKey) ?? {} - const headers = options.headers ? Headers.merge(Headers.fromInput(options.headers), request.headers) : request.headers + let headers = options.headers ? Headers.merge(Headers.fromInput(options.headers), request.headers) : request.headers + if (headers["content-length"]) { + headers = Headers.remove(headers, "content-length") + } const send = (body: BodyInit | undefined) => Effect.map( Effect.tryPromise({ diff --git a/packages/platform/test/HttpClient.test.ts b/packages/platform/test/HttpClient.test.ts index ca1c6067343..d9dfba4f4f8 100644 --- a/packages/platform/test/HttpClient.test.ts +++ b/packages/platform/test/HttpClient.test.ts @@ -306,6 +306,35 @@ describe("HttpClient", () => { Effect.provideService(FetchHttpClient.RequestInit, { redirect: "manual" }) ), 30000) + it.effect("fetch removes content-length header", () => { + let headers: globalThis.RequestInit["headers"] | undefined + const fetch: typeof globalThis.fetch = (_, init) => { + headers = init?.headers + return Promise.resolve(new Response("ok")) + } + return Effect.gen(function*() { + const client = yield* HttpClient.HttpClient + + yield* HttpClientRequest.post("http://test/").pipe( + HttpClientRequest.bodyText("hello"), + client.execute + ) + + strictEqual((headers as Record | undefined)?.["content-length"], undefined) + strictEqual((headers as Record | undefined)?.["content-type"], "text/plain") + strictEqual((headers as Record | undefined)?.["x-test"], "ok") + }).pipe( + Effect.provide(FetchHttpClient.layer), + Effect.provideService(FetchHttpClient.Fetch, fetch), + Effect.provideService(FetchHttpClient.RequestInit, { + headers: { + "content-length": "100", + "x-test": "ok" + } + }) + ) + }) + describe("retryTransient", () => { const makeTestClient = (status: number) => { const attemptsRef = Ref.unsafeMake(0)