From 6bf36e426357b1b3efeff8becf66b9e0ae5db394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= Date: Fri, 12 Mar 2021 17:12:13 +0100 Subject: [PATCH 01/12] Cherry picked change from scalablecory/msquic-update. --- .../System.Net.Quic/ref/System.Net.Quic.cs | 184 +++---- .../src/System.Net.Quic.csproj | 2 +- .../MsQuic/Internal/MsQuicApi.cs | 412 ++++---------- .../MsQuic/Internal/MsQuicParameterHelpers.cs | 89 +-- .../MsQuic/Internal/MsQuicSecurityConfig.cs | 44 -- .../MsQuic/Internal/MsQuicSession.cs | 157 ------ .../MsQuic}/Interop/Interop.MsQuic.cs | 0 .../MsQuic/Interop/MsQuicAlpnHelper.cs | 58 ++ .../MsQuic}/Interop/MsQuicEnums.cs | 89 +-- .../MsQuic}/Interop/MsQuicNativeMethods.cs | 220 +++++--- .../MsQuic}/Interop/MsQuicStatusCodes.cs | 0 .../MsQuic}/Interop/MsQuicStatusHelper.cs | 0 .../Interop/SafeMsQuicConfigurationHandle.cs | 134 +++++ .../Interop/SafeMsQuicConnectionHandle.cs | 27 + .../Interop/SafeMsQuicListenerHandle.cs | 22 + .../Interop/SafeMsQuicRegistrationHandle.cs | 22 + .../MsQuic/Interop/SafeMsQuicStreamHandle.cs | 27 + .../MsQuic/MsQuicConnection.cs | 357 ++++++------ .../Implementations/MsQuic/MsQuicListener.cs | 223 ++++---- .../Implementations/MsQuic/MsQuicStream.cs | 515 ++++++++---------- .../Net/Quic/QuicClientConnectionOptions.cs | 26 +- .../System/Net/Quic/QuicListenerOptions.cs | 35 +- .../src/System/Net/Quic/QuicOptions.cs | 36 ++ .../tests/FunctionalTests/MsQuicTestBase.cs | 3 +- .../tests/FunctionalTests/MsQuicTests.cs | 3 +- 25 files changed, 1259 insertions(+), 1426 deletions(-) delete mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSecurityConfig.cs delete mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs rename src/libraries/System.Net.Quic/src/System/Net/Quic/{ => Implementations/MsQuic}/Interop/Interop.MsQuic.cs (100%) create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicAlpnHelper.cs rename src/libraries/System.Net.Quic/src/System/Net/Quic/{ => Implementations/MsQuic}/Interop/MsQuicEnums.cs (71%) rename src/libraries/System.Net.Quic/src/System/Net/Quic/{ => Implementations/MsQuic}/Interop/MsQuicNativeMethods.cs (70%) rename src/libraries/System.Net.Quic/src/System/Net/Quic/{ => Implementations/MsQuic}/Interop/MsQuicStatusCodes.cs (100%) rename src/libraries/System.Net.Quic/src/System/Net/Quic/{ => Implementations/MsQuic}/Interop/MsQuicStatusHelper.cs (100%) create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/QuicOptions.cs diff --git a/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs b/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs index 12f167a2a66238..805d17e77c4ed6 100644 --- a/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs +++ b/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs @@ -6,121 +6,123 @@ namespace System.Net.Quic { - public static class QuicImplementationProviders + public partial class QuicClientConnectionOptions : System.Net.Quic.QuicOptions { - public static Implementations.QuicImplementationProvider Mock => throw null; - public static Implementations.QuicImplementationProvider MsQuic => throw null; - public static Implementations.QuicImplementationProvider Default => throw null; + public QuicClientConnectionOptions() { } + public System.Net.Security.SslClientAuthenticationOptions? ClientAuthenticationOptions { get { throw null; } set { } } + public System.Net.IPEndPoint? LocalEndPoint { get { throw null; } set { } } + public System.Net.EndPoint? RemoteEndPoint { get { throw null; } set { } } } - public sealed class QuicListener : IDisposable + public sealed partial class QuicConnection : System.IDisposable { - public QuicListener(IPEndPoint listenEndPoint, System.Net.Security.SslServerAuthenticationOptions sslServerAuthenticationOptions) { throw null; } - public QuicListener(QuicListenerOptions options) { throw null; } - public QuicListener(Implementations.QuicImplementationProvider implementationProvider, IPEndPoint listenEndPoint, System.Net.Security.SslServerAuthenticationOptions sslServerAuthenticationOptions) { throw null; } - public QuicListener(Implementations.QuicImplementationProvider implementationProvider, QuicListenerOptions options) { throw null; } - public IPEndPoint ListenEndPoint => throw null; - public System.Threading.Tasks.ValueTask AcceptConnectionAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - public void Start() => throw null; - public void Close() => throw null; - public void Dispose() => throw null; + public QuicConnection(System.Net.EndPoint remoteEndPoint, System.Net.Security.SslClientAuthenticationOptions? sslClientAuthenticationOptions, System.Net.IPEndPoint? localEndPoint = null) { } + public QuicConnection(System.Net.Quic.Implementations.QuicImplementationProvider implementationProvider, System.Net.EndPoint remoteEndPoint, System.Net.Security.SslClientAuthenticationOptions? sslClientAuthenticationOptions, System.Net.IPEndPoint? localEndPoint = null) { } + public QuicConnection(System.Net.Quic.Implementations.QuicImplementationProvider implementationProvider, System.Net.Quic.QuicClientConnectionOptions options) { } + public QuicConnection(System.Net.Quic.QuicClientConnectionOptions options) { } + public bool Connected { get { throw null; } } + public System.Net.IPEndPoint LocalEndPoint { get { throw null; } } + public System.Net.Security.SslApplicationProtocol NegotiatedApplicationProtocol { get { throw null; } } + public System.Net.EndPoint RemoteEndPoint { get { throw null; } } + public System.Threading.Tasks.ValueTask AcceptStreamAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.ValueTask CloseAsync(long errorCode, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.ValueTask ConnectAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public void Dispose() { } + public long GetRemoteAvailableBidirectionalStreamCount() { throw null; } + public long GetRemoteAvailableUnidirectionalStreamCount() { throw null; } + public System.Net.Quic.QuicStream OpenBidirectionalStream() { throw null; } + public System.Net.Quic.QuicStream OpenUnidirectionalStream() { throw null; } } - public class QuicListenerOptions + public partial class QuicConnectionAbortedException : System.Net.Quic.QuicException { - public System.Net.Security.SslServerAuthenticationOptions? ServerAuthenticationOptions { get => throw null; set => throw null; } - public string? CertificateFilePath { get => throw null; set => throw null; } - public string? PrivateKeyFilePath { get => throw null; set => throw null; } - public IPEndPoint? ListenEndPoint { get => throw null; set => throw null; } - public int ListenBacklog { get => throw null; set => throw null; } - public long MaxBidirectionalStreams { get => throw null; set => throw null; } - public long MaxUnidirectionalStreams { get => throw null; set => throw null; } - public TimeSpan IdleTimeout { get => throw null; set => throw null; } + public QuicConnectionAbortedException(string message, long errorCode) : base (default(string)) { } + public long ErrorCode { get { throw null; } } } - public sealed class QuicConnection : IDisposable + public partial class QuicException : System.Exception { - public QuicConnection(System.Net.EndPoint remoteEndPoint, System.Net.Security.SslClientAuthenticationOptions? sslClientAuthenticationOptions, System.Net.IPEndPoint? localEndPoint = null) { throw null; } - public QuicConnection(QuicClientConnectionOptions options) { throw null; } - public QuicConnection(Implementations.QuicImplementationProvider implementationProvider, System.Net.EndPoint remoteEndPoint, System.Net.Security.SslClientAuthenticationOptions? sslClientAuthenticationOptions, System.Net.IPEndPoint? localEndPoint = null) { throw null; } - public QuicConnection(Implementations.QuicImplementationProvider implementationProvider, QuicClientConnectionOptions options) { throw null; } - public bool Connected => throw null; - public System.Net.IPEndPoint LocalEndPoint => throw null; - public System.Net.EndPoint RemoteEndPoint => throw null; - public System.Net.Security.SslApplicationProtocol NegotiatedApplicationProtocol => throw null; - public System.Threading.Tasks.ValueTask ConnectAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - public QuicStream OpenUnidirectionalStream() => throw null; - public QuicStream OpenBidirectionalStream() => throw null; - public System.Threading.Tasks.ValueTask AcceptStreamAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - public System.Threading.Tasks.ValueTask CloseAsync(long errorCode, System.Threading.CancellationToken cancellationToken = default) => throw null; - public void Dispose() => throw null; - public long GetRemoteAvailableUnidirectionalStreamCount() => throw null; - public long GetRemoteAvailableBidirectionalStreamCount() => throw null; + public QuicException(string? message) { } + public QuicException(string? message, System.Exception? innerException) { } } - public class QuicClientConnectionOptions + public static partial class QuicImplementationProviders { - public System.Net.Security.SslClientAuthenticationOptions? ClientAuthenticationOptions { get => throw null; set => throw null; } - public IPEndPoint? LocalEndPoint { get => throw null; set => throw null; } - public EndPoint? RemoteEndPoint { get => throw null; set => throw null; } - public long MaxBidirectionalStreams { get => throw null; set => throw null; } - public long MaxUnidirectionalStreams { get => throw null; set => throw null; } - public TimeSpan IdleTimeout { get => throw null; set => throw null; } + public static System.Net.Quic.Implementations.QuicImplementationProvider Default { get { throw null; } } + public static System.Net.Quic.Implementations.QuicImplementationProvider Mock { get { throw null; } } + public static System.Net.Quic.Implementations.QuicImplementationProvider MsQuic { get { throw null; } } } - public sealed class QuicStream : System.IO.Stream + public sealed partial class QuicListener : System.IDisposable { - internal QuicStream() { throw null; } - public override bool CanSeek => throw null; - public override long Length => throw null; - public override long Seek(long offset, System.IO.SeekOrigin origin) => throw null; - public override void SetLength(long value) => throw null; - public override long Position { get => throw null; set => throw null; } - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => throw null; - public override int EndRead(IAsyncResult asyncResult) => throw null; - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => throw null; - public override void EndWrite(IAsyncResult asyncResult) => throw null; - public override int Read(byte[] buffer, int offset, int count) => throw null; - public override System.Threading.Tasks.Task ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null; - public override void Write(byte[] buffer, int offset, int count) => throw null; - public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null; - public long StreamId => throw null; - public override bool CanRead => throw null; - public override int Read(Span buffer) => throw null; - public override System.Threading.Tasks.ValueTask ReadAsync(Memory buffer, System.Threading.CancellationToken cancellationToken = default) => throw null; - public override bool CanWrite => throw null; - public override void Write(ReadOnlySpan buffer) => throw null; - public override System.Threading.Tasks.ValueTask WriteAsync(ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default) => throw null; - public override void Flush() => throw null; - public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null; - public void AbortRead(long errorCode) => throw null; - public void AbortWrite(long errorCode) => throw null; - public System.Threading.Tasks.ValueTask WriteAsync(ReadOnlyMemory buffer, bool endStream, System.Threading.CancellationToken cancellationToken = default) => throw null; - public System.Threading.Tasks.ValueTask WriteAsync(System.Buffers.ReadOnlySequence buffers, System.Threading.CancellationToken cancellationToken = default) => throw null; - public System.Threading.Tasks.ValueTask WriteAsync(System.Buffers.ReadOnlySequence buffers, bool endStream, System.Threading.CancellationToken cancellationToken = default) => throw null; - public System.Threading.Tasks.ValueTask WriteAsync(ReadOnlyMemory> buffers, System.Threading.CancellationToken cancellationToken = default) => throw null; - public System.Threading.Tasks.ValueTask WriteAsync(ReadOnlyMemory> buffers, bool endStream, System.Threading.CancellationToken cancellationToken = default) => throw null; - public System.Threading.Tasks.ValueTask ShutdownWriteCompleted(System.Threading.CancellationToken cancellationToken = default) => throw null; - public void Shutdown() => throw null; + public QuicListener(System.Net.IPEndPoint listenEndPoint, System.Net.Security.SslServerAuthenticationOptions sslServerAuthenticationOptions) { } + public QuicListener(System.Net.Quic.Implementations.QuicImplementationProvider implementationProvider, System.Net.IPEndPoint listenEndPoint, System.Net.Security.SslServerAuthenticationOptions sslServerAuthenticationOptions) { } + public QuicListener(System.Net.Quic.Implementations.QuicImplementationProvider implementationProvider, System.Net.Quic.QuicListenerOptions options) { } + public QuicListener(System.Net.Quic.QuicListenerOptions options) { } + public System.Net.IPEndPoint ListenEndPoint { get { throw null; } } + public System.Threading.Tasks.ValueTask AcceptConnectionAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public void Close() { } + public void Dispose() { } + public void Start() { } } - public class QuicException : Exception + public partial class QuicListenerOptions : System.Net.Quic.QuicOptions { - public QuicException(string? message) { throw null; } - public QuicException(string? message, Exception? innerException) { throw null; } + public QuicListenerOptions() { } + public int ListenBacklog { get { throw null; } set { } } + public System.Net.IPEndPoint? ListenEndPoint { get { throw null; } set { } } + public System.Net.Security.SslServerAuthenticationOptions? ServerAuthenticationOptions { get { throw null; } set { } } } - public class QuicConnectionAbortedException : QuicException + public partial class QuicOperationAbortedException : System.Net.Quic.QuicException { - public QuicConnectionAbortedException(string message, long errorCode) : base(default) { throw null; } - public long ErrorCode { get { throw null; } } + public QuicOperationAbortedException(string message) : base (default(string)) { } + } + public partial class QuicOptions + { + public QuicOptions() { } + public System.TimeSpan IdleTimeout { get { throw null; } set { } } + public long MaxBidirectionalStreams { get { throw null; } set { } } + public long MaxUnidirectionalStreams { get { throw null; } set { } } } - public class QuicOperationAbortedException : QuicException + public sealed partial class QuicStream : System.IO.Stream { - public QuicOperationAbortedException(string message) : base(default) { throw null; } + internal QuicStream() { } + public override bool CanRead { get { throw null; } } + public override bool CanSeek { get { throw null; } } + public override bool CanWrite { get { throw null; } } + public override long Length { get { throw null; } } + public override long Position { get { throw null; } set { } } + public long StreamId { get { throw null; } } + public void AbortRead(long errorCode) { } + public void AbortWrite(long errorCode) { } + public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback? callback, object? state) { throw null; } + public override System.IAsyncResult BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback? callback, object? state) { throw null; } + protected override void Dispose(bool disposing) { } + public override int EndRead(System.IAsyncResult asyncResult) { throw null; } + public override void EndWrite(System.IAsyncResult asyncResult) { } + public override void Flush() { } + public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) { throw null; } + public override int Read(byte[] buffer, int offset, int count) { throw null; } + public override int Read(System.Span buffer) { throw null; } + public override System.Threading.Tasks.Task ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } + public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override long Seek(long offset, System.IO.SeekOrigin origin) { throw null; } + public override void SetLength(long value) { } + public void Shutdown() { } + public System.Threading.Tasks.ValueTask ShutdownWriteCompleted(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override void Write(byte[] buffer, int offset, int count) { } + public override void Write(System.ReadOnlySpan buffer) { } + public System.Threading.Tasks.ValueTask WriteAsync(System.Buffers.ReadOnlySequence buffers, bool endStream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.ValueTask WriteAsync(System.Buffers.ReadOnlySequence buffers, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } + public System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, bool endStream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory> buffers, bool endStream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory> buffers, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } - public class QuicStreamAbortedException : QuicException + public partial class QuicStreamAbortedException : System.Net.Quic.QuicException { - public QuicStreamAbortedException(string message, long errorCode) : base(default) { throw null; } + public QuicStreamAbortedException(string message, long errorCode) : base (default(string)) { } public long ErrorCode { get { throw null; } } } } namespace System.Net.Quic.Implementations { - public abstract class QuicImplementationProvider + public abstract partial class QuicImplementationProvider { internal QuicImplementationProvider() { } public abstract bool IsSupported { get; } diff --git a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj index f516d28789e30f..1e1e84e05bff4f 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -15,7 +15,7 @@ - + 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 d483c76a0ac368..65948e4b0c8b90 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 @@ -1,30 +1,34 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; -using System.Collections.Generic; -using System.IO; -using System.Net.Security; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; +using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; namespace System.Net.Quic.Implementations.MsQuic.Internal { - internal class MsQuicApi : IDisposable + internal unsafe sealed class MsQuicApi { - private bool _disposed; - - private readonly IntPtr _registrationContext; - + private static MsQuicApi? _api; + + public SafeMsQuicRegistrationHandle Registration { get; } + + // This is workaround for a bug in ILTrimmer. + // Withough these DynamicDependency attributes, .ctor() will be removed from the safe handles. + // Remove once fixed: https://github.com/mono/linker/issues/1660 + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicRegistrationHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicConfigurationHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicListenerHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicConnectionHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicStreamHandle))] private unsafe MsQuicApi() { - MsQuicNativeMethods.NativeApi* registration; + MsQuicNativeMethods.NativeApi* vtable; + uint status; try { - uint status = Interop.MsQuic.MsQuicOpen(out registration); + status = Interop.MsQuic.MsQuicOpen(out vtable); if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) { throw new NotSupportedException(SR.net_quic_notsupported); @@ -35,141 +39,138 @@ private unsafe MsQuicApi() throw new NotSupportedException(SR.net_quic_notsupported); } - MsQuicNativeMethods.NativeApi nativeRegistration = *registration; + SetParamDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->SetParam); + + GetParamDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->GetParam); + + SetCallbackHandlerDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->SetCallbackHandler); RegistrationOpenDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.RegistrationOpen); + vtable->RegistrationOpen); RegistrationCloseDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.RegistrationClose); - - SecConfigCreateDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SecConfigCreate); - SecConfigDeleteDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SecConfigDelete); - SessionOpenDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SessionOpen); - SessionCloseDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SessionClose); - SessionShutdownDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SessionShutdown); + vtable->RegistrationClose); + + ConfigurationOpenDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->ConfigurationOpen); + ConfigurationCloseDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->ConfigurationClose); + ConfigurationLoadCredentialDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->ConfigurationLoadCredential); ListenerOpenDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ListenerOpen); + vtable->ListenerOpen); ListenerCloseDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ListenerClose); + vtable->ListenerClose); ListenerStartDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ListenerStart); + vtable->ListenerStart); ListenerStopDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ListenerStop); + vtable->ListenerStop); ConnectionOpenDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ConnectionOpen); + vtable->ConnectionOpen); ConnectionCloseDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ConnectionClose); + vtable->ConnectionClose); + ConnectionSetConfigurationDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->ConnectionSetConfiguration); ConnectionShutdownDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ConnectionShutdown); + vtable->ConnectionShutdown); ConnectionStartDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ConnectionStart); + vtable->ConnectionStart); StreamOpenDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamOpen); + vtable->StreamOpen); StreamCloseDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamClose); + vtable->StreamClose); StreamStartDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamStart); + vtable->StreamStart); StreamShutdownDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamShutdown); + vtable->StreamShutdown); StreamSendDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamSend); + vtable->StreamSend); StreamReceiveCompleteDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamReceiveComplete); + vtable->StreamReceiveComplete); StreamReceiveSetEnabledDelegate = Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamReceiveSetEnabled); - SetContextDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SetContext); - GetContextDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.GetContext); - SetCallbackHandlerDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SetCallbackHandler); + vtable->StreamReceiveSetEnabled); - SetParamDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SetParam); - GetParamDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.GetParam); + byte* appName = stackalloc byte[5]; + appName[0] = (byte)'.'; + appName[1] = (byte)'N'; + appName[2] = (byte)'E'; + appName[3] = (byte)'T'; + appName[4] = 0; - var registrationConfig = new MsQuicNativeMethods.RegistrationConfig + var cfg = new RegistrationConfig { - AppName = "SystemNetQuic", - ExecutionProfile = QUIC_EXECUTION_PROFILE.QUIC_EXECUTION_PROFILE_LOW_LATENCY + AppName = appName, + ExecutionProfile = (uint)QUIC_EXECUTION_PROFILE.QUIC_EXECUTION_PROFILE_LOW_LATENCY }; - RegistrationOpenDelegate(ref registrationConfig, out IntPtr ctx); - _registrationContext = ctx; + status = RegistrationOpenDelegate(ref cfg, out SafeMsQuicRegistrationHandle handle); + QuicExceptionHelpers.ThrowIfFailed(status, "RegistrationOpen failed."); + + Registration = handle; } - internal static MsQuicApi Api { get; } = null!; + internal static MsQuicApi Api => _api ??= new MsQuicApi(); - internal static bool IsQuicSupported { get; } + internal static bool IsQuicSupported => true; - static MsQuicApi() - { - // MsQuicOpen will succeed even if the platform will not support it. It will then fail with unspecified - // platform-specific errors in subsequent callbacks. For now, check for the minimum build we've tested it on. + //static MsQuicApi() + //{ + // // MsQuicOpen will succeed even if the platform will not support it. It will then fail with unspecified + // // platform-specific errors in subsequent callbacks. For now, check for the minimum build we've tested it on. - // TODO: - // - Hopefully, MsQuicOpen will perform this check for us and give us a consistent error code. - // - Otherwise, dial this in to reflect actual minimum requirements and add some sort of platform - // error code mapping when creating exceptions. + // // TODO: + // // - Hopefully, MsQuicOpen will perform this check for us and give us a consistent error code. + // // - Otherwise, dial this in to reflect actual minimum requirements and add some sort of platform + // // error code mapping when creating exceptions. - // TODO: try to initialize TLS 1.3 in SslStream. + // // TODO: try to initialize TLS 1.3 in SslStream. - try - { - Api = new MsQuicApi(); - IsQuicSupported = true; - } - catch (NotSupportedException) - { - IsQuicSupported = false; - } - } + // try + // { + // Api = new MsQuicApi(); + // IsQuicSupported = true; + // } + // catch (NotSupportedException) + // { + // IsQuicSupported = false; + // } + //} internal MsQuicNativeMethods.RegistrationOpenDelegate RegistrationOpenDelegate { get; } internal MsQuicNativeMethods.RegistrationCloseDelegate RegistrationCloseDelegate { get; } - internal MsQuicNativeMethods.SecConfigCreateDelegate SecConfigCreateDelegate { get; } - internal MsQuicNativeMethods.SecConfigDeleteDelegate SecConfigDeleteDelegate { get; } - - internal MsQuicNativeMethods.SessionOpenDelegate SessionOpenDelegate { get; } - internal MsQuicNativeMethods.SessionCloseDelegate SessionCloseDelegate { get; } - internal MsQuicNativeMethods.SessionShutdownDelegate SessionShutdownDelegate { get; } + internal MsQuicNativeMethods.ConfigurationOpenDelegate ConfigurationOpenDelegate { get; } + internal MsQuicNativeMethods.ConfigurationCloseDelegate ConfigurationCloseDelegate { get; } + internal MsQuicNativeMethods.ConfigurationLoadCredentialDelegate ConfigurationLoadCredentialDelegate { get; } internal MsQuicNativeMethods.ListenerOpenDelegate ListenerOpenDelegate { get; } internal MsQuicNativeMethods.ListenerCloseDelegate ListenerCloseDelegate { get; } @@ -178,6 +179,7 @@ static MsQuicApi() internal MsQuicNativeMethods.ConnectionOpenDelegate ConnectionOpenDelegate { get; } internal MsQuicNativeMethods.ConnectionCloseDelegate ConnectionCloseDelegate { get; } + internal MsQuicNativeMethods.ConnectionSetConfigurationDelegate ConnectionSetConfigurationDelegate { get; } internal MsQuicNativeMethods.ConnectionShutdownDelegate ConnectionShutdownDelegate { get; } internal MsQuicNativeMethods.ConnectionStartDelegate ConnectionStartDelegate { get; } @@ -189,229 +191,9 @@ static MsQuicApi() internal MsQuicNativeMethods.StreamReceiveCompleteDelegate StreamReceiveCompleteDelegate { get; } internal MsQuicNativeMethods.StreamReceiveSetEnabledDelegate StreamReceiveSetEnabledDelegate { get; } - internal MsQuicNativeMethods.SetContextDelegate SetContextDelegate { get; } - internal MsQuicNativeMethods.GetContextDelegate GetContextDelegate { get; } internal MsQuicNativeMethods.SetCallbackHandlerDelegate SetCallbackHandlerDelegate { get; } internal MsQuicNativeMethods.SetParamDelegate SetParamDelegate { get; } internal MsQuicNativeMethods.GetParamDelegate GetParamDelegate { get; } - - internal unsafe uint UnsafeSetParam( - IntPtr Handle, - uint Level, - uint Param, - MsQuicNativeMethods.QuicBuffer Buffer) - { - return SetParamDelegate( - Handle, - Level, - Param, - Buffer.Length, - Buffer.Buffer); - } - - internal unsafe uint UnsafeGetParam( - IntPtr Handle, - uint Level, - uint Param, - ref MsQuicNativeMethods.QuicBuffer Buffer) - { - uint bufferLength = Buffer.Length; - byte* buf = Buffer.Buffer; - return GetParamDelegate( - Handle, - Level, - Param, - &bufferLength, - buf); - } - - public async ValueTask CreateSecurityConfig(X509Certificate certificate, string? certFilePath, string? privateKeyFilePath) - { - MsQuicSecurityConfig? secConfig = null; - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - uint secConfigCreateStatus = MsQuicStatusCodes.InternalError; - uint createConfigStatus; - IntPtr unmanagedAddr = IntPtr.Zero; - MsQuicNativeMethods.CertFileParams fileParams = default; - - try - { - if (certFilePath != null && privateKeyFilePath != null) - { - fileParams = new MsQuicNativeMethods.CertFileParams - { - PrivateKeyFilePath = Marshal.StringToCoTaskMemUTF8(privateKeyFilePath), - CertificateFilePath = Marshal.StringToCoTaskMemUTF8(certFilePath) - }; - - unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(fileParams)); - Marshal.StructureToPtr(fileParams, unmanagedAddr, fDeleteOld: false); - - createConfigStatus = SecConfigCreateDelegate( - _registrationContext, - (uint)QUIC_SEC_CONFIG_FLAG.CERT_FILE, - unmanagedAddr, - null, - IntPtr.Zero, - SecCfgCreateCallbackHandler); - } - else if (certificate != null) - { - createConfigStatus = SecConfigCreateDelegate( - _registrationContext, - (uint)QUIC_SEC_CONFIG_FLAG.CERT_CONTEXT, - certificate.Handle, - null, - IntPtr.Zero, - SecCfgCreateCallbackHandler); - } - else - { - // If no certificate is provided, provide a null one. - createConfigStatus = SecConfigCreateDelegate( - _registrationContext, - (uint)QUIC_SEC_CONFIG_FLAG.CERT_NULL, - IntPtr.Zero, - null, - IntPtr.Zero, - SecCfgCreateCallbackHandler); - } - - QuicExceptionHelpers.ThrowIfFailed( - createConfigStatus, - "Could not create security configuration."); - - void SecCfgCreateCallbackHandler( - IntPtr context, - uint status, - IntPtr securityConfig) - { - secConfig = new MsQuicSecurityConfig(this, securityConfig); - secConfigCreateStatus = status; - tcs.SetResult(); - } - - await tcs.Task.ConfigureAwait(false); - - QuicExceptionHelpers.ThrowIfFailed( - secConfigCreateStatus, - "Could not create security configuration."); - } - finally - { - if (fileParams.CertificateFilePath != IntPtr.Zero) - { - Marshal.FreeCoTaskMem(fileParams.CertificateFilePath); - } - - if (fileParams.PrivateKeyFilePath != IntPtr.Zero) - { - Marshal.FreeCoTaskMem(fileParams.PrivateKeyFilePath); - } - - if (unmanagedAddr != IntPtr.Zero) - { - Marshal.FreeHGlobal(unmanagedAddr); - } - } - - return secConfig; - } - - public unsafe IntPtr SessionOpen(List alpnProtocols) - { - if (alpnProtocols.Count == 1) - { - return SessionOpen(alpnProtocols[0]); - } - - var memoryHandles = ArrayPool.Shared.Rent(alpnProtocols.Count); - var quicBuffers = ArrayPool.Shared.Rent(alpnProtocols.Count); - - try - { - for (int i = 0; i < alpnProtocols.Count; ++i) - { - ReadOnlyMemory alpnProtocol = alpnProtocols[i].Protocol; - MemoryHandle h = alpnProtocol.Pin(); - - memoryHandles[i] = h; - quicBuffers[i].Buffer = (byte*)h.Pointer; - quicBuffers[i].Length = (uint)alpnProtocol.Length; - } - - IntPtr session; - - fixed (MsQuicNativeMethods.QuicBuffer* pQuicBuffers = quicBuffers) - { - session = SessionOpen(pQuicBuffers, (uint)alpnProtocols.Count); - } - - ArrayPool.Shared.Return(quicBuffers); - ArrayPool.Shared.Return(memoryHandles); - - return session; - } - finally - { - foreach (MemoryHandle handle in memoryHandles) - { - handle.Dispose(); - } - } - } - - private unsafe IntPtr SessionOpen(SslApplicationProtocol alpnProtocol) - { - ReadOnlyMemory memory = alpnProtocol.Protocol; - using MemoryHandle h = memory.Pin(); - - var quicBuffer = new MsQuicNativeMethods.QuicBuffer() - { - Buffer = (byte*)h.Pointer, - Length = (uint)memory.Length - }; - - return SessionOpen(&quicBuffer, 1); - } - - private unsafe IntPtr SessionOpen(MsQuicNativeMethods.QuicBuffer *alpnBuffers, uint bufferCount) - { - IntPtr sessionPtr = IntPtr.Zero; - uint status = SessionOpenDelegate( - _registrationContext, - alpnBuffers, - bufferCount, - IntPtr.Zero, - ref sessionPtr); - - QuicExceptionHelpers.ThrowIfFailed(status, "Could not open session."); - - return sessionPtr; - } - - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - ~MsQuicApi() - { - Dispose(disposing: false); - } - - private void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - RegistrationCloseDelegate?.Invoke(_registrationContext); - - _disposed = true; - } } } 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 afeb151a0436b0..8697c0658f4813 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 @@ -1,97 +1,62 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; +using System.Runtime.InteropServices; using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; namespace System.Net.Quic.Implementations.MsQuic.Internal { internal static class MsQuicParameterHelpers { - internal static unsafe SOCKADDR_INET GetINetParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param) + internal static unsafe SOCKADDR_INET GetINetParam(MsQuicApi api, SafeHandle nativeObject, uint level, uint param) { - byte* ptr = stackalloc byte[sizeof(SOCKADDR_INET)]; - QuicBuffer buffer = new QuicBuffer - { - Length = (uint)sizeof(SOCKADDR_INET), - Buffer = ptr - }; + SOCKADDR_INET value; + uint valueLen = (uint)sizeof(SOCKADDR_INET); - QuicExceptionHelpers.ThrowIfFailed( - api.UnsafeGetParam(nativeObject, level, param, ref buffer), - "Could not get SOCKADDR_INET."); + uint status = api.GetParamDelegate(nativeObject, level, param, ref valueLen, (byte*)&value); + QuicExceptionHelpers.ThrowIfFailed(status, "GetINETParam failed."); + Debug.Assert(valueLen == sizeof(SOCKADDR_INET)); - return *(SOCKADDR_INET*)ptr; + return value; } - internal static unsafe ushort GetUShortParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param) + internal static unsafe ushort GetUShortParam(MsQuicApi api, SafeHandle nativeObject, uint level, uint param) { - byte* ptr = stackalloc byte[sizeof(ushort)]; - QuicBuffer buffer = new QuicBuffer() - { - Length = sizeof(ushort), - Buffer = ptr - }; + ushort value; + uint valueLen = (uint)sizeof(ushort); - QuicExceptionHelpers.ThrowIfFailed( - api.UnsafeGetParam(nativeObject, level, param, ref buffer), - "Could not get ushort."); + uint status = api.GetParamDelegate(nativeObject, level, param, ref valueLen, (byte*)&value); + QuicExceptionHelpers.ThrowIfFailed(status, "GetUShortParam failed."); + Debug.Assert(valueLen == sizeof(ushort)); - return *(ushort*)ptr; + return value; } - internal static unsafe void SetUshortParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param, ushort value) + internal static unsafe void SetUshortParam(MsQuicApi api, SafeHandle nativeObject, uint level, uint param, ushort value) { - QuicBuffer buffer = new QuicBuffer() - { - Length = sizeof(ushort), - Buffer = (byte*)&value - }; - QuicExceptionHelpers.ThrowIfFailed( - api.UnsafeSetParam(nativeObject, level, param, buffer), + api.SetParamDelegate(nativeObject, level, param, sizeof(ushort), (byte*)&value), "Could not set ushort."); } - internal static unsafe ulong GetULongParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param) + internal static unsafe ulong GetULongParam(MsQuicApi api, SafeHandle nativeObject, uint level, uint param) { - byte* ptr = stackalloc byte[sizeof(ulong)]; - QuicBuffer buffer = new QuicBuffer() - { - Length = sizeof(ulong), - Buffer = ptr - }; + ulong value; + uint valueLen = (uint)sizeof(ulong); - QuicExceptionHelpers.ThrowIfFailed( - api.UnsafeGetParam(nativeObject, level, param, ref buffer), - "Could not get ulong."); + uint status = api.GetParamDelegate(nativeObject, level, param, ref valueLen, (byte*)&value); + QuicExceptionHelpers.ThrowIfFailed(status, "GetULongParam failed."); + Debug.Assert(valueLen == sizeof(ulong)); - return *(ulong*)ptr; + return value; } - internal static unsafe void SetULongParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param, ulong value) + internal static unsafe void SetULongParam(MsQuicApi api, SafeHandle nativeObject, uint level, uint param, ulong value) { - QuicBuffer buffer = new QuicBuffer() - { - Length = sizeof(ulong), - Buffer = (byte*)&value - }; - QuicExceptionHelpers.ThrowIfFailed( - api.UnsafeGetParam(nativeObject, level, param, ref buffer), + api.SetParamDelegate(nativeObject, level, param, sizeof(ulong), (byte*)&value), "Could not set ulong."); } - - internal static unsafe void SetSecurityConfig(MsQuicApi api, IntPtr nativeObject, uint level, uint param, IntPtr value) - { - QuicBuffer buffer = new QuicBuffer() - { - Length = (uint)sizeof(void*), - Buffer = (byte*)&value - }; - - QuicExceptionHelpers.ThrowIfFailed( - api.UnsafeSetParam(nativeObject, level, param, buffer), - "Could not set security configuration."); - } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSecurityConfig.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSecurityConfig.cs deleted file mode 100644 index 68b1432028b66d..00000000000000 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSecurityConfig.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Net.Quic.Implementations.MsQuic.Internal -{ - // TODO this will eventually be abstracted to support both Client and Server - // certificates - internal class MsQuicSecurityConfig : IDisposable - { - private bool _disposed; - private MsQuicApi _registration; - - public MsQuicSecurityConfig(MsQuicApi registration, IntPtr nativeObjPtr) - { - _registration = registration; - NativeObjPtr = nativeObjPtr; - } - - public IntPtr NativeObjPtr { get; private set; } - - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - _registration.SecConfigDeleteDelegate?.Invoke(NativeObjPtr); - NativeObjPtr = IntPtr.Zero; - _disposed = true; - } - - ~MsQuicSecurityConfig() - { - Dispose(disposing: false); - } - } -} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs deleted file mode 100644 index 8b0e28e290ef7c..00000000000000 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; -using System.Net.Security; - -namespace System.Net.Quic.Implementations.MsQuic.Internal -{ - internal sealed class MsQuicSession : IDisposable - { - private bool _disposed; - private IntPtr _nativeObjPtr; - private bool _opened; - - internal MsQuicSession() - { - if (!MsQuicApi.IsQuicSupported) - { - throw new NotSupportedException(SR.net_quic_notsupported); - } - } - - public IntPtr ConnectionOpen(QuicClientConnectionOptions options) - { - if (!_opened) - { - OpenSession(options.ClientAuthenticationOptions!.ApplicationProtocols!, - (ushort)options.MaxBidirectionalStreams, - (ushort)options.MaxUnidirectionalStreams); - } - - QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ConnectionOpenDelegate( - _nativeObjPtr, - MsQuicConnection.s_connectionDelegate, - IntPtr.Zero, - out IntPtr connectionPtr), - "Could not open the connection."); - - return connectionPtr; - } - - private void OpenSession(List alpn, ushort bidirectionalStreamCount, ushort undirectionalStreamCount) - { - _opened = true; - _nativeObjPtr = MsQuicApi.Api.SessionOpen(alpn); - SetPeerBiDirectionalStreamCount(bidirectionalStreamCount); - SetPeerUnidirectionalStreamCount(undirectionalStreamCount); - } - - // TODO allow for a callback to select the certificate (SNI). - public IntPtr ListenerOpen(QuicListenerOptions options) - { - if (!_opened) - { - OpenSession(options.ServerAuthenticationOptions!.ApplicationProtocols!, - (ushort)options.MaxBidirectionalStreams, - (ushort)options.MaxUnidirectionalStreams); - } - - QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ListenerOpenDelegate( - _nativeObjPtr, - MsQuicListener.s_listenerDelegate, - IntPtr.Zero, - out IntPtr listenerPointer), - "Could not open listener."); - - return listenerPointer; - } - - // TODO call this for graceful shutdown? - public void ShutDown( - QUIC_CONNECTION_SHUTDOWN_FLAG Flags, - ushort ErrorCode) - { - MsQuicApi.Api.SessionShutdownDelegate( - _nativeObjPtr, - (uint)Flags, - ErrorCode); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - public void SetPeerBiDirectionalStreamCount(ushort count) - { - SetUshortParameter(QUIC_PARAM_SESSION.PEER_BIDI_STREAM_COUNT, count); - } - - public void SetPeerUnidirectionalStreamCount(ushort count) - { - SetUshortParameter(QUIC_PARAM_SESSION.PEER_UNIDI_STREAM_COUNT, count); - } - - private unsafe void SetUshortParameter(QUIC_PARAM_SESSION param, ushort count) - { - var buffer = new MsQuicNativeMethods.QuicBuffer() - { - Length = sizeof(ushort), - Buffer = (byte*)&count - }; - - SetParam(param, buffer); - } - - public void SetDisconnectTimeout(TimeSpan timeout) - { - SetULongParamter(QUIC_PARAM_SESSION.DISCONNECT_TIMEOUT, (ulong)timeout.TotalMilliseconds); - } - - public void SetIdleTimeout(TimeSpan timeout) - { - SetULongParamter(QUIC_PARAM_SESSION.IDLE_TIMEOUT, (ulong)timeout.TotalMilliseconds); - - } - private unsafe void SetULongParamter(QUIC_PARAM_SESSION param, ulong count) - { - var buffer = new MsQuicNativeMethods.QuicBuffer() - { - Length = sizeof(ulong), - Buffer = (byte*)&count - }; - SetParam(param, buffer); - } - - private void SetParam( - QUIC_PARAM_SESSION param, - MsQuicNativeMethods.QuicBuffer buf) - { - QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.UnsafeSetParam( - _nativeObjPtr, - (uint)QUIC_PARAM_LEVEL.SESSION, - (uint)param, - buf), - "Could not set parameter on session."); - } - - ~MsQuicSession() - { - Dispose(false); - } - - private void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - MsQuicApi.Api.SessionCloseDelegate?.Invoke(_nativeObjPtr); - _nativeObjPtr = IntPtr.Zero; - - _disposed = true; - } - } -} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/Interop.MsQuic.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/Interop.MsQuic.cs similarity index 100% rename from src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/Interop.MsQuic.cs rename to src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/Interop.MsQuic.cs diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicAlpnHelper.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicAlpnHelper.cs new file mode 100644 index 00000000000000..c5d9cce43d4a07 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicAlpnHelper.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Security; +using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal static class MsQuicAlpnHelper + { + public static unsafe void Prepare(List alpnProtocols, [NotNull] out MemoryHandle[]? handles, [NotNull] out QuicBuffer[]? buffers) + { + handles = ArrayPool.Shared.Rent(alpnProtocols.Count); + buffers = ArrayPool.Shared.Rent(alpnProtocols.Count); + + try + { + for (int i = 0; i < alpnProtocols.Count; ++i) + { + ReadOnlyMemory alpnProtocol = alpnProtocols[i].Protocol; + MemoryHandle h = alpnProtocol.Pin(); + + handles[i] = h; + buffers[i].Buffer = (byte*)h.Pointer; + buffers[i].Length = (uint)alpnProtocol.Length; + } + } + catch + { + Destroy(ref handles, ref buffers); + throw; + } + } + + public static void Destroy(ref MemoryHandle[]? handles, ref QuicBuffer[]? buffers) + { + if (handles is MemoryHandle[] notNullHandles) + { + foreach (MemoryHandle h in notNullHandles) + { + h.Dispose(); + } + + handles = null; + ArrayPool.Shared.Return(notNullHandles); + } + + if (buffers is QuicBuffer[] notNullBuffers) + { + buffers = null; + ArrayPool.Shared.Return(notNullBuffers); + } + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicEnums.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs similarity index 71% rename from src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicEnums.cs rename to src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs index 5fc281c8670d47..8ac7c5c1dab6fd 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicEnums.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs @@ -11,18 +11,24 @@ internal enum QUIC_EXECUTION_PROFILE : uint QUIC_EXECUTION_PROFILE_TYPE_REAL_TIME } - /// - /// Flags to pass when creating a security config. - /// + internal enum QUIC_CREDENTIAL_TYPE : uint + { + NONE, + HASH, + HASH_STORE, + CONTEXT, + FILE, + STUB_NULL = 0xF0000000 // pass as server cert to stubtls implementation + } + [Flags] - internal enum QUIC_SEC_CONFIG_FLAG : uint + internal enum QUIC_CREDENTIAL_FLAGS : uint { - CERT_HASH = 0x00000001, - CERT_HASH_STORE = 0x00000002, - CERT_CONTEXT = 0x00000004, - CERT_FILE = 0x00000008, - ENABL_OCSP = 0x00000010, - CERT_NULL = 0xF0000000 // TODO: only valid for stub TLS. + NONE = 0, + CLIENT = 1, // lack of client flag indicates server. + LOAD_ASYNCHRONOUS = 2, + NO_CERTIFICATE_VALIDATION = 4, + ENABLE_OCSP = 8 } [Flags] @@ -73,15 +79,16 @@ internal enum QUIC_SEND_FLAG : uint { NONE = 0, ALLOW_0_RTT = 0x00000001, - FIN = 0x00000002, - DGRAM_PRIORITY = 0x00000004 + START = 0x00000002, + FIN = 0x00000004, + DGRAM_PRIORITY = 0x00000008 } internal enum QUIC_PARAM_LEVEL : uint { GLOBAL, REGISTRATION, - SESSION, + CONFIGURATION, LISTENER, CONNECTION, TLS, @@ -90,9 +97,11 @@ internal enum QUIC_PARAM_LEVEL : uint internal enum QUIC_PARAM_GLOBAL : uint { - RETRY_MEMORY_PERCENT = 0, - SUPPORTED_VERSIONS = 1, - LOAD_BALANCING_MODE = 2, + RETRY_MEMORY_PERCENT, + SUPPORTED_VERSIONS, + LOAD_BALANCING_MODE, + PERF_COUNTERS, + SETTINGS } internal enum QUIC_PARAM_REGISTRATION : uint @@ -121,30 +130,23 @@ internal enum QUIC_PARAM_LISTENER : uint internal enum QUIC_PARAM_CONN : uint { - QUIC_VERSION = 0, - LOCAL_ADDRESS = 1, - REMOTE_ADDRESS = 2, - IDLE_TIMEOUT = 3, - PEER_BIDI_STREAM_COUNT = 4, - PEER_UNIDI_STREAM_COUNT = 5, - LOCAL_BIDI_STREAM_COUNT = 6, - LOCAL_UNIDI_STREAM_COUNT = 7, - CLOSE_REASON_PHRASE = 8, - STATISTICS = 9, - STATISTICS_PLAT = 10, - CERT_VALIDATION_FLAGS = 11, - KEEP_ALIVE = 12, - DISCONNECT_TIMEOUT = 13, - SEC_CONFIG = 14, - SEND_BUFFERING = 15, - SEND_PACING = 16, - SHARE_UDP_BINDING = 17, - IDEAL_PROCESSOR = 18, - MAX_STREAM_IDS = 19, - STREAM_SCHEDULING_SCHEME = 20, - DATAGRAM_RECEIVE_ENABLED = 21, - DATAGRAM_SEND_ENABLED = 22, - DISABLE_1RTT_ENCRYPTION = 23 + QUIC_VERSION, + LOCAL_ADDRESS, + REMOTE_ADDRESS, + IDEAL_PROCESSOR, + SETTINGS, + STATISTICS, + STATISTICS_PLAT, + SHARE_UDP_BINDING, + LOCAL_BIDI_STREAM_COUNT, + LOCAL_UNIDI_STREAM_COUNT, + MAX_STREAM_IDS, + CLOSE_REASON_PHRASE, + STREAM_SCHEDULING_SCHEME, + DATAGRAM_RECEIVE_ENABLED, + DATAGRAM_SEND_ENABLED, + DISABLE_1RTT_ENCRYPTION, + RESUMPTION_TICKET } internal enum QUIC_PARAM_STREAM : uint @@ -190,4 +192,11 @@ internal enum QUIC_STREAM_EVENT : uint SHUTDOWN_COMPLETE = 7, IDEAL_SEND_BUFFER_SIZE = 8, } + + internal enum QUIC_SERVER_RESUMPTION_LEVEL : byte + { + NO_RESUME, + RESUME_ONLY, + RESUME_AND_ZERORTT + } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicNativeMethods.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs similarity index 70% rename from src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicNativeMethods.cs rename to src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs index c8ff6d6a8422cf..497d36c733ba3e 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicNativeMethods.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs @@ -1,9 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Net.Sockets; using System.Runtime.InteropServices; -using System.Text; namespace System.Net.Quic.Implementations.MsQuic.Internal { @@ -24,13 +22,11 @@ internal struct NativeApi internal IntPtr RegistrationOpen; internal IntPtr RegistrationClose; + internal IntPtr RegistrationShutdown; - internal IntPtr SecConfigCreate; - internal IntPtr SecConfigDelete; - - internal IntPtr SessionOpen; - internal IntPtr SessionClose; - internal IntPtr SessionShutdown; + internal IntPtr ConfigurationOpen; + internal IntPtr ConfigurationClose; + internal IntPtr ConfigurationLoadCredential; internal IntPtr ListenerOpen; internal IntPtr ListenerClose; @@ -41,6 +37,7 @@ internal struct NativeApi internal IntPtr ConnectionClose; internal IntPtr ConnectionShutdown; internal IntPtr ConnectionStart; + internal IntPtr ConnectionSetConfiguration; internal IntPtr ConnectionSendResumptionTicket; internal IntPtr StreamOpen; @@ -54,24 +51,15 @@ internal struct NativeApi internal IntPtr DatagramSend; } - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint SetContextDelegate( - IntPtr handle, - IntPtr context); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate IntPtr GetContextDelegate( - IntPtr handle); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void SetCallbackHandlerDelegate( - IntPtr handle, + SafeHandle handle, Delegate del, IntPtr context); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint SetParamDelegate( - IntPtr handle, + SafeHandle handle, uint level, uint param, uint bufferLength, @@ -79,14 +67,16 @@ internal delegate uint SetParamDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint GetParamDelegate( - IntPtr handle, + SafeHandle handle, uint level, uint param, - uint* bufferLength, + ref uint bufferLength, byte* buffer); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint RegistrationOpenDelegate(ref RegistrationConfig config, out IntPtr registrationContext); + internal delegate uint RegistrationOpenDelegate( + ref RegistrationConfig config, + out SafeMsQuicRegistrationHandle registrationContext); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void RegistrationCloseDelegate(IntPtr registrationContext); @@ -94,49 +84,33 @@ internal delegate uint GetParamDelegate( [StructLayout(LayoutKind.Sequential)] internal struct RegistrationConfig { - [MarshalAs(UnmanagedType.LPUTF8Str)] - internal string AppName; - internal QUIC_EXECUTION_PROFILE ExecutionProfile; + internal byte* AppName; + internal uint ExecutionProfile; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void SecConfigCreateCompleteDelegate(IntPtr context, uint status, IntPtr securityConfig); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint SecConfigCreateDelegate( - IntPtr registrationContext, - uint flags, - IntPtr certificate, - [MarshalAs(UnmanagedType.LPUTF8Str)]string? principal, - IntPtr context, - SecConfigCreateCompleteDelegate completionHandler); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void SecConfigDeleteDelegate( - IntPtr securityConfig); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint SessionOpenDelegate( - IntPtr registrationContext, - QuicBuffer *alpnBuffers, + internal delegate uint ConfigurationOpenDelegate( + SafeMsQuicRegistrationHandle registrationContext, + ref QuicBuffer alpnBuffers, uint alpnBufferCount, + ref Settings settings, + uint settingsSize, IntPtr context, - ref IntPtr session); + out SafeMsQuicConfigurationHandle configuration); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void SessionCloseDelegate( - IntPtr session); + internal delegate void ConfigurationCloseDelegate( + IntPtr configuration); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void SessionShutdownDelegate( - IntPtr session, - uint flags, - ulong errorCode); + internal delegate uint ConfigurationLoadCredentialDelegate( + SafeMsQuicConfigurationHandle configuration, + CredentialConfig *credConfig); [StructLayout(LayoutKind.Sequential)] internal struct ListenerEvent { - internal QUIC_LISTENER_EVENT Type; + internal uint Type; internal ListenerEventDataUnion Data; } @@ -179,23 +153,25 @@ internal delegate uint ListenerCallbackDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ListenerOpenDelegate( - IntPtr session, + SafeMsQuicRegistrationHandle registration, ListenerCallbackDelegate handler, IntPtr context, - out IntPtr listener); + out SafeMsQuicListenerHandle listener); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint ListenerCloseDelegate( + internal delegate void ListenerCloseDelegate( IntPtr listener); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ListenerStartDelegate( - IntPtr listener, + SafeMsQuicListenerHandle listener, + ref QuicBuffer alpnBuffers, + uint alpnBufferCount, ref SOCKADDR_INET localAddress); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint ListenerStopDelegate( - IntPtr listener); + internal delegate void ListenerStopDelegate( + SafeMsQuicListenerHandle listener); [StructLayout(LayoutKind.Sequential)] internal struct ConnectionEventDataConnected @@ -239,7 +215,7 @@ internal struct ConnectionEventDataPeerAddrChanged internal struct ConnectionEventDataStreamStarted { internal IntPtr Stream; - internal QUIC_STREAM_OPEN_FLAG Flags; + internal uint Flags; } [StructLayout(LayoutKind.Sequential)] @@ -280,7 +256,7 @@ internal struct ConnectionEventDataUnion [StructLayout(LayoutKind.Sequential)] internal struct ConnectionEvent { - internal QUIC_CONNECTION_EVENT Type; + internal uint Type; internal ConnectionEventDataUnion Data; internal bool EarlyDataAccepted => Data.Connected.SessionResumed; @@ -290,7 +266,7 @@ internal struct ConnectionEvent internal bool ShutdownTimedOut => Data.ShutdownComplete.TimedOut; internal ushort BiDirectionalCount => Data.StreamsAvailable.BiDirectionalCount; internal ushort UniDirectionalCount => Data.StreamsAvailable.UniDirectionalCount; - internal QUIC_STREAM_OPEN_FLAG StreamFlags => Data.StreamStarted.Flags; + internal QUIC_STREAM_OPEN_FLAG StreamFlags => (QUIC_STREAM_OPEN_FLAG)Data.StreamStarted.Flags; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -301,26 +277,32 @@ internal delegate uint ConnectionCallbackDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ConnectionOpenDelegate( - IntPtr session, + SafeMsQuicRegistrationHandle registration, ConnectionCallbackDelegate handler, IntPtr context, - out IntPtr connection); + out SafeMsQuicConnectionHandle connection); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint ConnectionCloseDelegate( + internal delegate void ConnectionCloseDelegate( IntPtr connection); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate uint ConnectionSetConfigurationDelegate( + SafeMsQuicConnectionHandle connection, + SafeMsQuicConfigurationHandle configuration); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ConnectionStartDelegate( - IntPtr connection, + SafeMsQuicConnectionHandle connection, + SafeMsQuicConfigurationHandle configuration, ushort family, [MarshalAs(UnmanagedType.LPUTF8Str)] string serverName, ushort serverPort); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint ConnectionShutdownDelegate( - IntPtr connection, + internal delegate void ConnectionShutdownDelegate( + SafeMsQuicConnectionHandle connection, uint flags, long errorCode); @@ -331,7 +313,7 @@ internal struct StreamEventDataRecv internal ulong TotalBufferLength; internal QuicBuffer* Buffers; internal uint BufferCount; - internal QUIC_RECEIVE_FLAG Flags; + internal uint Flags; } [StructLayout(LayoutKind.Sequential)] @@ -381,7 +363,7 @@ internal struct StreamEventDataUnion [StructLayout(LayoutKind.Sequential)] internal struct StreamEvent { - internal QUIC_STREAM_EVENT Type; + internal uint Type; internal StreamEventDataUnion Data; } @@ -460,43 +442,43 @@ internal delegate uint StreamCallbackDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamOpenDelegate( - IntPtr connection, + SafeMsQuicConnectionHandle connection, uint flags, StreamCallbackDelegate handler, IntPtr context, - out IntPtr stream); + out SafeMsQuicStreamHandle stream); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamStartDelegate( - IntPtr stream, + SafeMsQuicStreamHandle stream, uint flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint StreamCloseDelegate( + internal delegate void StreamCloseDelegate( IntPtr stream); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamShutdownDelegate( - IntPtr stream, + SafeMsQuicStreamHandle stream, uint flags, long errorCode); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamSendDelegate( - IntPtr stream, - QuicBuffer* buffers, + SafeMsQuicStreamHandle stream, + QuicBuffer *buffers, uint bufferCount, uint flags, IntPtr clientSendContext); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamReceiveCompleteDelegate( - IntPtr stream, + SafeMsQuicStreamHandle stream, ulong bufferLength); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamReceiveSetEnabledDelegate( - IntPtr stream, + SafeMsQuicStreamHandle stream, [MarshalAs(UnmanagedType.U1)] bool enabled); @@ -508,10 +490,86 @@ internal unsafe struct QuicBuffer } [StructLayout(LayoutKind.Sequential)] - internal struct CertFileParams + internal struct CredentialConfig + { + internal uint Type; + internal uint Flags; + internal IntPtr Certificate; // PCERT_CONTEXT (SCHANNEL) + internal IntPtr Principal; + internal IntPtr TicketKey; // optional. + internal IntPtr AsyncHandler; // optional, must set LOAD_ASYNCHRONOUS flag. + } + + [StructLayout(LayoutKind.Sequential)] + internal struct Settings + { + internal ulong IsSetFlags; + internal ulong MaxBytesPerKey; + internal ulong HandshakeIdleTimeoutMs; + internal ulong IdleTimeoutMs; + internal uint TlsClientMaxSendBuffer; + internal uint TlsServerMaxSendBuffer; + internal uint StreamRecvWindowDefault; + internal uint StreamRecvBufferDefault; + internal uint ConnFlowControlWindow; + internal uint MaxWorkerQueueDelayUs; + internal uint MaxStatelessOperations; + internal uint InitialWindowPackets; + internal uint SendIdleTimeoutMs; + internal uint InitialRttMs; + internal uint MaxAckDelayMs; + internal uint DisconnectTimeoutMs; + internal uint KeepAliveIntervalMs; + internal ushort PeerBidiStreamCount; + internal ushort PeerUnidiStreamCount; + internal ushort RetryMemoryLimit; // Global only + internal ushort LoadBalancingMode; // Global only + internal byte MaxOperationsPerDrain; + internal byte BitField; + } + + internal enum SettingsBitField : byte + { + SendBufferingEnabled = 1, + PacingEnabled = 1 << 1, + MigrationEnabled = 1 << 2, + DatagramReceiveEnabled = 1 << 3, + + // single enum field, 2 bits. + NoResume = QUIC_SERVER_RESUMPTION_LEVEL.NO_RESUME << 4, + ResumeOnly = QUIC_SERVER_RESUMPTION_LEVEL.RESUME_ONLY << 4, + ResumeAndZeroRtt = QUIC_SERVER_RESUMPTION_LEVEL.RESUME_AND_ZERORTT << 4 + } + + [Flags] + internal enum SettingsFlags : ulong { - internal IntPtr PrivateKeyFilePath; - internal IntPtr CertificateFilePath; + MaxBytesPerKey = 1, + 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 } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicStatusCodes.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusCodes.cs similarity index 100% rename from src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicStatusCodes.cs rename to src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusCodes.cs diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicStatusHelper.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusHelper.cs similarity index 100% rename from src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicStatusHelper.cs rename to src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusHelper.cs diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs new file mode 100644 index 00000000000000..18ab5e3f5687c4 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net.Security; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal sealed class SafeMsQuicConfigurationHandle : SafeHandle + { + public override bool IsInvalid => handle == IntPtr.Zero; + + private SafeMsQuicConfigurationHandle() : base(IntPtr.Zero, ownsHandle: true) + { + } + + protected override bool ReleaseHandle() + { + MsQuicApi.Api.ConfigurationCloseDelegate(handle); + return true; + } + + public static unsafe SafeMsQuicConfigurationHandle Create(QuicClientConnectionOptions options) + { + // TODO: lots of ClientAuthenticationOptions are not yet supported by MsQuic. + return Create(options, QUIC_CREDENTIAL_FLAGS.CLIENT, certificate: null, options.ClientAuthenticationOptions?.ApplicationProtocols); + } + + public static unsafe SafeMsQuicConfigurationHandle Create(QuicListenerOptions options) + { + // TODO: lots of ServerAuthenticationOptions are not yet supported by MsQuic. + return Create(options, QUIC_CREDENTIAL_FLAGS.NONE, options.ServerAuthenticationOptions?.ServerCertificate, options.ServerAuthenticationOptions?.ApplicationProtocols); + } + + private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, QUIC_CREDENTIAL_FLAGS flags, X509Certificate? certificate, List? alpnProtocols) + { + // TODO: some of these checks should be done by the QuicOptions type. + if (alpnProtocols == null || alpnProtocols.Count == 0) + { + throw new Exception("At least one SslApplicationProtocol value must be present in SslClientAuthenticationOptions or SslServerAuthenticationOptions."); + } + + if (options.MaxBidirectionalStreams > ushort.MaxValue) + { + throw new Exception("MaxBidirectionalStreams overflow."); + } + + if (options.MaxBidirectionalStreams > ushort.MaxValue) + { + throw new Exception("MaxBidirectionalStreams overflow."); + } + + Debug.Assert(!MsQuicApi.Api.Registration.IsInvalid); + + var settings = new Settings + { + IsSetFlags = + (ulong)SettingsFlags.PeerBidiStreamCount + | (ulong)SettingsFlags.PeerUnidiStreamCount, + PeerBidiStreamCount = (ushort)options.MaxBidirectionalStreams, + PeerUnidiStreamCount = (ushort)options.MaxUnidirectionalStreams + }; + + if (options.IdleTimeout != Timeout.InfiniteTimeSpan) + { + if (options.IdleTimeout <= TimeSpan.Zero) throw new Exception("IdleTimeout must not be negative."); + + ulong ms = (ulong)options.IdleTimeout.Ticks / TimeSpan.TicksPerMillisecond; + if (ms > (1ul << 62) - 1) throw new Exception("IdleTimeout is too large (max 2^62-1 milliseconds)"); + + settings.IsSetFlags |= (ulong)SettingsFlags.IdleTimeoutMs; + settings.IdleTimeoutMs = (ulong)options.IdleTimeout.TotalMilliseconds; + } + + uint status; + SafeMsQuicConfigurationHandle? configurationHandle; + + MemoryHandle[]? handles = null; + QuicBuffer[]? buffers = null; + try + { + MsQuicAlpnHelper.Prepare(alpnProtocols, out handles, out buffers); + status = MsQuicApi.Api.ConfigurationOpenDelegate(MsQuicApi.Api.Registration, ref MemoryMarshal.GetReference(buffers.AsSpan()), (uint)alpnProtocols.Count, ref settings, (uint)sizeof(Settings), context: IntPtr.Zero, out configurationHandle); + } + finally + { + MsQuicAlpnHelper.Destroy(ref handles, ref buffers); + } + + QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationOpen failed."); + + try + { + // TODO: find out what to do for OpenSSL here -- passing handle won't work, because + // MsQuic has a private copy of OpenSSL so the SSL_CTX will be incompatible. + + CredentialConfig config = default; + + config.Flags = (uint)flags; // TODO: consider using LOAD_ASYNCHRONOUS with a callback. + + if (certificate != null) + { +#if true + // If using stub TLS. + config.Type = (uint)QUIC_CREDENTIAL_TYPE.STUB_NULL; +#else + config.Type = (uint)QUIC_CREDENTIAL_TYPE.CONTEXT; + config.Certificate = certificate.Handle; +#endif + } + else + { + config.Type = (uint)QUIC_CREDENTIAL_TYPE.NONE; + } + + status = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, &config); + QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationLoadCredential failed."); + } + catch + { + configurationHandle.Dispose(); + throw; + } + + return configurationHandle; + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs new file mode 100644 index 00000000000000..fa7f42c7130364 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal sealed class SafeMsQuicConnectionHandle : SafeHandle + { + public override bool IsInvalid => handle == IntPtr.Zero; + + private SafeMsQuicConnectionHandle() : base(IntPtr.Zero, ownsHandle: true) + { + } + + public SafeMsQuicConnectionHandle(IntPtr connectionHandle) : this() + { + SetHandle(connectionHandle); + } + + protected override bool ReleaseHandle() + { + MsQuicApi.Api.ConnectionCloseDelegate(handle); + return true; + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs new file mode 100644 index 00000000000000..ffbfddb50757a6 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal sealed class SafeMsQuicListenerHandle : SafeHandle + { + public override bool IsInvalid => handle == IntPtr.Zero; + + private SafeMsQuicListenerHandle() : base(IntPtr.Zero, ownsHandle: true) + { + } + + protected override bool ReleaseHandle() + { + MsQuicApi.Api.ListenerCloseDelegate(handle); + return true; + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs new file mode 100644 index 00000000000000..69336aaadb9e2b --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal sealed class SafeMsQuicRegistrationHandle : SafeHandle + { + public override bool IsInvalid => handle == IntPtr.Zero; + + private SafeMsQuicRegistrationHandle() : base(IntPtr.Zero, ownsHandle: true) + { + } + + protected override bool ReleaseHandle() + { + MsQuicApi.Api.RegistrationCloseDelegate(handle); + return true; + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs new file mode 100644 index 00000000000000..4769174e46a09c --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal sealed class SafeMsQuicStreamHandle : SafeHandle + { + public override bool IsInvalid => handle == IntPtr.Zero; + + private SafeMsQuicStreamHandle() : base(IntPtr.Zero, ownsHandle: true) + { + } + + public SafeMsQuicStreamHandle(IntPtr streamHandle) : this() + { + SetHandle(streamHandle); + } + + protected override bool ReleaseHandle() + { + MsQuicApi.Api.StreamCloseDelegate(handle); + return true; + } + } +} 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 9a2063f89f087a..e4f142018025f5 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 @@ -2,13 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.IO; using System.Net.Quic.Implementations.MsQuic.Internal; using System.Net.Security; using System.Net.Sockets; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; @@ -18,175 +16,166 @@ namespace System.Net.Quic.Implementations.MsQuic { internal sealed class MsQuicConnection : QuicConnectionProvider { - private readonly MsQuicSession? _session; - - // Pointer to the underlying connection - // TODO replace all IntPtr with SafeHandles - private IntPtr _ptr; + // Delegate that wraps the static function that will be called when receiving an event. + private static readonly ConnectionCallbackDelegate s_connectionDelegate = new ConnectionCallbackDelegate(NativeCallbackHandler); - // Handle to this object for native callbacks. - private GCHandle _handle; + // TODO: remove this. + // This is only used for client-initiated connections, and isn't needed even then once Connect() has been called. + private readonly SafeMsQuicConfigurationHandle? _configuration; - // Delegate that wraps the static function that will be called when receiving an event. - internal static readonly ConnectionCallbackDelegate s_connectionDelegate = new ConnectionCallbackDelegate(NativeCallbackHandler); + private readonly State _state = new State(); + private GCHandle _stateHandle; + private bool _disposed; - // Endpoint to either connect to or the endpoint already accepted. private IPEndPoint? _localEndPoint; private readonly EndPoint _remoteEndPoint; - private SslApplicationProtocol _negotiatedAlpnProtocol; - // TODO: only allocate these when there is an outstanding connect/shutdown. - private readonly TaskCompletionSource _connectTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - private readonly TaskCompletionSource _shutdownTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + private sealed class State + { + public SafeMsQuicConnectionHandle Handle = null!; // set inside of MsQuicConnection ctor. - private bool _disposed; - private bool _connected; - private MsQuicSecurityConfig? _securityConfig; - private long _abortErrorCode = -1; + // These exists to prevent GC of the MsQuicConnection in the middle of an async op (Connect or Shutdown). + public MsQuicConnection? Connection; - // Queue for accepted streams - private readonly Channel _acceptQueue = Channel.CreateUnbounded(new UnboundedChannelOptions() - { - SingleReader = true, - SingleWriter = true, - }); + // TODO: only allocate these when there is an outstanding connect/shutdown. + public readonly TaskCompletionSource ConnectTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + public readonly TaskCompletionSource ShutdownTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + public IPEndPoint? LocalEndPoint; + public bool Connected; + public long AbortErrorCode = -1; + + // Queue for accepted streams. + // Backlog limit is managed by MsQuic so it can be unbounded here. + public readonly Channel AcceptQueue = Channel.CreateUnbounded(new UnboundedChannelOptions() + { + SingleReader = true, + SingleWriter = true, + }); + } // constructor for inbound connections - public MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, IntPtr nativeObjPtr, TimeSpan idleTimeout) + public MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SafeMsQuicConnectionHandle handle) { + _state.Handle = handle; + _state.Connected = true; _localEndPoint = localEndPoint; _remoteEndPoint = remoteEndPoint; - _ptr = nativeObjPtr; - _connected = true; - SetCallbackHandler(); + _stateHandle = GCHandle.Alloc(_state); - SetIdleTimeout(idleTimeout); + try + { + MsQuicApi.Api.SetCallbackHandlerDelegate( + _state.Handle, + s_connectionDelegate, + GCHandle.ToIntPtr(_stateHandle)); + } + catch + { + _stateHandle.Free(); + throw; + } } // constructor for outbound connections public MsQuicConnection(QuicClientConnectionOptions options) { - // TODO need to figure out if/how we want to expose sessions - // Creating a session per connection isn't ideal. - _session = new MsQuicSession(); - _ptr = _session.ConnectionOpen(options); _remoteEndPoint = options.RemoteEndPoint!; + _configuration = SafeMsQuicConfigurationHandle.Create(options); - SetCallbackHandler(); - SetIdleTimeout(options.IdleTimeout); - } + _stateHandle = GCHandle.Alloc(_state); + try + { + // this handle is ref counted by MsQuic, so safe to dispose here. + using SafeMsQuicConfigurationHandle config = SafeMsQuicConfigurationHandle.Create(options); - internal override IPEndPoint LocalEndPoint - { - get + uint status = MsQuicApi.Api.ConnectionOpenDelegate( + MsQuicApi.Api.Registration, + s_connectionDelegate, + GCHandle.ToIntPtr(_stateHandle), + out _state.Handle); + + QuicExceptionHelpers.ThrowIfFailed(status, "Could not open the connection."); + } + catch { - return new IPEndPoint(_localEndPoint!.Address, _localEndPoint.Port); + _stateHandle.Free(); + throw; } } - internal async ValueTask SetSecurityConfigForConnection(X509Certificate cert, string? certFilePath, string? privateKeyFilePath) - { - _securityConfig = await MsQuicApi.Api.CreateSecurityConfig(cert, certFilePath, privateKeyFilePath).ConfigureAwait(false); - // TODO this isn't being set correctly - MsQuicParameterHelpers.SetSecurityConfig(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.SEC_CONFIG, _securityConfig!.NativeObjPtr); - } + internal override IPEndPoint LocalEndPoint => + new IPEndPoint(_localEndPoint!.Address, _localEndPoint!.Port); internal override EndPoint RemoteEndPoint => _remoteEndPoint; internal override SslApplicationProtocol NegotiatedApplicationProtocol => _negotiatedAlpnProtocol; - internal override bool Connected => _connected; - - internal uint HandleEvent(ref ConnectionEvent connectionEvent) - { - try - { - switch (connectionEvent.Type) - { - case QUIC_CONNECTION_EVENT.CONNECTED: - return HandleEventConnected(ref connectionEvent); - case QUIC_CONNECTION_EVENT.SHUTDOWN_INITIATED_BY_TRANSPORT: - return HandleEventShutdownInitiatedByTransport(ref connectionEvent); - case QUIC_CONNECTION_EVENT.SHUTDOWN_INITIATED_BY_PEER: - return HandleEventShutdownInitiatedByPeer(ref connectionEvent); - case QUIC_CONNECTION_EVENT.SHUTDOWN_COMPLETE: - return HandleEventShutdownComplete(ref connectionEvent); - case QUIC_CONNECTION_EVENT.PEER_STREAM_STARTED: - return HandleEventNewStream(ref connectionEvent); - case QUIC_CONNECTION_EVENT.STREAMS_AVAILABLE: - return HandleEventStreamsAvailable(ref connectionEvent); - default: - return MsQuicStatusCodes.Success; - } - } - catch (Exception ex) - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Error(this, $"Exception occurred during connection callback: {ex.Message}"); - } - - // TODO: trigger an exception on any outstanding async calls. - - return MsQuicStatusCodes.InternalError; - } - } + internal override bool Connected => _state.Connected; - private uint HandleEventConnected(ref ConnectionEvent connectionEvent) + private static uint HandleEventConnected(State state, ref ConnectionEvent connectionEvent) { - if (!_connected) + if (!state.Connected) { - // _connected will already be true for connections accepted from a listener. + // Connected will already be true for connections accepted from a listener. - SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS); - _localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); + SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, state.Handle, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS); + state.LocalEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); - SetNegotiatedAlpn(connectionEvent.Data.Connected.NegotiatedAlpn, connectionEvent.Data.Connected.NegotiatedAlpnLength); + Debug.Assert(state.Connection != null); + state.Connection.SetNegotiatedAlpn(connectionEvent.Data.Connected.NegotiatedAlpn, connectionEvent.Data.Connected.NegotiatedAlpnLength); + state.Connection = null; - _connected = true; - _connectTcs.SetResult(MsQuicStatusCodes.Success); + state.Connected = true; + state.ConnectTcs.SetResult(MsQuicStatusCodes.Success); } return MsQuicStatusCodes.Success; } - private uint HandleEventShutdownInitiatedByTransport(ref ConnectionEvent connectionEvent) + private static uint HandleEventShutdownInitiatedByTransport(State state, ref ConnectionEvent connectionEvent) { - if (!_connected) + if (!state.Connected) { + Debug.Assert(state.Connection != null); + state.Connection = null; + uint hresult = connectionEvent.Data.ShutdownInitiatedByTransport.Status; Exception ex = QuicExceptionHelpers.CreateExceptionForHResult(hresult, "Connection has been shutdown by transport."); - _connectTcs.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(ex)); + state.ConnectTcs.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(ex)); } + state.AcceptQueue.Writer.Complete(); return MsQuicStatusCodes.Success; } - private uint HandleEventShutdownInitiatedByPeer(ref ConnectionEvent connectionEvent) + private static uint HandleEventShutdownInitiatedByPeer(State state, ref ConnectionEvent connectionEvent) { - _abortErrorCode = connectionEvent.Data.ShutdownInitiatedByPeer.ErrorCode; + state.AbortErrorCode = connectionEvent.Data.ShutdownInitiatedByPeer.ErrorCode; + state.AcceptQueue.Writer.Complete(); return MsQuicStatusCodes.Success; } - private uint HandleEventShutdownComplete(ref ConnectionEvent connectionEvent) + private static uint HandleEventShutdownComplete(State state, ref ConnectionEvent connectionEvent) { - _shutdownTcs.SetResult(MsQuicStatusCodes.Success); + state.Connection = null; - // Stop accepting new streams. - _acceptQueue?.Writer.Complete(); + state.ShutdownTcs.SetResult(MsQuicStatusCodes.Success); return MsQuicStatusCodes.Success; } - private uint HandleEventNewStream(ref ConnectionEvent connectionEvent) + private static uint HandleEventNewStream(State state, ref ConnectionEvent connectionEvent) { - MsQuicStream msQuicStream = new MsQuicStream(this, connectionEvent.StreamFlags, connectionEvent.Data.StreamStarted.Stream, inbound: true); - _acceptQueue.Writer.TryWrite(msQuicStream); + var streamHandle = new SafeMsQuicStreamHandle(connectionEvent.Data.StreamStarted.Stream); + var stream = new MsQuicStream(streamHandle, connectionEvent.StreamFlags); + + state.AcceptQueue.Writer.TryWrite(stream); return MsQuicStatusCodes.Success; } - private uint HandleEventStreamsAvailable(ref ConnectionEvent connectionEvent) + private static uint HandleEventStreamsAvailable(State state, ref ConnectionEvent connectionEvent) { return MsQuicStatusCodes.Success; } @@ -199,11 +188,11 @@ internal override async ValueTask AcceptStreamAsync(Cancella try { - stream = await _acceptQueue.Reader.ReadAsync(cancellationToken).ConfigureAwait(false); + stream = await _state.AcceptQueue.Reader.ReadAsync(cancellationToken).ConfigureAwait(false); } catch (ChannelClosedException) { - throw _abortErrorCode switch + throw _state.AbortErrorCode switch { -1 => new QuicOperationAbortedException(), // Shutdown initiated by us. long err => new QuicConnectionAbortedException(err) // Shutdown initiated by peer. @@ -216,36 +205,34 @@ internal override async ValueTask AcceptStreamAsync(Cancella internal override QuicStreamProvider OpenUnidirectionalStream() { ThrowIfDisposed(); - - return StreamOpen(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); + return new MsQuicStream(_state.Handle, QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); } internal override QuicStreamProvider OpenBidirectionalStream() { ThrowIfDisposed(); - - return StreamOpen(QUIC_STREAM_OPEN_FLAG.NONE); + return new MsQuicStream(_state.Handle, QUIC_STREAM_OPEN_FLAG.NONE); } internal override long GetRemoteAvailableUnidirectionalStreamCount() { - return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.PEER_UNIDI_STREAM_COUNT); + return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_UNIDI_STREAM_COUNT); } internal override long GetRemoteAvailableBidirectionalStreamCount() { - return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.PEER_BIDI_STREAM_COUNT); - } - - private void SetIdleTimeout(TimeSpan timeout) - { - MsQuicParameterHelpers.SetULongParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.IDLE_TIMEOUT, (ulong)timeout.TotalMilliseconds); + return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_BIDI_STREAM_COUNT); } internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default) { ThrowIfDisposed(); + if (_configuration is null) + { + throw new Exception($"{nameof(ConnectAsync)} must not be called on a connection obtained from a listener."); + } + (string address, int port) = _remoteEndPoint switch { DnsEndPoint dnsEp => (dnsEp.Host, dnsEp.Port), @@ -253,7 +240,7 @@ internal override ValueTask ConnectAsync(CancellationToken cancellationToken = d _ => throw new Exception($"Unsupported remote endpoint type '{_remoteEndPoint.GetType()}'.") }; - // values taken from https://github.com/microsoft/msquic/blob/main/docs/api/ConnectionStart.md + // TODO: MsQuic will use system constants, so we should use the Socket PAL to translate these. int af = _remoteEndPoint.AddressFamily switch { AddressFamily.Unspecified => 0, @@ -262,55 +249,51 @@ internal override ValueTask ConnectAsync(CancellationToken cancellationToken = d _ => throw new Exception(SR.Format(SR.net_quic_unsupported_address_family, _remoteEndPoint.AddressFamily)) }; - QuicExceptionHelpers.ThrowIfFailed( - MsQuicApi.Api.ConnectionStartDelegate( - _ptr, - (ushort)af, - address, - (ushort)port), - "Failed to connect to peer."); - - return new ValueTask(_connectTcs.Task); - } - - private MsQuicStream StreamOpen( - QUIC_STREAM_OPEN_FLAG flags) - { - IntPtr streamPtr = IntPtr.Zero; - QuicExceptionHelpers.ThrowIfFailed( - MsQuicApi.Api.StreamOpenDelegate( - _ptr, - (uint)flags, - MsQuicStream.s_streamDelegate, - IntPtr.Zero, - out streamPtr), - "Failed to open stream to peer."); - - return new MsQuicStream(this, flags, streamPtr, inbound: false); - } - - private void SetCallbackHandler() - { - Debug.Assert(!_handle.IsAllocated, "callback handler allocated already"); - _handle = GCHandle.Alloc(this); + _state.Connection = this; + try + { + uint status = MsQuicApi.Api.ConnectionStartDelegate( + _state.Handle, + _configuration, + (ushort)af, + address, + (ushort)port); + + QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer."); + } + catch + { + _state.Connection = null; + throw; + } - MsQuicApi.Api.SetCallbackHandlerDelegate( - _ptr, - s_connectionDelegate, - GCHandle.ToIntPtr(_handle)); + return new ValueTask(_state.ConnectTcs.Task); } private ValueTask ShutdownAsync( QUIC_CONNECTION_SHUTDOWN_FLAG Flags, long ErrorCode) { - uint status = MsQuicApi.Api.ConnectionShutdownDelegate( - _ptr, - (uint)Flags, - ErrorCode); - QuicExceptionHelpers.ThrowIfFailed(status, "Failed to shutdown connection."); + Debug.Assert(!_state.ShutdownTcs.Task.IsCompleted); + + // Store the connection into the GCHandle'd state to prevent GC if user calls ShutdownAsync and gets rid of all references to the MsQuicConnection. + Debug.Assert(_state.Connection == null); + _state.Connection = this; + + try + { + MsQuicApi.Api.ConnectionShutdownDelegate( + _state.Handle, + (uint)Flags, + ErrorCode); + } + catch + { + _state.Connection = null; + throw; + } - return new ValueTask(_shutdownTcs.Task); + return new ValueTask(_state.ShutdownTcs.Task); } internal void SetNegotiatedAlpn(IntPtr alpn, int alpnLength) @@ -326,11 +309,41 @@ internal void SetNegotiatedAlpn(IntPtr alpn, int alpnLength) private static uint NativeCallbackHandler( IntPtr connection, IntPtr context, - ref ConnectionEvent connectionEventStruct) + ref ConnectionEvent connectionEvent) { - GCHandle handle = GCHandle.FromIntPtr(context); - MsQuicConnection quicConnection = (MsQuicConnection)handle.Target!; - return quicConnection.HandleEvent(ref connectionEventStruct); + var state = (State)GCHandle.FromIntPtr(context).Target!; + + try + { + switch ((QUIC_CONNECTION_EVENT)connectionEvent.Type) + { + case QUIC_CONNECTION_EVENT.CONNECTED: + return HandleEventConnected(state, ref connectionEvent); + case QUIC_CONNECTION_EVENT.SHUTDOWN_INITIATED_BY_TRANSPORT: + return HandleEventShutdownInitiatedByTransport(state, ref connectionEvent); + case QUIC_CONNECTION_EVENT.SHUTDOWN_INITIATED_BY_PEER: + return HandleEventShutdownInitiatedByPeer(state, ref connectionEvent); + case QUIC_CONNECTION_EVENT.SHUTDOWN_COMPLETE: + return HandleEventShutdownComplete(state, ref connectionEvent); + case QUIC_CONNECTION_EVENT.PEER_STREAM_STARTED: + return HandleEventNewStream(state, ref connectionEvent); + case QUIC_CONNECTION_EVENT.STREAMS_AVAILABLE: + return HandleEventStreamsAvailable(state, ref connectionEvent); + default: + return MsQuicStatusCodes.Success; + } + } + catch (Exception ex) + { + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Error(state, $"Exception occurred during connection callback: {ex.Message}"); + } + + // TODO: trigger an exception on any outstanding async calls. + + return MsQuicStatusCodes.InternalError; + } } public override void Dispose() @@ -351,20 +364,8 @@ private void Dispose(bool disposing) return; } - if (_ptr != IntPtr.Zero) - { - MsQuicApi.Api.ConnectionCloseDelegate?.Invoke(_ptr); - } - - _ptr = IntPtr.Zero; - - if (disposing) - { - _handle.Free(); - _session?.Dispose(); - _securityConfig?.Dispose(); - } - + _state.Handle.Dispose(); + if (_stateHandle.IsAllocated) _stateHandle.Free(); _disposed = true; } 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 169ce5435507a8..c169c39cdd9f51 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 @@ -1,7 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; +using System.Buffers; +using System.Collections.Generic; using System.Net.Quic.Implementations.MsQuic.Internal; using System.Net.Security; using System.Runtime.InteropServices; @@ -14,42 +15,58 @@ namespace System.Net.Quic.Implementations.MsQuic { internal sealed class MsQuicListener : QuicListenerProvider, IDisposable { - // Security configuration for MsQuic - private readonly MsQuicSession _session; + private static readonly ListenerCallbackDelegate s_listenerDelegate = new ListenerCallbackDelegate(NativeCallbackHandler); - // Pointer to the underlying listener - // TODO replace all IntPtr with SafeHandles - private IntPtr _ptr; + private readonly State _state; + private GCHandle _stateHandle; + private volatile bool _disposed; - // Handle to this object for native callbacks. - private GCHandle _handle; + private IPEndPoint _listenEndPoint; + private readonly List _applicationProtocols; - // Delegate that wraps the static function that will be called when receiving an event. - internal static readonly ListenerCallbackDelegate s_listenerDelegate = new ListenerCallbackDelegate(NativeCallbackHandler); + private sealed class State + { + // set immediately in ctor, but we need a GCHandle to State in order to create the handle. + public SafeMsQuicListenerHandle Handle = null!; - // Ssl listening options (ALPN, cert, etc) - private readonly SslServerAuthenticationOptions _sslOptions; + public readonly SafeMsQuicConfigurationHandle ConnectionConfiguration; + public readonly Channel AcceptConnectionQueue; - private QuicListenerOptions _options; - private volatile bool _disposed; - private IPEndPoint _listenEndPoint; - private bool _started; - private readonly Channel _acceptConnectionQueue; + public State(QuicListenerOptions options) + { + ConnectionConfiguration = SafeMsQuicConfigurationHandle.Create(options); + + AcceptConnectionQueue = Channel.CreateBounded(new BoundedChannelOptions(options.ListenBacklog) + { + SingleReader = true, + SingleWriter = true + }); + } + } internal MsQuicListener(QuicListenerOptions options) { - _session = new MsQuicSession(); - _acceptConnectionQueue = Channel.CreateBounded(new BoundedChannelOptions(options.ListenBacklog) - { - SingleReader = true, - SingleWriter = true - }); - - _options = options; - _sslOptions = options.ServerAuthenticationOptions!; + _applicationProtocols = options.ServerAuthenticationOptions!.ApplicationProtocols!; _listenEndPoint = options.ListenEndPoint!; - _ptr = _session.ListenerOpen(options); + _state = new State(options); + _stateHandle = GCHandle.Alloc(_state); + try + { + uint status = MsQuicApi.Api.ListenerOpenDelegate( + MsQuicApi.Api.Registration, + s_listenerDelegate, + GCHandle.ToIntPtr(_stateHandle), + out _state.Handle); + + QuicExceptionHelpers.ThrowIfFailed(status, "ListenerOpen failed."); + } + catch + { + _state.Handle?.Dispose(); + _stateHandle.Free(); + throw; + } } internal override IPEndPoint ListenEndPoint @@ -64,22 +81,14 @@ internal override async ValueTask AcceptConnectionAsync( { ThrowIfDisposed(); - MsQuicConnection connection; - try { - connection = await _acceptConnectionQueue.Reader.ReadAsync(cancellationToken).ConfigureAwait(false); + return await _state.AcceptConnectionQueue.Reader.ReadAsync(cancellationToken).ConfigureAwait(false); } catch (ChannelClosedException) { throw new QuicOperationAbortedException(); } - - await connection.SetSecurityConfigForConnection(_sslOptions.ServerCertificate!, - _options.CertificateFilePath, - _options.PrivateKeyFilePath).ConfigureAwait(false); - - return connection; } public override void Dispose() @@ -101,120 +110,94 @@ private void Dispose(bool disposing) } StopAcceptingConnections(); - - if (_ptr != IntPtr.Zero) - { - MsQuicApi.Api.ListenerStopDelegate(_ptr); - MsQuicApi.Api.ListenerCloseDelegate(_ptr); - } - - _ptr = IntPtr.Zero; - - // TODO this call to session dispose hangs. - //_session.Dispose(); + _state.Handle.Dispose(); + if (_stateHandle.IsAllocated) _stateHandle.Free(); + _state.ConnectionConfiguration.Dispose(); _disposed = true; } - internal override void Start() + internal override unsafe void Start() { ThrowIfDisposed(); - // protect against double starts. - if (_started) - { - throw new QuicException("Cannot start Listener multiple times"); - } + SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(_listenEndPoint); - _started = true; - SetCallbackHandler(); + uint status; - SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(_listenEndPoint); + MemoryHandle[]? handles = null; + QuicBuffer[]? buffers = null; + try + { + MsQuicAlpnHelper.Prepare(_applicationProtocols, out handles, out buffers); + status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, ref MemoryMarshal.GetReference(buffers.AsSpan()), (uint)_applicationProtocols.Count, ref address); + } + finally + { + MsQuicAlpnHelper.Destroy(ref handles, ref buffers); + } - QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ListenerStartDelegate( - _ptr, - ref address), - "Failed to start listener."); + QuicExceptionHelpers.ThrowIfFailed(status, "ListenerStart failed."); - SetListenPort(); + SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); + _listenEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); } internal override void Close() { ThrowIfDisposed(); - - MsQuicApi.Api.ListenerStopDelegate(_ptr); + MsQuicApi.Api.ListenerStopDelegate(_state.Handle); } - private void SetListenPort() + private void StopAcceptingConnections() { - SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); - - _listenEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); + _state.AcceptConnectionQueue.Writer.TryComplete(); } - internal unsafe uint ListenerCallbackHandler(ref ListenerEvent evt) + private static unsafe uint NativeCallbackHandler( + IntPtr listener, + IntPtr context, + ref ListenerEvent evt) { - try + if ((QUIC_LISTENER_EVENT)evt.Type != QUIC_LISTENER_EVENT.NEW_CONNECTION) { - switch (evt.Type) - { - case QUIC_LISTENER_EVENT.NEW_CONNECTION: - { - ref NewConnectionInfo connectionInfo = ref *(NewConnectionInfo*)evt.Data.NewConnection.Info; - - IPEndPoint localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET*)connectionInfo.LocalAddress); - IPEndPoint remoteEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET*)connectionInfo.RemoteAddress); - - MsQuicConnection msQuicConnection = new MsQuicConnection(localEndPoint, remoteEndPoint, evt.Data.NewConnection.Connection, _options.IdleTimeout); - msQuicConnection.SetNegotiatedAlpn(connectionInfo.NegotiatedAlpn, connectionInfo.NegotiatedAlpnLength); - - _acceptConnectionQueue.Writer.TryWrite(msQuicConnection); - } - // Always pend the new connection to wait for the security config to be resolved - // TODO this doesn't need to be async always - return MsQuicStatusCodes.Pending; - default: - return MsQuicStatusCodes.InternalError; - } + return MsQuicStatusCodes.InternalError; } - catch (Exception ex) - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Error(this, $"Exception occurred during connection callback: {ex.Message}"); - } - // TODO: trigger an exception on any outstanding async calls. + State state = (State)GCHandle.FromIntPtr(context).Target!; + SafeMsQuicConnectionHandle? connectionHandle = null; - return MsQuicStatusCodes.InternalError; - } - } + try + { + ref NewConnectionInfo connectionInfo = ref *(NewConnectionInfo*)evt.Data.NewConnection.Info; - private void StopAcceptingConnections() - { - _acceptConnectionQueue.Writer.TryComplete(); - } + IPEndPoint localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET*)connectionInfo.LocalAddress); + IPEndPoint remoteEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET*)connectionInfo.RemoteAddress); - private static uint NativeCallbackHandler( - IntPtr listener, - IntPtr context, - ref ListenerEvent connectionEventStruct) - { - GCHandle handle = GCHandle.FromIntPtr(context); - MsQuicListener quicListener = (MsQuicListener)handle.Target!; + connectionHandle = new SafeMsQuicConnectionHandle(evt.Data.NewConnection.Connection); - return quicListener.ListenerCallbackHandler(ref connectionEventStruct); - } + uint status = MsQuicApi.Api.ConnectionSetConfigurationDelegate(connectionHandle, state.ConnectionConfiguration); + QuicExceptionHelpers.ThrowIfFailed(status, "ConnectionSetConfiguration failed."); - internal void SetCallbackHandler() - { - Debug.Assert(!_handle.IsAllocated, "listener allocated"); - _handle = GCHandle.Alloc(this); + var msQuicConnection = new MsQuicConnection(localEndPoint, remoteEndPoint, connectionHandle); + msQuicConnection.SetNegotiatedAlpn(connectionInfo.NegotiatedAlpn, connectionInfo.NegotiatedAlpnLength); - MsQuicApi.Api.SetCallbackHandlerDelegate( - _ptr, - s_listenerDelegate, - GCHandle.ToIntPtr(_handle)); + if (!state.AcceptConnectionQueue.Writer.TryWrite(msQuicConnection)) + { + // This handle will be cleaned up by MsQuic. + connectionHandle.SetHandleAsInvalid(); + msQuicConnection.Dispose(); + return MsQuicStatusCodes.InternalError; + } + + return MsQuicStatusCodes.Success; + } + catch (Exception ex) + { + // This handle will be cleaned up by MsQuic by returning InternalError. + connectionHandle?.SetHandleAsInvalid(); + state.AcceptConnectionQueue.Writer.TryComplete(ex); + return MsQuicStatusCodes.InternalError; + } } private void ThrowIfDisposed() 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 d98110d15be4e8..c491312fd157eb 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 @@ -14,46 +14,18 @@ namespace System.Net.Quic.Implementations.MsQuic { internal sealed class MsQuicStream : QuicStreamProvider { - // Pointer to the underlying stream - // TODO replace all IntPtr with SafeHandles - private readonly IntPtr _ptr; - - // Handle to this object for native callbacks. - private GCHandle _handle; - // Delegate that wraps the static function that will be called when receiving an event. internal static readonly StreamCallbackDelegate s_streamDelegate = new StreamCallbackDelegate(NativeCallbackHandler); + private readonly State _state = new State(); + private GCHandle _stateHandle; + // Backing for StreamId private long _streamId = -1; - // Resettable completions to be used for multiple calls to send, start, and shutdown. - private readonly ResettableCompletionSource _sendResettableCompletionSource; - - // Resettable completions to be used for multiple calls to receive. - private readonly ResettableCompletionSource _receiveResettableCompletionSource; - - // Set once writes have been shutdown. - private readonly TaskCompletionSource _shutdownWriteCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - // Buffers to hold during a call to send. - private MemoryHandle[] _bufferArrays = new MemoryHandle[1]; - private QuicBuffer[] _sendQuicBuffers = new QuicBuffer[1]; - - // Handle to hold when sending. - private GCHandle _sendHandle; - // Used to check if StartAsync has been called. private bool _started; - private ReadState _readState; - private long _readErrorCode = -1; - - private ShutdownWriteState _shutdownWriteState; - - private SendState _sendState; - private long _sendErrorCode = -1; - // Used by the class to indicate that the stream is m_Readable. private readonly bool _canRead; @@ -62,35 +34,87 @@ internal sealed class MsQuicStream : QuicStreamProvider private volatile bool _disposed; - private readonly List _receiveQuicBuffers = new List(); + private sealed class State + { + public SafeMsQuicStreamHandle Handle = null!; // set in ctor. + + public ReadState ReadState; + public long ReadErrorCode = -1; + public readonly List ReceiveQuicBuffers = new List(); + + // Resettable completions to be used for multiple calls to receive. + public readonly ResettableCompletionSource ReceiveResettableCompletionSource = new ResettableCompletionSource(); + + public SendState SendState; + public long SendErrorCode = -1; - // TODO consider using Interlocked.Exchange instead of a sync if we can avoid it. - private readonly object _sync = new object(); + // Buffers to hold during a call to send. + public MemoryHandle[] BufferArrays = new MemoryHandle[1]; + public QuicBuffer[] SendQuicBuffers = new QuicBuffer[1]; - // Creates a new MsQuicStream - internal MsQuicStream(MsQuicConnection connection, QUIC_STREAM_OPEN_FLAG flags, IntPtr nativeObjPtr, bool inbound) + // Handle to pinned SendQuicBuffers. + public GCHandle SendHandle; + + // Resettable completions to be used for multiple calls to send, start, and shutdown. + public readonly ResettableCompletionSource SendResettableCompletionSource = new ResettableCompletionSource(); + + public ShutdownWriteState ShutdownWriteState; + + // Set once writes have been shutdown. + public readonly TaskCompletionSource ShutdownWriteCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + } + + // inbound. + internal MsQuicStream(SafeMsQuicStreamHandle streamHandle, QUIC_STREAM_OPEN_FLAG flags) { - Debug.Assert(connection != null, "Connection null"); + _state.Handle = streamHandle; + _canRead = true; + _canWrite = !flags.HasFlag(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); + _started = true; - _ptr = nativeObjPtr; + _stateHandle = GCHandle.Alloc(_state); + try + { + MsQuicApi.Api.SetCallbackHandlerDelegate( + _state.Handle, + s_streamDelegate, + GCHandle.ToIntPtr(_stateHandle)); + } + catch + { + _stateHandle.Free(); + throw; + } + } - _sendResettableCompletionSource = new ResettableCompletionSource(); - _receiveResettableCompletionSource = new ResettableCompletionSource(); - SetCallbackHandler(); + // outbound. + internal MsQuicStream(SafeMsQuicConnectionHandle connection, QUIC_STREAM_OPEN_FLAG flags) + { + Debug.Assert(connection != null); - bool isBidirectional = !flags.HasFlag(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); + _canRead = !flags.HasFlag(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); + _canWrite = true; - if (inbound) + _stateHandle = GCHandle.Alloc(_state); + try { - _canRead = true; - _canWrite = isBidirectional; - _started = true; + uint status = MsQuicApi.Api.StreamOpenDelegate( + connection, + (uint)flags, + s_streamDelegate, + GCHandle.ToIntPtr(_stateHandle), + out _state.Handle); + + QuicExceptionHelpers.ThrowIfFailed(status, "Failed to open stream to peer."); + + status = MsQuicApi.Api.StreamStartDelegate(_state.Handle, (uint)QUIC_STREAM_START_FLAG.ASYNC); + QuicExceptionHelpers.ThrowIfFailed(status, "Could not start stream."); } - else + catch { - _canRead = isBidirectional; - _canWrite = true; - StartLocalStream(); + _state.Handle?.Dispose(); + _stateHandle.Free(); + throw; } } @@ -144,7 +168,6 @@ internal override async ValueTask WriteAsync(ReadOnlyMemory ThrowIfDisposed(); using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken).ConfigureAwait(false); - await SendReadOnlyMemoryListAsync(buffers, endStream ? QUIC_SEND_FLAG.FIN : QUIC_SEND_FLAG.NONE).ConfigureAwait(false); HandleWriteCompletedState(); @@ -168,9 +191,9 @@ private async ValueTask HandleWriteStartState(Can throw new InvalidOperationException(SR.net_quic_writing_notallowed); } - lock (_sync) + lock (_state) { - if (_sendState == SendState.Aborted) + if (_state.SendState == SendState.Aborted) { throw new OperationCanceledException(SR.net_quic_sending_aborted); } @@ -179,25 +202,25 @@ private async ValueTask HandleWriteStartState(Can CancellationTokenRegistration registration = cancellationToken.Register(() => { bool shouldComplete = false; - lock (_sync) + lock (_state) { - if (_sendState == SendState.None) + if (_state.SendState == SendState.None) { - _sendState = SendState.Aborted; + _state.SendState = SendState.Aborted; shouldComplete = true; } } if (shouldComplete) { - _sendResettableCompletionSource.CompleteException(new OperationCanceledException("Write was canceled", cancellationToken)); + _state.SendResettableCompletionSource.CompleteException(new OperationCanceledException("Write was canceled", cancellationToken)); } }); // Make sure start has completed if (!_started) { - await _sendResettableCompletionSource.GetTypelessValueTask().ConfigureAwait(false); + await _state.SendResettableCompletionSource.GetTypelessValueTask().ConfigureAwait(false); _started = true; } @@ -206,11 +229,11 @@ private async ValueTask HandleWriteStartState(Can private void HandleWriteCompletedState() { - lock (_sync) + lock (_state) { - if (_sendState == SendState.Finished) + if (_state.SendState == SendState.Finished) { - _sendState = SendState.None; + _state.SendState = SendState.None; } } } @@ -229,15 +252,15 @@ internal override async ValueTask ReadAsync(Memory destination, Cance NetEventSource.Info(this, $"[{GetHashCode()}] reading into Memory of '{destination.Length}' bytes."); } - lock (_sync) + lock (_state) { - if (_readState == ReadState.ReadsCompleted) + if (_state.ReadState == ReadState.ReadsCompleted) { return 0; } - else if (_readState == ReadState.Aborted) + else if (_state.ReadState == ReadState.Aborted) { - throw _readErrorCode switch + throw _state.ReadErrorCode switch { -1 => new QuicOperationAbortedException(), long err => new QuicStreamAbortedException(err) @@ -248,26 +271,26 @@ internal override async ValueTask ReadAsync(Memory destination, Cance using CancellationTokenRegistration registration = cancellationToken.Register(() => { bool shouldComplete = false; - lock (_sync) + lock (_state) { - if (_readState == ReadState.None) + if (_state.ReadState == ReadState.None) { shouldComplete = true; } - _readState = ReadState.Aborted; + _state.ReadState = ReadState.Aborted; } if (shouldComplete) { - _receiveResettableCompletionSource.CompleteException(new OperationCanceledException("Read was canceled", cancellationToken)); + _state.ReceiveResettableCompletionSource.CompleteException(new OperationCanceledException("Read was canceled", cancellationToken)); } }); // TODO there could potentially be a perf gain by storing the buffer from the inital read // This reduces the amount of async calls, however it makes it so MsQuic holds onto the buffers // longer than it needs to. We will need to benchmark this. - int length = (int)await _receiveResettableCompletionSource.GetValueTask().ConfigureAwait(false); + int length = (int)await _state.ReceiveResettableCompletionSource.GetValueTask().ConfigureAwait(false); int actual = Math.Min(length, destination.Length); @@ -288,16 +311,16 @@ static unsafe void CopyToBuffer(Span destinationBuffer, List s } } - CopyToBuffer(destination.Span, _receiveQuicBuffers); + CopyToBuffer(destination.Span, _state.ReceiveQuicBuffers); - lock (_sync) + lock (_state) { - if (_readState == ReadState.IndividualReadComplete) + if (_state.ReadState == ReadState.IndividualReadComplete) { - _receiveQuicBuffers.Clear(); + _state.ReceiveQuicBuffers.Clear(); ReceiveComplete(actual); EnableReceive(); - _readState = ReadState.None; + _state.ReadState = ReadState.None; } } @@ -310,13 +333,12 @@ internal override void AbortRead(long errorCode) { ThrowIfDisposed(); - lock (_sync) + lock (_state) { - _readState = ReadState.Aborted; + _state.ReadState = ReadState.Aborted; } - MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT_RECV, errorCode); - + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG.ABORT_RECV, errorCode); } internal override void AbortWrite(long errorCode) @@ -325,24 +347,30 @@ internal override void AbortWrite(long errorCode) bool shouldComplete = false; - lock (_sync) + lock (_state) { - if (_shutdownWriteState == ShutdownWriteState.None) + if (_state.ShutdownWriteState == ShutdownWriteState.None) { - _shutdownWriteState = ShutdownWriteState.Canceled; + _state.ShutdownWriteState = ShutdownWriteState.Canceled; shouldComplete = true; } } if (shouldComplete) { - _shutdownWriteCompletionSource.SetException(new QuicStreamAbortedException("Shutdown was aborted.", errorCode)); + _state.ShutdownWriteCompletionSource.SetException(new QuicStreamAbortedException("Shutdown was aborted.", errorCode)); } - MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT_SEND, errorCode); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG.ABORT_SEND, errorCode); } - internal override ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default) + private void StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG flags, long errorCode) + { + uint status = MsQuicApi.Api.StreamShutdownDelegate(_state.Handle, (uint)flags, errorCode); + QuicExceptionHelpers.ThrowIfFailed(status, "StreamShutdown failed."); + } + + internal override async ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default) { ThrowIfDisposed(); @@ -350,29 +378,28 @@ internal override ValueTask ShutdownWriteCompleted(CancellationToken cancellatio using CancellationTokenRegistration registration = cancellationToken.Register(() => { bool shouldComplete = false; - lock (_sync) + lock (_state) { - if (_shutdownWriteState == ShutdownWriteState.None) + if (_state.ShutdownWriteState == ShutdownWriteState.None) { - _shutdownWriteState = ShutdownWriteState.Canceled; + _state.ShutdownWriteState = ShutdownWriteState.Canceled; shouldComplete = true; } } if (shouldComplete) { - _shutdownWriteCompletionSource.SetException(new OperationCanceledException("Shutdown was canceled", cancellationToken)); + _state.ShutdownWriteCompletionSource.SetException(new OperationCanceledException("Shutdown was canceled", cancellationToken)); } }); - return new ValueTask(_shutdownWriteCompletionSource.Task); + await _state.ShutdownWriteCompletionSource.Task.ConfigureAwait(false); } internal override void Shutdown() { ThrowIfDisposed(); - - MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); } // TODO consider removing sync-over-async with blocking calls. @@ -407,24 +434,9 @@ internal override Task FlushAsync(CancellationToken cancellationToken = default) public override ValueTask DisposeAsync() { - if (_disposed) - { - return default; - } - - if (_ptr != IntPtr.Zero) - { - // TODO resolve graceful vs abortive dispose here. Will file a separate issue. - //MsQuicApi.Api._streamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT, 1); - MsQuicApi.Api.StreamCloseDelegate?.Invoke(_ptr); - } - - _handle.Free(); - - CleanupSendState(); - - _disposed = true; + // TODO: perform a graceful shutdown and wait for completion? + Dispose(true); return default; } @@ -446,23 +458,16 @@ private void Dispose(bool disposing) return; } - if (_ptr != IntPtr.Zero) - { - // TODO resolve graceful vs abortive dispose here. Will file a separate issue. - //MsQuicApi.Api._streamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT, 1); - MsQuicApi.Api.StreamCloseDelegate?.Invoke(_ptr); - } - - _handle.Free(); - - CleanupSendState(); + _state.Handle.Dispose(); + if (_stateHandle.IsAllocated) _stateHandle.Free(); + CleanupSendState(_state); _disposed = true; } private void EnableReceive() { - MsQuicApi.Api.StreamReceiveSetEnabledDelegate(_ptr, enabled: true); + MsQuicApi.Api.StreamReceiveSetEnabledDelegate(_state.Handle, enabled: true); } private static uint NativeCallbackHandler( @@ -470,141 +475,108 @@ private static uint NativeCallbackHandler( IntPtr context, ref StreamEvent streamEvent) { - var handle = GCHandle.FromIntPtr(context); - var quicStream = (MsQuicStream)handle.Target!; - - return quicStream.HandleEvent(ref streamEvent); + var state = (State)GCHandle.FromIntPtr(context).Target!; + return HandleEvent(state, ref streamEvent); } - private uint HandleEvent(ref StreamEvent evt) + private static uint HandleEvent(State state, ref StreamEvent evt) { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(this, $"[{GetHashCode()}] handling event '{evt.Type}'."); - } - - uint status = MsQuicStatusCodes.Success; - try { - switch (evt.Type) + switch ((QUIC_STREAM_EVENT)evt.Type) { // Stream has started. // Will only be done for outbound streams (inbound streams have already started) case QUIC_STREAM_EVENT.START_COMPLETE: - status = HandleStartComplete(); - break; + return HandleStartComplete(state); // Received data on the stream case QUIC_STREAM_EVENT.RECEIVE: - { - status = HandleEventRecv(ref evt); - } - break; + return HandleEventRecv(state, ref evt); // Send has completed. // Contains a canceled bool to indicate if the send was canceled. case QUIC_STREAM_EVENT.SEND_COMPLETE: - { - status = HandleEventSendComplete(ref evt); - } - break; + return HandleEventSendComplete(state, ref evt); // Peer has told us to shutdown the reading side of the stream. case QUIC_STREAM_EVENT.PEER_SEND_SHUTDOWN: - { - status = HandleEventPeerSendShutdown(); - } - break; + return HandleEventPeerSendShutdown(state); // Peer has told us to abort the reading side of the stream. case QUIC_STREAM_EVENT.PEER_SEND_ABORTED: - { - status = HandleEventPeerSendAborted(ref evt); - } - break; + return HandleEventPeerSendAborted(state, ref evt); // Peer has stopped receiving data, don't send anymore. case QUIC_STREAM_EVENT.PEER_RECEIVE_ABORTED: - { - status = HandleEventPeerRecvAborted(ref evt); - } - break; + return HandleEventPeerRecvAborted(state, ref evt); // Occurs when shutdown is completed for the send side. // This only happens for shutdown on sending, not receiving // Receive shutdown can only be abortive. case QUIC_STREAM_EVENT.SEND_SHUTDOWN_COMPLETE: - { - status = HandleEventSendShutdownComplete(ref evt); - } - break; + return HandleEventSendShutdownComplete(state, ref evt); // Shutdown for both sending and receiving is completed. case QUIC_STREAM_EVENT.SHUTDOWN_COMPLETE: - { - status = HandleEventShutdownComplete(); - } - break; + return HandleEventShutdownComplete(state); default: - break; + return MsQuicStatusCodes.Success; } } catch (Exception) { return MsQuicStatusCodes.InternalError; } - - return status; } - private unsafe uint HandleEventRecv(ref MsQuicNativeMethods.StreamEvent evt) + private static unsafe uint HandleEventRecv(State state, ref StreamEvent evt) { StreamEventDataRecv receieveEvent = evt.Data.Recv; for (int i = 0; i < receieveEvent.BufferCount; i++) { - _receiveQuicBuffers.Add(receieveEvent.Buffers[i]); + state.ReceiveQuicBuffers.Add(receieveEvent.Buffers[i]); } bool shouldComplete = false; - lock (_sync) + lock (state) { - if (_readState == ReadState.None) + if (state.ReadState == ReadState.None) { shouldComplete = true; } - _readState = ReadState.IndividualReadComplete; + state.ReadState = ReadState.IndividualReadComplete; } if (shouldComplete) { - _receiveResettableCompletionSource.Complete((uint)receieveEvent.TotalBufferLength); + state.ReceiveResettableCompletionSource.Complete((uint)receieveEvent.TotalBufferLength); } return MsQuicStatusCodes.Pending; } - private uint HandleEventPeerRecvAborted(ref StreamEvent evt) + private static uint HandleEventPeerRecvAborted(State state, ref StreamEvent evt) { bool shouldComplete = false; - lock (_sync) + lock (state) { - if (_sendState == SendState.None) + if (state.SendState == SendState.None) { shouldComplete = true; } - _sendState = SendState.Aborted; - _sendErrorCode = evt.Data.PeerSendAbort.ErrorCode; + state.SendState = SendState.Aborted; + state.SendErrorCode = evt.Data.PeerSendAbort.ErrorCode; } if (shouldComplete) { - _sendResettableCompletionSource.CompleteException(new QuicStreamAbortedException(_sendErrorCode)); + state.SendResettableCompletionSource.CompleteException(new QuicStreamAbortedException(state.SendErrorCode)); } return MsQuicStatusCodes.Success; } - private uint HandleStartComplete() + private static uint HandleStartComplete(State state) { bool shouldComplete = false; - lock (_sync) + lock (state) { // Check send state before completing as send cancellation is shared between start and send. - if (_sendState == SendState.None) + if (state.SendState == SendState.None) { shouldComplete = true; } @@ -612,163 +584,153 @@ private uint HandleStartComplete() if (shouldComplete) { - _sendResettableCompletionSource.Complete(MsQuicStatusCodes.Success); + state.SendResettableCompletionSource.Complete(MsQuicStatusCodes.Success); } return MsQuicStatusCodes.Success; } - private uint HandleEventSendShutdownComplete(ref MsQuicNativeMethods.StreamEvent evt) + private static uint HandleEventSendShutdownComplete(State state, ref MsQuicNativeMethods.StreamEvent evt) { bool shouldComplete = false; - lock (_sync) + lock (state) { - if (_shutdownWriteState == ShutdownWriteState.None) + if (state.ShutdownWriteState == ShutdownWriteState.None) { - _shutdownWriteState = ShutdownWriteState.Finished; + state.ShutdownWriteState = ShutdownWriteState.Finished; shouldComplete = true; } } if (shouldComplete) { - _shutdownWriteCompletionSource.TrySetResult(); + state.ShutdownWriteCompletionSource.TrySetResult(); } return MsQuicStatusCodes.Success; } - private uint HandleEventShutdownComplete() + private static uint HandleEventShutdownComplete(State state) { bool shouldReadComplete = false; bool shouldShutdownWriteComplete = false; - lock (_sync) + lock (state) { // This event won't occur within the middle of a receive. if (NetEventSource.Log.IsEnabled()) NetEventSource.Info("Completing resettable event source."); - if (_readState == ReadState.None) + if (state.ReadState == ReadState.None) { shouldReadComplete = true; } - _readState = ReadState.ReadsCompleted; + state.ReadState = ReadState.ReadsCompleted; - if (_shutdownWriteState == ShutdownWriteState.None) + if (state.ShutdownWriteState == ShutdownWriteState.None) { - _shutdownWriteState = ShutdownWriteState.Finished; + state.ShutdownWriteState = ShutdownWriteState.Finished; shouldShutdownWriteComplete = true; } } if (shouldReadComplete) { - _receiveResettableCompletionSource.Complete(0); + state.ReceiveResettableCompletionSource.Complete(0); } if (shouldShutdownWriteComplete) { - _shutdownWriteCompletionSource.TrySetResult(); + state.ShutdownWriteCompletionSource.TrySetResult(); } return MsQuicStatusCodes.Success; } - private uint HandleEventPeerSendAborted(ref StreamEvent evt) + private static uint HandleEventPeerSendAborted(State state, ref StreamEvent evt) { bool shouldComplete = false; - lock (_sync) + lock (state) { - if (_readState == ReadState.None) + if (state.ReadState == ReadState.None) { shouldComplete = true; } - _readState = ReadState.Aborted; - _readErrorCode = evt.Data.PeerSendAbort.ErrorCode; + state.ReadState = ReadState.Aborted; + state.ReadErrorCode = evt.Data.PeerSendAbort.ErrorCode; } if (shouldComplete) { - _receiveResettableCompletionSource.CompleteException(new QuicStreamAbortedException(_readErrorCode)); + state.ReceiveResettableCompletionSource.CompleteException(new QuicStreamAbortedException(state.ReadErrorCode)); } return MsQuicStatusCodes.Success; } - private uint HandleEventPeerSendShutdown() + private static uint HandleEventPeerSendShutdown(State state) { bool shouldComplete = false; - lock (_sync) + lock (state) { // This event won't occur within the middle of a receive. if (NetEventSource.Log.IsEnabled()) NetEventSource.Info("Completing resettable event source."); - if (_readState == ReadState.None) + if (state.ReadState == ReadState.None) { shouldComplete = true; } - _readState = ReadState.ReadsCompleted; + state.ReadState = ReadState.ReadsCompleted; } if (shouldComplete) { - _receiveResettableCompletionSource.Complete(0); + state.ReceiveResettableCompletionSource.Complete(0); } return MsQuicStatusCodes.Success; } - private uint HandleEventSendComplete(ref StreamEvent evt) + private static uint HandleEventSendComplete(State state, ref StreamEvent evt) { - CleanupSendState(); + bool complete = false; - // TODO throw if a write was canceled. - - bool shouldComplete = false; - lock (_sync) + lock (state) { - if (_sendState == SendState.None) + if (state.SendState == SendState.None) { - _sendState = SendState.Finished; - shouldComplete = true; + state.SendState = SendState.Finished; + complete = true; } } - if (shouldComplete) + if (complete) { - _sendResettableCompletionSource.Complete(MsQuicStatusCodes.Success); + CleanupSendState(state); + + // TODO throw if a write was canceled. + state.SendResettableCompletionSource.Complete(MsQuicStatusCodes.Success); } return MsQuicStatusCodes.Success; } - private void CleanupSendState() + private static void CleanupSendState(State state) { - if (_sendHandle.IsAllocated) + if (state.SendHandle.IsAllocated) { - _sendHandle.Free(); + state.SendHandle.Free(); } // Callings dispose twice on a memory handle should be okay - foreach (MemoryHandle buffer in _bufferArrays) + foreach (MemoryHandle buffer in state.BufferArrays) { buffer.Dispose(); } } - private void SetCallbackHandler() - { - _handle = GCHandle.Alloc(this); - - MsQuicApi.Api.SetCallbackHandlerDelegate( - _ptr, - s_streamDelegate, - GCHandle.ToIntPtr(_handle)); - } - // TODO prevent overlapping sends or consider supporting it. private unsafe ValueTask SendReadOnlyMemoryAsync( ReadOnlyMemory buffer, @@ -779,38 +741,38 @@ private unsafe ValueTask SendReadOnlyMemoryAsync( if ((flags & QUIC_SEND_FLAG.FIN) == QUIC_SEND_FLAG.FIN) { // Start graceful shutdown sequence if passed in the fin flag and there is an empty buffer. - MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); } return default; } MemoryHandle handle = buffer.Pin(); - _sendQuicBuffers[0].Length = (uint)buffer.Length; - _sendQuicBuffers[0].Buffer = (byte*)handle.Pointer; + _state.SendQuicBuffers[0].Length = (uint)buffer.Length; + _state.SendQuicBuffers[0].Buffer = (byte*)handle.Pointer; - _bufferArrays[0] = handle; + _state.BufferArrays[0] = handle; - _sendHandle = GCHandle.Alloc(_sendQuicBuffers, GCHandleType.Pinned); + _state.SendHandle = GCHandle.Alloc(_state.SendQuicBuffers, GCHandleType.Pinned); - var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_sendQuicBuffers, 0); + var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_state.SendQuicBuffers, 0); uint status = MsQuicApi.Api.StreamSendDelegate( - _ptr, + _state.Handle, quicBufferPointer, bufferCount: 1, (uint)flags, - _ptr); + IntPtr.Zero); if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) { - CleanupSendState(); + CleanupSendState(_state); // TODO this may need to be an aborted exception. QuicExceptionHelpers.ThrowIfFailed(status, "Could not send data to peer."); } - return _sendResettableCompletionSource.GetTypelessValueTask(); + return _state.SendResettableCompletionSource.GetTypelessValueTask(); } private unsafe ValueTask SendReadOnlySequenceAsync( @@ -822,7 +784,7 @@ private unsafe ValueTask SendReadOnlySequenceAsync( if ((flags & QUIC_SEND_FLAG.FIN) == QUIC_SEND_FLAG.FIN) { // Start graceful shutdown sequence if passed in the fin flag and there is an empty buffer. - MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); } return default; } @@ -834,10 +796,10 @@ private unsafe ValueTask SendReadOnlySequenceAsync( ++count; } - if (_sendQuicBuffers.Length < count) + if (_state.SendQuicBuffers.Length < count) { - _sendQuicBuffers = new QuicBuffer[count]; - _bufferArrays = new MemoryHandle[count]; + _state.SendQuicBuffers = new QuicBuffer[count]; + _state.BufferArrays = new MemoryHandle[count]; } count = 0; @@ -845,33 +807,33 @@ private unsafe ValueTask SendReadOnlySequenceAsync( foreach (ReadOnlyMemory buffer in buffers) { MemoryHandle handle = buffer.Pin(); - _sendQuicBuffers[count].Length = (uint)buffer.Length; - _sendQuicBuffers[count].Buffer = (byte*)handle.Pointer; - _bufferArrays[count] = handle; + _state.SendQuicBuffers[count].Length = (uint)buffer.Length; + _state.SendQuicBuffers[count].Buffer = (byte*)handle.Pointer; + _state.BufferArrays[count] = handle; ++count; } - _sendHandle = GCHandle.Alloc(_sendQuicBuffers, GCHandleType.Pinned); + _state.SendHandle = GCHandle.Alloc(_state.SendQuicBuffers, GCHandleType.Pinned); - var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_sendQuicBuffers, 0); + var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_state.SendQuicBuffers, 0); uint status = MsQuicApi.Api.StreamSendDelegate( - _ptr, + _state.Handle, quicBufferPointer, count, (uint)flags, - _ptr); + IntPtr.Zero); if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) { - CleanupSendState(); + CleanupSendState(_state); // TODO this may need to be an aborted exception. QuicExceptionHelpers.ThrowIfFailed(status, "Could not send data to peer."); } - return _sendResettableCompletionSource.GetTypelessValueTask(); + return _state.SendResettableCompletionSource.GetTypelessValueTask(); } private unsafe ValueTask SendReadOnlyMemoryListAsync( @@ -883,7 +845,7 @@ private unsafe ValueTask SendReadOnlyMemoryListAsync( if ((flags & QUIC_SEND_FLAG.FIN) == QUIC_SEND_FLAG.FIN) { // Start graceful shutdown sequence if passed in the fin flag and there is an empty buffer. - MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); } return default; } @@ -892,67 +854,54 @@ private unsafe ValueTask SendReadOnlyMemoryListAsync( uint length = (uint)array.Length; - if (_sendQuicBuffers.Length < length) + if (_state.SendQuicBuffers.Length < length) { - _sendQuicBuffers = new QuicBuffer[length]; - _bufferArrays = new MemoryHandle[length]; + _state.SendQuicBuffers = new QuicBuffer[length]; + _state.BufferArrays = new MemoryHandle[length]; } for (int i = 0; i < length; i++) { ReadOnlyMemory buffer = array[i]; MemoryHandle handle = buffer.Pin(); - _sendQuicBuffers[i].Length = (uint)buffer.Length; - _sendQuicBuffers[i].Buffer = (byte*)handle.Pointer; - _bufferArrays[i] = handle; + _state.SendQuicBuffers[i].Length = (uint)buffer.Length; + _state.SendQuicBuffers[i].Buffer = (byte*)handle.Pointer; + _state.BufferArrays[i] = handle; } - _sendHandle = GCHandle.Alloc(_sendQuicBuffers, GCHandleType.Pinned); + _state.SendHandle = GCHandle.Alloc(_state.SendQuicBuffers, GCHandleType.Pinned); - var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_sendQuicBuffers, 0); + var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_state.SendQuicBuffers, 0); uint status = MsQuicApi.Api.StreamSendDelegate( - _ptr, + _state.Handle, quicBufferPointer, length, (uint)flags, - _ptr); + IntPtr.Zero); if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) { - CleanupSendState(); + CleanupSendState(_state); // TODO this may need to be an aborted exception. QuicExceptionHelpers.ThrowIfFailed(status, "Could not send data to peer."); } - return _sendResettableCompletionSource.GetTypelessValueTask(); - } - - /// - /// Assigns a stream ID and begins process the stream. - /// - private void StartLocalStream() - { - Debug.Assert(!_started, "start local stream"); - uint status = MsQuicApi.Api.StreamStartDelegate( - _ptr, - (uint)QUIC_STREAM_START_FLAG.ASYNC); - - QuicExceptionHelpers.ThrowIfFailed(status, "Could not start stream."); + return _state.SendResettableCompletionSource.GetTypelessValueTask(); } private void ReceiveComplete(int bufferLength) { - uint status = MsQuicApi.Api.StreamReceiveCompleteDelegate(_ptr, (ulong)bufferLength); + uint status = MsQuicApi.Api.StreamReceiveCompleteDelegate(_state.Handle, (ulong)bufferLength); QuicExceptionHelpers.ThrowIfFailed(status, "Could not complete receive call."); } // This can fail if the stream isn't started. private long GetStreamId() { - return (long)MsQuicParameterHelpers.GetULongParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.STREAM, (uint)QUIC_PARAM_STREAM.ID); + return (long)MsQuicParameterHelpers.GetULongParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_LEVEL.STREAM, (uint)QUIC_PARAM_STREAM.ID); } private void ThrowIfDisposed() @@ -971,7 +920,7 @@ private enum ReadState None, /// - /// Data is available in . + /// Data is available in . /// IndividualReadComplete, diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicClientConnectionOptions.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicClientConnectionOptions.cs index cc5691b1cc72b7..4b2ce8fcb84de9 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicClientConnectionOptions.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicClientConnectionOptions.cs @@ -8,7 +8,7 @@ namespace System.Net.Quic /// /// Options to provide to the when connecting to a Listener. /// - public class QuicClientConnectionOptions + public class QuicClientConnectionOptions : QuicOptions { /// /// Client authentication options to use when establishing a . @@ -25,25 +25,9 @@ public class QuicClientConnectionOptions /// public EndPoint? RemoteEndPoint { get; set; } - /// - /// Limit on the number of bidirectional streams the peer connection can create - /// on an accepted connection. - /// Default is 100. - /// - // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. - public long MaxBidirectionalStreams { get; set; } = 100; - - /// - /// Limit on the number of unidirectional streams the peer connection can create - /// on an accepted connection. - /// Default is 100. - /// - // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. - public long MaxUnidirectionalStreams { get; set; } = 100; - - /// - /// Idle timeout for connections, after which the connection will be closed. - /// - public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(2); + public QuicClientConnectionOptions() + { + IdleTimeout = TimeSpan.FromTicks(2 * TimeSpan.TicksPerMinute); + } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListenerOptions.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListenerOptions.cs index 6e861b39ca8156..b20a8b71285e21 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListenerOptions.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListenerOptions.cs @@ -8,23 +8,13 @@ namespace System.Net.Quic /// /// Options to provide to the . /// - public class QuicListenerOptions + public class QuicListenerOptions : QuicOptions { /// /// Server Ssl options to use for ALPN, SNI, etc. /// public SslServerAuthenticationOptions? ServerAuthenticationOptions { get; set; } - /// - /// Optional path to certificate file to configure the security configuration. - /// - public string? CertificateFilePath { get; set; } - - /// - /// Optional path to private key file to configure the security configuration. - /// - public string? PrivateKeyFilePath { get; set; } - /// /// The endpoint to listen on. /// @@ -35,24 +25,9 @@ public class QuicListenerOptions /// public int ListenBacklog { get; set; } = 512; - /// - /// Limit on the number of bidirectional streams an accepted connection can create - /// back to the client. - /// Default is 100. - /// - // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. - public long MaxBidirectionalStreams { get; set; } = 100; - - /// - /// Limit on the number of unidirectional streams the peer connection can create. - /// Default is 100. - /// - // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. - public long MaxUnidirectionalStreams { get; set; } = 100; - - /// - /// Idle timeout for connections, afterwhich the connection will be closed. - /// - public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(10); + public QuicListenerOptions() + { + IdleTimeout = TimeSpan.FromTicks(10 * TimeSpan.TicksPerMinute); + } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicOptions.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicOptions.cs new file mode 100644 index 00000000000000..86dd644aaac1c7 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicOptions.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace System.Net.Quic +{ + /// + /// Options for QUIC + /// + public class QuicOptions + { + /// + /// Limit on the number of bidirectional streams the remote peer connection can create on an open connection. + /// Default is 100. + /// + // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. + public long MaxBidirectionalStreams { get; set; } = 100; + + /// + /// Limit on the number of unidirectional streams the remote peer connection can create on an open connection. + /// Default is 100. + /// + // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. + public long MaxUnidirectionalStreams { get; set; } = 100; + + /// + /// Idle timeout for connections, after which the connection will be closed. + /// + public TimeSpan IdleTimeout { get; set; } + } +} diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs index 98e8438a7afda5..8f49d8b6113830 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs @@ -13,7 +13,8 @@ public SslServerAuthenticationOptions GetSslServerAuthenticationOptions() { return new SslServerAuthenticationOptions() { - ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") } + ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") }, + // TODO: use a cert. MsQuic currently only allows certs that are trusted. }; } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index 39d2eebc668b88..a035e243600f3e 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -5,14 +5,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Net.Security; using System.Text; using System.Threading.Tasks; using Xunit; namespace System.Net.Quic.Tests { - [ConditionalClass(typeof(MsQuicTests), nameof(MsQuicTests.IsMsQuicSupported))] + [ConditionalClass(typeof(MsQuicTests), nameof(IsMsQuicSupported))] public class MsQuicTests : MsQuicTestBase { public static bool IsMsQuicSupported => QuicImplementationProviders.MsQuic.IsSupported; From e96522e5180e41831dbfd261949d78f4b6ccad5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= Date: Fri, 12 Mar 2021 17:13:04 +0100 Subject: [PATCH 02/12] Enum rename _FLAG --> _FLAGS to follow msquic naming. --- .../MsQuic/Internal/MsQuicApi.cs | 2 +- .../MsQuic/Interop/MsQuicEnums.cs | 45 ++++++++++--------- .../MsQuic/Interop/MsQuicNativeMethods.cs | 15 +------ .../MsQuic/MsQuicConnection.cs | 8 ++-- .../Implementations/MsQuic/MsQuicStream.cs | 42 ++++++++--------- 5 files changed, 52 insertions(+), 60 deletions(-) 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 65948e4b0c8b90..3574c19fc2ff17 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 @@ -14,7 +14,7 @@ internal unsafe sealed class MsQuicApi public SafeMsQuicRegistrationHandle Registration { get; } // This is workaround for a bug in ILTrimmer. - // Withough these DynamicDependency attributes, .ctor() will be removed from the safe handles. + // Without these DynamicDependency attributes, .ctor() will be removed from the safe handles. // Remove once fixed: https://github.com/mono/linker/issues/1660 [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicRegistrationHandle))] [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicConfigurationHandle))] 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 8ac7c5c1dab6fd..1b00abb5305c7e 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 @@ -5,7 +5,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal { internal enum QUIC_EXECUTION_PROFILE : uint { - QUIC_EXECUTION_PROFILE_LOW_LATENCY, // Default + QUIC_EXECUTION_PROFILE_LOW_LATENCY, // Default QUIC_EXECUTION_PROFILE_TYPE_MAX_THROUGHPUT, QUIC_EXECUTION_PROFILE_TYPE_SCAVENGER, QUIC_EXECUTION_PROFILE_TYPE_REAL_TIME @@ -18,28 +18,40 @@ internal enum QUIC_CREDENTIAL_TYPE : uint HASH_STORE, CONTEXT, FILE, - STUB_NULL = 0xF0000000 // pass as server cert to stubtls implementation + FILE_PROTECTED, + STUB_NULL = 0xF0000000 // Pass as server cert to stubtls implementation } [Flags] internal enum QUIC_CREDENTIAL_FLAGS : uint { - NONE = 0, - CLIENT = 1, // lack of client flag indicates server. - LOAD_ASYNCHRONOUS = 2, - NO_CERTIFICATE_VALIDATION = 4, - ENABLE_OCSP = 8 + NONE = 0x00000000, + CLIENT = 0x00000001, // Lack of client flag indicates server. + LOAD_ASYNCHRONOUS = 0x00000002, + NO_CERTIFICATE_VALIDATION = 0x00000004, + ENABLE_OCSP = 0x00000008, // Schannel only currently + INDICATE_CERTIFICATE_RECEIVED = 0x00000010, + DEFER_CERTIFICATE_VALIDATION = 0x00000020, // Schannel only currently + REQUIRE_CLIENT_AUTHENTICATION = 0x00000040, // Schannel only currently + USE_TLS_BUILTIN_CERTIFICATE_VALIDATION = 0x00000080 } [Flags] - internal enum QUIC_CONNECTION_SHUTDOWN_FLAG : uint + internal enum QUIC_CONNECTION_SHUTDOWN_FLAGS : uint { NONE = 0x0, SILENT = 0x1 } + internal enum QUIC_SERVER_RESUMPTION_LEVEL : uint + { + NO_RESUME, + RESUME_ONLY, + RESUME_AND_ZERORTT + } + [Flags] - internal enum QUIC_STREAM_OPEN_FLAG : uint + internal enum QUIC_STREAM_OPEN_FLAGS : uint { NONE = 0, UNIDIRECTIONAL = 0x1, @@ -47,7 +59,7 @@ internal enum QUIC_STREAM_OPEN_FLAG : uint } [Flags] - internal enum QUIC_STREAM_START_FLAG : uint + internal enum QUIC_STREAM_START_FLAGS : uint { NONE = 0, FAIL_BLOCKED = 0x1, @@ -56,7 +68,7 @@ internal enum QUIC_STREAM_START_FLAG : uint } [Flags] - internal enum QUIC_STREAM_SHUTDOWN_FLAG : uint + internal enum QUIC_STREAM_SHUTDOWN_FLAGS : uint { NONE = 0, GRACEFUL = 0x1, @@ -67,7 +79,7 @@ internal enum QUIC_STREAM_SHUTDOWN_FLAG : uint } [Flags] - internal enum QUIC_RECEIVE_FLAG : uint + internal enum QUIC_RECEIVE_FLAGS : uint { NONE = 0, ZERO_RTT = 0x1, @@ -75,7 +87,7 @@ internal enum QUIC_RECEIVE_FLAG : uint } [Flags] - internal enum QUIC_SEND_FLAG : uint + internal enum QUIC_SEND_FLAGS : uint { NONE = 0, ALLOW_0_RTT = 0x00000001, @@ -192,11 +204,4 @@ internal enum QUIC_STREAM_EVENT : uint SHUTDOWN_COMPLETE = 7, IDEAL_SEND_BUFFER_SIZE = 8, } - - internal enum QUIC_SERVER_RESUMPTION_LEVEL : byte - { - NO_RESUME, - RESUME_ONLY, - RESUME_AND_ZERORTT - } } 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 497d36c733ba3e..298c993a9cdc19 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 @@ -266,7 +266,7 @@ internal struct ConnectionEvent internal bool ShutdownTimedOut => Data.ShutdownComplete.TimedOut; internal ushort BiDirectionalCount => Data.StreamsAvailable.BiDirectionalCount; internal ushort UniDirectionalCount => Data.StreamsAvailable.UniDirectionalCount; - internal QUIC_STREAM_OPEN_FLAG StreamFlags => (QUIC_STREAM_OPEN_FLAG)Data.StreamStarted.Flags; + internal QUIC_STREAM_OPEN_FLAGS StreamFlags => (QUIC_STREAM_OPEN_FLAGS)Data.StreamStarted.Flags; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -528,19 +528,6 @@ internal struct Settings internal byte BitField; } - internal enum SettingsBitField : byte - { - SendBufferingEnabled = 1, - PacingEnabled = 1 << 1, - MigrationEnabled = 1 << 2, - DatagramReceiveEnabled = 1 << 3, - - // single enum field, 2 bits. - NoResume = QUIC_SERVER_RESUMPTION_LEVEL.NO_RESUME << 4, - ResumeOnly = QUIC_SERVER_RESUMPTION_LEVEL.RESUME_ONLY << 4, - ResumeAndZeroRtt = QUIC_SERVER_RESUMPTION_LEVEL.RESUME_AND_ZERORTT << 4 - } - [Flags] internal enum SettingsFlags : ulong { 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 e4f142018025f5..e26dac45687c91 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 @@ -205,13 +205,13 @@ internal override async ValueTask AcceptStreamAsync(Cancella internal override QuicStreamProvider OpenUnidirectionalStream() { ThrowIfDisposed(); - return new MsQuicStream(_state.Handle, QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); + return new MsQuicStream(_state.Handle, QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); } internal override QuicStreamProvider OpenBidirectionalStream() { ThrowIfDisposed(); - return new MsQuicStream(_state.Handle, QUIC_STREAM_OPEN_FLAG.NONE); + return new MsQuicStream(_state.Handle, QUIC_STREAM_OPEN_FLAGS.NONE); } internal override long GetRemoteAvailableUnidirectionalStreamCount() @@ -271,7 +271,7 @@ internal override ValueTask ConnectAsync(CancellationToken cancellationToken = d } private ValueTask ShutdownAsync( - QUIC_CONNECTION_SHUTDOWN_FLAG Flags, + QUIC_CONNECTION_SHUTDOWN_FLAGS Flags, long ErrorCode) { Debug.Assert(!_state.ShutdownTcs.Task.IsCompleted); @@ -375,7 +375,7 @@ internal override ValueTask CloseAsync(long errorCode, CancellationToken cancell { ThrowIfDisposed(); - return ShutdownAsync(QUIC_CONNECTION_SHUTDOWN_FLAG.NONE, errorCode); + return ShutdownAsync(QUIC_CONNECTION_SHUTDOWN_FLAGS.NONE, errorCode); } private void ThrowIfDisposed() 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 c491312fd157eb..f096fa2d88c349 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 @@ -65,11 +65,11 @@ private sealed class State } // inbound. - internal MsQuicStream(SafeMsQuicStreamHandle streamHandle, QUIC_STREAM_OPEN_FLAG flags) + internal MsQuicStream(SafeMsQuicStreamHandle streamHandle, QUIC_STREAM_OPEN_FLAGS flags) { _state.Handle = streamHandle; _canRead = true; - _canWrite = !flags.HasFlag(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); + _canWrite = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); _started = true; _stateHandle = GCHandle.Alloc(_state); @@ -88,11 +88,11 @@ internal MsQuicStream(SafeMsQuicStreamHandle streamHandle, QUIC_STREAM_OPEN_FLAG } // outbound. - internal MsQuicStream(SafeMsQuicConnectionHandle connection, QUIC_STREAM_OPEN_FLAG flags) + internal MsQuicStream(SafeMsQuicConnectionHandle connection, QUIC_STREAM_OPEN_FLAGS flags) { Debug.Assert(connection != null); - _canRead = !flags.HasFlag(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); + _canRead = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); _canWrite = true; _stateHandle = GCHandle.Alloc(_state); @@ -107,7 +107,7 @@ internal MsQuicStream(SafeMsQuicConnectionHandle connection, QUIC_STREAM_OPEN_FL QuicExceptionHelpers.ThrowIfFailed(status, "Failed to open stream to peer."); - status = MsQuicApi.Api.StreamStartDelegate(_state.Handle, (uint)QUIC_STREAM_START_FLAG.ASYNC); + status = MsQuicApi.Api.StreamStartDelegate(_state.Handle, (uint)QUIC_STREAM_START_FLAGS.ASYNC); QuicExceptionHelpers.ThrowIfFailed(status, "Could not start stream."); } catch @@ -153,7 +153,7 @@ internal override async ValueTask WriteAsync(ReadOnlySequence buffers, boo using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken).ConfigureAwait(false); - await SendReadOnlySequenceAsync(buffers, endStream ? QUIC_SEND_FLAG.FIN : QUIC_SEND_FLAG.NONE).ConfigureAwait(false); + await SendReadOnlySequenceAsync(buffers, endStream ? QUIC_SEND_FLAGS.FIN : QUIC_SEND_FLAGS.NONE).ConfigureAwait(false); HandleWriteCompletedState(); } @@ -168,7 +168,7 @@ internal override async ValueTask WriteAsync(ReadOnlyMemory ThrowIfDisposed(); using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken).ConfigureAwait(false); - await SendReadOnlyMemoryListAsync(buffers, endStream ? QUIC_SEND_FLAG.FIN : QUIC_SEND_FLAG.NONE).ConfigureAwait(false); + await SendReadOnlyMemoryListAsync(buffers, endStream ? QUIC_SEND_FLAGS.FIN : QUIC_SEND_FLAGS.NONE).ConfigureAwait(false); HandleWriteCompletedState(); } @@ -179,7 +179,7 @@ internal override async ValueTask WriteAsync(ReadOnlyMemory buffer, bool e using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken).ConfigureAwait(false); - await SendReadOnlyMemoryAsync(buffer, endStream ? QUIC_SEND_FLAG.FIN : QUIC_SEND_FLAG.NONE).ConfigureAwait(false); + await SendReadOnlyMemoryAsync(buffer, endStream ? QUIC_SEND_FLAGS.FIN : QUIC_SEND_FLAGS.NONE).ConfigureAwait(false); HandleWriteCompletedState(); } @@ -338,7 +338,7 @@ internal override void AbortRead(long errorCode) _state.ReadState = ReadState.Aborted; } - StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG.ABORT_RECV, errorCode); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_RECV, errorCode); } internal override void AbortWrite(long errorCode) @@ -361,10 +361,10 @@ internal override void AbortWrite(long errorCode) _state.ShutdownWriteCompletionSource.SetException(new QuicStreamAbortedException("Shutdown was aborted.", errorCode)); } - StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG.ABORT_SEND, errorCode); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_SEND, errorCode); } - private void StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG flags, long errorCode) + private void StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS flags, long errorCode) { uint status = MsQuicApi.Api.StreamShutdownDelegate(_state.Handle, (uint)flags, errorCode); QuicExceptionHelpers.ThrowIfFailed(status, "StreamShutdown failed."); @@ -399,7 +399,7 @@ internal override async ValueTask ShutdownWriteCompleted(CancellationToken cance internal override void Shutdown() { ThrowIfDisposed(); - StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.GRACEFUL, errorCode: 0); } // TODO consider removing sync-over-async with blocking calls. @@ -734,14 +734,14 @@ private static void CleanupSendState(State state) // TODO prevent overlapping sends or consider supporting it. private unsafe ValueTask SendReadOnlyMemoryAsync( ReadOnlyMemory buffer, - QUIC_SEND_FLAG flags) + QUIC_SEND_FLAGS flags) { if (buffer.IsEmpty) { - if ((flags & QUIC_SEND_FLAG.FIN) == QUIC_SEND_FLAG.FIN) + if ((flags & QUIC_SEND_FLAGS.FIN) == QUIC_SEND_FLAGS.FIN) { // Start graceful shutdown sequence if passed in the fin flag and there is an empty buffer. - StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.GRACEFUL, errorCode: 0); } return default; } @@ -777,14 +777,14 @@ private unsafe ValueTask SendReadOnlyMemoryAsync( private unsafe ValueTask SendReadOnlySequenceAsync( ReadOnlySequence buffers, - QUIC_SEND_FLAG flags) + QUIC_SEND_FLAGS flags) { if (buffers.IsEmpty) { - if ((flags & QUIC_SEND_FLAG.FIN) == QUIC_SEND_FLAG.FIN) + if ((flags & QUIC_SEND_FLAGS.FIN) == QUIC_SEND_FLAGS.FIN) { // Start graceful shutdown sequence if passed in the fin flag and there is an empty buffer. - StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.GRACEFUL, errorCode: 0); } return default; } @@ -838,14 +838,14 @@ private unsafe ValueTask SendReadOnlySequenceAsync( private unsafe ValueTask SendReadOnlyMemoryListAsync( ReadOnlyMemory> buffers, - QUIC_SEND_FLAG flags) + QUIC_SEND_FLAGS flags) { if (buffers.IsEmpty) { - if ((flags & QUIC_SEND_FLAG.FIN) == QUIC_SEND_FLAG.FIN) + if ((flags & QUIC_SEND_FLAGS.FIN) == QUIC_SEND_FLAGS.FIN) { // Start graceful shutdown sequence if passed in the fin flag and there is an empty buffer. - StartShutdown(QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.GRACEFUL, errorCode: 0); } return default; } From b92bd1d95baa63bfdf1aeb810a612975331807b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= Date: Fri, 12 Mar 2021 17:57:44 +0100 Subject: [PATCH 03/12] Enums updated --- .../MsQuic/Interop/MsQuicEnums.cs | 151 ++++++++---------- .../MsQuic/MsQuicConnection.cs | 14 +- .../Implementations/MsQuic/MsQuicStream.cs | 20 +-- 3 files changed, 88 insertions(+), 97 deletions(-) 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 1b00abb5305c7e..1eac84d8d6397c 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 @@ -8,7 +8,7 @@ internal enum QUIC_EXECUTION_PROFILE : uint QUIC_EXECUTION_PROFILE_LOW_LATENCY, // Default QUIC_EXECUTION_PROFILE_TYPE_MAX_THROUGHPUT, QUIC_EXECUTION_PROFILE_TYPE_SCAVENGER, - QUIC_EXECUTION_PROFILE_TYPE_REAL_TIME + QUIC_EXECUTION_PROFILE_TYPE_REAL_TIME, } internal enum QUIC_CREDENTIAL_TYPE : uint @@ -19,7 +19,7 @@ internal enum QUIC_CREDENTIAL_TYPE : uint CONTEXT, FILE, FILE_PROTECTED, - STUB_NULL = 0xF0000000 // Pass as server cert to stubtls implementation + STUB_NULL = 0xF0000000, // Pass as server cert to stubtls implementation. } [Flags] @@ -29,71 +29,73 @@ internal enum QUIC_CREDENTIAL_FLAGS : uint CLIENT = 0x00000001, // Lack of client flag indicates server. LOAD_ASYNCHRONOUS = 0x00000002, NO_CERTIFICATE_VALIDATION = 0x00000004, - ENABLE_OCSP = 0x00000008, // Schannel only currently + ENABLE_OCSP = 0x00000008, // Schannel only currently. INDICATE_CERTIFICATE_RECEIVED = 0x00000010, - DEFER_CERTIFICATE_VALIDATION = 0x00000020, // Schannel only currently - REQUIRE_CLIENT_AUTHENTICATION = 0x00000040, // Schannel only currently - USE_TLS_BUILTIN_CERTIFICATE_VALIDATION = 0x00000080 + DEFER_CERTIFICATE_VALIDATION = 0x00000020, // Schannel only currently. + REQUIRE_CLIENT_AUTHENTICATION = 0x00000040, // Schannel only currently. + USE_TLS_BUILTIN_CERTIFICATE_VALIDATION = 0x00000080, } [Flags] internal enum QUIC_CONNECTION_SHUTDOWN_FLAGS : uint { - NONE = 0x0, - SILENT = 0x1 + NONE = 0x0000, + SILENT = 0x0001, // Don't send the close frame over the network. } internal enum QUIC_SERVER_RESUMPTION_LEVEL : uint { NO_RESUME, RESUME_ONLY, - RESUME_AND_ZERORTT + RESUME_AND_ZERORTT, } [Flags] internal enum QUIC_STREAM_OPEN_FLAGS : uint { - NONE = 0, - UNIDIRECTIONAL = 0x1, - ZERO_RTT = 0x2, + NONE = 0x0000, + UNIDIRECTIONAL = 0x0001, // Indicates the stream is unidirectional. + ZERO_RTT = 0x0002, // The stream was opened via a 0-RTT packet. } [Flags] internal enum QUIC_STREAM_START_FLAGS : uint { - NONE = 0, - FAIL_BLOCKED = 0x1, - IMMEDIATE = 0x2, - ASYNC = 0x4, + 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. } [Flags] internal enum QUIC_STREAM_SHUTDOWN_FLAGS : uint { - NONE = 0, - GRACEFUL = 0x1, - ABORT_SEND = 0x2, - ABORT_RECV = 0x4, - ABORT = ABORT_SEND | ABORT_RECV, - IMMEDIATE = 0x8 + NONE = 0x0000, + GRACEFUL = 0x0001, // Cleanly closes the send path. + ABORT_SEND = 0x0002, // Abruptly closes the send path. + ABORT_RECEIVE = 0x0004, // Abruptly closes the receive path. + ABORT = 0x0006, // Abruptly closes both send and receive paths. + IMMEDIATE = 0x0008, // Immediately sends completion events to app. } [Flags] internal enum QUIC_RECEIVE_FLAGS : uint { - NONE = 0, - ZERO_RTT = 0x1, - FIN = 0x02 + NONE = 0x0000, + ZERO_RTT = 0x0001, // Data was encrypted with 0-RTT key. + FIN = 0x0002, // FIN was included with this data. } [Flags] internal enum QUIC_SEND_FLAGS : uint { - NONE = 0, - ALLOW_0_RTT = 0x00000001, - START = 0x00000002, - FIN = 0x00000004, - DGRAM_PRIORITY = 0x00000008 + NONE = 0x0000, + ALLOW_0_RTT = 0x0001, // Allows the use of encrypting with 0-RTT key. + START = 0x0002, // Asynchronously starts the stream with the sent data. + FIN = 0x0004, // Indicates the request is the one last sent on the stream. + DGRAM_PRIORITY = 0x0008, // Indicates the datagram is higher priority than others. + DELAY_SEND = 0x0010, // Indicates the send should be delayed because more will be queued soon. } internal enum QUIC_PARAM_LEVEL : uint @@ -104,81 +106,69 @@ internal enum QUIC_PARAM_LEVEL : uint LISTENER, CONNECTION, TLS, - STREAM + STREAM, } internal enum QUIC_PARAM_GLOBAL : uint { - RETRY_MEMORY_PERCENT, - SUPPORTED_VERSIONS, - LOAD_BALANCING_MODE, - PERF_COUNTERS, - SETTINGS + 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 } internal enum QUIC_PARAM_REGISTRATION : uint { - CID_PREFIX = 0 - } - - internal enum QUIC_PARAM_SESSION : uint - { - TLS_TICKET_KEY = 0, - PEER_BIDI_STREAM_COUNT = 1, - PEER_UNIDI_STREAM_COUNT = 2, - IDLE_TIMEOUT = 3, - DISCONNECT_TIMEOUT = 4, - MAX_BYTES_PER_KEY = 5, - MIGRATION_ENABLED = 6, - DATAGRAM_RECEIVE_ENABLED = 7, - SERVER_RESUMPTION_LEVEL = 8 + CID_PREFIX = 0, // uint8_t[] } internal enum QUIC_PARAM_LISTENER : uint { - LOCAL_ADDRESS = 0, - STATS = 1 + LOCAL_ADDRESS = 0, // QUIC_ADDR + STATS = 1, // QUIC_LISTENER_STATISTICS } internal enum QUIC_PARAM_CONN : uint { - QUIC_VERSION, - LOCAL_ADDRESS, - REMOTE_ADDRESS, - IDEAL_PROCESSOR, - SETTINGS, - STATISTICS, - STATISTICS_PLAT, - SHARE_UDP_BINDING, - LOCAL_BIDI_STREAM_COUNT, - LOCAL_UNIDI_STREAM_COUNT, - MAX_STREAM_IDS, - CLOSE_REASON_PHRASE, - STREAM_SCHEDULING_SCHEME, - DATAGRAM_RECEIVE_ENABLED, - DATAGRAM_SEND_ENABLED, - DISABLE_1RTT_ENCRYPTION, - RESUMPTION_TICKET + 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) } internal enum QUIC_PARAM_STREAM : uint { - ID = 0, - ZERORTT_LENGTH = 1, - IDEAL_SEND_BUFFER = 2 + ID = 0, // QUIC_UINT62 + ZERRTT_LENGTH = 1, // uint64_t + IDEAL_SEND_BUFFER_SIZE = 2, // uint64_t - bytes } internal enum QUIC_LISTENER_EVENT : uint { - NEW_CONNECTION = 0 + NEW_CONNECTION = 0, } - internal enum QUIC_CONNECTION_EVENT : uint + internal enum QUIC_CONNECTION_EVENT_TYPE : uint { CONNECTED = 0, - SHUTDOWN_INITIATED_BY_TRANSPORT = 1, - SHUTDOWN_INITIATED_BY_PEER = 2, - SHUTDOWN_COMPLETE = 3, + SHUTDOWN_INITIATED_BY_TRANSPORT = 1, // The transport started the shutdown process. + SHUTDOWN_INITIATED_BY_PEER = 2, // The peer application started the shutdown process. + SHUTDOWN_COMPLETE = 3, // Ready for the handle to be closed. LOCAL_ADDRESS_CHANGED = 4, PEER_ADDRESS_CHANGED = 5, PEER_STREAM_STARTED = 6, @@ -188,11 +178,12 @@ internal enum QUIC_CONNECTION_EVENT : uint DATAGRAM_STATE_CHANGED = 10, DATAGRAM_RECEIVED = 11, DATAGRAM_SEND_STATE_CHANGED = 12, - RESUMED = 13, - RESUMPTION_TICKET_RECEIVED = 14 + RESUMED = 13, // Server-only; provides resumption data, if any. + RESUMPTION_TICKET_RECEIVED = 14, // Client-only; provides ticket to persist, if any. + PEER_CERTIFICATE_RECEIVED = 15, // Only with QUIC_CREDENTIAL_FLAG_INDICATE_CERTIFICATE_RECEIVED set. } - internal enum QUIC_STREAM_EVENT : uint + internal enum QUIC_STREAM_EVENT_TYPE : uint { START_COMPLETE = 0, RECEIVE = 1, 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 e26dac45687c91..815780f3e055cb 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 @@ -315,19 +315,19 @@ private static uint NativeCallbackHandler( try { - switch ((QUIC_CONNECTION_EVENT)connectionEvent.Type) + switch ((QUIC_CONNECTION_EVENT_TYPE)connectionEvent.Type) { - case QUIC_CONNECTION_EVENT.CONNECTED: + case QUIC_CONNECTION_EVENT_TYPE.CONNECTED: return HandleEventConnected(state, ref connectionEvent); - case QUIC_CONNECTION_EVENT.SHUTDOWN_INITIATED_BY_TRANSPORT: + case QUIC_CONNECTION_EVENT_TYPE.SHUTDOWN_INITIATED_BY_TRANSPORT: return HandleEventShutdownInitiatedByTransport(state, ref connectionEvent); - case QUIC_CONNECTION_EVENT.SHUTDOWN_INITIATED_BY_PEER: + case QUIC_CONNECTION_EVENT_TYPE.SHUTDOWN_INITIATED_BY_PEER: return HandleEventShutdownInitiatedByPeer(state, ref connectionEvent); - case QUIC_CONNECTION_EVENT.SHUTDOWN_COMPLETE: + case QUIC_CONNECTION_EVENT_TYPE.SHUTDOWN_COMPLETE: return HandleEventShutdownComplete(state, ref connectionEvent); - case QUIC_CONNECTION_EVENT.PEER_STREAM_STARTED: + case QUIC_CONNECTION_EVENT_TYPE.PEER_STREAM_STARTED: return HandleEventNewStream(state, ref connectionEvent); - case QUIC_CONNECTION_EVENT.STREAMS_AVAILABLE: + case QUIC_CONNECTION_EVENT_TYPE.STREAMS_AVAILABLE: return HandleEventStreamsAvailable(state, ref connectionEvent); default: return MsQuicStatusCodes.Success; 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 f096fa2d88c349..32b87fb2731a15 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 @@ -338,7 +338,7 @@ internal override void AbortRead(long errorCode) _state.ReadState = ReadState.Aborted; } - StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_RECV, errorCode); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_RECEIVE, errorCode); } internal override void AbortWrite(long errorCode) @@ -483,35 +483,35 @@ private static uint HandleEvent(State state, ref StreamEvent evt) { try { - switch ((QUIC_STREAM_EVENT)evt.Type) + switch ((QUIC_STREAM_EVENT_TYPE)evt.Type) { // Stream has started. // Will only be done for outbound streams (inbound streams have already started) - case QUIC_STREAM_EVENT.START_COMPLETE: + case QUIC_STREAM_EVENT_TYPE.START_COMPLETE: return HandleStartComplete(state); // Received data on the stream - case QUIC_STREAM_EVENT.RECEIVE: + case QUIC_STREAM_EVENT_TYPE.RECEIVE: return HandleEventRecv(state, ref evt); // Send has completed. // Contains a canceled bool to indicate if the send was canceled. - case QUIC_STREAM_EVENT.SEND_COMPLETE: + case QUIC_STREAM_EVENT_TYPE.SEND_COMPLETE: return HandleEventSendComplete(state, ref evt); // Peer has told us to shutdown the reading side of the stream. - case QUIC_STREAM_EVENT.PEER_SEND_SHUTDOWN: + case QUIC_STREAM_EVENT_TYPE.PEER_SEND_SHUTDOWN: return HandleEventPeerSendShutdown(state); // Peer has told us to abort the reading side of the stream. - case QUIC_STREAM_EVENT.PEER_SEND_ABORTED: + case QUIC_STREAM_EVENT_TYPE.PEER_SEND_ABORTED: return HandleEventPeerSendAborted(state, ref evt); // Peer has stopped receiving data, don't send anymore. - case QUIC_STREAM_EVENT.PEER_RECEIVE_ABORTED: + case QUIC_STREAM_EVENT_TYPE.PEER_RECEIVE_ABORTED: return HandleEventPeerRecvAborted(state, ref evt); // Occurs when shutdown is completed for the send side. // This only happens for shutdown on sending, not receiving // Receive shutdown can only be abortive. - case QUIC_STREAM_EVENT.SEND_SHUTDOWN_COMPLETE: + case QUIC_STREAM_EVENT_TYPE.SEND_SHUTDOWN_COMPLETE: return HandleEventSendShutdownComplete(state, ref evt); // Shutdown for both sending and receiving is completed. - case QUIC_STREAM_EVENT.SHUTDOWN_COMPLETE: + case QUIC_STREAM_EVENT_TYPE.SHUTDOWN_COMPLETE: return HandleEventShutdownComplete(state); default: return MsQuicStatusCodes.Success; From 7aa745c41febfd8fbdf9377d216faaefd2511f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= Date: Sat, 13 Mar 2021 23:56:20 +0100 Subject: [PATCH 04/12] Updated interop, compilable project. --- .../MsQuic/Internal/MsQuicApi.cs | 12 +- .../MsQuic/Internal/MsQuicParameterHelpers.cs | 10 +- .../MsQuic/Interop/MsQuicNativeMethods.cs | 293 +++++++++++------- .../Interop/SafeMsQuicConfigurationHandle.cs | 22 +- .../Interop/SafeMsQuicConnectionHandle.cs | 6 +- .../Interop/SafeMsQuicRegistrationHandle.cs | 6 +- .../MsQuic/MsQuicConnection.cs | 14 +- .../Implementations/MsQuic/MsQuicListener.cs | 6 +- .../Implementations/MsQuic/MsQuicStream.cs | 28 +- 9 files changed, 223 insertions(+), 174 deletions(-) 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 3574c19fc2ff17..e1a0a105128891 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 @@ -119,16 +119,9 @@ private unsafe MsQuicApi() Marshal.GetDelegateForFunctionPointer( vtable->StreamReceiveSetEnabled); - byte* appName = stackalloc byte[5]; - appName[0] = (byte)'.'; - appName[1] = (byte)'N'; - appName[2] = (byte)'E'; - appName[3] = (byte)'T'; - appName[4] = 0; - var cfg = new RegistrationConfig { - AppName = appName, + AppName = ".NET", ExecutionProfile = (uint)QUIC_EXECUTION_PROFILE.QUIC_EXECUTION_PROFILE_LOW_LATENCY }; @@ -177,11 +170,12 @@ private unsafe MsQuicApi() internal MsQuicNativeMethods.ListenerStartDelegate ListenerStartDelegate { get; } internal MsQuicNativeMethods.ListenerStopDelegate ListenerStopDelegate { get; } + // Mana: missing SendResumptionTicket internal MsQuicNativeMethods.ConnectionOpenDelegate ConnectionOpenDelegate { get; } internal MsQuicNativeMethods.ConnectionCloseDelegate ConnectionCloseDelegate { get; } - internal MsQuicNativeMethods.ConnectionSetConfigurationDelegate ConnectionSetConfigurationDelegate { get; } internal MsQuicNativeMethods.ConnectionShutdownDelegate ConnectionShutdownDelegate { get; } internal MsQuicNativeMethods.ConnectionStartDelegate ConnectionStartDelegate { get; } + internal MsQuicNativeMethods.ConnectionSetConfigurationDelegate ConnectionSetConfigurationDelegate { get; } internal MsQuicNativeMethods.StreamOpenDelegate StreamOpenDelegate { get; } internal MsQuicNativeMethods.StreamCloseDelegate StreamCloseDelegate { get; } 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 8697c0658f4813..63672d2a6e175a 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 @@ -9,7 +9,7 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal { internal static class MsQuicParameterHelpers { - internal static unsafe SOCKADDR_INET GetINetParam(MsQuicApi api, SafeHandle nativeObject, uint level, uint param) + internal static unsafe SOCKADDR_INET GetINetParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param) { SOCKADDR_INET value; uint valueLen = (uint)sizeof(SOCKADDR_INET); @@ -21,7 +21,7 @@ internal static unsafe SOCKADDR_INET GetINetParam(MsQuicApi api, SafeHandle nati return value; } - internal static unsafe ushort GetUShortParam(MsQuicApi api, SafeHandle nativeObject, uint level, uint param) + internal static unsafe ushort GetUShortParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param) { ushort value; uint valueLen = (uint)sizeof(ushort); @@ -33,14 +33,14 @@ internal static unsafe ushort GetUShortParam(MsQuicApi api, SafeHandle nativeObj return value; } - internal static unsafe void SetUshortParam(MsQuicApi api, SafeHandle nativeObject, uint level, uint param, ushort value) + internal static unsafe void SetUShortParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param, ushort value) { QuicExceptionHelpers.ThrowIfFailed( api.SetParamDelegate(nativeObject, level, param, sizeof(ushort), (byte*)&value), "Could not set ushort."); } - internal static unsafe ulong GetULongParam(MsQuicApi api, SafeHandle nativeObject, uint level, uint param) + internal static unsafe ulong GetULongParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param) { ulong value; uint valueLen = (uint)sizeof(ulong); @@ -52,7 +52,7 @@ internal static unsafe ulong GetULongParam(MsQuicApi api, SafeHandle nativeObjec return value; } - internal static unsafe void SetULongParam(MsQuicApi api, SafeHandle nativeObject, uint level, uint param, ulong value) + internal static unsafe void SetULongParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param, ulong value) { QuicExceptionHelpers.ThrowIfFailed( api.SetParamDelegate(nativeObject, level, param, sizeof(ulong), (byte*)&value), 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 298c993a9cdc19..a46d1c89ec80c3 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 @@ -51,6 +51,15 @@ internal struct NativeApi internal IntPtr DatagramSend; } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate uint SetContextDelegate( + SafeHandle handle, + IntPtr context); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate IntPtr GetContextDelegate( + SafeHandle handle); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void SetCallbackHandlerDelegate( SafeHandle handle, @@ -60,7 +69,7 @@ internal delegate void SetCallbackHandlerDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint SetParamDelegate( SafeHandle handle, - uint level, + QUIC_PARAM_LEVEL level, uint param, uint bufferLength, byte* buffer); @@ -68,7 +77,7 @@ internal delegate uint SetParamDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint GetParamDelegate( SafeHandle handle, - uint level, + QUIC_PARAM_LEVEL level, uint param, ref uint bufferLength, byte* buffer); @@ -79,21 +88,23 @@ internal delegate uint RegistrationOpenDelegate( out SafeMsQuicRegistrationHandle registrationContext); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void RegistrationCloseDelegate(IntPtr registrationContext); + internal delegate void RegistrationCloseDelegate( + IntPtr registrationContext); [StructLayout(LayoutKind.Sequential)] internal struct RegistrationConfig { - internal byte* AppName; - internal uint ExecutionProfile; + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string AppName; + internal QUIC_EXECUTION_PROFILE ExecutionProfile; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ConfigurationOpenDelegate( SafeMsQuicRegistrationHandle registrationContext, - ref QuicBuffer alpnBuffers, + QuicBuffer* alpnBuffers, uint alpnBufferCount, - ref Settings settings, + ref QuicSettings settings, uint settingsSize, IntPtr context, out SafeMsQuicConfigurationHandle configuration); @@ -105,12 +116,103 @@ internal delegate void ConfigurationCloseDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ConfigurationLoadCredentialDelegate( SafeMsQuicConfigurationHandle configuration, - CredentialConfig *credConfig); + ref CredentialConfig credConfig); + + [StructLayout(LayoutKind.Sequential)] + internal struct QuicSettings + { + internal QuicSettingsIsSetFlags IsSetFlags; + internal ulong MaxBytesPerKey; + internal ulong HandshakeIdleTimeoutMs; + internal ulong IdleTimeoutMs; + internal uint TlsClientMaxSendBuffer; + internal uint TlsServerMaxSendBuffer; + internal uint StreamRecvWindowDefault; + internal uint StreamRecvBufferDefault; + internal uint ConnFlowControlWindow; + internal uint MaxWorkerQueueDelayUs; + internal uint MaxStatelessOperations; + internal uint InitialWindowPackets; + internal uint SendIdleTimeoutMs; + internal uint InitialRttMs; + internal uint MaxAckDelayMs; + internal uint DisconnectTimeoutMs; + internal uint KeepAliveIntervalMs; + internal ushort PeerBidiStreamCount; + internal ushort PeerUnidiStreamCount; + internal ushort RetryMemoryLimit; // Global only + internal ushort LoadBalancingMode; // Global only + internal byte MaxOperationsPerDrain; + internal QuicSettingsEnabledFlagsFlags EnabledFlags; + internal uint* DesiredVersionsList; + internal uint DesiredVersionsListLength; + } + + [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, + } + + [Flags] + internal enum QuicSettingsEnabledFlagsFlags : byte + { + SendBufferingEnabled = 1 << 0, + PacingEnabled = 1 << 1, + MigrationEnabled = 1 << 2, + DatagramReceiveEnabled = 1 << 3, + // Contains values of QUIC_SERVER_RESUMPTION_LEVEL + ServerResumptionLevel = 1 << 4 | 1 << 5, + VersionNegotiationExtEnabled = 1 << 6, + } + + // Mana: get back to this, define delegate for AsyncHandler, define types fro IntPtr Certificate (union in C, plat dependent) and + // make proper use of it. + [StructLayout(LayoutKind.Sequential)] + internal struct CredentialConfig + { + internal QUIC_CREDENTIAL_TYPE Type; + internal QUIC_CREDENTIAL_FLAGS Flags; + // Mana: define struct for the union in C + internal IntPtr Certificate; + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string Principal; + internal IntPtr Reserved; // Currently unused + // Mana: define delegate for the async callback + internal IntPtr AsyncHandler; + } [StructLayout(LayoutKind.Sequential)] internal struct ListenerEvent { - internal uint Type; + internal QUIC_LISTENER_EVENT Type; internal ListenerEventDataUnion Data; } @@ -124,24 +226,30 @@ internal struct ListenerEventDataUnion [StructLayout(LayoutKind.Sequential)] internal struct ListenerEventDataNewConnection { + // Mana: struct QUIC_NEW_CONNECTION_INFO / NewConnectionInfo internal IntPtr Info; internal IntPtr Connection; - internal IntPtr SecurityConfig; } [StructLayout(LayoutKind.Sequential)] internal struct NewConnectionInfo { internal uint QuicVersion; + // QUIC_ADDR internal IntPtr LocalAddress; + // QUIC_ADDR internal IntPtr RemoteAddress; internal uint CryptoBufferLength; - internal ushort AlpnListLength; + internal ushort ClientAlpnListLength; internal ushort ServerNameLength; internal byte NegotiatedAlpnLength; + // byte[] internal IntPtr CryptoBuffer; + // byte[] internal IntPtr ClientAlpnList; + // byte[] internal IntPtr NegotiatedAlpn; + // string internal IntPtr ServerName; } @@ -165,7 +273,7 @@ internal delegate void ListenerCloseDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ListenerStartDelegate( SafeMsQuicListenerHandle listener, - ref QuicBuffer alpnBuffers, + QuicBuffer* alpnBuffers, uint alpnBufferCount, ref SOCKADDR_INET localAddress); @@ -178,6 +286,7 @@ internal struct ConnectionEventDataConnected { internal bool SessionResumed; internal byte NegotiatedAlpnLength; + // byte[] internal IntPtr NegotiatedAlpn; } @@ -190,32 +299,43 @@ internal struct ConnectionEventDataShutdownInitiatedByTransport [StructLayout(LayoutKind.Sequential)] internal struct ConnectionEventDataShutdownInitiatedByPeer { - internal long ErrorCode; + internal ulong ErrorCode; } + // Mana: it's a trap, those are flags with fixed sized exactly 3 bits!!! [StructLayout(LayoutKind.Sequential)] internal struct ConnectionEventDataShutdownComplete { - internal bool TimedOut; + internal ConnectionEventDataShutdownCompleteFlags Flags; + } + + [Flags] + internal enum ConnectionEventDataShutdownCompleteFlags : byte + { + HandshakeCompleted = 1 << 0, + PeerAcknowledgedShutdown = 1 << 1, + AppCloseInProgress = 1 << 2, } [StructLayout(LayoutKind.Sequential)] - internal struct ConnectionEventDataLocalAddrChanged + internal struct ConnectionEventDataLocalAddressChanged { + // QUIC_ADDR internal IntPtr Address; } [StructLayout(LayoutKind.Sequential)] - internal struct ConnectionEventDataPeerAddrChanged + internal struct ConnectionEventDataPeerAddressChanged { + // QUIC_ADDR internal IntPtr Address; } [StructLayout(LayoutKind.Sequential)] - internal struct ConnectionEventDataStreamStarted + internal struct ConnectionEventDataPeerStreamStarted { internal IntPtr Stream; - internal uint Flags; + internal QUIC_STREAM_OPEN_FLAGS Flags; } [StructLayout(LayoutKind.Sequential)] @@ -241,32 +361,25 @@ internal struct ConnectionEventDataUnion internal ConnectionEventDataShutdownComplete ShutdownComplete; [FieldOffset(0)] - internal ConnectionEventDataLocalAddrChanged LocalAddrChanged; + internal ConnectionEventDataLocalAddressChanged LocalAddressChanged; [FieldOffset(0)] - internal ConnectionEventDataPeerAddrChanged PeerAddrChanged; + internal ConnectionEventDataPeerAddressChanged PeerAddressChanged; [FieldOffset(0)] - internal ConnectionEventDataStreamStarted StreamStarted; + internal ConnectionEventDataPeerStreamStarted PeerStreamStarted; [FieldOffset(0)] internal ConnectionEventDataStreamsAvailable StreamsAvailable; + + // Mana: missing IDEAL_PROCESSOR_CHANGED, ..., PEER_CERTIFICATE_RECEIVED (7 total) } [StructLayout(LayoutKind.Sequential)] internal struct ConnectionEvent { - internal uint Type; + internal QUIC_CONNECTION_EVENT_TYPE Type; internal ConnectionEventDataUnion Data; - - internal bool EarlyDataAccepted => Data.Connected.SessionResumed; - //internal ulong NumBytes => Data.IdealSendBuffer.NumBytes; - internal uint ShutdownBeginStatus => Data.ShutdownInitiatedByTransport.Status; - internal long ShutdownBeginPeerStatus => Data.ShutdownInitiatedByPeer.ErrorCode; - internal bool ShutdownTimedOut => Data.ShutdownComplete.TimedOut; - internal ushort BiDirectionalCount => Data.StreamsAvailable.BiDirectionalCount; - internal ushort UniDirectionalCount => Data.StreamsAvailable.UniDirectionalCount; - internal QUIC_STREAM_OPEN_FLAGS StreamFlags => (QUIC_STREAM_OPEN_FLAGS)Data.StreamStarted.Flags; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -275,6 +388,7 @@ internal delegate uint ConnectionCallbackDelegate( IntPtr context, ref ConnectionEvent connectionEvent); + // Mana: order is Open, Close, Shutdown, Start, SetConfiguration, SendResumptionTicket [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ConnectionOpenDelegate( SafeMsQuicRegistrationHandle registration, @@ -295,6 +409,7 @@ internal delegate uint ConnectionSetConfigurationDelegate( internal delegate uint ConnectionStartDelegate( SafeMsQuicConnectionHandle connection, SafeMsQuicConfigurationHandle configuration, + // QUIC_ADDRESS_FAMILY = sa_family_t = ushort ushort family, [MarshalAs(UnmanagedType.LPUTF8Str)] string serverName, @@ -303,17 +418,18 @@ internal delegate uint ConnectionStartDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void ConnectionShutdownDelegate( SafeMsQuicConnectionHandle connection, - uint flags, + QUIC_CONNECTION_SHUTDOWN_FLAGS flags, long errorCode); + // Mana: missing SendResumptionTicket [StructLayout(LayoutKind.Sequential)] - internal struct StreamEventDataRecv + internal struct StreamEventDataReceive { internal ulong AbsoluteOffset; internal ulong TotalBufferLength; internal QuicBuffer* Buffers; internal uint BufferCount; - internal uint Flags; + internal QUIC_RECEIVE_FLAGS Flags; } [StructLayout(LayoutKind.Sequential)] @@ -324,49 +440,53 @@ internal struct StreamEventDataSendComplete } [StructLayout(LayoutKind.Sequential)] - internal struct StreamEventDataPeerSendAbort + internal struct StreamEventDataPeerSendAborted { - internal long ErrorCode; + internal ulong ErrorCode; } [StructLayout(LayoutKind.Sequential)] - internal struct StreamEventDataPeerRecvAbort + internal struct StreamEventDataPeerReceiveAborted { - internal long ErrorCode; + internal ulong ErrorCode; } [StructLayout(LayoutKind.Sequential)] internal struct StreamEventDataSendShutdownComplete { - internal byte Graceful; + internal bool Graceful; } [StructLayout(LayoutKind.Explicit)] internal struct StreamEventDataUnion { + //Mana: missing START_COMPLETE [FieldOffset(0)] - internal StreamEventDataRecv Recv; + internal StreamEventDataReceive Receive; [FieldOffset(0)] internal StreamEventDataSendComplete SendComplete; [FieldOffset(0)] - internal StreamEventDataPeerSendAbort PeerSendAbort; + internal StreamEventDataPeerSendAborted PeerSendAborted; [FieldOffset(0)] - internal StreamEventDataPeerRecvAbort PeerRecvAbort; + internal StreamEventDataPeerReceiveAborted PeerReceiveAborted; [FieldOffset(0)] internal StreamEventDataSendShutdownComplete SendShutdownComplete; + + // Mana: missing IDEAL_SEND_BUFFER_SIZE } [StructLayout(LayoutKind.Sequential)] internal struct StreamEvent { - internal uint Type; + internal QUIC_STREAM_EVENT_TYPE Type; internal StreamEventDataUnion Data; } + // Mana: rename to C#-like [StructLayout(LayoutKind.Sequential)] internal struct SOCKADDR_IN { @@ -386,6 +506,7 @@ internal byte[] Address } } + // Mana: rename to C#-like [StructLayout(LayoutKind.Sequential)] internal struct SOCKADDR_IN6 { @@ -423,6 +544,7 @@ internal byte[] Address } } + // Mana: why charset? rename to something C#-like, including fields. [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)] internal struct SOCKADDR_INET { @@ -443,7 +565,7 @@ internal delegate uint StreamCallbackDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamOpenDelegate( SafeMsQuicConnectionHandle connection, - uint flags, + QUIC_STREAM_OPEN_FLAGS flags, StreamCallbackDelegate handler, IntPtr context, out SafeMsQuicStreamHandle stream); @@ -451,7 +573,7 @@ internal delegate uint StreamOpenDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamStartDelegate( SafeMsQuicStreamHandle stream, - uint flags); + QUIC_STREAM_START_FLAGS flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void StreamCloseDelegate( @@ -460,15 +582,15 @@ internal delegate void StreamCloseDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamShutdownDelegate( SafeMsQuicStreamHandle stream, - uint flags, - long errorCode); + QUIC_STREAM_SHUTDOWN_FLAGS flags, + ulong errorCode); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamSendDelegate( SafeMsQuicStreamHandle stream, - QuicBuffer *buffers, + QuicBuffer* buffers, uint bufferCount, - uint flags, + QUIC_SEND_FLAGS flags, IntPtr clientSendContext); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -479,84 +601,17 @@ internal delegate uint StreamReceiveCompleteDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamReceiveSetEnabledDelegate( SafeMsQuicStreamHandle stream, + // Mana: do I need this? Should I add it over all booleans? [MarshalAs(UnmanagedType.U1)] bool enabled); [StructLayout(LayoutKind.Sequential)] - internal unsafe struct QuicBuffer + internal struct QuicBuffer { internal uint Length; internal byte* Buffer; } - [StructLayout(LayoutKind.Sequential)] - internal struct CredentialConfig - { - internal uint Type; - internal uint Flags; - internal IntPtr Certificate; // PCERT_CONTEXT (SCHANNEL) - internal IntPtr Principal; - internal IntPtr TicketKey; // optional. - internal IntPtr AsyncHandler; // optional, must set LOAD_ASYNCHRONOUS flag. - } - - [StructLayout(LayoutKind.Sequential)] - internal struct Settings - { - internal ulong IsSetFlags; - internal ulong MaxBytesPerKey; - internal ulong HandshakeIdleTimeoutMs; - internal ulong IdleTimeoutMs; - internal uint TlsClientMaxSendBuffer; - internal uint TlsServerMaxSendBuffer; - internal uint StreamRecvWindowDefault; - internal uint StreamRecvBufferDefault; - internal uint ConnFlowControlWindow; - internal uint MaxWorkerQueueDelayUs; - internal uint MaxStatelessOperations; - internal uint InitialWindowPackets; - internal uint SendIdleTimeoutMs; - internal uint InitialRttMs; - internal uint MaxAckDelayMs; - internal uint DisconnectTimeoutMs; - internal uint KeepAliveIntervalMs; - internal ushort PeerBidiStreamCount; - internal ushort PeerUnidiStreamCount; - internal ushort RetryMemoryLimit; // Global only - internal ushort LoadBalancingMode; // Global only - internal byte MaxOperationsPerDrain; - internal byte BitField; - } - - [Flags] - internal enum SettingsFlags : ulong - { - MaxBytesPerKey = 1, - 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 - } + // Mana: DatagramSend missing } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs index 18ab5e3f5687c4..e29cfb965f320f 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs @@ -26,6 +26,7 @@ protected override bool ReleaseHandle() return true; } + // Mana: Move the code from here. Inconsistency with other handles. public static unsafe SafeMsQuicConfigurationHandle Create(QuicClientConnectionOptions options) { // TODO: lots of ClientAuthenticationOptions are not yet supported by MsQuic. @@ -58,11 +59,10 @@ private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, Debug.Assert(!MsQuicApi.Api.Registration.IsInvalid); - var settings = new Settings + var settings = new QuicSettings { - IsSetFlags = - (ulong)SettingsFlags.PeerBidiStreamCount - | (ulong)SettingsFlags.PeerUnidiStreamCount, + IsSetFlags = QuicSettingsIsSetFlags.PeerBidiStreamCount | + QuicSettingsIsSetFlags.PeerUnidiStreamCount, PeerBidiStreamCount = (ushort)options.MaxBidirectionalStreams, PeerUnidiStreamCount = (ushort)options.MaxUnidirectionalStreams }; @@ -74,7 +74,7 @@ private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, ulong ms = (ulong)options.IdleTimeout.Ticks / TimeSpan.TicksPerMillisecond; if (ms > (1ul << 62) - 1) throw new Exception("IdleTimeout is too large (max 2^62-1 milliseconds)"); - settings.IsSetFlags |= (ulong)SettingsFlags.IdleTimeoutMs; + settings.IsSetFlags |= QuicSettingsIsSetFlags.IdleTimeoutMs; settings.IdleTimeoutMs = (ulong)options.IdleTimeout.TotalMilliseconds; } @@ -86,7 +86,7 @@ private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, try { MsQuicAlpnHelper.Prepare(alpnProtocols, out handles, out buffers); - status = MsQuicApi.Api.ConfigurationOpenDelegate(MsQuicApi.Api.Registration, ref MemoryMarshal.GetReference(buffers.AsSpan()), (uint)alpnProtocols.Count, ref settings, (uint)sizeof(Settings), context: IntPtr.Zero, out configurationHandle); + status = MsQuicApi.Api.ConfigurationOpenDelegate(MsQuicApi.Api.Registration, (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)alpnProtocols.Count, ref settings, (uint)sizeof(QuicSettings), context: IntPtr.Zero, out configurationHandle); } finally { @@ -102,24 +102,24 @@ private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, CredentialConfig config = default; - config.Flags = (uint)flags; // TODO: consider using LOAD_ASYNCHRONOUS with a callback. + config.Flags = flags; // TODO: consider using LOAD_ASYNCHRONOUS with a callback. if (certificate != null) { #if true // If using stub TLS. - config.Type = (uint)QUIC_CREDENTIAL_TYPE.STUB_NULL; + config.Type = QUIC_CREDENTIAL_TYPE.STUB_NULL; #else - config.Type = (uint)QUIC_CREDENTIAL_TYPE.CONTEXT; + config.Type = QUIC_CREDENTIAL_TYPE.CONTEXT; config.Certificate = certificate.Handle; #endif } else { - config.Type = (uint)QUIC_CREDENTIAL_TYPE.NONE; + config.Type = QUIC_CREDENTIAL_TYPE.NONE; } - status = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, &config); + status = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config); QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationLoadCredential failed."); } catch diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs index fa7f42c7130364..767c291150db9c 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs @@ -9,9 +9,9 @@ internal sealed class SafeMsQuicConnectionHandle : SafeHandle { public override bool IsInvalid => handle == IntPtr.Zero; - private SafeMsQuicConnectionHandle() : base(IntPtr.Zero, ownsHandle: true) - { - } + private SafeMsQuicConnectionHandle() + : base(IntPtr.Zero, ownsHandle: true) + { } public SafeMsQuicConnectionHandle(IntPtr connectionHandle) : this() { diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs index 69336aaadb9e2b..47a0599c43f0a3 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs @@ -9,9 +9,9 @@ internal sealed class SafeMsQuicRegistrationHandle : SafeHandle { public override bool IsInvalid => handle == IntPtr.Zero; - private SafeMsQuicRegistrationHandle() : base(IntPtr.Zero, ownsHandle: true) - { - } + private SafeMsQuicRegistrationHandle() + : base(IntPtr.Zero, ownsHandle: true) + { } protected override bool ReleaseHandle() { 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 815780f3e055cb..211333c8e5657f 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 @@ -121,7 +121,7 @@ private static uint HandleEventConnected(State state, ref ConnectionEvent connec { // Connected will already be true for connections accepted from a listener. - SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, state.Handle, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS); + SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS); state.LocalEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); Debug.Assert(state.Connection != null); @@ -153,7 +153,7 @@ private static uint HandleEventShutdownInitiatedByTransport(State state, ref Con private static uint HandleEventShutdownInitiatedByPeer(State state, ref ConnectionEvent connectionEvent) { - state.AbortErrorCode = connectionEvent.Data.ShutdownInitiatedByPeer.ErrorCode; + state.AbortErrorCode = (long)connectionEvent.Data.ShutdownInitiatedByPeer.ErrorCode; state.AcceptQueue.Writer.Complete(); return MsQuicStatusCodes.Success; } @@ -168,8 +168,8 @@ private static uint HandleEventShutdownComplete(State state, ref ConnectionEvent private static uint HandleEventNewStream(State state, ref ConnectionEvent connectionEvent) { - var streamHandle = new SafeMsQuicStreamHandle(connectionEvent.Data.StreamStarted.Stream); - var stream = new MsQuicStream(streamHandle, connectionEvent.StreamFlags); + var streamHandle = new SafeMsQuicStreamHandle(connectionEvent.Data.PeerStreamStarted.Stream); + var stream = new MsQuicStream(streamHandle, connectionEvent.Data.PeerStreamStarted.Flags); state.AcceptQueue.Writer.TryWrite(stream); return MsQuicStatusCodes.Success; @@ -216,12 +216,12 @@ internal override QuicStreamProvider OpenBidirectionalStream() internal override long GetRemoteAvailableUnidirectionalStreamCount() { - return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_UNIDI_STREAM_COUNT); + return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_UNIDI_STREAM_COUNT); } internal override long GetRemoteAvailableBidirectionalStreamCount() { - return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_BIDI_STREAM_COUNT); + return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_BIDI_STREAM_COUNT); } internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default) @@ -284,7 +284,7 @@ private ValueTask ShutdownAsync( { MsQuicApi.Api.ConnectionShutdownDelegate( _state.Handle, - (uint)Flags, + Flags, ErrorCode); } catch 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 c169c39cdd9f51..0e17735dc4ce45 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 @@ -129,7 +129,7 @@ internal override unsafe void Start() try { MsQuicAlpnHelper.Prepare(_applicationProtocols, out handles, out buffers); - status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, ref MemoryMarshal.GetReference(buffers.AsSpan()), (uint)_applicationProtocols.Count, ref address); + status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)_applicationProtocols.Count, ref address); } finally { @@ -138,7 +138,7 @@ internal override unsafe void Start() QuicExceptionHelpers.ThrowIfFailed(status, "ListenerStart failed."); - SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); + SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); _listenEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); } @@ -158,7 +158,7 @@ private static unsafe uint NativeCallbackHandler( IntPtr context, ref ListenerEvent evt) { - if ((QUIC_LISTENER_EVENT)evt.Type != QUIC_LISTENER_EVENT.NEW_CONNECTION) + if (evt.Type != QUIC_LISTENER_EVENT.NEW_CONNECTION) { return MsQuicStatusCodes.InternalError; } 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 32b87fb2731a15..4d6906250561cb 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 @@ -100,14 +100,14 @@ internal MsQuicStream(SafeMsQuicConnectionHandle connection, QUIC_STREAM_OPEN_FL { uint status = MsQuicApi.Api.StreamOpenDelegate( connection, - (uint)flags, + flags, s_streamDelegate, GCHandle.ToIntPtr(_stateHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to open stream to peer."); - status = MsQuicApi.Api.StreamStartDelegate(_state.Handle, (uint)QUIC_STREAM_START_FLAGS.ASYNC); + status = MsQuicApi.Api.StreamStartDelegate(_state.Handle, QUIC_STREAM_START_FLAGS.ASYNC); QuicExceptionHelpers.ThrowIfFailed(status, "Could not start stream."); } catch @@ -366,7 +366,7 @@ internal override void AbortWrite(long errorCode) private void StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS flags, long errorCode) { - uint status = MsQuicApi.Api.StreamShutdownDelegate(_state.Handle, (uint)flags, errorCode); + uint status = MsQuicApi.Api.StreamShutdownDelegate(_state.Handle, flags, (ulong)errorCode); QuicExceptionHelpers.ThrowIfFailed(status, "StreamShutdown failed."); } @@ -525,10 +525,10 @@ private static uint HandleEvent(State state, ref StreamEvent evt) private static unsafe uint HandleEventRecv(State state, ref StreamEvent evt) { - StreamEventDataRecv receieveEvent = evt.Data.Recv; - for (int i = 0; i < receieveEvent.BufferCount; i++) + StreamEventDataReceive receiveEvent = evt.Data.Receive; + for (int i = 0; i < receiveEvent.BufferCount; i++) { - state.ReceiveQuicBuffers.Add(receieveEvent.Buffers[i]); + state.ReceiveQuicBuffers.Add(receiveEvent.Buffers[i]); } bool shouldComplete = false; @@ -543,7 +543,7 @@ private static unsafe uint HandleEventRecv(State state, ref StreamEvent evt) if (shouldComplete) { - state.ReceiveResettableCompletionSource.Complete((uint)receieveEvent.TotalBufferLength); + state.ReceiveResettableCompletionSource.Complete((uint)receiveEvent.TotalBufferLength); } return MsQuicStatusCodes.Pending; @@ -559,7 +559,7 @@ private static uint HandleEventPeerRecvAborted(State state, ref StreamEvent evt) shouldComplete = true; } state.SendState = SendState.Aborted; - state.SendErrorCode = evt.Data.PeerSendAbort.ErrorCode; + state.SendErrorCode = (long)evt.Data.PeerSendAborted.ErrorCode; } if (shouldComplete) @@ -590,7 +590,7 @@ private static uint HandleStartComplete(State state) return MsQuicStatusCodes.Success; } - private static uint HandleEventSendShutdownComplete(State state, ref MsQuicNativeMethods.StreamEvent evt) + private static uint HandleEventSendShutdownComplete(State state, ref StreamEvent evt) { bool shouldComplete = false; lock (state) @@ -657,7 +657,7 @@ private static uint HandleEventPeerSendAborted(State state, ref StreamEvent evt) shouldComplete = true; } state.ReadState = ReadState.Aborted; - state.ReadErrorCode = evt.Data.PeerSendAbort.ErrorCode; + state.ReadErrorCode = (long)evt.Data.PeerSendAborted.ErrorCode; } if (shouldComplete) @@ -760,7 +760,7 @@ private unsafe ValueTask SendReadOnlyMemoryAsync( _state.Handle, quicBufferPointer, bufferCount: 1, - (uint)flags, + flags, IntPtr.Zero); if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) @@ -821,7 +821,7 @@ private unsafe ValueTask SendReadOnlySequenceAsync( _state.Handle, quicBufferPointer, count, - (uint)flags, + flags, IntPtr.Zero); if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) @@ -877,7 +877,7 @@ private unsafe ValueTask SendReadOnlyMemoryListAsync( _state.Handle, quicBufferPointer, length, - (uint)flags, + flags, IntPtr.Zero); if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) @@ -901,7 +901,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, (uint)QUIC_PARAM_LEVEL.STREAM, (uint)QUIC_PARAM_STREAM.ID); + return (long)MsQuicParameterHelpers.GetULongParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.STREAM, (uint)QUIC_PARAM_STREAM.ID); } private void ThrowIfDisposed() From 6a132c56f91b1dc1880ce3bc2277bd1ee5e9fffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= Date: Wed, 17 Mar 2021 12:29:40 +0100 Subject: [PATCH 05/12] Somewhat working update of msquic API --- .../src/System.Net.Quic.csproj | 6 +- .../Implementations/Mock/MockConnection.cs | 4 +- .../MsQuic/Interop/MsQuicEnums.cs | 6 ++ .../MsQuic/Interop/MsQuicNativeMethods.cs | 67 +++++++++++++++++-- .../Interop/SafeMsQuicConfigurationHandle.cs | 2 + .../MsQuic/MsQuicConnection.cs | 5 +- .../Implementations/MsQuic/MsQuicListener.cs | 2 +- .../Implementations/QuicConnectionProvider.cs | 2 +- .../src/System/Net/Quic/QuicConnection.cs | 2 +- .../tests/FunctionalTests/MsQuicTestBase.cs | 2 + .../FunctionalTests/QuicConnectionTests.cs | 1 - .../System.Net.Quic.Functional.Tests.csproj | 4 ++ 12 files changed, 90 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj index 1e1e84e05bff4f..6ad41a39fdf03f 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -62,11 +62,15 @@ PreserveNewest PreserveNewest + + PreserveNewest + PreserveNewest + PreserveNewest PreserveNewest - + PreserveNewest PreserveNewest diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs index 12f39ab1c00603..22e4b0137d7e3f 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs @@ -91,7 +91,9 @@ internal override bool Connected } // TODO: Should clone the endpoint since it is mutable - internal override IPEndPoint LocalEndPoint => _localEndPoint; + // TODO: could this be made back to non-nullable? + // For inbound we have it immidiatelly, for outbound after connect. + internal override IPEndPoint? LocalEndPoint => _localEndPoint; // TODO: Should clone the endpoint since it is mutable internal override EndPoint RemoteEndPoint => _remoteEndPoint!; 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 1eac84d8d6397c..26ed23b0da8716 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 @@ -36,6 +36,12 @@ internal enum QUIC_CREDENTIAL_FLAGS : uint USE_TLS_BUILTIN_CERTIFICATE_VALIDATION = 0x00000080, } + internal enum QUIC_CERTIFICATE_HASH_STORE_FLAGS + { + QUIC_CERTIFICATE_HASH_STORE_FLAG_NONE = 0x0000, + QUIC_CERTIFICATE_HASH_STORE_FLAG_MACHINE_STORE = 0x0001, + } + [Flags] internal enum QUIC_CONNECTION_SHUTDOWN_FLAGS : 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 a46d1c89ec80c3..861e0079d4c03f 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 @@ -200,7 +200,6 @@ internal struct CredentialConfig { internal QUIC_CREDENTIAL_TYPE Type; internal QUIC_CREDENTIAL_FLAGS Flags; - // Mana: define struct for the union in C internal IntPtr Certificate; [MarshalAs(UnmanagedType.LPUTF8Str)] internal string Principal; @@ -209,6 +208,67 @@ internal struct CredentialConfig internal IntPtr AsyncHandler; } + [StructLayout(LayoutKind.Explicit)] + internal struct CredentialConfigCertificateUnion + { + [FieldOffset(0)] + internal CredentialConfigCertificateCertificateHash CertificateHash; + + [FieldOffset(0)] + internal CredentialConfigCertificateCertificateHashStore CertificateHashStore; + + [FieldOffset(0)] + internal IntPtr CertificateContext; + + [FieldOffset(0)] + internal CredentialConfigCertificateCertificateFile CertificateFile; + + [FieldOffset(0)] + internal CredentialConfigCertificateCertificateFileProtected CertificateFileProtected; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct CredentialConfigCertificateCertificateHash + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + internal byte[] ShaHash; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct CredentialConfigCertificateCertificateHashStore + { + internal QUIC_CERTIFICATE_HASH_STORE_FLAGS Flags; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + internal byte[] ShaHash; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + internal char[] StoreName; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct CredentialConfigCertificateCertificateFile + { + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string PrivateKeyFile; + + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string CertificateFile; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct CredentialConfigCertificateCertificateFileProtected + { + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string PrivateKeyFile; + + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string CertificateFile; + + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string PrivateKeyPassword; + } + [StructLayout(LayoutKind.Sequential)] internal struct ListenerEvent { @@ -226,8 +286,7 @@ internal struct ListenerEventDataUnion [StructLayout(LayoutKind.Sequential)] internal struct ListenerEventDataNewConnection { - // Mana: struct QUIC_NEW_CONNECTION_INFO / NewConnectionInfo - internal IntPtr Info; + internal NewConnectionInfo* Info; internal IntPtr Connection; } @@ -545,7 +604,7 @@ internal byte[] Address } // Mana: why charset? rename to something C#-like, including fields. - [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)] + [StructLayout(LayoutKind.Explicit)] internal struct SOCKADDR_INET { [FieldOffset(0)] diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs index e29cfb965f320f..a519e59e1ab373 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs @@ -110,12 +110,14 @@ private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, // If using stub TLS. config.Type = QUIC_CREDENTIAL_TYPE.STUB_NULL; #else + // TODO: doesn't work on non-Windows config.Type = QUIC_CREDENTIAL_TYPE.CONTEXT; config.Certificate = certificate.Handle; #endif } else { + // TODO: not allowed for OpenSSL and server config.Type = QUIC_CREDENTIAL_TYPE.NONE; } 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 211333c8e5657f..e10b5ffb638c98 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 @@ -27,7 +27,7 @@ internal sealed class MsQuicConnection : QuicConnectionProvider private GCHandle _stateHandle; private bool _disposed; - private IPEndPoint? _localEndPoint; + private readonly IPEndPoint? _localEndPoint; private readonly EndPoint _remoteEndPoint; private SslApplicationProtocol _negotiatedAlpnProtocol; @@ -106,8 +106,7 @@ public MsQuicConnection(QuicClientConnectionOptions options) } } - internal override IPEndPoint LocalEndPoint => - new IPEndPoint(_localEndPoint!.Address, _localEndPoint!.Port); + internal override IPEndPoint? LocalEndPoint => _localEndPoint ?? _state.LocalEndPoint; internal override EndPoint RemoteEndPoint => _remoteEndPoint; 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 0e17735dc4ce45..2b2c625be87d19 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 @@ -168,7 +168,7 @@ private static unsafe uint NativeCallbackHandler( try { - ref NewConnectionInfo connectionInfo = ref *(NewConnectionInfo*)evt.Data.NewConnection.Info; + ref NewConnectionInfo connectionInfo = ref *evt.Data.NewConnection.Info; IPEndPoint localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET*)connectionInfo.LocalAddress); IPEndPoint remoteEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET*)connectionInfo.RemoteAddress); diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicConnectionProvider.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicConnectionProvider.cs index 7febd47be1ee17..94258334135896 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicConnectionProvider.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicConnectionProvider.cs @@ -10,7 +10,7 @@ internal abstract class QuicConnectionProvider : IDisposable { internal abstract bool Connected { get; } - internal abstract IPEndPoint LocalEndPoint { get; } + internal abstract IPEndPoint? LocalEndPoint { get; } internal abstract EndPoint RemoteEndPoint { get; } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs index fbd26f70002c43..ffe9196889a4c4 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs @@ -55,7 +55,7 @@ internal QuicConnection(QuicConnectionProvider provider) /// public bool Connected => _provider.Connected; - public IPEndPoint LocalEndPoint => _provider.LocalEndPoint; + public IPEndPoint? LocalEndPoint => _provider.LocalEndPoint; public EndPoint RemoteEndPoint => _provider.RemoteEndPoint; diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs index 8f49d8b6113830..a9908809b4b6d7 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs @@ -7,6 +7,7 @@ namespace System.Net.Quic.Tests { + // TODO: why do we hawe 2 base clase with some duplicated methods? public class MsQuicTestBase { public SslServerAuthenticationOptions GetSslServerAuthenticationOptions() @@ -15,6 +16,7 @@ public SslServerAuthenticationOptions GetSslServerAuthenticationOptions() { ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") }, // TODO: use a cert. MsQuic currently only allows certs that are trusted. + //ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate() }; } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs index ab4a96132e70d8..946a391771dadd 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs @@ -18,7 +18,6 @@ public async Task TestConnect() { using QuicListener listener = CreateQuicListener(); - listener.Start(); IPEndPoint listenEndPoint = listener.ListenEndPoint; using QuicConnection clientConnection = CreateQuicConnection(listenEndPoint); diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj index 84ea8ff2004109..2f27e2f4708511 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj @@ -9,6 +9,7 @@ + @@ -17,4 +18,7 @@ + + + From 68a5c485c423570d699a8d43690b77ab4229a35f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= Date: Wed, 17 Mar 2021 14:58:02 +0100 Subject: [PATCH 06/12] Post-merge compilation fixes. --- .../System/Net/Quic/Implementations/Mock/MockConnection.cs | 4 ++-- .../FunctionalTests/System.Net.Quic.Functional.Tests.csproj | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs index 22e4b0137d7e3f..6e5d2719452de7 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs @@ -91,8 +91,8 @@ internal override bool Connected } // TODO: Should clone the endpoint since it is mutable - // TODO: could this be made back to non-nullable? - // For inbound we have it immidiatelly, for outbound after connect. + // TODO: could this be made back to non-nullable? + // For inbound we have it immediately, for outbound after connect. internal override IPEndPoint? LocalEndPoint => _localEndPoint; // TODO: Should clone the endpoint since it is mutable diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj index dcbcad4080b33b..504bc9d62f9f99 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj @@ -8,14 +8,14 @@ - - + + - + From 851dc1b28edc1a9a083be6cbac4afab9f41b95d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= Date: Wed, 17 Mar 2021 19:18:59 +0100 Subject: [PATCH 07/12] Fixed most of the tests, one turned into ActiveIssue --- .../Implementations/MsQuic/Internal/MsQuicApi.cs | 2 +- .../MsQuic/Interop/MsQuicNativeMethods.cs | 14 +++++++------- .../Interop/SafeMsQuicConfigurationHandle.cs | 4 +++- .../Implementations/MsQuic/MsQuicConnection.cs | 3 +++ .../Quic/Implementations/MsQuic/MsQuicListener.cs | 10 +++++++--- .../tests/FunctionalTests/MsQuicTestBase.cs | 2 +- .../tests/FunctionalTests/MsQuicTests.cs | 10 +++++----- .../tests/FunctionalTests/QuicStreamTests.cs | 4 +++- .../tests/FunctionalTests/QuicTestBase.cs | 3 ++- 9 files changed, 32 insertions(+), 20 deletions(-) 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 e1a0a105128891..cf8a105e2a6ab3 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 @@ -170,7 +170,7 @@ private unsafe MsQuicApi() internal MsQuicNativeMethods.ListenerStartDelegate ListenerStartDelegate { get; } internal MsQuicNativeMethods.ListenerStopDelegate ListenerStopDelegate { get; } - // Mana: missing SendResumptionTicket + // TODO: missing SendResumptionTicket internal MsQuicNativeMethods.ConnectionOpenDelegate ConnectionOpenDelegate { get; } internal MsQuicNativeMethods.ConnectionCloseDelegate ConnectionCloseDelegate { get; } internal MsQuicNativeMethods.ConnectionShutdownDelegate ConnectionShutdownDelegate { get; } 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 861e0079d4c03f..16f86eaf967943 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 @@ -343,7 +343,7 @@ internal delegate void ListenerStopDelegate( [StructLayout(LayoutKind.Sequential)] internal struct ConnectionEventDataConnected { - internal bool SessionResumed; + internal byte SessionResumed; internal byte NegotiatedAlpnLength; // byte[] internal IntPtr NegotiatedAlpn; @@ -479,7 +479,8 @@ internal delegate void ConnectionShutdownDelegate( SafeMsQuicConnectionHandle connection, QUIC_CONNECTION_SHUTDOWN_FLAGS flags, long errorCode); - // Mana: missing SendResumptionTicket + + // TODO: missing SendResumptionTicket [StructLayout(LayoutKind.Sequential)] internal struct StreamEventDataReceive @@ -494,7 +495,7 @@ internal struct StreamEventDataReceive [StructLayout(LayoutKind.Sequential)] internal struct StreamEventDataSendComplete { - internal bool Canceled; + internal byte Canceled; internal IntPtr ClientContext; } @@ -513,7 +514,7 @@ internal struct StreamEventDataPeerReceiveAborted [StructLayout(LayoutKind.Sequential)] internal struct StreamEventDataSendShutdownComplete { - internal bool Graceful; + internal byte Graceful; } [StructLayout(LayoutKind.Explicit)] @@ -603,7 +604,7 @@ internal byte[] Address } } - // Mana: why charset? rename to something C#-like, including fields. + // Mana: rename to C#-like [StructLayout(LayoutKind.Explicit)] internal struct SOCKADDR_INET { @@ -660,7 +661,6 @@ internal delegate uint StreamReceiveCompleteDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamReceiveSetEnabledDelegate( SafeMsQuicStreamHandle stream, - // Mana: do I need this? Should I add it over all booleans? [MarshalAs(UnmanagedType.U1)] bool enabled); @@ -671,6 +671,6 @@ internal struct QuicBuffer internal byte* Buffer; } - // Mana: DatagramSend missing + // TODO: DatagramSend missing } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs index a519e59e1ab373..b062098e20acfd 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs @@ -26,7 +26,7 @@ protected override bool ReleaseHandle() return true; } - // Mana: Move the code from here. Inconsistency with other handles. + // TODO: consider moving the static code from here to keep all the handle classes small and simple. public static unsafe SafeMsQuicConfigurationHandle Create(QuicClientConnectionOptions options) { // TODO: lots of ClientAuthenticationOptions are not yet supported by MsQuic. @@ -39,6 +39,8 @@ public static unsafe SafeMsQuicConfigurationHandle Create(QuicListenerOptions op return Create(options, QUIC_CREDENTIAL_FLAGS.NONE, options.ServerAuthenticationOptions?.ServerCertificate, options.ServerAuthenticationOptions?.ApplicationProtocols); } + // TODO: this is called from MsQuicListener and when it fails it wreaks havoc in MsQuicListener finalizer. + // Consider moving bigger logic like this outside of constructor call chains. private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, QUIC_CREDENTIAL_FLAGS flags, X509Certificate? certificate, List? alpnProtocols) { // TODO: some of these checks should be done by the QuicOptions type. 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 e10b5ffb638c98..19f0a20da120b7 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 @@ -162,6 +162,9 @@ private static uint HandleEventShutdownComplete(State state, ref ConnectionEvent state.Connection = null; state.ShutdownTcs.SetResult(MsQuicStatusCodes.Success); + + // Stop accepting new streams. + state.AcceptQueue.Writer.Complete(); return MsQuicStatusCodes.Success; } 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 2b2c625be87d19..37d42963dd54ff 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 @@ -110,9 +110,9 @@ private void Dispose(bool disposing) } StopAcceptingConnections(); - _state.Handle.Dispose(); + _state?.Handle?.Dispose(); if (_stateHandle.IsAllocated) _stateHandle.Free(); - _state.ConnectionConfiguration.Dispose(); + _state?.ConnectionConfiguration?.Dispose(); _disposed = true; } @@ -150,7 +150,11 @@ internal override void Close() private void StopAcceptingConnections() { - _state.AcceptConnectionQueue.Writer.TryComplete(); + // TODO finalizers are called even if the object construction fails. + if (_state != null) + { + _state.AcceptConnectionQueue.Writer.TryComplete(); + } } private static unsafe uint NativeCallbackHandler( diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs index a9908809b4b6d7..2178ec12c46fd2 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs @@ -16,7 +16,7 @@ public SslServerAuthenticationOptions GetSslServerAuthenticationOptions() { ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") }, // TODO: use a cert. MsQuic currently only allows certs that are trusted. - //ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate() + ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate() }; } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index 9c13f6eeb10495..4f12d6c6830dc7 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -49,14 +49,14 @@ public async Task UnidirectionalAndBidirectionalChangeValues() ValueTask clientTask = clientConnection.ConnectAsync(); using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); await clientTask; - Assert.Equal(20, clientConnection.GetRemoteAvailableUnidirectionalStreamCount()); - Assert.Equal(10, clientConnection.GetRemoteAvailableBidirectionalStreamCount()); - Assert.Equal(100, serverConnection.GetRemoteAvailableBidirectionalStreamCount()); - Assert.Equal(100, serverConnection.GetRemoteAvailableUnidirectionalStreamCount()); + Assert.Equal(100, clientConnection.GetRemoteAvailableBidirectionalStreamCount()); + Assert.Equal(100, clientConnection.GetRemoteAvailableUnidirectionalStreamCount()); + Assert.Equal(10, serverConnection.GetRemoteAvailableBidirectionalStreamCount()); + Assert.Equal(20, serverConnection.GetRemoteAvailableUnidirectionalStreamCount()); } [Fact] - [OuterLoop("May take serveral seconds")] + [OuterLoop("May take several seconds")] public async Task SetListenerTimeoutWorksWithSmallTimeout() { var quicOptions = new QuicListenerOptions(); diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index 5cefeea94ceb1d..b849b8f919e89b 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -16,6 +16,7 @@ public abstract class QuicStreamTests : QuicTestBase { private static ReadOnlyMemory s_data = Encoding.UTF8.GetBytes("Hello world!"); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] [Fact] public async Task BasicTest() { @@ -470,7 +471,8 @@ await RunClientServer( totalBytesRead += bytesRead; } - Assert.True(receiveBuffer.AsSpan().SequenceEqual(testBuffer)); + Assert.Equal(testBuffer.Length, receiveBuffer.Length); + Assert.Equal(testBuffer, receiveBuffer); }); } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs index e5fc3779f3b9a8..6fbcc41bdc6d5f 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs @@ -22,7 +22,8 @@ public SslServerAuthenticationOptions GetSslServerAuthenticationOptions() { return new SslServerAuthenticationOptions() { - ApplicationProtocols = new List() { ApplicationProtocol } + ApplicationProtocols = new List() { ApplicationProtocol }, + ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate() }; } From 78e3981cf8f42d47f89dea2452d3ab54543a2f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= Date: Thu, 18 Mar 2021 15:07:57 +0100 Subject: [PATCH 08/12] Comments. --- .../MsQuic/Interop/MsQuicNativeMethods.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) 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 16f86eaf967943..74005a7c4fae01 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 @@ -193,18 +193,17 @@ internal enum QuicSettingsEnabledFlagsFlags : byte VersionNegotiationExtEnabled = 1 << 6, } - // Mana: get back to this, define delegate for AsyncHandler, define types fro IntPtr Certificate (union in C, plat dependent) and - // make proper use of it. [StructLayout(LayoutKind.Sequential)] internal struct CredentialConfig { internal QUIC_CREDENTIAL_TYPE Type; internal QUIC_CREDENTIAL_FLAGS Flags; + // CredentialConfigCertificateUnion* internal IntPtr Certificate; [MarshalAs(UnmanagedType.LPUTF8Str)] internal string Principal; internal IntPtr Reserved; // Currently unused - // Mana: define delegate for the async callback + // TODO: define delegate for AsyncHandler and make proper use of it. internal IntPtr AsyncHandler; } @@ -361,10 +360,10 @@ internal struct ConnectionEventDataShutdownInitiatedByPeer internal ulong ErrorCode; } - // Mana: it's a trap, those are flags with fixed sized exactly 3 bits!!! [StructLayout(LayoutKind.Sequential)] internal struct ConnectionEventDataShutdownComplete { + // The flags have fixed sized exactly 3 bits internal ConnectionEventDataShutdownCompleteFlags Flags; } @@ -431,7 +430,7 @@ internal struct ConnectionEventDataUnion [FieldOffset(0)] internal ConnectionEventDataStreamsAvailable StreamsAvailable; - // Mana: missing IDEAL_PROCESSOR_CHANGED, ..., PEER_CERTIFICATE_RECEIVED (7 total) + // TODO: missing IDEAL_PROCESSOR_CHANGED, ..., PEER_CERTIFICATE_RECEIVED (7 total) } [StructLayout(LayoutKind.Sequential)] @@ -447,7 +446,7 @@ internal delegate uint ConnectionCallbackDelegate( IntPtr context, ref ConnectionEvent connectionEvent); - // Mana: order is Open, Close, Shutdown, Start, SetConfiguration, SendResumptionTicket + // TODO: order is Open, Close, Shutdown, Start, SetConfiguration, SendResumptionTicket [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ConnectionOpenDelegate( SafeMsQuicRegistrationHandle registration, @@ -536,7 +535,7 @@ internal struct StreamEventDataUnion [FieldOffset(0)] internal StreamEventDataSendShutdownComplete SendShutdownComplete; - // Mana: missing IDEAL_SEND_BUFFER_SIZE + // TODO: missing IDEAL_SEND_BUFFER_SIZE } [StructLayout(LayoutKind.Sequential)] @@ -546,7 +545,7 @@ internal struct StreamEvent internal StreamEventDataUnion Data; } - // Mana: rename to C#-like + // TODO: rename to C#-like [StructLayout(LayoutKind.Sequential)] internal struct SOCKADDR_IN { @@ -566,7 +565,7 @@ internal byte[] Address } } - // Mana: rename to C#-like + // TODO: rename to C#-like [StructLayout(LayoutKind.Sequential)] internal struct SOCKADDR_IN6 { @@ -604,7 +603,7 @@ internal byte[] Address } } - // Mana: rename to C#-like + // TODO: rename to C#-like [StructLayout(LayoutKind.Explicit)] internal struct SOCKADDR_INET { From 7ad516366b4929f471d50846a4e51a5c4d8864f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= Date: Thu, 18 Mar 2021 15:21:12 +0100 Subject: [PATCH 09/12] Regenerated ref sources. --- src/libraries/System.Net.Quic/ref/System.Net.Quic.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs b/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs index 805d17e77c4ed6..847773e8d501da 100644 --- a/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs +++ b/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs @@ -20,7 +20,7 @@ public QuicConnection(System.Net.Quic.Implementations.QuicImplementationProvider public QuicConnection(System.Net.Quic.Implementations.QuicImplementationProvider implementationProvider, System.Net.Quic.QuicClientConnectionOptions options) { } public QuicConnection(System.Net.Quic.QuicClientConnectionOptions options) { } public bool Connected { get { throw null; } } - public System.Net.IPEndPoint LocalEndPoint { get { throw null; } } + public System.Net.IPEndPoint? LocalEndPoint { get { throw null; } } public System.Net.Security.SslApplicationProtocol NegotiatedApplicationProtocol { get { throw null; } } public System.Net.EndPoint RemoteEndPoint { get { throw null; } } public System.Threading.Tasks.ValueTask AcceptStreamAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } From f67ed0d61074706b222b8277391d2e8e61279ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= Date: Fri, 19 Mar 2021 16:06:02 +0100 Subject: [PATCH 10/12] Addressed some feedback. --- .../MsQuic/Internal/MsQuicApi.cs | 25 +------------------ .../MsQuic/Interop/MsQuicAlpnHelper.cs | 4 +-- .../MsQuic/Interop/MsQuicNativeMethods.cs | 10 ++++---- .../Interop/SafeMsQuicConfigurationHandle.cs | 8 +++--- .../Interop/SafeMsQuicConnectionHandle.cs | 3 ++- .../Interop/SafeMsQuicListenerHandle.cs | 6 ++--- .../MsQuic/Interop/SafeMsQuicStreamHandle.cs | 9 ++++--- .../MsQuic/MsQuicConnection.cs | 9 +++---- .../Implementations/MsQuic/MsQuicListener.cs | 2 +- .../Implementations/MsQuic/MsQuicStream.cs | 2 +- .../src/System/Net/Quic/QuicListener.cs | 2 +- ...icStreamConnectedStreamConformanceTests.cs | 12 ++++++++- 12 files changed, 40 insertions(+), 52 deletions(-) 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 cf8a105e2a6ab3..3403f5faeaef83 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 @@ -122,7 +122,7 @@ private unsafe MsQuicApi() var cfg = new RegistrationConfig { AppName = ".NET", - ExecutionProfile = (uint)QUIC_EXECUTION_PROFILE.QUIC_EXECUTION_PROFILE_LOW_LATENCY + ExecutionProfile = QUIC_EXECUTION_PROFILE.QUIC_EXECUTION_PROFILE_LOW_LATENCY }; status = RegistrationOpenDelegate(ref cfg, out SafeMsQuicRegistrationHandle handle); @@ -135,29 +135,6 @@ private unsafe MsQuicApi() internal static bool IsQuicSupported => true; - //static MsQuicApi() - //{ - // // MsQuicOpen will succeed even if the platform will not support it. It will then fail with unspecified - // // platform-specific errors in subsequent callbacks. For now, check for the minimum build we've tested it on. - - // // TODO: - // // - Hopefully, MsQuicOpen will perform this check for us and give us a consistent error code. - // // - Otherwise, dial this in to reflect actual minimum requirements and add some sort of platform - // // error code mapping when creating exceptions. - - // // TODO: try to initialize TLS 1.3 in SslStream. - - // try - // { - // Api = new MsQuicApi(); - // IsQuicSupported = true; - // } - // catch (NotSupportedException) - // { - // IsQuicSupported = false; - // } - //} - internal MsQuicNativeMethods.RegistrationOpenDelegate RegistrationOpenDelegate { get; } internal MsQuicNativeMethods.RegistrationCloseDelegate RegistrationCloseDelegate { get; } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicAlpnHelper.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicAlpnHelper.cs index c5d9cce43d4a07..916cfc4c550cdf 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicAlpnHelper.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicAlpnHelper.cs @@ -30,12 +30,12 @@ public static unsafe void Prepare(List alpnProtocols, [N } catch { - Destroy(ref handles, ref buffers); + Return(ref handles, ref buffers); throw; } } - public static void Destroy(ref MemoryHandle[]? handles, ref QuicBuffer[]? buffers) + public static void Return(ref MemoryHandle[]? handles, ref QuicBuffer[]? buffers) { if (handles is MemoryHandle[] notNullHandles) { 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 74005a7c4fae01..57efde31dcb70f 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 @@ -357,7 +357,7 @@ internal struct ConnectionEventDataShutdownInitiatedByTransport [StructLayout(LayoutKind.Sequential)] internal struct ConnectionEventDataShutdownInitiatedByPeer { - internal ulong ErrorCode; + internal long ErrorCode; } [StructLayout(LayoutKind.Sequential)] @@ -501,13 +501,13 @@ internal struct StreamEventDataSendComplete [StructLayout(LayoutKind.Sequential)] internal struct StreamEventDataPeerSendAborted { - internal ulong ErrorCode; + internal long ErrorCode; } [StructLayout(LayoutKind.Sequential)] internal struct StreamEventDataPeerReceiveAborted { - internal ulong ErrorCode; + internal long ErrorCode; } [StructLayout(LayoutKind.Sequential)] @@ -519,7 +519,7 @@ internal struct StreamEventDataSendShutdownComplete [StructLayout(LayoutKind.Explicit)] internal struct StreamEventDataUnion { - //Mana: missing START_COMPLETE + // TODO: missing START_COMPLETE [FieldOffset(0)] internal StreamEventDataReceive Receive; @@ -642,7 +642,7 @@ internal delegate void StreamCloseDelegate( internal delegate uint StreamShutdownDelegate( SafeMsQuicStreamHandle stream, QUIC_STREAM_SHUTDOWN_FLAGS flags, - ulong errorCode); + long errorCode); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamSendDelegate( diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs index b062098e20acfd..24a5b2fa5f9210 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs @@ -16,9 +16,9 @@ internal sealed class SafeMsQuicConfigurationHandle : SafeHandle { public override bool IsInvalid => handle == IntPtr.Zero; - private SafeMsQuicConfigurationHandle() : base(IntPtr.Zero, ownsHandle: true) - { - } + private SafeMsQuicConfigurationHandle() + : base(IntPtr.Zero, ownsHandle: true) + { } protected override bool ReleaseHandle() { @@ -92,7 +92,7 @@ private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, } finally { - MsQuicAlpnHelper.Destroy(ref handles, ref buffers); + MsQuicAlpnHelper.Return(ref handles, ref buffers); } QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationOpen failed."); diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs index 767c291150db9c..38d908d3adfa9e 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs @@ -13,7 +13,8 @@ private SafeMsQuicConnectionHandle() : base(IntPtr.Zero, ownsHandle: true) { } - public SafeMsQuicConnectionHandle(IntPtr connectionHandle) : this() + public SafeMsQuicConnectionHandle(IntPtr connectionHandle) + : this() { SetHandle(connectionHandle); } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs index ffbfddb50757a6..8a22ab84875424 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs @@ -9,9 +9,9 @@ internal sealed class SafeMsQuicListenerHandle : SafeHandle { public override bool IsInvalid => handle == IntPtr.Zero; - private SafeMsQuicListenerHandle() : base(IntPtr.Zero, ownsHandle: true) - { - } + private SafeMsQuicListenerHandle() + : base(IntPtr.Zero, ownsHandle: true) + { } protected override bool ReleaseHandle() { diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs index 4769174e46a09c..c9e775b885e8ec 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs @@ -9,11 +9,12 @@ internal sealed class SafeMsQuicStreamHandle : SafeHandle { public override bool IsInvalid => handle == IntPtr.Zero; - private SafeMsQuicStreamHandle() : base(IntPtr.Zero, ownsHandle: true) - { - } + private SafeMsQuicStreamHandle() + : base(IntPtr.Zero, ownsHandle: true) + { } - public SafeMsQuicStreamHandle(IntPtr streamHandle) : this() + public SafeMsQuicStreamHandle(IntPtr streamHandle) + : this() { SetHandle(streamHandle); } 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 19f0a20da120b7..0d58e9ed8c2b3f 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 @@ -27,7 +27,7 @@ internal sealed class MsQuicConnection : QuicConnectionProvider private GCHandle _stateHandle; private bool _disposed; - private readonly IPEndPoint? _localEndPoint; + private IPEndPoint? _localEndPoint; private readonly EndPoint _remoteEndPoint; private SslApplicationProtocol _negotiatedAlpnProtocol; @@ -42,7 +42,6 @@ private sealed class State public readonly TaskCompletionSource ConnectTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); public readonly TaskCompletionSource ShutdownTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - public IPEndPoint? LocalEndPoint; public bool Connected; public long AbortErrorCode = -1; @@ -106,7 +105,7 @@ public MsQuicConnection(QuicClientConnectionOptions options) } } - internal override IPEndPoint? LocalEndPoint => _localEndPoint ?? _state.LocalEndPoint; + internal override IPEndPoint? LocalEndPoint => _localEndPoint; internal override EndPoint RemoteEndPoint => _remoteEndPoint; @@ -121,9 +120,9 @@ private static uint HandleEventConnected(State state, ref ConnectionEvent connec // Connected will already be true for connections accepted from a listener. SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS); - state.LocalEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); Debug.Assert(state.Connection != null); + state.Connection._localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); state.Connection.SetNegotiatedAlpn(connectionEvent.Data.Connected.NegotiatedAlpn, connectionEvent.Data.Connected.NegotiatedAlpnLength); state.Connection = null; @@ -366,7 +365,7 @@ private void Dispose(bool disposing) return; } - _state.Handle.Dispose(); + _state?.Handle?.Dispose(); if (_stateHandle.IsAllocated) _stateHandle.Free(); _disposed = true; } 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 37d42963dd54ff..0b55939205d1c8 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 @@ -133,7 +133,7 @@ internal override unsafe void Start() } finally { - MsQuicAlpnHelper.Destroy(ref handles, ref buffers); + MsQuicAlpnHelper.Return(ref handles, ref buffers); } QuicExceptionHelpers.ThrowIfFailed(status, "ListenerStart failed."); 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 4d6906250561cb..45476d1a08de4c 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 @@ -366,7 +366,7 @@ internal override void AbortWrite(long errorCode) private void StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS flags, long errorCode) { - uint status = MsQuicApi.Api.StreamShutdownDelegate(_state.Handle, flags, (ulong)errorCode); + uint status = MsQuicApi.Api.StreamShutdownDelegate(_state.Handle, flags, errorCode); QuicExceptionHelpers.ThrowIfFailed(status, "StreamShutdown failed."); } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs index e465bc2b631a3b..991a6b5925b346 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs @@ -33,7 +33,7 @@ public QuicListener(QuicListenerOptions options) // !!! TEMPORARY: Remove or make internal before shipping public QuicListener(QuicImplementationProvider implementationProvider, IPEndPoint listenEndPoint, SslServerAuthenticationOptions sslServerAuthenticationOptions) - : this(implementationProvider, new QuicListenerOptions() { ListenEndPoint = listenEndPoint, ServerAuthenticationOptions = sslServerAuthenticationOptions }) + : this(implementationProvider, new QuicListenerOptions() { ListenEndPoint = listenEndPoint, ServerAuthenticationOptions = sslServerAuthenticationOptions }) { } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs index 5eba6cc14089c7..94984496af6af8 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs @@ -83,6 +83,16 @@ public sealed class MsQuicQuicStreamConformanceTests : QuicStreamConformanceTest public abstract class QuicStreamConformanceTests : ConnectedStreamConformanceTests { + public SslServerAuthenticationOptions GetSslServerAuthenticationOptions() + { + return new SslServerAuthenticationOptions() + { + ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") }, + // TODO: use a cert. MsQuic currently only allows certs that are trusted. + ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate() + }; + } + protected abstract QuicImplementationProvider Provider { get; } protected override async Task CreateConnectedStreamsAsync() @@ -93,7 +103,7 @@ protected override async Task CreateConnectedStreamsAsync() var listener = new QuicListener( provider, new IPEndPoint(IPAddress.Loopback, 0), - new SslServerAuthenticationOptions { ApplicationProtocols = new List { protocol } }); + GetSslServerAuthenticationOptions()); listener.Start(); QuicConnection connection1 = null, connection2 = null; From 204f4fa5fc1d4ebda32ab8bc6b43f2ee3bbfd7b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= Date: Tue, 23 Mar 2021 19:01:57 +0100 Subject: [PATCH 11/12] Added readme. --- src/libraries/System.Net.Quic/readme.md | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/libraries/System.Net.Quic/readme.md diff --git a/src/libraries/System.Net.Quic/readme.md b/src/libraries/System.Net.Quic/readme.md new file mode 100644 index 00000000000000..16ac0d31a04909 --- /dev/null +++ b/src/libraries/System.Net.Quic/readme.md @@ -0,0 +1,45 @@ +# MsQuic + +`System.Net.Quic` depends on [MsQuic](https://github.com/microsoft/msquic), Microsoft, cross-platform, native implementation of the [QUIC](https://datatracker.ietf.org/wg/quic/about/) protocol. +Currently, `System.Net.Quic` depends on [**msquic@cc104e836a5d4a5e0d324bc08b42136d2acac997**](https://github.com/microsoft/msquic/commit/cc104e836a5d4a5e0d324bc08b42136d2acac997) revision. + +## Usage + +### Build MsQuic + +[MsQuic build docs](https://github.com/microsoft/msquic/blob/main/docs/BUILD.md) + +> **Note**: At the moment, we're using stub_tls option to bypass OpenSSL/SChannel, since work with certificates is not fully figured out. + +#### Linux +Prerequisites: +- build-essential +- cmake +- lttng-ust +- lttng-tools + +`dotnet tool install --global`: +- microsoft.logging.clog +- microsoft.logging.clog2text.lttng + + +Run inside the msquic directory (for **Debug** build with logging on): +```bash +# build msquic in debug with logging and stub tls +rm -rf build +mkdir build +cmake -B build -DCMAKE_BUILD_TYPE=Debug -DQUIC_ENABLE_LOGGING=on -DQUIC_TLS=stub +cd build +cmake --build . --config Debug + +# copy msquic into runtime +yes | cp -rf bin/Debug/libmsquic.* /src/libraries/System.Net.Quic/src/ +``` + +#### Windows +Prerequisites: +- Latest [Windows Insider Builds](https://insider.windows.com/en-us/), Insiders Fast build. This is required for SChannel support for QUIC. + - To confirm you have a new enough build, run winver on command line and confirm you version is greater than Version 2004 (OS Build 20145.1000). + +TODO + From 5d3884f9552be216154fab4b9b34f0f923c6458d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20P=C3=ADchov=C3=A1?= Date: Thu, 25 Mar 2021 13:27:13 +0100 Subject: [PATCH 12/12] QUIC_ADDRESS_FAMILY --- .../Implementations/MsQuic/Interop/MsQuicEnums.cs | 7 +++++++ .../MsQuic/Interop/MsQuicNativeMethods.cs | 3 +-- .../Quic/Implementations/MsQuic/MsQuicConnection.cs | 11 +++++------ 3 files changed, 13 insertions(+), 8 deletions(-) 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 26ed23b0da8716..34dd31bf70ce83 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 @@ -201,4 +201,11 @@ internal enum QUIC_STREAM_EVENT_TYPE : uint SHUTDOWN_COMPLETE = 7, IDEAL_SEND_BUFFER_SIZE = 8, } + + internal enum QUIC_ADDRESS_FAMILY : ushort + { + UNSPEC = 0, + INET = 2, + INET6 = 23, + } } 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 8ad78ba062c79b..131227746c3ad3 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 @@ -471,8 +471,7 @@ internal delegate uint ConnectionSetConfigurationDelegate( internal delegate uint ConnectionStartDelegate( SafeMsQuicConnectionHandle connection, SafeMsQuicConfigurationHandle configuration, - // QUIC_ADDRESS_FAMILY = sa_family_t = ushort - ushort family, + QUIC_ADDRESS_FAMILY family, [MarshalAs(UnmanagedType.LPUTF8Str)] string serverName, ushort serverPort); 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 0d58e9ed8c2b3f..87897c19511594 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 @@ -241,12 +241,11 @@ internal override ValueTask ConnectAsync(CancellationToken cancellationToken = d _ => throw new Exception($"Unsupported remote endpoint type '{_remoteEndPoint.GetType()}'.") }; - // TODO: MsQuic will use system constants, so we should use the Socket PAL to translate these. - int af = _remoteEndPoint.AddressFamily switch + QUIC_ADDRESS_FAMILY af = _remoteEndPoint.AddressFamily switch { - AddressFamily.Unspecified => 0, - AddressFamily.InterNetwork => 2, - AddressFamily.InterNetworkV6 => 23, + AddressFamily.Unspecified => QUIC_ADDRESS_FAMILY.UNSPEC, + AddressFamily.InterNetwork => QUIC_ADDRESS_FAMILY.INET, + AddressFamily.InterNetworkV6 => QUIC_ADDRESS_FAMILY.INET6, _ => throw new Exception(SR.Format(SR.net_quic_unsupported_address_family, _remoteEndPoint.AddressFamily)) }; @@ -256,7 +255,7 @@ internal override ValueTask ConnectAsync(CancellationToken cancellationToken = d uint status = MsQuicApi.Api.ConnectionStartDelegate( _state.Handle, _configuration, - (ushort)af, + af, address, (ushort)port);