Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle GOAWAY frames with NO_ERROR in Node.js clients #681

Merged
merged 2 commits into from
Jun 21, 2023

Conversation

timostamm
Copy link
Member

@timostamm timostamm commented Jun 20, 2023

In v0.10.0, we added support for basic keepalive for Node.js HTTP/2 clients (implemented in #673).

We made a mistake and expected all GOAWAY frames to close a client session, but that is not the case for the HTTP/2 error code NO_ERROR:

NO_ERROR (0x0): The associated condition is not a result of an error. For example, a GOAWAY might include this code to indicate graceful shutdown of a connection.

The graceful shutdown is further specified:

GOAWAY allows an endpoint to gracefully stop accepting new streams while still finishing processing of previously established streams. This enables administrative actions, like server maintenance.

The Node.js http2 module implements this behavior, and raises an error when new request is issued on a session. This is the issue we are seeing with #680.

This PR fixes #680 by opening a new connection if the current one has received a GOAWAY with NO_ERROR. Connections that are gracefully shutting down are maintained until all streams have finished.

Comment on lines 707 to 722
if (errorCode === http2.constants.NGHTTP2_NO_ERROR) {
receivedGoAwayNoError = true;
const nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
// Node.js v16 closes a connection on its own when it receives a GOAWAY
// frame and there are no open streams (emitting a "close" event and
// destroying the session), but more recent versions do not.
if (streamCount == 0 && nodeMajor >= 18) {
conn.destroy(
new ConnectError(
"received GOAWAY without any open streams",
Code.Canceled
),
http2.constants.NGHTTP2_NO_ERROR
);
}
}
Copy link

@jeanp413 jeanp413 Jun 20, 2023

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

// Node.js v16 closes a connection on its own when it receives a GOAWAY
// frame and there are no open streams (emitting a "close" event and
// destroying the session), but more recent versions do not.

Does it mean the bug shouldn't happen in node 16? I'm using node 16 and I can confirm it does happen

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure if @grpc/grpc-js is really always closing the connection when receiving a GOAWAY. I've updated the PR description with details about the graceful shutdown. I would expect an HTTP/2 client to drain streams first. It is possible that @grpc/grpc-js does not do so for historical reasons.

Note that there is a distinction between error code NO_ERROR and actual error codes, and also the special behavior in case of ENHANCE_YOUR_CALM (last paragraph in basic keepalive).

Regarding the comment: The http2 module implements graceful shutdown. It rejects new streams, and it automatically closes the connection after all streams have finished. But if a connection without open streams receives a GOAWAY with NO_ERROR, behavior differs between versions. Seeing #680 in v16 is expected.

@timostamm timostamm requested a review from smaye81 June 21, 2023 13:45
@timostamm timostamm marked this pull request as ready for review June 21, 2023 13:56
@timostamm timostamm merged commit 50aad18 into main Jun 21, 2023
@timostamm timostamm deleted the tstamm/goaway-no-error-client branch June 21, 2023 14:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

New streams cannot be created after receiving a GOAWAY
3 participants