diff --git a/.changeset/toclient-raw-response-readable.md b/.changeset/toclient-raw-response-readable.md new file mode 100644 index 00000000000..dd8307783ab --- /dev/null +++ b/.changeset/toclient-raw-response-readable.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Fix repeated `text`, `json`, `arrayBuffer`, and `urlParamsBody` reads in `HttpServerResponse.toClientResponse` for raw Web `Response` bodies, preserving the original response for serving. diff --git a/packages/effect/src/unstable/http/HttpServerResponse.ts b/packages/effect/src/unstable/http/HttpServerResponse.ts index 948eda7df80..b98dd8d6075 100644 --- a/packages/effect/src/unstable/http/HttpServerResponse.ts +++ b/packages/effect/src/unstable/http/HttpServerResponse.ts @@ -1229,7 +1229,7 @@ class ServerHttpClientResponse extends Inspectable.Class implements HttpClientRe const rawBody = body.body if (rawBody instanceof Response) { return Effect.tryPromise({ - try: () => rawBody.arrayBuffer().then((buffer) => new Uint8Array(buffer)), + try: () => rawBody.clone().arrayBuffer().then((buffer) => new Uint8Array(buffer)), catch: (cause) => this.decodeError(cause) }) } diff --git a/packages/effect/test/unstable/http/HttpServerResponse.test.ts b/packages/effect/test/unstable/http/HttpServerResponse.test.ts index b44082cf5a4..4dc1c122799 100644 --- a/packages/effect/test/unstable/http/HttpServerResponse.test.ts +++ b/packages/effect/test/unstable/http/HttpServerResponse.test.ts @@ -81,6 +81,27 @@ describe("HttpServerResponse", () => { assert.strictEqual(roundTrip.headers.get("content-length"), "5") })) + describe("toClientResponse", () => { + it.effect("supports repeated reads of raw Web Response bodies", () => + Effect.gen(function*() { + const clientResponse = HttpServerResponse.toClientResponse( + HttpServerResponse.raw(new Response("hello")) + ) + + assert.strictEqual(yield* clientResponse.text, "hello") + assert.strictEqual(yield* clientResponse.text, "hello") + assert.deepStrictEqual(new Uint8Array(yield* clientResponse.arrayBuffer), new TextEncoder().encode("hello")) + })) + + it.effect("leaves the raw Web Response servable after a client-body read", () => + Effect.gen(function*() { + const response = HttpServerResponse.raw(new Response("hello")) + + assert.strictEqual(yield* HttpServerResponse.toClientResponse(response).text, "hello") + assert.strictEqual(yield* Effect.promise(() => HttpServerResponse.toWeb(response).text()), "hello") + })) + }) + it.effect("fromClientResponse preserves status, headers, cookies, and json", () => Effect.gen(function*() { const request = HttpClientRequest.get("http://localhost:3000/todos/1?existing=1", {