Streaming a text/plain async generator route works on a direct HTTP/1.1 connection but breaks behind any HTTP/2 reverse proxy (Traefik, nginx, Caddy, Cloudflare, etc.). The detection added in #213 requires Transfer-Encoding: chunked, which HTTP/2 doesn't allow as a response header (RFC 7540 §8.1.2.2), so proxies strip it on h1→h2. Eden then falls into await response.text() and the request hangs forever on long-lived streams (while (true) generators stay in pending).
sse() works because the SSE branch matches on Content-Type only.
Repro
new Elysia()
.get("/live", async function* () {
let i = 0
while (true) {
yield \`\${i}\`
i++
await new Promise(r => setTimeout(r, 1000))
}
})
.listen(3000)
- Direct: `curl -i http://localhost:3000/live\` → `Transfer-Encoding: chunked` present, Eden streams.
- Through Traefik on HTTPS/h2: response has no `Transfer-Encoding` header, Eden hangs.
Fix
Drop the `Transfer-Encoding: chunked` clause from the streaming detection in the default branch of the Content-Type switch:
```ts
response.headers.get('content-type')?.startsWith('text/') &&
!response.headers.has('content-length')
```
This does not regress #213 (the issue that originally added the chunked check). Per RFC 7230 §3.3.1, `Transfer-Encoding: chunked` and `Content-Length` are mutually exclusive, so any HTTP/1.1 chunked response already satisfies the remaining `!has('content-length')` check. A non-streaming `text/plain` response continues to be buffered because it carries a `Content-Length`.
Happy to PR with regression tests.
Possibly related: #255 / PR #256 fix non-SSE chunk yield semantics (chunks weren't emitting until end). Both fixes are needed for HTTP/2 plain-text streams to work end-to-end.
Streaming a
text/plainasync generator route works on a direct HTTP/1.1 connection but breaks behind any HTTP/2 reverse proxy (Traefik, nginx, Caddy, Cloudflare, etc.). The detection added in #213 requiresTransfer-Encoding: chunked, which HTTP/2 doesn't allow as a response header (RFC 7540 §8.1.2.2), so proxies strip it on h1→h2. Eden then falls intoawait response.text()and the request hangs forever on long-lived streams (while (true)generators stay inpending).sse()works because the SSE branch matches onContent-Typeonly.Repro
Fix
Drop the `Transfer-Encoding: chunked` clause from the streaming detection in the default branch of the Content-Type switch:
```ts
response.headers.get('content-type')?.startsWith('text/') &&
!response.headers.has('content-length')
```
This does not regress #213 (the issue that originally added the chunked check). Per RFC 7230 §3.3.1, `Transfer-Encoding: chunked` and `Content-Length` are mutually exclusive, so any HTTP/1.1 chunked response already satisfies the remaining `!has('content-length')` check. A non-streaming `text/plain` response continues to be buffered because it carries a `Content-Length`.
Happy to PR with regression tests.
Possibly related: #255 / PR #256 fix non-SSE chunk yield semantics (chunks weren't emitting until end). Both fixes are needed for HTTP/2 plain-text streams to work end-to-end.