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

Improve tests for Node.js clients and fix hanging requests #619

Merged
merged 2 commits into from
May 5, 2023

Conversation

timostamm
Copy link
Member

@timostamm timostamm commented May 5, 2023

Investigating #463 and #576, I found situations where both HTTP/1.1 and HTTP/2 client requests on Node.js can hang. This PR fixes them.

The issues were found by adding test coverage. We do not have definitive proof that this fixes #463 because we do not have a reproducible case, but I am fairly confident that it does.

@timostamm timostamm force-pushed the tstamm/fix-hanging-node-requests branch from e183a09 to aa83250 Compare May 5, 2023 16:38
Comment on lines +117 to +120
/**
* Returns a ConnectError for a HTTP/2 error code.
*/
export function connectErrorFromH2ResetCode(
Copy link
Member Author

Choose a reason for hiding this comment

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

Unrelated to the bug fixes.

This is a continuation of #552, where we closed a cancelled request with the HTTP/2 code CANCEL. Now we also interpret HTTP/2 RST codes received by the client correctly.

Comment on lines +144 to +145
sentinel.catch((reason) =>
response.destroy(connectErrorFromNodeReason(reason))
Copy link
Member Author

Choose a reason for hiding this comment

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

In #552, we started handling abort signals in the client ourselves. We do the same here now, to reliably close the request on cancellation.

Without this change, the tests added in this PR fail.

Copy link
Member

Choose a reason for hiding this comment

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

Will this address the issue of explicit cancellations from the client and how the server handles those?

Comment on lines +381 to +385
stream.on("close", function h2StreamClose() {
const err = connectErrorFromH2ResetCode(stream.rstCode);
if (err) {
sentinel.reject(err);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This makes sure we always raise an error if the request dies.

Without this change, the tests added in this PR fail.

try {
await client({
url: "http://example.com",
describe("universal node http client", function () {
Copy link
Member Author

Choose a reason for hiding this comment

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

These added tests spin up a Node.js HTTP/1.1 or HTTP/2.2 server that aborts incoming requests. We run a universal Node.js client (which is used by all Transports from @bufbuild/connect-node) against the servers. We test closing the request at several stages, for example before the first response byte is written, or while the request body is still read.

Comment on lines +354 to +356
reason instanceof ConnectError && reason.code == Code.Canceled
? H2Code.CANCEL
: H2Code.INTERNAL_ERROR;
Copy link
Member Author

Choose a reason for hiding this comment

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

No logical changes here, just making use of the added enumeration for error codes.

Comment on lines +556 to +557
signal?.reason ??
new ConnectError("This operation was aborted", Code.Canceled)
Copy link
Member Author

Choose a reason for hiding this comment

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

Unrelated to the bug fixes. With Node.js v16 being EOL soon, we can make better use of AbortSignal.reason.

@timostamm timostamm marked this pull request as ready for review May 5, 2023 17:18
@timostamm timostamm requested review from smaye81 and dimitropoulos May 5, 2023 17:18
Comment on lines +144 to +145
sentinel.catch((reason) =>
response.destroy(connectErrorFromNodeReason(reason))
Copy link
Member

Choose a reason for hiding this comment

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

Will this address the issue of explicit cancellations from the client and how the server handles those?

@timostamm
Copy link
Member Author

Will this address the issue of explicit cancellations from the client and how the server handles those?

Do you mean #576? No, it does not.

@timostamm timostamm merged commit ea443b7 into main May 5, 2023
@timostamm timostamm deleted the tstamm/fix-hanging-node-requests branch May 5, 2023 17:55
@smaye81
Copy link
Member

smaye81 commented May 5, 2023

Will this address the issue of explicit cancellations from the client and how the server handles those?

Do you mean #576? No, it does not.

Ah no, I meant this discussion: #315 (comment) (I think they're different issues).

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.

Stream connection hanging forever
2 participants