Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUIC] API QuicListener #71579

Merged
merged 14 commits into from
Jul 10, 2022
37 changes: 24 additions & 13 deletions src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public sealed class Http3LoopbackServer : GenericLoopbackServer
private X509Certificate2 _cert;
private QuicListener _listener;

public override Uri Address => new Uri($"https://{_listener.ListenEndPoint}/");
public override Uri Address => new Uri($"https://{_listener.LocalEndPoint}/");

public Http3LoopbackServer(Http3Options options = null)
{
Expand All @@ -28,18 +28,29 @@ public Http3LoopbackServer(Http3Options options = null)
var listenerOptions = new QuicListenerOptions()
{
ListenEndPoint = new IPEndPoint(options.Address, 0),
ServerAuthenticationOptions = new SslServerAuthenticationOptions
ApplicationProtocols = new List<SslApplicationProtocol>
{
EnabledSslProtocols = options.SslProtocols,
ApplicationProtocols = new List<SslApplicationProtocol>
{
new SslApplicationProtocol(options.Alpn)
},
ServerCertificate = _cert,
ClientCertificateRequired = false
new SslApplicationProtocol(options.Alpn)
},
MaxUnidirectionalStreams = options.MaxUnidirectionalStreams,
MaxBidirectionalStreams = options.MaxBidirectionalStreams,
ConnectionOptionsCallback = (_, _, _) =>
{
var serverOptions = new QuicServerConnectionOptions()
{
MaxBidirectionalStreams = options.MaxBidirectionalStreams,
MaxUnidirectionalStreams = options.MaxUnidirectionalStreams,
ServerAuthenticationOptions = new SslServerAuthenticationOptions
{
EnabledSslProtocols = options.SslProtocols,
ApplicationProtocols = new List<SslApplicationProtocol>
{
new SslApplicationProtocol(options.Alpn)
},
ServerCertificate = _cert,
ClientCertificateRequired = false
}
};
return ValueTask.FromResult(serverOptions);
}
};

ValueTask<QuicListener> valueTask = QuicListener.ListenAsync(listenerOptions);
Expand All @@ -49,7 +60,7 @@ public Http3LoopbackServer(Http3Options options = null)

public override void Dispose()
{
_listener.Dispose();
_listener.DisposeAsync().GetAwaiter().GetResult();
_cert.Dispose();
}

Expand Down Expand Up @@ -133,7 +144,7 @@ public class Http3Options : GenericLoopbackOptions

public Http3Options()
{
MaxUnidirectionalStreams = 100;
MaxUnidirectionalStreams = 10;
MaxBidirectionalStreams = 100;
Alpn = SslApplicationProtocol.Http3.ToString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ public static async ValueTask<QuicConnection> ConnectQuicAsync(HttpRequestMessag
clientAuthenticationOptions = SetUpRemoteCertificateValidationCallback(clientAuthenticationOptions, request);
QuicConnection connection = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions()
{
MaxBidirectionalStreams = 0, // Set to 100 if/when support for PUSH streams is added.
MaxUnidirectionalStreams = 5, // Should be enough for Control and QPack streams.
IdleTimeout = TimeSpan.FromMinutes(2), // To align with HttpClient default timeout.
RemoteEndPoint = endPoint,
ClientAuthenticationOptions = clientAuthenticationOptions
}, cancellationToken).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,8 @@ private async Task ProcessServerStreamAsync(QuicStream stream)
NetEventSource.Info(this, $"Ignoring server-initiated stream of unknown type {unknownStreamType}.");
}

stream.AbortWrite((long)Http3ErrorCode.StreamCreationError);
stream.AbortRead((long)Http3ErrorCode.StreamCreationError);
stream.Dispose();
return;
}
}
Expand Down
41 changes: 24 additions & 17 deletions src/libraries/System.Net.Quic/ref/System.Net.Quic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

