Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Set StatusCode before disposing HttpContext (#876) #993

Merged
merged 2 commits into from
Oct 17, 2016

Conversation

mikeharder
Copy link
Contributor

@mikeharder mikeharder commented Jul 19, 2016

}

await ProduceEnd();
_application.DisposeContext(context, _applicationException);
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be called even if _requestAborted is true. This can be the case if the app calls HttpContext.Abort(). Might want to add a test for that.

It will also probably require another finally block because messageBody.Consume(); can throw too.

Copy link
Contributor Author

@mikeharder mikeharder Jul 20, 2016

Choose a reason for hiding this comment

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

Thanks, I will investigate further. This code was changed recently in 3186e1b.

CC: @CesarBS

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently, if the app calls HttpContext.Abort(), then HttpContext.Response.StatusCode will be 200 when it's disposed (which appears in the logs). I agree this seems wrong. However, if the app aborts the request then no status code is sent to the client, so the status of the request is technically undefined.

What should HttpContext.Response.StatusCode be in this case? We can set it to 500 which is arguably better than 200, but something like null or -1 (property is an int rather than HttpStatusCode) might be more appropriate.

Copy link
Contributor

Choose a reason for hiding this comment

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

503? Service Unavailable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Perhaps HttpContext.Response.StatusCode should just be ignored in this case, and the logging code should check HttpContext.RequestAborted, and display something other than the status code (e.g. "aborted").

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@benaadams: I think HttpContext.Response.StatusCode should remain whatever it was before the request was aborted, which is already working with my current changes. This seems like a separate issue of whether logging should differentiate between completed and aborted requests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One edge case currently not handled, is an app which calls HttpContext.Abort() and then throws an exception, which should probably result in HttpContext.Response.StatusCode = 500. We can try to call ProduceEnd() even if the request has been aborted, which will correct set the status code, but I'm not sure if ProduceEnd() assumes the request was not aborted.

Copy link
Contributor

Choose a reason for hiding this comment

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

Not changing it to something related to it being aborted might confuse automated log parsing?

@cesarblum
Copy link
Contributor

Check the failures on Travis - you might need to update those tests that failed.

}

// ProduceEnd() must be called before _application.DisposeContext(), to ensure
// HttpContext.Response.StatusCode is correctly set when
// IHttpContextFactory.Dispose(HttpContext) is called.
await ProduceEnd();
Copy link
Member

Choose a reason for hiding this comment

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

EngineTests.NoResponseSentWhenConnectionIsClosedByServerBeforeClientFinishesSendingRequest seems to be failing because ProduceEnd was moved outside the if block.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure that's the case. That test calls Abort(), which will close the connection, so anything written after that (the error response written by ProduceEnd() should not be received by the client.

@cesarblum cesarblum force-pushed the mikeharder/httpcontext-dispose branch from 760225c to b822cd2 Compare October 7, 2016 19:23
// IHttpContextFactory.Dispose(HttpContext) is called.
await ProduceEnd();
}
finally
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks a bit awful. An alternative would be to call DisposeContext() is the catch block for BadHttpRequestException (this is addressing what @halter73 pointed out - DisposeContext() won't be called if messageBody.Consume() throws because of a malformed request.

Copy link
Member

Choose a reason for hiding this comment

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

I think we can get rid of the finally above. I've wanted to clean up the control flow of this method for a while. It's not exactly buggy, but i's confusing because it's not immediately clear what we expect can throw.

Copy link
Member

Choose a reason for hiding this comment

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

To be clear, not this finally, but the one after catch { ReportApplicationError }.

Copy link
Member

Choose a reason for hiding this comment

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

My suggestion is that we have a new try immediately after _application.CreateContext(this);. This try block would continue all the way until this finally.

Yes. This means you would have try { try { await _application.ProcessRequestAsync(context).ConfigureAwait(false); } catch { ..., but at least that's easier to follow.

@cesarblum
Copy link
Contributor

Ping.

@cesarblum cesarblum force-pushed the mikeharder/httpcontext-dispose branch from b822cd2 to 81a182a Compare October 11, 2016 17:29
@mikeharder
Copy link
Contributor Author

:shipit:

@cesarblum cesarblum force-pushed the mikeharder/httpcontext-dispose branch from 81a182a to 25b2c94 Compare October 11, 2016 23:17
@cesarblum
Copy link
Contributor

Updated. @halter73 I'm setting status code to 0 when the request is aborted and no response has started.

@cesarblum cesarblum force-pushed the mikeharder/httpcontext-dispose branch from 25b2c94 to 80b7252 Compare October 11, 2016 23:24
}
}

StopStreams();
Copy link
Member

Choose a reason for hiding this comment

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

Do you think StopStreams() should go in the finally?

{
// If the request was aborted and no response was sent, there's no
// meaningful status code to log.
StatusCode = 0;
Copy link
Member

Choose a reason for hiding this comment

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

How does this interact with TryProduceInvalidRequestResponse()?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm thinking I should move await TryProduceInvalidRequestResponse(); inside the check for _requestAborted == 0.

},
expectedClientStatusCode: null,
expectedServerStatusCode: HttpStatusCode.BadRequest,
sendMalformedRequest: true);
Copy link
Member

