cloudflare / cloudflare/containers

Unable to proxy ReadableStream to client

Open
#53 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
270
Forks
42
Avg merge
1d 5h
Merged PRs (30d)
4

Description

Hey all,

I'm experiencing an issue streaming results from a container to a client.

I have a reproduction of the following issue with instructions: https://github.com/Ehesp/cf-containers-repro

My container (a Bun server) had an endpoint which streams back a 10 messages every second:

```ts
app.post("/stream", async (c) => {
let remaining = 10;
const stream = new ReadableStream({
async start(controller) {
while (remaining > 0) {
console.log("enqueue:", remaining);
controller.enqueue(
new TextEncoder().encode(`Hello, world! ${remaining}\n\n`)
);
await new Promise((resolve) => setTimeout(resolve, 1000));
remaining--;
}

console.log("closing stream");
controller.close();
},
});

return new Response(stream, {
headers: {
"Content-Type": "text/plain",
"Transfer-Encoding": "chunked",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Content-Encoding": "identity",
},
});
});
```

Inside the DO, this is proxied via `containerFetch`:

```ts
async stream() {
const response = await this.containerFetch("http://container/stream", {
method: "POST",
body: JSON.stringify({
message: "Hello, world!",
}),
});

return new Response(response.body, {
headers: response.headers,
status: response.status,
statusText: response.statusText,
});
}
```

My worker then proxies:

```ts
app.post("/stream", async (c) => {
const container = getContainer(c.env.MY_CONTAINER);
const response = await container.stream();

return new Response(response.body, {
headers: response.headers,
status: response.status,
statusText: response.statusText,
});
});
```

My Node client script:

```ts
async function main() {
const response = await fetch("http://localhost:8787/stream", {
duplex: "half",
method: "POST",
body: JSON.stringify({}),
headers: {
"Accept-Encoding": "identity", // Tell the worker to not compress the stream
},
});

console.log("client headers", Object.fromEntries(response.headers));

for await (const chunk of response.body) {
console.log("Client>", new TextDecoder().decode(chunk));
}
}

main().catch((error) => {
console.error(error);
});
```

I constantly get inconsistent results, either the whole thing streams (less often) or some results:

Run 1:

```
client headers {
'cache-control': 'no-cache',
'content-encoding': 'identity',
'content-type': 'text/plain',
date: 'Thu, 17 Jul 2025 08:54:32 GMT',
'transfer-encoding': 'chunked'
}
Client> Hello, world! 10
Client> Hello, world! 9
```

Run 2:

```
client headers {
'cache-control': 'no-cache',
'content-encoding': 'identity',
'content-type': 'text/plain',
date: 'Thu, 17 Jul 2025 08:58:56 GMT',
'transfer-encoding': 'chunked'
}
Client> Hello, world! 10
Client> Hello, world! 9
Client> Hello, world! 8
Client> Hello, world! 7
Client> Hello, world! 6
Client> Hello, world! 5
Client> Hello, world! 4
```

I'm totally unsure where the issue lies, but I've not had this issue when the origin isn't a Cloudflare Container.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.