#8205·effect

转换一个 FerveHttpClient 响应流 滴出工人已知的长度能力

作者: third774创建于 2026年9月12日更新于 2026年9月12日

运行的 Effect 版本是什么? - Effect 4.0.0-rc.112 - @Cloudflare/vitest-pool-workers 0.22.0 - Miniflare 5.20260815.0-alpha / workerd 1.20260815.1 ## 如何重现此错误? import { env } from "Cloudflare:workers" import { Effect, Stream } from "effect" import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http" import { expect, it, vi } from "vitest" it("在 Effect 转换后丢弃原生体的已知长度", async () => { const bytes = new Uint8Array([1, 2, 3]) const url = "https://example.com/archive.tgz" vi.stubGlobal("fetch", () => Promise.resolve(new Response(bytes))) const nativeResponse = await fetch(url) if (nativeResponse.body === null) throw new Error("缺少响应体") await expect(env.BACKUP.put("native.tgz", nativeResponse.body)).resolves.not.toBeNull() const program = Effect.gen(function*() { const request = HttpClientRequest.get(url) const response = yield* HttpClient.execute(request) const body = yield* Stream.toReadableStreamEffect(response.stream) yield* Effect.tryPromise(() => env.BACKUP.put("effect.tgz", body)) }).pipe(Effect.provide(FetchHttpClient.layer)) await expect(Effect.runPromise(program)).rejects.toThrow("提供的可读流必须具有已知长度") }) 在同一运行时中,原生响应体写入成功。FixedLengthStream.readable也成功。 ## 预期行为是什么? 对于上面展示的直接转换,Stream.toReadableStreamEffect(response.stream) 应生成一个 Web 流,保留源体的运行时已知长度,并被 R2 接受。 ## 你看到的是什么? 公共 HttpClientResponse body API 是一个 Effect Stream。使用 Stream.toReadableStreamEffect() 将其转换为一个通用 Web ReadableStream,它不保留 R2 所查询的运行时内部身份长度能力。体字节保持不变,但 R2 拒绝转换的流。这阻止了通过 HttpClientResponse.stream 直接流式传输,而不重构长度或缓冲体。 ## 补充信息 HttpClientResponse.fromWeb() 将原始 Response 保存在一个私有字段中。HttpClientResponse.streamResponse.body 包装为 Stream.fromReadableStream。HTTP 客户端还添加了中断和最终化包装器。Stream.toReadableStreamEffect() 然后创建了一个新的 Web 流,因此 workerd 无法再查询源体的身份长度。 Cloudflare 文档中将 ReadableStream 定义为一个合法的 R2Bucket.put() 值,但 workerd 的 R2 实现要求流在开始写入之前报告身份长度。此检查位于 workerd 本身,而不仅仅是在 Miniflare 中。 本请求涉及直接转换 response.stream。一个 …

内容来源: Effect-TS/effect