Choose a reason for hiding this comment

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

We should have a test for when the app actual reads the request body, and the swallows the exception or doesn't.

@cesarblum
Copy link
Contributor

@halter73 5b03f68 splits request rejection and setting bad request state in Frame, as we dicussed yesterday.

@cesarblum cesarblum force-pushed the mikeharder/httpcontext-dispose branch from 5b03f68 to f7b7319 Compare October 12, 2016 17:13
@cesarblum
Copy link
Contributor

I'll add a test that validates that an app can write it's own 400 response if it chooses to handle a BadHttpRequestException.

@cesarblum cesarblum force-pushed the mikeharder/httpcontext-dispose branch from f7b7319 to 15dae1a Compare October 12, 2016 18:53
_requestRejected = true;

Log.ConnectionBadRequest(ConnectionId, ex);
_requestRejectedException = ex;
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't _requestRejectedException be set in RejectRequest?

Copy link
Contributor

@cesarblum cesarblum Oct 13, 2016

Choose a reason for hiding this comment

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

I think this is the appropriate place to set that. Connection calls SetBadRequestState() when a time out occurs, and that will be picked up by later by TryProduceInvalidRequestResponse(). Besides, _requestRejectedException is state too.

@cesarblum cesarblum force-pushed the mikeharder/httpcontext-dispose branch from 2fe2be0 to 2164f8a Compare October 13, 2016 18:38
@halter73
Copy link
Member

SingleErrorResponseSentWhenAppSwallowsBadRequestException is failed on the last commit on AppVeyor. It was testing this commit where the TCP loopback fast path code was already removed.

@cesarblum
Copy link
Contributor

TestConnection is failing to receive data when the FIN handshake isn't completed and the server sends an RST. My guess is that the shutdown callback is called before the FIN handshake is done, and the RST is sent when we dispose the socket.

It's a weird issue though since the response data was sent by the server (can see it in Message Analyzer), but it looks as if you can't read buffered data if the connection was reset. The problem is probably all the way down in winsock - we've seen a similar issue in the past.

@cesarblum
Copy link
Contributor

The test failure is not related to this PR. Any more feedback?

@cesarblum cesarblum force-pushed the mikeharder/httpcontext-dispose branch from 2164f8a to 5b79e7e Compare October 17, 2016 21:03
{
// End the connection for non keep alive as data incoming may have been thrown off
return;
SetBadRequestState(ex);
Copy link
Member

Choose a reason for hiding this comment

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

Add comment that this is for bad request data in the request body (not start line/headers) and SetBadRequestState can't be called later because _application.DisposeContext logs the status code.

Copy link
Contributor

Choose a reason for hiding this comment

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

Added comments.

@halter73
Copy link
Member

:shipit:

@cesarblum cesarblum force-pushed the mikeharder/httpcontext-dispose branch from a868f6f to 7858479 Compare October 17, 2016 22:17
@cesarblum cesarblum merged commit 7858479 into dev Oct 17, 2016
@cesarblum cesarblum deleted the mikeharder/httpcontext-dispose branch October 17, 2016 22:22
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants