-
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 & QUIC: fix abort read on dispose and cancellation #55724
HTTP/3 & QUIC: fix abort read on dispose and cancellation #55724
Conversation
Tagging subscribers to this area: @dotnet/ncl Issue DetailsThis is a new take on #48624 instead of my previous PR #54334 because further investigation shown this has opened a can of worms. Fixed behaviors:HTTP/3:
QUIC:
Other changes:
NOTES:I've spent a lot of time digging why server didn't get StreamAbortedException on Write even after client clearly called AbortRead and it was synchronized by semaphores. I found out that it is possible that PEER_RECEIVE_ABORTED event will arrive with a significant delay after peer calls AbortRead. In that case even with synchronization via semaphores, first writes after peer aborting may "succeed" (get SEND_COMPLETE event). I ended up asserting that PEER_RECEIVE_ABORTED would arrive eventually.
|
Tagging subscribers to this area: @dotnet/ncl Issue DetailsThis is a new take on #48624 instead of my previous PR #54334 because further investigation shown this has opened a can of worms. Fixed behaviors:HTTP/3:
QUIC:
Other changes:
NOTES:I've spent a lot of time digging why server didn't get StreamAbortedException on Write even after client clearly called AbortRead and it was synchronized by semaphores. I found out that it is possible that PEER_RECEIVE_ABORTED event will arrive with a significant delay after peer calls AbortRead. In that case even with synchronization via semaphores, first writes after peer aborting may "succeed" (get SEND_COMPLETE event). I ended up asserting that PEER_RECEIVE_ABORTED would arrive eventually.
|
/azp run runtime |
Azure Pipelines successfully started running 1 pipeline(s). |
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.
src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs
Outdated
Show resolved
Hide resolved
I observed a failure in
The test called Meanwhile, I guess I will have to add either a new state or a new flag to distinguish cancellation and user abort 🙁 |
Please open an issue on MsQuic and we'll fix. |
@nibanks thanks! |
Issue for updating msquic package: #55746 |
I see a timeout in an added test in CI.
I will be looking into that. |
/azp run runtime |
Azure Pipelines successfully started running 1 pipeline(s). |
/azp run runtime |
Azure Pipelines successfully started running 1 pipeline(s). |
/azp run runtime |
Azure Pipelines successfully started running 1 pipeline(s). |
After increasing the timeouts in As after the approval there were no changes except for NITs and aforementioned timeouts, and CI is green - merging. |
This is a new take on #48624 instead of my previous PR #54334 because further investigation shown this has opened a can of worms.
Fixed behaviors:
HTTP/3:
Dispose on Http3RequestStream doesn't clear the link to QuicStream anymore, so no NRE anymore; a race between Dispose and CancellationToken may still end up in ObjectDisposedException -- I hope that is expected? cc @JamesNK
Don't call Abort on QuicOperationAbortedException - it means stream was already aborted by user
HandleReadResponseContentException should abort Read side of the stream, not Write (was possibly a copy-paste bug)
QUIC:
Dispose should abort read on the wire not only in non-final states, but also in Aborted state that was entered in CancellationToken callback (because we only change state there). There is an issue Cancellation of QuicStream.ReadAsync/WriteAsync should either abort the stream or allow subsequent reads/writes #55485 about aborting on the wire right away in CT callback, but I decided not to do it that way yet, because there is no clear way how to pass correct HTTP/3 error code there.
Dispose should Complete an outstanding Read operation with an appropriate exception
Also a copy-paste bug (or naming confusion) for extracting error code from event HandleEventPeerRecvAborted
Other changes:
Added tests on aborting response content stream by CancellationToken, Dispose, and CancellationToken&Dispose at the same time
GetAsync_LargeHeader_Success was failing quite a few times while I was investigating, so I've put it under respective ActiveIssue [HTTP/3] Test bug (flaky) - premature connection close - need some synchro on app level #55508
NOTES:
I've spent a lot of time digging why server didn't get StreamAbortedException on Write even after client clearly called AbortRead and it was synchronized by semaphores. I found out that it is possible that PEER_RECEIVE_ABORTED event will arrive with a significant delay after peer calls AbortRead. In that case even with synchronization via semaphores, first writes after peer aborting may "succeed" (get SEND_COMPLETE event). I ended up asserting that PEER_RECEIVE_ABORTED would arrive eventually.
Fixes #48624