Skip to content

fix(@angular/ssr): settle writeResponseToNodeResponse when client disconnects - #33721

Merged
alan-agius4 merged 1 commit into
angular:mainfrom
alan-agius4:fix-33719
Jul 31, 2026
Merged

fix(@angular/ssr): settle writeResponseToNodeResponse when client disconnects#33721
alan-agius4 merged 1 commit into
angular:mainfrom
alan-agius4:fix-33719

Conversation

@alan-agius4

@alan-agius4 alan-agius4 commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

When a client disconnects while a response is backpressured or streaming, the Node response (ServerResponse or Http2ServerResponse) is closed or destroyed without emitting a drain event. Previously, this caused writeResponseToNodeResponse() to park indefinitely waiting for drain and never settle.

This change monitors whether the Node response is closed or destroyed and removes event listeners, cancels the reader, and resolves the returned Promise when a client disconnect occurs.

Closes #33719

gemini-code-assist[bot]

This comment was marked as outdated.

@alan-agius4
alan-agius4 marked this pull request as draft July 31, 2026 18:52
@alan-agius4 alan-agius4 added the target: patch This PR is targeted for the next patch release label Jul 31, 2026
@alan-agius4
alan-agius4 force-pushed the fix-33719 branch 2 times, most recently from 2cd9689 to e4a510a Compare July 31, 2026 19:29
@alan-agius4
alan-agius4 marked this pull request as ready for review July 31, 2026 19:30
@alan-agius4
alan-agius4 requested a review from dgp1130 July 31, 2026 19:30
@alan-agius4 alan-agius4 added action: review The PR is still awaiting reviews from at least one requested reviewer and removed area: @angular/cli labels Jul 31, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces robust handling for closed or destroyed Node.js server responses during streaming. It adds helper checks to prevent writing to terminated responses and ensures that the response stream reader is properly cancelled and cleaned up when a client disconnects or an error occurs, particularly under backpressure conditions. Tests have been added for both HTTP/1.1 and HTTP/2 to verify this behavior. The review feedback suggests using optional chaining when accessing destination.req.url to prevent potential TypeErrors in environments where req might be undefined.

Comment thread packages/angular/ssr/node/src/response.ts
Comment on lines +100 to +101
destination.once('close', cancelReader);
destination.once('error', cancelReader);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Is there a risk of leaking memory if ex. close is emitted, but then the error handler is still listening? Do we need to unsubscribe the other listener? I always get a little fuzzy with how event handlers behavior for GC. Looks like we're doing that below at least?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! In Node.js EventEmitter, leaving the 'error' listener attached after 'close' emits would keep cancelReader in destination._events['error'] until the finally block runs at the end of the response stream.

To make cleanup immediate and deterministic (matching the pattern used below in the backpressure Promise), I've updated cancelReader() to immediately unsubscribe both 'close' and 'error' listeners (destination.off('close', cancelReader) and destination.off('error', cancelReader)) as soon as it is invoked.

…connects

When a client disconnects while a response is backpressured or streaming,
the Node response (`ServerResponse` or `Http2ServerResponse`) is closed or
destroyed without emitting a `drain` event. Previously, this caused
`writeResponseToNodeResponse()` to park indefinitely waiting for `drain`
and never settle.

This change monitors whether the Node response is closed or destroyed
and removes event listeners, cancels the reader, and resolves the
returned Promise when a client disconnect occurs.

Closes angular#33719
@alan-agius4 alan-agius4 added action: merge The PR is ready for merge by the caretaker and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Jul 31, 2026
@alan-agius4
alan-agius4 merged commit b74eef8 into angular:main Jul 31, 2026
40 checks passed
@alan-agius4
alan-agius4 deleted the fix-33719 branch July 31, 2026 20:57
@alan-agius4

Copy link
Copy Markdown
Collaborator Author

This PR was merged into the repository. The changes were merged into the following branches:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action: merge The PR is ready for merge by the caretaker area: @angular/ssr target: patch This PR is targeted for the next patch release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@angular/ssr: writeResponseToNodeResponse() does not settle when the client disconnects while the response is backpressured

2 participants