fix(standard-server): settle sendStandardResponse when client disconnects before sending - #1738
Conversation
…ready closed When the client disconnects while the procedure is still running, the response's 'close' event has already fired before sendStandardResponse registers its listeners, so the returned promise never settles and anything awaited after handle() silently never runs. - Add isNodeResponseStreamEnded util that detects an ended response for both http/1 and http/2 (Http2ServerResponse does not expose closed/destroyed at runtime, they are read from its underlying Http2Stream instead) - Settle sendStandardResponse immediately when the response is already ended (rejecting with the stream error if any) and destroy the prepared body so event iterators are cleaned up, in the node, fastify and aws-lambda implementations - Make toAbortSignal abort when the response was already closed at call time, using writableEnded && writableFinished so http/2 client cancels are detected (Http2Stream force-ends its writable side on abnormal close) Fixes #1735
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
More templates
@orpc/ai-sdk
@orpc/arktype
@orpc/client
@orpc/contract
@orpc/experimental-durable-iterator
@orpc/hey-api
@orpc/interop
@orpc/json-schema
@orpc/nest
@orpc/openapi
@orpc/openapi-client
@orpc/otel
@orpc/experimental-pino
@orpc/experimental-publisher
@orpc/experimental-publisher-durable-object
@orpc/experimental-ratelimit
@orpc/react
@orpc/react-query
@orpc/experimental-react-swr
@orpc/server
@orpc/shared
@orpc/solid-query
@orpc/standard-server
@orpc/standard-server-aws-lambda
@orpc/standard-server-fastify
@orpc/standard-server-fetch
@orpc/standard-server-node
@orpc/standard-server-peer
@orpc/svelte-query
@orpc/tanstack-query
@orpc/trpc
@orpc/valibot
@orpc/vue-colada
@orpc/vue-query
@orpc/zod
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — fixes an event-race bug where sendStandardResponse never settles when the client disconnects before sending, blocking cleanup and logging after handle().
- Add
isNodeResponseStreamEndedutility — detects whether a writable response stream can no longer be written to (closed || destroyed || writableFinished). ForHttp2ServerResponse, reads flags from the underlyingHttp2Streambecause the compat API does not expose them at runtime. - Early-return in
sendStandardResponse(node, fastify, aws-lambda) — checks end-of-stream at entry and settles immediately, destroying the body stream so generatorfinallyblocks still run. Rejects witherroredwhen set, resolves otherwise. - Fix
toAbortSignalfor already-closed streams — same early-check pattern withwritableEnded && writableFinishedguard for http2 compat, wherewritableFinishedalone would report as finished even on abnormal close. - Export
utilsfrom@orpc/standard-server-node
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — two incremental improvements since the prior review: added the missing errored-case test for the Fastify adapter, and inlined the isFinishedWriting helper in toAbortSignal.
- Add
rejects when response was destroyed with an errortest for Fastify — fills the coverage gap; the node and aws-lambda adapters already had this test. - Inline
isFinishedWritingintoAbortSignal— replaces the extracted function with inline!stream.writableFinished || !stream.writableEnded, which is De Morgan equivalent. Removes the http2 compat comment since the intent is clear from the condition itself.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed change — one additional test in the Fastify adapter to match existing coverage in the node and aws-lambda adapters.
- Add
resolves when body is not a streamtest for Fastify — covers the already-closed path with a plain object body (no stream cleanup needed). Matches the equivalent tests already present for node and aws-lambda.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
9979bf4 to
bd98a72
Compare

Fixes #1735
handle()hung forever when the client disconnected while the procedure was still running, because the response's'close'event had already fired beforesendStandardResponse()started listening for it.Now:
sendStandardResponse(node, fastify, aws-lambda) settles immediately when the response is already ended and destroys the prepared body so event iterators are cleaned up.toAbortSignalaborts immediately when the response is already closed, including http/2 client cancels.isNodeResponseStreamEndedutil, which also handlesHttp2ServerResponse(it hides its stream state at runtime).Verified with real http/1 and http/2 servers: aborted requests now resolve instead of hanging.