From d5b1a7f5fea6469e82a612d7de6f584667506b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= <11718369+ManickaP@users.noreply.github.com> Date: Fri, 1 Apr 2022 23:26:31 +0200 Subject: [PATCH] [QUIC] Update to msquic 2 (#67383) * Update to msquic 2, test and code fixes * feedback * Fixed more HTTP/3 bugs * updated fedora image to contain the newest msquic * Better test debugging, fixed some timeouts * feedback * Return some msquic calls into a lock --- .../coreclr/templates/helix-queues-setup.yml | 2 +- .../libraries/helix-queues-setup.yml | 4 +- .../HttpClientHandlerTest.Cancellation.cs | 23 ++++- ...entHandlerTest.MaxResponseHeadersLength.cs | 14 ++- .../Net/Http/HttpClientHandlerTest.Proxy.cs | 29 +++++- .../System/Net/Http/HttpClientHandlerTest.cs | 42 ++++++--- .../FunctionalTests/WinHttpHandlerTest.cs | 4 +- .../HttpClientHandlerTest.Http2.cs | 7 +- .../HttpClientHandlerTest.Http3.cs | 7 +- .../tests/FunctionalTests/HttpClientTest.cs | 30 ++++-- .../tests/FunctionalTests/HttpContentTest.cs | 20 +++- .../FunctionalTests/SocketsHttpHandlerTest.cs | 15 ++- .../tests/FunctionalTests/SocksProxyTest.cs | 9 +- .../MsQuic/Internal/MsQuicApi.cs | 7 +- .../MsQuic/Internal/MsQuicParameterHelpers.cs | 24 ++--- .../MsQuic/Interop/MsQuicEnums.cs | 84 +++++++++-------- .../MsQuic/Interop/MsQuicNativeMethods.cs | 94 +++++++++++-------- .../MsQuic/MsQuicConnection.cs | 11 +-- .../Implementations/MsQuic/MsQuicListener.cs | 30 ++++-- .../Implementations/MsQuic/MsQuicStream.cs | 70 ++++++++------ .../tests/FunctionalTests/MsQuicTests.cs | 9 +- 21 files changed, 345 insertions(+), 190 deletions(-) diff --git a/eng/pipelines/coreclr/templates/helix-queues-setup.yml b/eng/pipelines/coreclr/templates/helix-queues-setup.yml index 71d0be5365f1..45ac52c4dcd9 100644 --- a/eng/pipelines/coreclr/templates/helix-queues-setup.yml +++ b/eng/pipelines/coreclr/templates/helix-queues-setup.yml @@ -100,7 +100,7 @@ jobs: - (Debian.11.Amd64)Ubuntu.1804.amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-11-helix-amd64-20210304164428-5a7c380 - Ubuntu.1804.Amd64 - (Centos.8.Amd64)Ubuntu.1604.amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759 - - (Fedora.34.Amd64)Ubuntu.1604.amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-34-helix-20210728124700-4f64125 + - (Fedora.34.Amd64)Ubuntu.1604.amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-34-helix-20220331150839-4f64125 - RedHat.7.Amd64 # OSX arm64 diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index 425e97d26107..d67e4cec1233 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -64,14 +64,14 @@ jobs: - (Centos.8.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759 - RedHat.7.Amd64.Open - SLES.15.Amd64.Open - - (Fedora.34.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-34-helix-20210913123654-4f64125 + - (Fedora.34.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-34-helix-20220331150839-4f64125 - (Ubuntu.2110.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-21.10-helix-amd64-20211116135132-0f8d97e - (Debian.10.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-10-helix-amd64-bfcd90a-20200121150006 - ${{ if or(ne(parameters.jobParameters.testScope, 'outerloop'), ne(parameters.jobParameters.runtimeFlavor, 'mono')) }}: - ${{ if or(eq(parameters.jobParameters.isExtraPlatforms, true), eq(parameters.jobParameters.includeAllPlatforms, true)) }}: - (Centos.8.Amd64.Open)Ubuntu.1604.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759 - SLES.15.Amd64.Open - - (Fedora.34.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-34-helix-20210913123654-4f64125 + - (Fedora.34.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-34-helix-20220331150839-4f64125 - (Ubuntu.2110.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-21.04-helix-amd64-20210922170909-34a2d72 - (Debian.11.Amd64.Open)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-11-helix-amd64-20210304164428-5a7c380 - (Mariner.1.0.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-1.0-helix-20210528192219-92bf620 diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs index ba65686815d5..06bcd8298cf5 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs @@ -78,7 +78,10 @@ public async Task PostAsync_CancelDuringRequestContentSend_TaskCanceledQuickly(b { await server.AcceptConnectionAsync(connection => serverRelease.Task); } - catch { }; // Ignore any closing errors since we did not really process anything. + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); } @@ -131,7 +134,11 @@ public async Task GetAsync_CancelDuringResponseHeadersReceived_TaskCanceledQuick { clientFinished.SetResult(true); await serverTask; - } catch { } + } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); } } @@ -188,7 +195,11 @@ public async Task GetAsync_CancelDuringResponseBodyReceived_Buffered_TaskCancele { clientFinished.SetResult(true); await serverTask; - } catch { } + } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); } } @@ -264,7 +275,11 @@ public async Task GetAsync_CancelDuringResponseBodyReceived_Unbuffered_TaskCance { clientFinished.SetResult(true); await serverTask; - } catch { } + } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); } } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs index 2c834e903af0..49b1280685af 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.MaxResponseHeadersLength.cs @@ -90,7 +90,10 @@ public async Task InfiniteSingleHeader_ThrowsException() await Task.Delay(1); } } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); Exception e = await Assert.ThrowsAsync(() => getAsync); @@ -136,7 +139,14 @@ public async Task ThresholdExceeded_ThrowsException(string responseHeaders, int? { Assert.Contains((handler.MaxResponseHeadersLength * 1024).ToString(), e.ToString()); } - try { await serverTask; } catch { } + try + { + await serverTask; + } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } } }); } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs index c6d7332108fc..d136c7966651 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs @@ -341,7 +341,7 @@ public async Task ProxyTunnelRequest_GetAsync_Success() await LoopbackServer.CreateServerAsync(async (server, uri) => { Assert.Equal(proxyServer.Uri, handler.Proxy.GetProxy(uri)); - + Task clientTask = client.GetAsync(uri); await server.AcceptConnectionSendResponseAndCloseAsync(content: Content); using (var response = await clientTask) @@ -578,7 +578,14 @@ public async Task ProxiedIPAddressRequest_NotDefaultPort_CorrectlyFormatted(stri using (HttpClient client = CreateHttpClient(handler)) { handler.Proxy = new WebProxy(proxyUri); - try { await client.GetAsync(uri); } catch { } + try + { + await client.GetAsync(uri); + } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } } }, server => server.AcceptConnectionAsync(async connection => { @@ -611,7 +618,14 @@ public async Task ProxiedRequest_DefaultPort_PortStrippedOffInUri(string host) using (HttpClient client = CreateHttpClient(handler)) { handler.Proxy = new WebProxy(proxyUri); - try { await client.GetAsync(addressUri); } catch { } + try + { + await client.GetAsync(addressUri); + } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } } }, server => server.AcceptConnectionAsync(async connection => { @@ -639,7 +653,14 @@ public async Task ProxyTunnelRequest_PortSpecified_NotStrippedOffInUri(string ho { handler.Proxy = new WebProxy(proxyUri); handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; - try { await client.GetAsync(addressUri); } catch { } + try + { + await client.GetAsync(addressUri); + } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } } }, server => server.AcceptConnectionAsync(async connection => { diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index a2775bb9135a..7f3f447737f7 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -248,7 +248,14 @@ public async Task GetAsync_IPv6AddressInHostHeader_CorrectlyFormatted(string hos using (HttpClient client = CreateHttpClient(handler)) { handler.Proxy = new WebProxy(proxyUri); - try { await client.GetAsync(ipv6Address); } catch { } + try + { + await client.GetAsync(ipv6Address); + } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } } }, server => server.AcceptConnectionAsync(async connection => { @@ -292,7 +299,14 @@ public async Task GetAsync_SecureAndNonSecureIPBasedUri_CorrectlyFormatted(IPAdd // we could not create SslStream in browser, [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; } - try { await client.GetAsync(url); } catch { } + try + { + await client.GetAsync(url); + } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } } }, server => server.AcceptConnectionAsync(async connection => { @@ -449,7 +463,7 @@ public async Task PostAsync_ManyDifferentRequestHeaders_SentCorrectly() request.Headers.Referrer = new Uri("http://en.wikipedia.org/wiki/Main_Page"); request.Headers.TE.Add(new TransferCodingWithQualityHeaderValue("trailers")); request.Headers.TE.Add(new TransferCodingWithQualityHeaderValue("deflate")); - if (PlatformDetection.IsNotNodeJS) + if (PlatformDetection.IsNotNodeJS) { request.Headers.Trailer.Add("MyTrailer"); request.Headers.TransferEncoding.Add(new TransferCodingHeaderValue("chunked")); @@ -468,7 +482,7 @@ public async Task PostAsync_ManyDifferentRequestHeaders_SentCorrectly() request.Headers.Add("X-Requested-With", "XMLHttpRequest"); request.Headers.Add("DNT", "1 (Do Not Track Enabled)"); request.Headers.Add("X-Forwarded-For", "client1"); - if (PlatformDetection.IsNotNodeJS) + if (PlatformDetection.IsNotNodeJS) { request.Headers.Add("X-Forwarded-For", "proxy1"); request.Headers.Add("X-Forwarded-For", "proxy2"); @@ -483,7 +497,7 @@ public async Task PostAsync_ManyDifferentRequestHeaders_SentCorrectly() request.Headers.Add("X-UIDH", "..."); request.Headers.Add("X-Csrf-Token", "i8XNjC4b8KVok4uw5RftR38Wgp2BFwql"); request.Headers.Add("X-Request-ID", "f058ebd6-02f7-4d3f-942e-904344e8cde5"); - if (PlatformDetection.IsNotNodeJS) + if (PlatformDetection.IsNotNodeJS) { request.Headers.Add("X-Request-ID", "f058ebd6-02f7-4d3f-942e-904344e8cde5"); } @@ -524,7 +538,7 @@ public async Task PostAsync_ManyDifferentRequestHeaders_SentCorrectly() Assert.Equal($"Basic {authSafeValue}", requestData.GetSingleHeaderValue("Proxy-Authorization")); Assert.Equal("Mozilla/5.0", requestData.GetSingleHeaderValue("User-Agent")); Assert.Equal("http://en.wikipedia.org/wiki/Main_Page", requestData.GetSingleHeaderValue("Referer")); - if (PlatformDetection.IsNotNodeJS) + if (PlatformDetection.IsNotNodeJS) { Assert.Equal("MyTrailer", requestData.GetSingleHeaderValue("Trailer")); } @@ -543,7 +557,7 @@ public async Task PostAsync_ManyDifferentRequestHeaders_SentCorrectly() Assert.Equal("bytes=500-999", requestData.GetSingleHeaderValue("Range")); Assert.Equal("199 - \"Miscellaneous warning\"", requestData.GetSingleHeaderValue("Warning")); Assert.Equal("XMLHttpRequest", requestData.GetSingleHeaderValue("X-Requested-With")); - if (PlatformDetection.IsNotNodeJS) + if (PlatformDetection.IsNotNodeJS) { Assert.Equal("client1, proxy1, proxy2", requestData.GetSingleHeaderValue("X-Forwarded-For")); } @@ -560,11 +574,11 @@ public async Task PostAsync_ManyDifferentRequestHeaders_SentCorrectly() Assert.Equal("http://wap.samsungmobile.com/uaprof/SGH-I777.xml", requestData.GetSingleHeaderValue("X-Wap-Profile")); Assert.Equal("...", requestData.GetSingleHeaderValue("X-UIDH")); Assert.Equal("i8XNjC4b8KVok4uw5RftR38Wgp2BFwql", requestData.GetSingleHeaderValue("X-Csrf-Token")); - if (PlatformDetection.IsNotNodeJS) + if (PlatformDetection.IsNotNodeJS) { Assert.Equal("f058ebd6-02f7-4d3f-942e-904344e8cde5, f058ebd6-02f7-4d3f-942e-904344e8cde5", requestData.GetSingleHeaderValue("X-Request-ID")); } - else + else { // node-fetch polyfill doesn't support combining multiple header values Assert.Equal("f058ebd6-02f7-4d3f-942e-904344e8cde5", requestData.GetSingleHeaderValue("X-Request-ID")); @@ -892,7 +906,10 @@ public async Task GetAsync_InfiniteChunkSize_ThrowsHttpRequestException() await Task.Delay(1); } } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); await Assert.ThrowsAsync(() => client.GetAsync(url)); @@ -1660,7 +1677,10 @@ public async Task SendAsync_Expect100Continue_RequestBodyFails_ThrowsContentExce { await connection.ReadRequestDataAsync(readBody: true); } - catch { } // Eat errors from client disconnect. + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } await clientFinished.Task.WaitAsync(TimeSpan.FromMinutes(2)); }); }); diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/WinHttpHandlerTest.cs b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/WinHttpHandlerTest.cs index 5178f3a9d529..5ac9a78702cb 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/WinHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/WinHttpHandlerTest.cs @@ -128,7 +128,7 @@ public async Task SendAsync_GetUsingChunkedEncoding_ThrowsHttpRequestException() using (HttpClient client = new HttpClient(handler)) { HttpRequestException ex = await Assert.ThrowsAsync(() => client.SendAsync(request)); - _output.WriteLine(ex.ToString()); + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); } } @@ -236,7 +236,7 @@ public async Task SendAsync_UseTcpKeepAliveOptions() _output.WriteLine(responseContent); // Uncomment this to observe an exchange of "TCP Keep-Alive" and "TCP Keep-Alive ACK" packets: - // await Task.Delay(5000); + // await Task.Delay(5000); } private async Task VerifyResponse(Task task, string payloadText) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs index fc94b54bba97..d22e553f7a29 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs @@ -204,7 +204,7 @@ public async Task Http2ClearText_SendAsync_Success(string clientContent, string Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); Assert.IsNotType(connection.Stream); - HttpRequestData requestData = await connection.ReadRequestDataAsync(); + HttpRequestData requestData = await connection.ReadRequestDataAsync(); string requestContent = requestData.Body is null ? (string)null : Encoding.ASCII.GetString(requestData.Body); Assert.Equal(clientContent, requestContent); await connection.SendResponseAsync(HttpStatusCode.OK, content: serverContent); @@ -3164,7 +3164,10 @@ public async Task SendAsync_ConcurentSendReceive_Fail() await connection.SendGoAway(streamId); await connection.WaitForConnectionShutdownAsync(); } - catch { }; + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs index 87e98fb7d468..33e403afda88 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs @@ -278,7 +278,6 @@ public async Task SendMoreThanStreamLimitRequestsConcurrently_LastWaits(int stre } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/53090")] public async Task ReservedFrameType_Throws() { const int ReservedHttp2PriorityFrameId = 0x2; @@ -1138,7 +1137,7 @@ public async Task RequestContentStreaming_Timeout_BothClientAndServerReceiveCanc await requestStream.WriteAsync(message); await requestStream.FlushAsync(); - + await Task.Delay(TimeSpan.FromSeconds(11)); // longer than client.Timeout // Http3WriteStream is disposed after cancellation fired @@ -1208,7 +1207,7 @@ public async Task RequestContentStreaming_Cancellation_BothClientAndServerReceiv await requestStream.WriteAsync(message); await requestStream.FlushAsync(); - + cts.Cancel(); await Task.Delay(250); @@ -1291,7 +1290,7 @@ public async Task DuplexStreaming_RequestCTCancellation_DoesNotApply() await requestStream.WriteAsync(message); await requestStream.FlushAsync(); - + // cancelling after SendAsync finished should not apply -- nothing should happen cts.Cancel(); await Task.Delay(250); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index 41cc4758882a..77ceebead041 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -430,7 +430,10 @@ public async Task GetStringAsync_CanBeCanceled() { await connection.ReadRequestHeaderAsync(); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } cts.Cancel(); }); }); @@ -597,7 +600,10 @@ public async Task GetByteArrayAsync_CanBeCanceled() { await connection.ReadRequestHeaderAsync(); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } cts.Cancel(); }); }); @@ -671,7 +677,10 @@ public async Task GetStreamAsync_CanBeCanceled() { await connection.ReadRequestHeaderAsync(); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } cts.Cancel(); }); }); @@ -1003,7 +1012,10 @@ public async Task Send_CancelledRequestContent_Throws() cts.Cancel(); await connection.ReadRequestBodyAsync(); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); }); } @@ -1048,7 +1060,10 @@ public async Task Send_TimeoutRequestContent_Throws() await connection.ReadRequestHeaderAsync(); await connection.ReadRequestBodyAsync(); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); }); } @@ -1100,7 +1115,10 @@ public async Task Send_CancelledResponseContent_Throws() await Task.Delay(TimeSpan.FromSeconds(0.1)); } } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); }); } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs index b09b0d2dc1a3..4ddd6633a2e9 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs @@ -661,7 +661,10 @@ public async Task ReadAsStringAsync_Unbuffered_CanBeCanceled_AlreadyCanceledCts( { await server.AcceptConnectionSendResponseAndCloseAsync(); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); } @@ -694,7 +697,10 @@ public async Task ReadAsStringAsync_Unbuffered_CanBeCanceled() { await connection.SendResponseAsync(new string('a', 100)); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); }); } @@ -749,7 +755,10 @@ public async Task ReadAsByteArrayAsync_Unbuffered_CanBeCanceled_AlreadyCanceledC { await server.AcceptConnectionSendResponseAndCloseAsync(); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); } @@ -782,7 +791,10 @@ public async Task ReadAsByteArrayAsync_Unbuffered_CanBeCanceled() { await connection.SendResponseAsync(new string('a', 100)); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }); }); } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 461dc8e1ac9b..ee824ca3de4d 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -3207,7 +3207,10 @@ public async Task PlaintextStreamFilter_ExceptionDuringCallback_ThrowsHttpReques { await server.AcceptConnectionSendResponseAndCloseAsync(content: "foo"); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }, options: options); } @@ -3242,7 +3245,10 @@ public async Task PlaintextStreamFilter_ReturnsNull_ThrowsHttpRequestException(b { await server.AcceptConnectionSendResponseAndCloseAsync(content: "foo"); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } }, options: options); } } @@ -3642,7 +3648,10 @@ public async Task ContentLength_DoesNotMatchRequestContentLength_Throws(int cont { await server.HandleRequestAsync(); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } // On HTTP/1.x, an exception being thrown while sending the request content will result in the connection being closed. // This test is ensuring that a subsequent request can succeed on a new connection. diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs index cb60661313b2..cda46fff4c68 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs @@ -104,8 +104,8 @@ public async Task TestExceptionalAsync(string scheme, string host, bool useAuth, HttpRequestMessage request = CreateRequest(HttpMethod.Get, new Uri($"http://{host}/"), UseVersion, exactVersion: true); // SocksException is not public - var ex = await Assert.ThrowsAnyAsync(() => client.SendAsync(TestAsync, request)); - var innerException = ex.InnerException; + var exception = await Assert.ThrowsAnyAsync(() => client.SendAsync(TestAsync, request)); + var innerException = exception.InnerException; Assert.Equal(exceptionMessage, innerException.Message); Assert.Equal("SocksException", innerException.GetType().Name); @@ -113,7 +113,10 @@ public async Task TestExceptionalAsync(string scheme, string host, bool useAuth, { await proxy.DisposeAsync(); } - catch { } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs index bbb445f4c72c..d606651d6ade 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs @@ -107,7 +107,7 @@ private MsQuicApi(NativeApi* vtable) internal static bool IsQuicSupported { get; } - private const int MsQuicVersion = 1; + private const int MsQuicVersion = 2; internal static bool Tls13MayBeDisabled { get; } @@ -128,8 +128,9 @@ static MsQuicApi() Tls13MayBeDisabled = IsTls13Disabled(); } - if (NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle) || - NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle)) + IntPtr msQuicHandle; + if (NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) || + NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle)) { try { diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs index 33ba18920d9d..7fef8a88e689 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs @@ -10,7 +10,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal { internal static class MsQuicParameterHelpers { - internal static unsafe IPEndPoint GetIPEndPointParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param) + internal static unsafe IPEndPoint GetIPEndPointParam(MsQuicApi api, SafeHandle nativeObject, uint param) { // MsQuic always uses storage size as if IPv6 was used uint valueLen = (uint)Internals.SocketAddress.IPv6AddressSize; @@ -18,7 +18,7 @@ internal static unsafe IPEndPoint GetIPEndPointParam(MsQuicApi api, SafeHandle n fixed (byte* paddress = &MemoryMarshal.GetReference(address)) { - uint status = api.GetParamDelegate(nativeObject, level, param, ref valueLen, paddress); + uint status = api.GetParamDelegate(nativeObject, param, ref valueLen, paddress); QuicExceptionHelpers.ThrowIfFailed(status, "GetIPEndPointParam failed."); } @@ -28,7 +28,7 @@ internal static unsafe IPEndPoint GetIPEndPointParam(MsQuicApi api, SafeHandle n .GetIPEndPoint(); } - internal static unsafe void SetIPEndPointParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param, IPEndPoint value) + internal static unsafe void SetIPEndPointParam(MsQuicApi api, SafeHandle nativeObject, uint param, IPEndPoint value) { Internals.SocketAddress socketAddress = IPEndPointExtensions.Serialize(value); @@ -40,46 +40,46 @@ internal static unsafe void SetIPEndPointParam(MsQuicApi api, SafeHandle nativeO fixed (byte* paddress = &MemoryMarshal.GetReference(address)) { QuicExceptionHelpers.ThrowIfFailed( - api.SetParamDelegate(nativeObject, level, param, (uint)address.Length, paddress), + api.SetParamDelegate(nativeObject, param, (uint)address.Length, paddress), "Could not set IPEndPoint"); } } - internal static unsafe ushort GetUShortParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param) + internal static unsafe ushort GetUShortParam(MsQuicApi api, SafeHandle nativeObject, uint param) { ushort value; uint valueLen = (uint)sizeof(ushort); - uint status = api.GetParamDelegate(nativeObject, level, param, ref valueLen, (byte*)&value); + uint status = api.GetParamDelegate(nativeObject, param, ref valueLen, (byte*)&value); QuicExceptionHelpers.ThrowIfFailed(status, "GetUShortParam failed."); Debug.Assert(valueLen == sizeof(ushort)); return value; } - internal static unsafe void SetUShortParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param, ushort value) + internal static unsafe void SetUShortParam(MsQuicApi api, SafeHandle nativeObject, uint param, ushort value) { QuicExceptionHelpers.ThrowIfFailed( - api.SetParamDelegate(nativeObject, level, param, sizeof(ushort), (byte*)&value), + api.SetParamDelegate(nativeObject, param, sizeof(ushort), (byte*)&value), "Could not set ushort."); } - internal static unsafe ulong GetULongParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param) + internal static unsafe ulong GetULongParam(MsQuicApi api, SafeHandle nativeObject, uint param) { ulong value; uint valueLen = (uint)sizeof(ulong); - uint status = api.GetParamDelegate(nativeObject, level, param, ref valueLen, (byte*)&value); + uint status = api.GetParamDelegate(nativeObject, param, ref valueLen, (byte*)&value); QuicExceptionHelpers.ThrowIfFailed(status, "GetULongParam failed."); Debug.Assert(valueLen == sizeof(ulong)); return value; } - internal static unsafe void SetULongParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param, ulong value) + internal static unsafe void SetULongParam(MsQuicApi api, SafeHandle nativeObject, uint param, ulong value) { QuicExceptionHelpers.ThrowIfFailed( - api.SetParamDelegate(nativeObject, level, param, sizeof(ulong), (byte*)&value), + api.SetParamDelegate(nativeObject, param, sizeof(ulong), (byte*)&value), "Could not set ulong."); } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs index 190275ebbda3..7779732cf9f7 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs @@ -69,10 +69,10 @@ internal enum QUIC_STREAM_OPEN_FLAGS : uint internal enum QUIC_STREAM_START_FLAGS : uint { NONE = 0x0000, - FAIL_BLOCKED = 0x0001, // Only opens the stream if flow control allows. - IMMEDIATE = 0x0002, // Immediately informs peer that stream is open. - ASYNC = 0x0004, // Don't block the API call to wait for completion. - SHUTDOWN_ON_FAIL = 0x0008, // Shutdown the stream immediately after start failure. + IMMEDIATE = 0x0001, // Immediately informs peer that stream is open. + FAIL_BLOCKED = 0x0002, // Only opens the stream if flow control allows. + SHUTDOWN_ON_FAIL = 0x0004, // Shutdown the stream immediately after start failure. + INDICATE_PEER_ACCEPT = 0x0008, // PEER_ACCEPTED event to be delivered if the stream isn't initially accepted. } [Flags] @@ -105,69 +105,71 @@ internal enum QUIC_SEND_FLAGS : uint DELAY_SEND = 0x0010, // Indicates the send should be delayed because more will be queued soon. } - internal enum QUIC_PARAM_LEVEL : uint + internal enum QUIC_PARAM_PREFIX : uint { - GLOBAL, - REGISTRATION, - CONFIGURATION, - LISTENER, - CONNECTION, - TLS, - STREAM, + GLOBAL = 0x01000000, + REGISTRATION = 0x02000000, + CONFIGURATION = 0x03000000, + LISTENER = 0x04000000, + CONNECTION = 0x05000000, + TLS = 0x06000000, + TLS_SCHANNEL = 0x07000000, + STREAM = 0x08000000, } internal enum QUIC_PARAM_GLOBAL : uint { - RETRY_MEMORY_PERCENT = 0, // uint16_t - SUPPORTED_VERSIONS = 1, // uint32_t[] - network byte order - LOAD_BALANCING_MODE = 2, // uint16_t - QUIC_LOAD_BALANCING_MODE - PERF_COUNTERS = 3, // uint64_t[] - Array size is QUIC_PERF_COUNTER_MAX - SETTINGS = 4, // QUIC_SETTINGS + RETRY_MEMORY_PERCENT = 0 | QUIC_PARAM_PREFIX.GLOBAL, // uint16_t + SUPPORTED_VERSIONS = 1 | QUIC_PARAM_PREFIX.GLOBAL, // uint32_t[] - network byte order + LOAD_BALANCING_MODE = 2 | QUIC_PARAM_PREFIX.GLOBAL, // uint16_t - QUIC_LOAD_BALANCING_MODE + PERF_COUNTERS = 3 | QUIC_PARAM_PREFIX.GLOBAL, // uint64_t[] - Array size is QUIC_PERF_COUNTER_MAX + SETTINGS = 4 | QUIC_PARAM_PREFIX.GLOBAL, // QUIC_SETTINGS } internal enum QUIC_PARAM_REGISTRATION : uint { - CID_PREFIX = 0, // uint8_t[] + CID_PREFIX = 0 | QUIC_PARAM_PREFIX.REGISTRATION, // uint8_t[] } internal enum QUIC_PARAM_LISTENER : uint { - LOCAL_ADDRESS = 0, // QUIC_ADDR - STATS = 1, // QUIC_LISTENER_STATISTICS + LOCAL_ADDRESS = 0 | QUIC_PARAM_PREFIX.LISTENER, // QUIC_ADDR + STATS = 1 | QUIC_PARAM_PREFIX.LISTENER, // QUIC_LISTENER_STATISTICS } internal enum QUIC_PARAM_CONN : uint { - QUIC_VERSION = 0, // uint32_t - LOCAL_ADDRESS = 1, // QUIC_ADDR - REMOTE_ADDRESS = 2, // QUIC_ADDR - IDEAL_PROCESSOR = 3, // uint16_t - SETTINGS = 4, // QUIC_SETTINGS - STATISTICS = 5, // QUIC_STATISTICS - STATISTICS_PLAT = 6, // QUIC_STATISTICS - SHARE_UDP_BINDING = 7, // uint8_t (BOOLEAN) - LOCAL_BIDI_STREAM_COUNT = 8, // uint16_t - LOCAL_UNIDI_STREAM_COUNT = 9, // uint16_t - MAX_STREAM_IDS = 10, // uint64_t[4] - CLOSE_REASON_PHRASE = 11, // char[] - STREAM_SCHEDULING_SCHEME = 12, // QUIC_STREAM_SCHEDULING_SCHEME - DATAGRAM_RECEIVE_ENABLED = 13, // uint8_t (BOOLEAN) - DATAGRAM_SEND_ENABLED = 14, // uint8_t (BOOLEAN) - DISABLE_1RTT_ENCRYPTION = 15, // uint8_t (BOOLEAN) - RESUMPTION_TICKET = 16, // uint8_t[] - PEER_CERTIFICATE_VALID = 17, // uint8_t (BOOLEAN) + QUIC_VERSION = 0 | QUIC_PARAM_PREFIX.CONNECTION, // uint32_t + LOCAL_ADDRESS = 1 | QUIC_PARAM_PREFIX.CONNECTION, // QUIC_ADDR + REMOTE_ADDRESS = 2 | QUIC_PARAM_PREFIX.CONNECTION, // QUIC_ADDR + IDEAL_PROCESSOR = 3 | QUIC_PARAM_PREFIX.CONNECTION, // uint16_t + SETTINGS = 4 | QUIC_PARAM_PREFIX.CONNECTION, // QUIC_SETTINGS + STATISTICS = 5 | QUIC_PARAM_PREFIX.CONNECTION, // QUIC_STATISTICS + STATISTICS_PLAT = 6 | QUIC_PARAM_PREFIX.CONNECTION, // QUIC_STATISTICS + SHARE_UDP_BINDING = 7 | QUIC_PARAM_PREFIX.CONNECTION, // uint8_t (BOOLEAN) + LOCAL_BIDI_STREAM_COUNT = 8 | QUIC_PARAM_PREFIX.CONNECTION, // uint16_t + LOCAL_UNIDI_STREAM_COUNT = 9 | QUIC_PARAM_PREFIX.CONNECTION, // uint16_t + MAX_STREAM_IDS = 10 | QUIC_PARAM_PREFIX.CONNECTION, // uint64_t[4] + CLOSE_REASON_PHRASE = 11 | QUIC_PARAM_PREFIX.CONNECTION, // char[] + STREAM_SCHEDULING_SCHEME = 12 | QUIC_PARAM_PREFIX.CONNECTION, // QUIC_STREAM_SCHEDULING_SCHEME + DATAGRAM_RECEIVE_ENABLED = 13 | QUIC_PARAM_PREFIX.CONNECTION, // uint8_t (BOOLEAN) + DATAGRAM_SEND_ENABLED = 14 | QUIC_PARAM_PREFIX.CONNECTION, // uint8_t (BOOLEAN) + DISABLE_1RTT_ENCRYPTION = 15 | QUIC_PARAM_PREFIX.CONNECTION, // uint8_t (BOOLEAN) + RESUMPTION_TICKET = 16 | QUIC_PARAM_PREFIX.CONNECTION, // uint8_t[] + PEER_CERTIFICATE_VALID = 17 | QUIC_PARAM_PREFIX.CONNECTION, // uint8_t (BOOLEAN) } internal enum QUIC_PARAM_STREAM : uint { - ID = 0, // QUIC_UINT62 - ZERRTT_LENGTH = 1, // uint64_t - IDEAL_SEND_BUFFER_SIZE = 2, // uint64_t - bytes + ID = 0 | QUIC_PARAM_PREFIX.STREAM, // QUIC_UINT62 + ZERRTT_LENGTH = 1 | QUIC_PARAM_PREFIX.STREAM, // uint64_t + IDEAL_SEND_BUFFER_SIZE = 2 | QUIC_PARAM_PREFIX.STREAM, // uint64_t - bytes } internal enum QUIC_LISTENER_EVENT : uint { NEW_CONNECTION = 0, + STOP_COMPLETE = 1, } internal enum QUIC_CONNECTION_EVENT_TYPE : uint diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs index 681bc94bdb40..c354d3690978 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs @@ -65,14 +65,12 @@ internal struct NativeApi internal delegate uint SetParamDelegate( SafeHandle handle, - QUIC_PARAM_LEVEL level, uint param, uint bufferLength, byte* buffer); internal delegate uint GetParamDelegate( SafeHandle handle, - QUIC_PARAM_LEVEL level, uint param, ref uint bufferLength, byte* buffer); @@ -162,6 +160,7 @@ internal struct QuicSettings internal ulong MaxBytesPerKey; internal ulong HandshakeIdleTimeoutMs; internal ulong IdleTimeoutMs; + internal ulong MtuDiscoverySearchCompleteTimeoutUs; internal uint TlsClientMaxSendBuffer; internal uint TlsServerMaxSendBuffer; internal uint StreamRecvWindowDefault; @@ -175,47 +174,52 @@ internal struct QuicSettings internal uint MaxAckDelayMs; internal uint DisconnectTimeoutMs; internal uint KeepAliveIntervalMs; + internal ushort CongestionControlAlgorithm; // QUIC_CONGESTION_CONTROL_ALGORITHM internal ushort PeerBidiStreamCount; internal ushort PeerUnidiStreamCount; - internal ushort RetryMemoryLimit; // Global only - internal ushort LoadBalancingMode; // Global only - internal byte MaxOperationsPerDrain; + internal ushort MaxBindingStatelessOperations; + internal ushort StatelessOperationExpirationMs; + internal ushort MinimumMtu; + internal ushort MaximumMtu; internal QuicSettingsEnabledFlagsFlags EnabledFlags; - internal uint* DesiredVersionsList; - internal uint DesiredVersionsListLength; + internal byte MaxOperationsPerDrain; + internal byte MtuDiscoveryMissingProbeCount; } [Flags] internal enum QuicSettingsIsSetFlags : ulong { - MaxBytesPerKey = 1 << 0, - HandshakeIdleTimeoutMs = 1 << 1, - IdleTimeoutMs = 1 << 2, - TlsClientMaxSendBuffer = 1 << 3, - TlsServerMaxSendBuffer = 1 << 4, - StreamRecvWindowDefault = 1 << 5, - StreamRecvBufferDefault = 1 << 6, - ConnFlowControlWindow = 1 << 7, - MaxWorkerQueueDelayUs = 1 << 8, - MaxStatelessOperations = 1 << 9, - InitialWindowPackets = 1 << 10, - SendIdleTimeoutMs = 1 << 11, - InitialRttMs = 1 << 12, - MaxAckDelayMs = 1 << 13, - DisconnectTimeoutMs = 1 << 14, - KeepAliveIntervalMs = 1 << 15, - PeerBidiStreamCount = 1 << 16, - PeerUnidiStreamCount = 1 << 17, - RetryMemoryLimit = 1 << 18, - LoadBalancingMode = 1 << 19, - MaxOperationsPerDrain = 1 << 20, - SendBufferingEnabled = 1 << 21, - PacingEnabled = 1 << 22, - MigrationEnabled = 1 << 23, - DatagramReceiveEnabled = 1 << 24, - ServerResumptionLevel = 1 << 25, - DesiredVersionsList = 1 << 26, - VersionNegotiationExtEnabled = 1 << 27, + MaxBytesPerKey = 1UL << 0, + HandshakeIdleTimeoutMs = 1UL << 1, + IdleTimeoutMs = 1UL << 2, + MtuDiscoverySearchCompleteTimeoutUs = 1UL << 3, + TlsClientMaxSendBuffer = 1UL << 4, + TlsServerMaxSendBuffer = 1UL << 5, + StreamRecvWindowDefault = 1UL << 6, + StreamRecvBufferDefault = 1UL << 7, + ConnFlowControlWindow = 1UL << 8, + MaxWorkerQueueDelayUs = 1UL << 9, + MaxStatelessOperations = 1UL << 10, + InitialWindowPackets = 1UL << 11, + SendIdleTimeoutMs = 1UL << 12, + InitialRttMs = 1UL << 13, + MaxAckDelayMs = 1UL << 14, + DisconnectTimeoutMs = 1UL << 15, + KeepAliveIntervalMs = 1UL << 16, + CongestionControlAlgorithm = 1UL << 17, + PeerBidiStreamCount = 1UL << 18, + PeerUnidiStreamCount = 1UL << 19, + MaxBindingStatelessOperations = 1UL << 20, + StatelessOperationExpirationMs = 1UL << 21, + MinimumMtu = 1UL << 22, + MaximumMtu = 1UL << 23, + SendBufferingEnabled = 1UL << 24, + PacingEnabled = 1UL << 25, + MigrationEnabled = 1UL << 26, + DatagramReceiveEnabled = 1UL << 27, + ServerResumptionLevel = 1UL << 28, + MaxOperationsPerDrain = 1UL << 29, + MtuDiscoveryMissingProbeCount = 1UL << 31, } [Flags] @@ -576,6 +580,14 @@ internal struct ConnectionEvent // TODO: missing SendResumptionTicket + [StructLayout(LayoutKind.Sequential)] + internal struct StreamEventDataStartComplete + { + internal uint Status; + internal ulong Id; + internal byte PeerAccepted; + }; + [StructLayout(LayoutKind.Sequential)] internal struct StreamEventDataReceive { @@ -620,7 +632,9 @@ internal struct StreamEventDataShutdownComplete [StructLayout(LayoutKind.Explicit)] internal struct StreamEventDataUnion { - // TODO: missing START_COMPLETE + [FieldOffset(0)] + internal StreamEventDataStartComplete StartComplete; + [FieldOffset(0)] internal StreamEventDataReceive Receive; @@ -791,7 +805,7 @@ internal void SetCallbackHandler(SafeHandle handle, Delegate del, IntPtr context __del_gen_native__marshaller.FreeNative(); } } - internal uint SetParam(SafeHandle handle, QUIC_PARAM_LEVEL level, uint param, uint bufferLength, byte* buffer) + internal uint SetParam(SafeHandle handle, uint param, uint bufferLength, byte* buffer) { uint __retVal; // @@ -805,7 +819,7 @@ internal uint SetParam(SafeHandle handle, QUIC_PARAM_LEVEL level, uint param, ui // handle.DangerousAddRef(ref handle__addRefd); IntPtr __handle_gen_native = handle.DangerousGetHandle(); - __retVal = ((delegate* unmanaged[Cdecl])_functionPointer)(__handle_gen_native, level, param, bufferLength, buffer); + __retVal = ((delegate* unmanaged[Cdecl])_functionPointer)(__handle_gen_native, param, bufferLength, buffer); } finally { @@ -818,7 +832,7 @@ internal uint SetParam(SafeHandle handle, QUIC_PARAM_LEVEL level, uint param, ui return __retVal; } - internal uint GetParam(SafeHandle handle, QUIC_PARAM_LEVEL level, uint param, ref uint bufferLength, byte* buffer) + internal uint GetParam(SafeHandle handle, uint param, ref uint bufferLength, byte* buffer) { IntPtr __handle_gen_native = default; uint __retVal; @@ -835,7 +849,7 @@ internal uint GetParam(SafeHandle handle, QUIC_PARAM_LEVEL level, uint param, re __handle_gen_native = handle.DangerousGetHandle(); fixed (uint* __bufferLength_gen_native = &bufferLength) { - __retVal = ((delegate* unmanaged[Cdecl])_functionPointer)(__handle_gen_native, level, param, __bufferLength_gen_native, buffer); + __retVal = ((delegate* unmanaged[Cdecl])_functionPointer)(__handle_gen_native, param, __bufferLength_gen_native, buffer); } } finally diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index 5d3a7e868a77..99dc15398e9f 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -255,7 +255,7 @@ private static uint HandleEventConnected(State state, ref ConnectionEvent connec Debug.Assert(state.Connection != null); - state.Connection._localEndPoint = MsQuicParameterHelpers.GetIPEndPointParam(MsQuicApi.Api, state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS); + state.Connection._localEndPoint = MsQuicParameterHelpers.GetIPEndPointParam(MsQuicApi.Api, state.Handle, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS); state.Connection.SetNegotiatedAlpn(connectionEvent.Data.Connected.NegotiatedAlpn, connectionEvent.Data.Connected.NegotiatedAlpnLength); state.Connection = null; @@ -607,13 +607,13 @@ internal override QuicStreamProvider OpenBidirectionalStream() internal override int GetRemoteAvailableUnidirectionalStreamCount() { Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); - return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_UNIDI_STREAM_COUNT); + return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_CONN.LOCAL_UNIDI_STREAM_COUNT); } internal override int GetRemoteAvailableBidirectionalStreamCount() { Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); - return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_BIDI_STREAM_COUNT); + return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_CONN.LOCAL_BIDI_STREAM_COUNT); } internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default) @@ -643,7 +643,7 @@ internal override ValueTask ConnectAsync(CancellationToken cancellationToken = d if (_remoteEndPoint is IPEndPoint ipEndPoint) { Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); - MsQuicParameterHelpers.SetIPEndPointParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, ipEndPoint); + MsQuicParameterHelpers.SetIPEndPointParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, ipEndPoint); targetHost = _state.TargetHost ?? ((IPEndPoint)_remoteEndPoint).Address.ToString(); port = ((IPEndPoint)_remoteEndPoint).Port; @@ -660,7 +660,7 @@ internal override ValueTask ConnectAsync(CancellationToken cancellationToken = d { // This is form of IPAddress and _state.TargetHost is set to different string Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); - MsQuicParameterHelpers.SetIPEndPointParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, new IPEndPoint(address, port)); + MsQuicParameterHelpers.SetIPEndPointParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, new IPEndPoint(address, port)); targetHost = _state.TargetHost!; } else @@ -694,7 +694,6 @@ internal override ValueTask ConnectAsync(CancellationToken cancellationToken = d } catch { - _state.StateGCHandle.Free(); _state.Connection = null; throw; } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs index ab4969a81784..a5c4e642d049 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs @@ -33,6 +33,8 @@ internal sealed class State public SafeMsQuicListenerHandle Handle = null!; public string TraceId = null!; // set in ctor. + public TaskCompletionSource StopCompletion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + public readonly SafeMsQuicConfigurationHandle? ConnectionConfiguration; public readonly Channel AcceptConnectionQueue; // Pending connections are held back until they're ready to be used, which includes TLS negotiation. @@ -157,7 +159,8 @@ private void Dispose(bool disposing) return; } - Stop(); + // TODO: solve listener stopping in better way now that it receives STOP_COMPLETED event. + StopAsync().GetAwaiter().GetResult(); _state?.Handle?.Dispose(); // Note that it's safe to free the state GCHandle here, because: @@ -207,15 +210,15 @@ private unsafe IPEndPoint Start(QuicListenerOptions options) QuicExceptionHelpers.ThrowIfFailed(status, "ListenerStart failed."); Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); - return MsQuicParameterHelpers.GetIPEndPointParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); + return MsQuicParameterHelpers.GetIPEndPointParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); } - private void Stop() + private Task StopAsync() { // TODO finalizers are called even if the object construction fails. if (_state == null) { - return; + return Task.CompletedTask; } _state.AcceptConnectionQueue?.Writer.TryComplete(); @@ -225,18 +228,27 @@ private void Stop() Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); MsQuicApi.Api.ListenerStopDelegate(_state.Handle); } + return _state.StopCompletion.Task; } private static unsafe uint NativeCallbackHandler( IntPtr listener, IntPtr context, - ListenerEvent* evt) + ListenerEvent* listenerEvent) { GCHandle gcHandle = GCHandle.FromIntPtr(context); Debug.Assert(gcHandle.IsAllocated); Debug.Assert(gcHandle.Target is not null); var state = (State)gcHandle.Target; - if (evt->Type != QUIC_LISTENER_EVENT.NEW_CONNECTION) + + + if (listenerEvent->Type == QUIC_LISTENER_EVENT.STOP_COMPLETE) + { + state.StopCompletion.TrySetResult(); + return MsQuicStatusCodes.Success; + } + + if (listenerEvent->Type != QUIC_LISTENER_EVENT.NEW_CONNECTION) { return MsQuicStatusCodes.InternalError; } @@ -245,7 +257,7 @@ private void Stop() MsQuicConnection? msQuicConnection = null; try { - ref NewConnectionInfo connectionInfo = ref *evt->Data.NewConnection.Info; + ref NewConnectionInfo connectionInfo = ref *listenerEvent->Data.NewConnection.Info; IPEndPoint localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(connectionInfo.LocalAddress); IPEndPoint remoteEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(connectionInfo.RemoteAddress); @@ -282,7 +294,7 @@ private void Stop() } } - connectionHandle = new SafeMsQuicConnectionHandle(evt->Data.NewConnection.Connection); + connectionHandle = new SafeMsQuicConnectionHandle(listenerEvent->Data.NewConnection.Connection); Debug.Assert(!Monitor.IsEntered(state), "!Monitor.IsEntered(state)"); uint status = MsQuicApi.Api.ConnectionSetConfigurationDelegate(connectionHandle, connectionConfiguration); @@ -305,7 +317,7 @@ private void Stop() { if (NetEventSource.Log.IsEnabled()) { - NetEventSource.Error(state, $"[Listener#{state.GetHashCode()}] Exception occurred during handling {(QUIC_LISTENER_EVENT)evt->Type} connection callback: {ex}"); + NetEventSource.Error(state, $"[Listener#{state.GetHashCode()}] Exception occurred during handling {(QUIC_LISTENER_EVENT)listenerEvent->Type} connection callback: {ex}"); } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index db7f1a73ad8c..675471528b50 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -40,6 +40,8 @@ private sealed class State public MsQuicConnection.State ConnectionState = null!; // set in ctor. public string TraceId = null!; // set in ctor. + public uint StartStatus = MsQuicStatusCodes.Success; + public ReadState ReadState; // set when ReadState.Aborted: @@ -182,7 +184,7 @@ internal MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM_OPEN_F QuicExceptionHelpers.ThrowIfFailed(status, "Failed to open stream to peer."); Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); - status = MsQuicApi.Api.StreamStartDelegate(_state.Handle, QUIC_STREAM_START_FLAGS.FAIL_BLOCKED); + status = MsQuicApi.Api.StreamStartDelegate(_state.Handle, QUIC_STREAM_START_FLAGS.FAIL_BLOCKED | QUIC_STREAM_START_FLAGS.SHUTDOWN_ON_FAIL); QuicExceptionHelpers.ThrowIfFailed(status, "Could not start stream."); } catch @@ -429,8 +431,6 @@ internal override ValueTask ReadAsync(Memory destination, Cancellatio long abortError; bool preCanceled = false; - int bytesRead = -1; - bool reenableReceive = false; lock (_state) { initialReadState = _state.ReadState; @@ -493,32 +493,22 @@ internal override ValueTask ReadAsync(Memory destination, Cancellatio { _state.ReadState = ReadState.None; - bytesRead = CopyMsQuicBuffersToUserBuffer(_state.ReceiveQuicBuffers.AsSpan(0, _state.ReceiveQuicBuffersCount), destination.Span); + int taken = CopyMsQuicBuffersToUserBuffer(_state.ReceiveQuicBuffers.AsSpan(0, _state.ReceiveQuicBuffersCount), destination.Span); + ReceiveComplete(taken); - if (bytesRead != _state.ReceiveQuicBuffersTotalBytes) + if (taken != _state.ReceiveQuicBuffersTotalBytes) { // Need to re-enable receives because MsQuic will pause them when we don't consume the entire buffer. - reenableReceive = true; + EnableReceive(); } else if (_state.ReceiveIsFinal) { // This was a final message and we've consumed everything. We can complete the state without waiting for PEER_SEND_SHUTDOWN _state.ReadState = ReadState.ReadsCompleted; } - } - } - - // methods below need to be called outside of the lock - if (bytesRead > -1) - { - ReceiveComplete(bytesRead); - if (reenableReceive) - { - EnableReceive(); + return new ValueTask(taken); } - - return new ValueTask(bytesRead); } // All success scenarios returned at this point. Failure scenarios below: @@ -869,7 +859,6 @@ private void Dispose(bool disposing) private void EnableReceive() { - Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); uint status = MsQuicApi.Api.StreamReceiveSetEnabledDelegate(_state.Handle, enabled: true); QuicExceptionHelpers.ThrowIfFailed(status, "StreamReceiveSetEnabled failed."); } @@ -1095,9 +1084,8 @@ private static uint HandleEventPeerRecvAborted(State state, ref StreamEvent evt) private static uint HandleEventStartComplete(State state, ref StreamEvent evt) { - // TODO: We should probably check for a failure as indicated by the event data (or at least assert no failure if we aren't expecting it). - // However, since there is no definition for START_COMPLETE event data currently, we can't do this right now. - + // Store the start status code and check it when propagating shutdown event, which we'll get since we set SHUTDOWN_ON_FAIL in StreamStart. + state.StartStatus = evt.Data.StartComplete.Status; return MsQuicStatusCodes.Success; } @@ -1171,12 +1159,28 @@ private static uint HandleEventShutdownComplete(State state, ref StreamEvent evt if (shouldReadComplete) { - state.ReceiveResettableCompletionSource.Complete(0); + if (state.StartStatus == MsQuicStatusCodes.Success) + { + state.ReceiveResettableCompletionSource.Complete(0); + } + else + { + state.ReceiveResettableCompletionSource.CompleteException( + ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException($"Stream start failed with {MsQuicStatusCodes.GetError(state.StartStatus)}"))); + } } if (shouldShutdownWriteComplete) { - state.ShutdownWriteCompletionSource.SetResult(); + if (state.StartStatus == MsQuicStatusCodes.Success) + { + state.ShutdownWriteCompletionSource.SetResult(); + } + else + { + state.ShutdownWriteCompletionSource.SetException( + ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException($"Stream start failed with {MsQuicStatusCodes.GetError(state.StartStatus)}"))); + } } if (shouldShutdownComplete) @@ -1327,7 +1331,10 @@ private static void CleanupSendState(State state) HandleWriteFailedState(); CleanupSendState(_state); - // TODO this may need to be an aborted exception. + if (status == MsQuicStatusCodes.Aborted) + { + throw ThrowHelper.GetConnectionAbortedException(_state.ConnectionState.AbortErrorCode); + } QuicExceptionHelpers.ThrowIfFailed(status, "Could not send data to peer."); } @@ -1391,7 +1398,10 @@ private static void CleanupSendState(State state) HandleWriteFailedState(); CleanupSendState(_state); - // TODO this may need to be an aborted exception. + if (status == MsQuicStatusCodes.Aborted) + { + throw ThrowHelper.GetConnectionAbortedException(_state.ConnectionState.AbortErrorCode); + } QuicExceptionHelpers.ThrowIfFailed(status, "Could not send data to peer."); } @@ -1452,7 +1462,10 @@ private static void CleanupSendState(State state) HandleWriteFailedState(); CleanupSendState(_state); - // TODO this may need to be an aborted exception. + if (status == MsQuicStatusCodes.Aborted) + { + throw ThrowHelper.GetConnectionAbortedException(_state.ConnectionState.AbortErrorCode); + } QuicExceptionHelpers.ThrowIfFailed(status, "Could not send data to peer."); } @@ -1462,7 +1475,6 @@ private static void CleanupSendState(State state) private void ReceiveComplete(int bufferLength) { - Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); uint status = MsQuicApi.Api.StreamReceiveCompleteDelegate(_state.Handle, (ulong)bufferLength); QuicExceptionHelpers.ThrowIfFailed(status, "Could not complete receive call."); } @@ -1470,7 +1482,7 @@ private void ReceiveComplete(int bufferLength) // This can fail if the stream isn't started. private long GetStreamId() { - return (long)MsQuicParameterHelpers.GetULongParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.STREAM, (uint)QUIC_PARAM_STREAM.ID); + return (long)MsQuicParameterHelpers.GetULongParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_STREAM.ID); } private void ThrowIfDisposed() diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index 7db083a8da22..9cd0dad78c32 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -139,7 +139,10 @@ public async Task UntrustedClientCertificateFails() { await t; } - catch { }; + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } } [Fact] @@ -307,7 +310,7 @@ public async Task ConnectWithCertificateForDifferentName_Throws() await Assert.ThrowsAsync(async () => await clientTask); } - [Theory] + [ConditionalTheory] [InlineData("127.0.0.1", true)] [InlineData("::1", true)] [InlineData("127.0.0.1", false)] @@ -386,6 +389,7 @@ public async Task ConnectWithClientCertificate(bool sendCerttificate) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/67302")] public async Task WaitForAvailableUnidirectionStreamsAsyncWorks() { QuicListenerOptions listenerOptions = CreateQuicListenerOptions(); @@ -411,6 +415,7 @@ public async Task WaitForAvailableUnidirectionStreamsAsyncWorks() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/67302")] public async Task WaitForAvailableBidirectionStreamsAsyncWorks() { QuicListenerOptions listenerOptions = CreateQuicListenerOptions();