namespace System.Net.Quic
{
public partial class QuicClientConnectionOptions : System.Net.Quic.QuicOptions
public sealed partial class QuicClientConnectionOptions : System.Net.Quic.QuicConnectionOptions
{
public QuicClientConnectionOptions() { }
public System.Net.Security.SslClientAuthenticationOptions? ClientAuthenticationOptions { get { throw null; } set { } }
public required 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 required System.Net.EndPoint RemoteEndPoint { get { throw null; } set { } }
}
public sealed partial class QuicConnection : System.IDisposable
{
Expand All @@ -34,41 +34,48 @@ public void Dispose() { }
}
public partial class QuicConnectionAbortedException : System.Net.Quic.QuicException
{
public QuicConnectionAbortedException(string message, long errorCode) : base(default(string)) { }
public QuicConnectionAbortedException(string message, long errorCode) : base (default(string)) { }
public long ErrorCode { get { throw null; } }
}
public abstract partial class QuicConnectionOptions
{
internal QuicConnectionOptions() { }
public System.TimeSpan IdleTimeout { get { throw null; } set { } }
public int MaxBidirectionalStreams { get { throw null; } set { } }
public int MaxUnidirectionalStreams { get { throw null; } set { } }
}
public partial class QuicException : System.Exception
{
public QuicException(string? message) { }
public QuicException(string? message, System.Exception? innerException) { }
public QuicException(string? message, System.Exception? innerException, int result) { }
}
public sealed partial class QuicListener : System.IDisposable
public sealed partial class QuicListener : System.IAsyncDisposable
{
internal QuicListener() { }
public static bool IsSupported { get { throw null; } }
public System.Net.IPEndPoint ListenEndPoint { get { throw null; } }
public System.Net.IPEndPoint LocalEndPoint { get { throw null; } }
public System.Threading.Tasks.ValueTask<System.Net.Quic.QuicConnection> AcceptConnectionAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public void Dispose() { }
public System.Threading.Tasks.ValueTask DisposeAsync() { throw null; }
public static System.Threading.Tasks.ValueTask<System.Net.Quic.QuicListener> ListenAsync(System.Net.Quic.QuicListenerOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public override string ToString() { throw null; }
}
public partial class QuicListenerOptions : System.Net.Quic.QuicOptions
public sealed partial class QuicListenerOptions
{
public QuicListenerOptions() { }
public required System.Collections.Generic.List<System.Net.Security.SslApplicationProtocol> ApplicationProtocols { get { throw null; } set { } }
public required System.Func<System.Net.Quic.QuicConnection, System.Net.Security.SslClientHelloInfo, System.Threading.CancellationToken, System.Threading.Tasks.ValueTask<System.Net.Quic.QuicServerConnectionOptions>> ConnectionOptionsCallback { get { throw null; } set { } }
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 required System.Net.IPEndPoint ListenEndPoint { get { throw null; } set { } }
}
public partial class QuicOperationAbortedException : System.Net.Quic.QuicException
{
public QuicOperationAbortedException(string message) : base(default(string)) { }
public QuicOperationAbortedException(string message) : base (default(string)) { }
}
public partial class QuicOptions
public sealed partial class QuicServerConnectionOptions : System.Net.Quic.QuicConnectionOptions
{
public QuicOptions() { }
public System.TimeSpan IdleTimeout { get { throw null; } set { } }
public int MaxBidirectionalStreams { get { throw null; } set { } }
public int MaxUnidirectionalStreams { get { throw null; } set { } }
public QuicServerConnectionOptions() { }
public required System.Net.Security.SslServerAuthenticationOptions ServerAuthenticationOptions { get { throw null; } set { } }
}
public sealed partial class QuicStream : System.IO.Stream
{
Expand Down Expand Up @@ -113,7 +120,7 @@ public override void WriteByte(byte value) { }
}
public partial class QuicStreamAbortedException : System.Net.Quic.QuicException
{
public QuicStreamAbortedException(string message, long errorCode) : base(default(string)) { }
public QuicStreamAbortedException(string message, long errorCode) : base (default(string)) { }
public long ErrorCode { get { throw null; } }
}
}
6 changes: 5 additions & 1 deletion src/libraries/System.Net.Quic/src/ExcludeApiList.PNSE.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
P:System.Net.Quic.QuicConnection.IsSupported
P:System.Net.Quic.QuicListener.IsSupported
P:System.Net.Quic.QuicListener.IsSupported
C:System.Net.Quic.QuicListenerOptions
C:System.Net.Quic.QuicConnectionOptions
C:System.Net.Quic.QuicClientConnectionOptions
C:System.Net.Quic.QuicServerConnectionOptions
3 changes: 3 additions & 0 deletions src/libraries/System.Net.Quic/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -181,5 +181,8 @@
<data name="net_InvalidSocketAddressSize" xml:space="preserve">
<value>The supplied {0} is an invalid size for the {1} end point.</value>
</data>
<data name="net_quic_accept_not_allowed" xml:space="preserve">
<value>QuicConnection is not configured to accept any streams.</value>
</data>
</root>

22 changes: 5 additions & 17 deletions src/libraries/System.Net.Quic/src/System.Net.Quic.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,7 @@
</PropertyGroup>
<!-- Source files -->
<ItemGroup Condition="'$(TargetPlatformIdentifier)' != ''">
<Compile Include="System\Net\Quic\NetEventSource.Quic.cs" />
<Compile Include="System\Net\Quic\QuicClientConnectionOptions.cs" />
<Compile Include="System\Net\Quic\QuicConnection.cs" />
<Compile Include="System\Net\Quic\QuicConnectionAbortedException.cs" />
<Compile Include="System\Net\Quic\QuicException.cs" />
<Compile Include="System\Net\Quic\QuicListener.cs" />
<Compile Include="System\Net\Quic\QuicListenerOptions.cs" />
<Compile Include="System\Net\Quic\QuicOperationAbortedException.cs" />
<Compile Include="System\Net\Quic\QuicOptions.cs" />
<Compile Include="System\Net\Quic\QuicStream.cs" />
<Compile Include="System\Net\Quic\QuicStreamAbortedException.cs" />
<Compile Include="System\Net\Quic\Implementations\*.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\*.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Internal\*.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\*.cs" />
<Compile Include="System\Net\Quic\**\*.cs" Exclude="System\Net\Quic\*.Unsupported.cs"/>
<!-- System.Net common -->
<Compile Include="$(CommonPath)DisableRuntimeMarshalling.cs" Link="Common\DisableRuntimeMarshalling.cs" />
<Compile Include="$(CommonPath)System\Threading\Tasks\TaskToApm.cs" Link="Common\System\Threading\Tasks\TaskToApm.cs" />
Expand All @@ -41,8 +27,10 @@
</ItemGroup>
<!-- Unsupported platforms -->
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == ''">
<Compile Include="System\Net\Quic\QuicListener.Unsupported.cs" />
<Compile Include="System\Net\Quic\QuicConnection.Unsupported.cs" />
<Compile Include="System\Net\Quic\*.Unsupported.cs" />
<!-- [ActiveIssue("https://github.com/dotnet/runtime/issues/71559")]: PNSE generator cannot handle required keyword, so excluding affected classes for now. -->
<Compile Include="System\Net\Quic\QuicListenerOptions.cs" />
<Compile Include="System\Net\Quic\QuicConnectionOptions.cs" />
</ItemGroup>
<!-- Windows specific files -->
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'windows'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@

using System.Diagnostics;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.Quic;

namespace System.Net.Quic.Implementations.MsQuic.Internal
{
internal static class MsQuicAddressHelpers
{
internal static unsafe IPEndPoint INetToIPEndPoint(IntPtr pInetAddress)
internal static unsafe IPEndPoint ToIPEndPoint(this ref QuicAddr quicAddress)
{
// MsQuic always uses storage size as if IPv6 was used
Span<byte> addressBytes = new Span<byte>((byte*)pInetAddress, Internals.SocketAddress.IPv6AddressSize);
Span<byte> addressBytes = new Span<byte>((byte*)Unsafe.AsPointer(ref quicAddress), Internals.SocketAddress.IPv6AddressSize);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is potential corruption if quicAddress isn't fixed in memory. I assume it is, in which case it's worth a comment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's native memory, so it cannot be moved by GC. I'll add a comment.

return new Internals.SocketAddress(SocketAddressPal.GetAddressFamily(addressBytes), addressBytes).GetIPEndPoint();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ internal sealed unsafe class MsQuicApi

private static readonly Version MsQuicVersion = new Version(2, 0);

public SafeMsQuicRegistrationHandle Registration { get; }
public MsQuicSafeHandle Registration { get; }

public QUIC_API_TABLE* ApiTable { get; }

// This is workaround for a bug in ILTrimmer.
// Without these DynamicDependency attributes, .ctor() will be removed from the safe handles.
// Remove once fixed: https://github.com/mono/linker/issues/1660
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicRegistrationHandle))]
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicConfigurationHandle))]
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicListenerHandle))]
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicConnectionHandle))]
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicStreamHandle))]
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(MsQuicSafeHandle))]
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(MsQuicContextSafeHandle))]
private MsQuicApi(QUIC_API_TABLE* apiTable)
{
ApiTable = apiTable;
Expand All @@ -47,7 +47,7 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
QUIC_HANDLE* handle;
ThrowIfFailure(ApiTable->RegistrationOpen(&cfg, &handle), "RegistrationOpen failed");

Registration = new SafeMsQuicRegistrationHandle(handle);
Registration = new MsQuicSafeHandle(handle, apiTable->RegistrationClose, SafeHandleType.Registration);
}
}

Expand Down

This file was deleted.

Loading