未验证 提交 d5b1a7f5 编写于 作者: M Marie Píchová 提交者: GitHub

[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
上级 f249a3d5
......@@ -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
......
......@@ -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
......
......@@ -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}");
}
});
}
}
......
......@@ -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<HttpRequestException>(() => 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}");
}
}
});
}
......
......@@ -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<HttpResponseMessage> 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 =>
{
......
......@@ -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<HttpRequestException>(() => 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));
});
});
......
......@@ -128,7 +128,7 @@ public async Task SendAsync_GetUsingChunkedEncoding_ThrowsHttpRequestException()
using (HttpClient client = new HttpClient(handler))
{
HttpRequestException ex = await Assert.ThrowsAsync<HttpRequestException>(() => 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<HttpResponseMessage> task, string payloadText)
......
......@@ -204,7 +204,7 @@ public async Task Http2ClearText_SendAsync_Success(string clientContent, string
Http2LoopbackConnection connection = await server.EstablishConnectionAsync();
Assert.IsNotType<SslStream>(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}");
}
});
}
......
......@@ -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);
......
......@@ -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}");
}
});
});
}
......
......@@ -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}");
}
});
});
}
......
......@@ -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.
......
......@@ -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<HttpRequestException>(() => client.SendAsync(TestAsync, request));
var innerException = ex.InnerException;
var exception = await Assert.ThrowsAnyAsync<HttpRequestException>(() => 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}");
}
}
}
......
......@@ -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
{
......
......@@ -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.");
}
}
......
......@@ -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
......
......@@ -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]<IntPtr, QUIC_PARAM_LEVEL, uint, uint, byte*, uint>)_functionPointer)(__handle_gen_native, level, param, bufferLength, buffer);
__retVal = ((delegate* unmanaged[Cdecl]<IntPtr, uint, uint, byte*, uint>)_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]<IntPtr, QUIC_PARAM_LEVEL, uint, uint*, byte*, uint>)_functionPointer)(__handle_gen_native, level, param, __bufferLength_gen_native, buffer);
__retVal = ((delegate* unmanaged[Cdecl]<IntPtr, uint, uint*, byte*, uint>)_functionPointer)(__handle_gen_native, param, __bufferLength_gen_native, buffer);
}
}
finally
......
......@@ -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;
}
......
......@@ -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<MsQuicConnection> 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}");
}
}
......
......@@ -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<int> ReadAsync(Memory<byte> 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<int> ReadAsync(Memory<byte> 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<int>(taken);
}
return new ValueTask<int>(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()
......
......@@ -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<AuthenticationException>(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();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册