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

Revised request / inject abort handling #4295

Merged
merged 1 commit into from
Oct 9, 2021
Merged

Conversation

kanongil
Copy link
Contributor

@kanongil kanongil commented Oct 5, 2021

This is a targeted PR to fix the crash issue in #4294. Unfortunately it grew quite complex due to inconsistencies around how aborts & transmission errors are reported.

The issue in #4294 was mostly covered by this existing test:

it('does not crash when request is aborted', async () => {

The only thing missing is to trigger the internals.chain() loop in transmit.js, which I have done by adding a accept-encoding: gzip header to the request.

Most of the PR comes from revising server.inject() to handle an inconsistency, where it converts transmission errors to appear as actual server responses. This is contrary to regular http clients, which will emit / throw an error. I'm not sure whether this can be considered a bug fix, but its plausible given that it is supposed to work similar to a regular http client, and the docs does not cover how transmission errors are signalled.

FYI, I felt I had to do something, since currently 499 request abortion errors have two different response signatures depending on which stage of the requests it arrives at. One is a simple Boom error, while the other is a regular response. Ie. the statusCode is either found at the logged response.statusCode or response.output.statusCode.

@kanongil kanongil added the bug Bug or defect label Oct 5, 2021
@kanongil
Copy link
Contributor Author

kanongil commented Oct 5, 2021

Why are there no CI checks active?

@devinivy
Copy link
Member

devinivy commented Oct 5, 2021

@kanongil odd! Looking into it. (Thanks for all this work!)

@Nargonath
Copy link
Member

I can see them in the PR, perhaps there was just a delay on GH infra side? 🤔

@cjihrig
Copy link
Contributor

cjihrig commented Oct 5, 2021

Yes, there was an issue on GitHub's side: https://www.githubstatus.com/incidents/bdbzpz7qxmbx

@@ -298,14 +297,19 @@ internals.end = function (env, event, err) {
request._core.Response.drain(stream);
}

err = err || new Boom.Boom(`Request ${event}`, { statusCode: request.route.settings.response.disconnectStatusCode });
const error = internals.error(request, Boom.boomify(err));
Copy link
Member

Choose a reason for hiding this comment

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

Is there a particular reason you dropped the use of internals.error() here? After the end of the request lifecycle in all other cases I believe hapi does ensure request.response is a response object.

The repro case in #4244 is an interesting example, since the 'response' event is used and assumes a response object exists on request.response at that point (was that a fair assumption?). In this case 'response' is emitted and request.response is instead a boom error.

Want to be clear that I am not opposed to this, just want to think through the implications together and ensure they are what we want 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the concern. I did it for consistency and to signal that it was not actually delivered. Current aborts can also set a boom object as the response, when they happen before the transmit has started:

request._setResponse(new Boom.Boom('Request aborted', { statusCode: request.route.settings.response.disconnectStatusCode }));

and tested as such here:

hapi/test/transmit.js

Lines 567 to 569 in 7b4d7d8

const [request] = await log;
expect(request.response.isBoom).to.be.true();
expect(request.response.output.statusCode).to.equal(499);

However, a case can be made that better consistency will be achieved by always making a response from it, but it seems quite wasteful and confusing to me, as there is no actual transmitted response.

WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

Taking a closer look, I agree with you that this is the most consistent behavior 👍

Copy link
Member

@devinivy devinivy left a comment

Choose a reason for hiding this comment

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

I had one open question, but I poked at this quite a bit and I feel it is a solid patch! I am pleased to see that this addresses #4244 without running into the awkward coverage issue I ran into, which I think is a good sign.

Copy link
Member

@Nargonath Nargonath left a comment

Choose a reason for hiding this comment

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

I'm not that familiar with that part of the framework but it LGTM. Well done @kanongil 💪 .

@kanongil
Copy link
Contributor Author

kanongil commented Oct 7, 2021

I am pleased to see that this addresses #4244 without running into the awkward coverage issue I ran into, which I think is a good sign.

I'm not sure this PR fully addresses #4244. I have made no effort towards always calling res.end().

@Nargonath Nargonath linked an issue Oct 7, 2021 that may be closed by this pull request
@Nargonath
Copy link
Member

I've linked the issue #4294 to this MR for it to be closed automatically but feel free to remove it @kanongil in case it was a mistake.

@devinivy
Copy link
Member

devinivy commented Oct 8, 2021

I plan to release this patch tomorrow (Friday) pending any additional review.

Also, Gil is correct that this only resolves half of #4244. I think this resolves the more important and more difficult half, but it will still be worth taking another look at it to ensure we always call res.end() during aborts. Thanks, all!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Bug or defect
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Server crashes if a requested aborted during sending a response.
4 participants