-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
[QUIC] QuicStream
reading/writing work
#90253
Changes from 22 commits
844c88e
ddb8ee7
bd5a627
08f1bae
a3ae2b7
aeb7495
9d9334f
80185f0
33e18ed
f6da31e
79023fe
3b24271
e9655b7
2800015
0c35a2b
d3ba302
5c33e2e
77b7636
e4c2f17
d6f5061
dbd5aec
8d22715
f6261e9
9f626a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1664,10 +1664,17 @@ public async Task ServerSendsTrailingHeaders_Success() | |||||||||||||||||||||
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
public enum CloseOutboundControlStream | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
BogusData, | ||||||||||||||||||||||
Dispose, | ||||||||||||||||||||||
Abort, | ||||||||||||||||||||||
} | ||||||||||||||||||||||
[Theory] | ||||||||||||||||||||||
[InlineData(true)] | ||||||||||||||||||||||
[InlineData(false)] | ||||||||||||||||||||||
public async Task ServerClosesOutboundControlStream_ClientClosesConnection(bool graceful) | ||||||||||||||||||||||
[InlineData(CloseOutboundControlStream.BogusData)] | ||||||||||||||||||||||
[InlineData(CloseOutboundControlStream.Dispose)] | ||||||||||||||||||||||
[InlineData(CloseOutboundControlStream.Abort)] | ||||||||||||||||||||||
public async Task ServerClosesOutboundControlStream_ClientClosesConnection(CloseOutboundControlStream closeType) | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
using Http3LoopbackServer server = CreateHttp3LoopbackServer(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
@@ -1680,13 +1687,31 @@ public async Task ServerClosesOutboundControlStream_ClientClosesConnection(bool | |||||||||||||||||||||
await using Http3LoopbackStream requestStream = await connection.AcceptRequestStreamAsync(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
// abort the control stream | ||||||||||||||||||||||
if (graceful) | ||||||||||||||||||||||
if (closeType == CloseOutboundControlStream.BogusData) | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
await connection.OutboundControlStream.SendResponseBodyAsync(Array.Empty<byte>(), isFinal: true); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
else | ||||||||||||||||||||||
else if (closeType == CloseOutboundControlStream.Dispose) | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
connection.OutboundControlStream.Abort(Http3LoopbackConnection.H3_INTERNAL_ERROR); | ||||||||||||||||||||||
await connection.OutboundControlStream.DisposeAsync(); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
else if (closeType == CloseOutboundControlStream.Abort) | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
int iterations = 5; | ||||||||||||||||||||||
while (iterations-- > 0) | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why are iterations and delay needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Depending on timing, the client can receive the control stream abort, i.e. RESET_STREAM, before it even read the HTTP/3 stream type from it, see runtime/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs Line 459 in f69470a
QUIC spec says, RESET can discard data, which can happen in this case. Add to it H/3 spec, which says that unrecognized or aborted stream should be ignored: runtime/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs Lines 477 to 485 in f69470a
Leading to ignoring the incoming control stream, because client doesn't know it's control, instead of reacting to the abort and closing the connection. So this fix attempts to do the again, but with slight delay between sending the data and abort, line 1710. And why this worked before: we would return data for one QuicStream.ReadAsync before propagating the error, not the whole buffer, just one call to read succeed, which was enough to get that 1 byte saying it's control. Also, MsQuic could always discard the data, we just never observed it (timing, too fast on the same machine...) |
||||||||||||||||||||||
{ | ||||||||||||||||||||||
connection.OutboundControlStream.Abort(Http3LoopbackConnection.H3_INTERNAL_ERROR); | ||||||||||||||||||||||
// This sends RESET_FRAME which might cause complete discard of any data including stream type, leading to client ignoring the stream. | ||||||||||||||||||||||
// Attempt to establish the control stream again then. | ||||||||||||||||||||||
if (await semaphore.WaitAsync(100)) | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
// Client finished with the expected error. | ||||||||||||||||||||||
return; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
await connection.OutboundControlStream.DisposeAsync(); | ||||||||||||||||||||||
await connection.EstablishControlStreamAsync(Array.Empty<SettingsEntry>()); | ||||||||||||||||||||||
await Task.Delay(100); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
// wait for client task before tearing down the requestStream and connection | ||||||||||||||||||||||
|
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.
Is it possible this task throws? E.g. connection closed before we were able to send settings? Or it is impossible at this point in time?
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.
No,
SendSettingsAsync
catches all exceptions and callsAbort
on them:runtime/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs
Lines 389 to 392 in f69470a