-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
[HTTP/3] Abort response stream on dispose if content not finished #57156
[HTTP/3] Abort response stream on dispose if content not finished #57156
Conversation
Tagging subscribers to this area: @dotnet/ncl Issue DetailsSends abort read/write if H/3 stream is disposed before respective contents are finished. Fixes #56129 cc: @CarnaViire
|
@@ -1187,6 +1192,25 @@ private async ValueTask<bool> ReadNextDataFrameAsync(HttpResponseMessage respons | |||
public void Trace(string message, [CallerMemberName] string? memberName = null) => | |||
_connection.Trace(StreamId, message, memberName); | |||
|
|||
private void AbortStream() | |||
{ | |||
// ToDo : locking??? we don't have any in H/3 stream, we don't have a sync root or anything. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was looking at H2Stream corresponding method:
runtime/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs
Line 1292 in ae0e5e8
private void CloseResponseBody() |
And the main difference is that we lack any locking mechanism in H3Stream, which kind of worries me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TL;DR: if we check the flag and it's not EOF and on the next line when we call Abort the flag is changed already - it is not much better than just calling Abort without any checks, is it?
We actually had a bit of a discussion on using flags to symbolize EOFs in my first attempt on cancellation PR #54334 (comment)
I guess we might want to put some proper locking here. as HTTP/2 has.. or just always call abort ant let quic stream/msquic handle the race? If we imagine Dispose being called synchronously after finishing reading the response -- I imagine graceful shutdown should be complete inside QuicStream by that time. So Abort will be just a no-op. But I may be wrong. Always calling Abort may lead to unexpected behavior in streaming scenarios 🤔 but I feel like this check without any locks will not be much better that always aborting.
@geoffkizer what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say that checking is still better then not checking at all. There's a potential for race, but in a happy path (dispose the response after you've finished the reading), this should work and not send any aborts.
Anyway, I was surprised that there's no locking mechanism at all. So my question is, do we use any other technique I'm missing? Or is this an oversight that just works because we're testing the right thing? Or are we just not thread-safe on the level of a response message thus we don't care here?
@CarnaViire add #56683: could you point me at the test which might hit that problem? I'll try to repro it locally and then see if I can fix it as well. |
The test is runtime/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs Line 546 in acb6b9b
Namely here you can remove ODE from expected exceptions runtime/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs Line 609 in acb6b9b
The runtime/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs Lines 494 to 496 in acb6b9b
runtime/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs Lines 522 to 524 in acb6b9b
But I thought you touched MsQuicStream, but you did not. So I can do the change myself in a separate PR tomorrow. |
Ok then, do it. I won't try to cram it in this PR. |
Newly added test is failing in CI, need to investigate, hold off reviews for now please. |
03537c9
to
af6a189
Compare
af6a189
to
60949a3
Compare
…ding state within Send* methods.
60949a3
to
3ad021f
Compare
This is ready for review @CarnaViire @geoffkizer |
{ | ||
throw GetConnectionAbortedException(_state); | ||
} | ||
|
||
// Change the state in the same lock where we check for final states to prevent coming back from Aborted/ConnectionClosed. | ||
Debug.Assert(_state.SendState != SendState.Pending); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: should we do an actual throw here? for Pending and Finished states. Both of them would mean someone tries to do a send with an another concurrent send in flight -- that we don't support, and Send state machine, fragile as it is, may blow up because of it. Concurrent reads do throw now. But we can choose not to do it now, your choice.
I also feel bad that we don't have any final state after we send FIN flag (like ReadsCompleted) so that means we also can't prevent user calling Send after passing the FIN flag which I would guess will result in msquic exception (INVALID_STATE most possibly). But that's out of scope of this PR, just me ranting. I guess that's what #55817 is for in the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I share the same grievances and worries as you. I chose to just shuffle the code around to fix the problem on hand instead of doing the ultimate right thing, which I think we should leave to #55817. We've lived with just the assert until now, this change shouldn't make any worse (hopefully 😅 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. I commented my observations but we may not act on it now.
/backport to release/6.0 |
Started backporting to release/6.0: https://github.com/dotnet/runtime/actions/runs/1147767776 |
@ManickaP do you want this to go into RC1 or RC2? The branch that you picked is RC2. |
@ViktorHofer RC2 |
/backport to release/6.0-rc1 |
Started backporting to release/6.0-rc1: https://github.com/dotnet/runtime/actions/runs/1161485673 |
Sends abort read/write if H/3 stream is disposed before respective contents are finished.
Fixes #56129
cc: @CarnaViire