-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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] Should QuicConnection.Dispose abort? #60235
Comments
Tagging subscribers to this area: @dotnet/ncl Issue DetailsWhen QuicConnection.Dispose is called it sends an abort with error code 0. Is sending an abort here the right behavior? For comparison: When a browser using HTTP/3 is closed, the browser closes the QUIC transport without an abort.
|
Triage: We should make it part of QUIC API review. We might stick to current implementation. |
To clarify: There are essentially two ways to close a QUIC connection: Additionally, the "immediate close" comes in two flavors: When you say this:
Do you mean we are sending a CONNECTION_CLOSE with app error code = 0? That seems like incorrect behavior. Or do you mean we are sending CONNECTION_CLOSE with QUIC error code = 0, i.e. NO_ERROR? That seems reasonable.
Meaning, it lets the connection idle timeout? It's not clear to me whether it's preferred to send CONNECTION_CLOSE with QUIC error code = NO_ERROR vs just letting the connection idle timeout. The former seems better to me, in that it lets the server know promptly that the connection can be closed, as opposed to waiting for the idle timeout. That said, if the consensus behavior here is just to idle timeout, maybe we should do that. |
I tried to reproduce this with H/3 Kestrel server and In runtime/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs Lines 858 to 861 in 8a06254
Which doesn't send anything on the wire docs:
Leading to the peer side connection waiting on the idle timeout and then getting closed "by transport". But if you're talking about runtime/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs Line 122 in 8a06254
It'll always wait for all streams to finish and then it'll gracefully close the connection (informing the peer with application code NoError ). And I think this behavior is correct as it is now.
BTW, you can achieve the same thing by killing the client app right after processing the response without disposing the Or did I misunderstood your description? Please let me know. |
Hmm, I recall my testing with a browser + Kestrel has the server immediately end its connection when the browser is closed. Next time I have a HTTP/3 website setup I'll test again. |
Pure speculation -- Is it possible for browsers to store TLS parameters and "fast resume" a connection without 0-RTT? This would explain them wanting to time out. |
@JamesNK Could you collect msquic logs or at least |
Logs of this happening are already recorded here - #60133 (comment) |
Yeah, thanks, but there are no timestamps though so we don't know when the 'shutdown initiated by transport' happened (right after stream dispose or after a certain timeout):
Unless you can say from the logs somehow. |
Logs with timestamp:
Screen capture that was used to create these logs. As you can see, the server immediately receives SHUTDOWN_INITIATED_BY_TRANSPORT when the browser is closed. |
I see, I don't think this is possible with msquic at the moment. |
AFAIU, @JamesNK would like to simulate behavior of a closed browser window with System.Net.Quic, which is sending CONNECTION_CLOSE frame of "transport" layer (0x1C, and getting This can be achieved by dropping the connection and waiting IDLE_TIMEOUT on the peer, but it's not immediate. |
By design, the MsQuic API only exposes the ability for an app to send application close frames, not transport connection close ones. If you want to trigger a faster idle timeout, just set a shorter idle timeout period for that particular test. |
Thanks for the explanation Nick. @JamesNK in the light of all this info I'll close this. |
What's the reason for this? Given that browsers do send a transport connection close, I suspect we will get customer questions about this. |
Per protocol, apps send application close frames. Only the QUIC transport itself sends connection close frames. |
So basically, the browsers are doing the wrong thing? |
I still don't quite understand the problem here. Can someone elaborate more please?
What error is being sent in this case? |
Actually, I'm not sure I understand either. I would expect that a browser (or any other client app) that wants to close an HTTP3 connection would send app-level (0x1d) CONNECTION_CLOSE with error code = H3_NO_ERROR. @JamesNK @ManickaP Is that what we actually see with browsers in practice? It's unclear to me from the trace above. And similarly, I would expect that our HTTP3 logic in SocketsHttpHandler would do the same when it wants to close a connection. Note, though, that users of SocketsHttpHandler cannot directly close a specific connection; rather, a connection will be closed when either (a) the connection pool decides it's no longer needed/wanted; or (b) the user explicitly Disposes the SocketsHttpHandler. Also note that SocketsHttpHandler should close the QUIC connection by calling QuicConnection.CloseAsync() explicitly, so that it can specify the proper app-level error code for CONNECTION_CLOSE. There's a separate issue of what QuicConnection.Dispose should do if the user does not call QuicConnection.CloseAsync() first, but it's not relevant to the HTTP3 behavior since our HTTP3 code should always call CloseAsync first. QUIC idle timeout seems fine here to me. |
According to logs above, browser sends 0x1C when closed, i.e.: SHUTDOWN_INITIATED_BY_TRANSPORT. Comparing with TCP, closed browser tab (in Firefox) will send graceful FIN to end the connection. Finally, HttpClient is not just used as a browser, it's used in many different ways (proxy, gRPC, REST APIs, ...) so mimicking browser behavior to a T in all cases might not be desirable. |
When QuicConnection.Dispose is called it sends an abort with error code 0. Is QuicConnection sending an abort here the right behavior?
For comparison: When a browser using HTTP/3 is closed, the browser closes the QUIC transport without an abort.
One of the reasons I bring it up is there doesn't appear to be a way to simulate browser behavior using QuicConnection. Connections always close with an abort, not with the transport being closed.
The text was updated successfully, but these errors were encountered: