From 4a7b24b01df775a7056f7a424af0bbd529a116eb Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Fri, 8 Nov 2019 08:39:44 -0800 Subject: [PATCH] Add temporary QUIC internal implementation abstraction (dotnet/corefx#42432) Commit migrated from https://github.com/dotnet/corefx/commit/1ab8b616f0b622265c0911484e9794858047a748 --- .../ref/System.Net.Quic.Temporary.cs | 31 +++ .../System.Net.Quic/ref/System.Net.Quic.cs | 8 +- .../System.Net.Quic/ref/System.Net.Quic.csproj | 1 + .../System.Net.Quic/src/System.Net.Quic.csproj | 9 + .../Quic/Implementations/Mock/MockConnection.cs | 214 +++++++++++++++++ .../Mock/MockImplementationProvider.cs | 21 ++ .../Net/Quic/Implementations/Mock/MockListener.cs | 114 ++++++++++ .../Net/Quic/Implementations/Mock/MockStream.cs | 165 ++++++++++++++ .../Quic/Implementations/QuicConnectionProvider.cs | 30 +++ .../Implementations/QuicImplementationProvider.cs | 17 ++ .../Quic/Implementations/QuicListenerProvider.cs | 20 ++ .../Net/Quic/Implementations/QuicStreamProvider.cs | 36 +++ .../src/System/Net/Quic/QuicConnection.cs | 253 ++------------------- .../System/Net/Quic/QuicImplementationProviders.cs | 11 + .../src/System/Net/Quic/QuicListener.cs | 133 ++--------- .../src/System/Net/Quic/QuicStream.cs | 198 ++-------------- .../tests/FunctionalTests/QuicConnectionTests.cs | 8 +- 17 files changed, 732 insertions(+), 537 deletions(-) create mode 100644 src/libraries/System.Net.Quic/ref/System.Net.Quic.Temporary.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockImplementationProvider.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockListener.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicConnectionProvider.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicImplementationProvider.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicListenerProvider.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicStreamProvider.cs create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/QuicImplementationProviders.cs diff --git a/src/libraries/System.Net.Quic/ref/System.Net.Quic.Temporary.cs b/src/libraries/System.Net.Quic/ref/System.Net.Quic.Temporary.cs new file mode 100644 index 0000000..4cb50b8 --- /dev/null +++ b/src/libraries/System.Net.Quic/ref/System.Net.Quic.Temporary.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the http://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +using System.Threading; + +namespace System.Net.Quic +{ + public sealed partial class QuicConnection : System.IDisposable + { + public QuicConnection(IPEndPoint remoteEndPoint, System.Net.Security.SslClientAuthenticationOptions sslClientAuthenticationOptions, IPEndPoint localEndPoint = null, System.Net.Quic.Implementations.QuicImplementationProvider implementationProvider = null) { } + } + public sealed partial class QuicListener : IDisposable + { + public QuicListener(IPEndPoint listenEndPoint, System.Net.Security.SslServerAuthenticationOptions sslServerAuthenticationOptions, System.Net.Quic.Implementations.QuicImplementationProvider implementationProvider = null) { } + } + public static class QuicImplementationProviders + { + public static System.Net.Quic.Implementations.QuicImplementationProvider Mock { get { throw null; } } + } +} +namespace System.Net.Quic.Implementations +{ + public abstract class QuicImplementationProvider + { + internal QuicImplementationProvider() { } + } +} 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 6fe9208..8b55f7b 100644 --- a/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs +++ b/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs @@ -9,9 +9,9 @@ using System.Threading; namespace System.Net.Quic { - public sealed class QuicConnection : System.IDisposable + public sealed partial class QuicConnection : System.IDisposable { - public QuicConnection(IPEndPoint remoteEndPoint, System.Net.Security.SslClientAuthenticationOptions sslClientAuthenticationOptions, IPEndPoint localEndPoint = null, bool mock = false) { } + public QuicConnection(IPEndPoint remoteEndPoint, System.Net.Security.SslClientAuthenticationOptions sslClientAuthenticationOptions, IPEndPoint localEndPoint = null) { } public System.Threading.Tasks.ValueTask ConnectAsync(System.Threading.CancellationToken cancellationToken = default) { throw null; } public bool Connected => throw null; public IPEndPoint LocalEndPoint => throw null; @@ -22,9 +22,9 @@ namespace System.Net.Quic public void Close() => throw null; public void Dispose() => throw null; } - public sealed class QuicListener : IDisposable + public sealed partial class QuicListener : IDisposable { - public QuicListener(IPEndPoint listenEndPoint, System.Net.Security.SslServerAuthenticationOptions sslServerAuthenticationOptions, bool mock = false) { } + public QuicListener(IPEndPoint listenEndPoint, System.Net.Security.SslServerAuthenticationOptions sslServerAuthenticationOptions) { } public IPEndPoint ListenEndPoint => throw null; public System.Threading.Tasks.ValueTask AcceptConnectionAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; public void Close() => throw null; diff --git a/src/libraries/System.Net.Quic/ref/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/ref/System.Net.Quic.csproj index 6ba1da7..4bf7220 100644 --- a/src/libraries/System.Net.Quic/ref/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/ref/System.Net.Quic.csproj @@ -3,6 +3,7 @@ netcoreapp-Debug;netcoreapp-Release + 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 ba83e1c..676826e 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -6,6 +6,15 @@ + + + + + + + + + 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 new file mode 100644 index 0000000..f495336 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs @@ -0,0 +1,214 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers.Binary; +using System.Diagnostics; +using System.Net.Security; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic.Implementations.Mock +{ + internal sealed class MockConnection : QuicConnectionProvider + { + private readonly bool _isClient; + private bool _disposed = false; + private IPEndPoint _remoteEndPoint; + private IPEndPoint _localEndPoint; + private object _syncObject = new object(); + private Socket _socket = null; + private IPEndPoint _peerListenEndPoint = null; + private TcpListener _inboundListener = null; + private long _nextOutboundBidirectionalStream; + private long _nextOutboundUnidirectionalStream; + + // Constructor for outbound connections + internal MockConnection(IPEndPoint remoteEndPoint, SslClientAuthenticationOptions sslClientAuthenticationOptions, IPEndPoint localEndPoint = null) + { + _remoteEndPoint = remoteEndPoint; + _localEndPoint = localEndPoint; + + _isClient = true; + _nextOutboundBidirectionalStream = 0; + _nextOutboundUnidirectionalStream = 2; + } + + // Constructor for accepted inbound connections + internal MockConnection(Socket socket, IPEndPoint peerListenEndPoint, TcpListener inboundListener) + { + _isClient = false; + _nextOutboundBidirectionalStream = 1; + _nextOutboundUnidirectionalStream = 3; + _socket = socket; + _peerListenEndPoint = peerListenEndPoint; + _inboundListener = inboundListener; + _localEndPoint = (IPEndPoint)socket.LocalEndPoint; + _remoteEndPoint = (IPEndPoint)socket.RemoteEndPoint; + } + + internal override bool Connected + { + get + { + CheckDisposed(); + + return _socket != null; + } + } + + internal override IPEndPoint LocalEndPoint => new IPEndPoint(_localEndPoint.Address, _localEndPoint.Port); + + internal override IPEndPoint RemoteEndPoint => new IPEndPoint(_remoteEndPoint.Address, _remoteEndPoint.Port); + + internal override async ValueTask ConnectAsync(CancellationToken cancellationToken = default) + { + CheckDisposed(); + + if (Connected) + { + // TODO: Exception text + throw new InvalidOperationException("Already connected"); + } + + Socket socket = new Socket(_remoteEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + await socket.ConnectAsync(_remoteEndPoint).ConfigureAwait(false); + socket.NoDelay = true; + + _localEndPoint = (IPEndPoint)socket.LocalEndPoint; + + // Listen on a new local endpoint for inbound streams + TcpListener inboundListener = new TcpListener(_localEndPoint.Address, 0); + inboundListener.Start(); + int inboundListenPort = ((IPEndPoint)inboundListener.LocalEndpoint).Port; + + // Write inbound listen port to socket so server can read it + byte[] buffer = new byte[4]; + BinaryPrimitives.WriteInt32LittleEndian(buffer, inboundListenPort); + await socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false); + + // Read first 4 bytes to get server listen port + int bytesRead = 0; + do + { + bytesRead += await socket.ReceiveAsync(buffer.AsMemory().Slice(bytesRead), SocketFlags.None).ConfigureAwait(false); + } while (bytesRead != buffer.Length); + + int peerListenPort = BinaryPrimitives.ReadInt32LittleEndian(buffer); + IPEndPoint peerListenEndPoint = new IPEndPoint(((IPEndPoint)socket.RemoteEndPoint).Address, peerListenPort); + + _socket = socket; + _peerListenEndPoint = peerListenEndPoint; + _inboundListener = inboundListener; + } + + internal override QuicStreamProvider OpenUnidirectionalStream() + { + long streamId; + lock (_syncObject) + { + streamId = _nextOutboundUnidirectionalStream; + _nextOutboundUnidirectionalStream += 4; + } + + return new MockStream(this, streamId, bidirectional: false); + } + + internal override QuicStreamProvider OpenBidirectionalStream() + { + long streamId; + lock (_syncObject) + { + streamId = _nextOutboundBidirectionalStream; + _nextOutboundBidirectionalStream += 4; + } + + return new MockStream(this, streamId, bidirectional: true); + } + + internal async Task CreateOutboundMockStreamAsync(long streamId) + { + Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + await socket.ConnectAsync(_peerListenEndPoint).ConfigureAwait(false); + socket.NoDelay = true; + + // Write stream ID to socket so server can read it + byte[] buffer = new byte[8]; + BinaryPrimitives.WriteInt64LittleEndian(buffer, streamId); + await socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false); + + return socket; + } + + internal override async ValueTask AcceptStreamAsync(CancellationToken cancellationToken = default) + { + CheckDisposed(); + + Socket socket = await _inboundListener.AcceptSocketAsync().ConfigureAwait(false); + + // Read first bytes to get stream ID + byte[] buffer = new byte[8]; + int bytesRead = 0; + do + { + bytesRead += await socket.ReceiveAsync(buffer.AsMemory().Slice(bytesRead), SocketFlags.None).ConfigureAwait(false); + } while (bytesRead != buffer.Length); + + long streamId = BinaryPrimitives.ReadInt64LittleEndian(buffer); + + bool clientInitiated = ((streamId & 0b01) == 0); + if (clientInitiated == _isClient) + { + throw new Exception($"Wrong initiator on accepted stream??? streamId={streamId}, _isClient={_isClient}"); + } + + bool bidirectional = ((streamId & 0b10) == 0); + return new MockStream(socket, streamId, bidirectional: bidirectional); + } + + internal override void Close() + { + Dispose(); + } + + private void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(QuicConnection)); + } + } + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _socket?.Dispose(); + _socket = null; + + _inboundListener?.Stop(); + _inboundListener = null; + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + _disposed = true; + } + } + + ~MockConnection() + { + Dispose(false); + } + + public override void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockImplementationProvider.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockImplementationProvider.cs new file mode 100644 index 0000000..fd674c1 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockImplementationProvider.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Security; + +namespace System.Net.Quic.Implementations.Mock +{ + internal sealed class MockImplementationProvider : QuicImplementationProvider + { + internal override QuicListenerProvider CreateListener(IPEndPoint listenEndPoint, SslServerAuthenticationOptions sslServerAuthenticationOptions) + { + return new MockListener(listenEndPoint, sslServerAuthenticationOptions); + } + + internal override QuicConnectionProvider CreateConnection(IPEndPoint remoteEndPoint, SslClientAuthenticationOptions sslClientAuthenticationOptions, IPEndPoint localEndPoint) + { + return new MockConnection(remoteEndPoint, sslClientAuthenticationOptions, localEndPoint); + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockListener.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockListener.cs new file mode 100644 index 0000000..d224111 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockListener.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Sockets; +using System.Net.Security; +using System.Threading.Tasks; +using System.Threading; +using System.Buffers.Binary; + +namespace System.Net.Quic.Implementations.Mock +{ + internal sealed class MockListener : QuicListenerProvider + { + private bool _disposed = false; + private SslServerAuthenticationOptions _sslOptions; + private IPEndPoint _listenEndPoint; + private TcpListener _tcpListener = null; + + internal MockListener(IPEndPoint listenEndPoint, SslServerAuthenticationOptions sslServerAuthenticationOptions) + { + if (listenEndPoint == null) + { + throw new ArgumentNullException(nameof(listenEndPoint)); + } + + _sslOptions = sslServerAuthenticationOptions; + _listenEndPoint = listenEndPoint; + + _tcpListener = new TcpListener(listenEndPoint); + _tcpListener.Start(); + + if (listenEndPoint.Port == 0) + { + // Get auto-assigned port + _listenEndPoint = (IPEndPoint)_tcpListener.LocalEndpoint; + } + } + + // IPEndPoint is mutable, so we must create a new instance every time this is retrieved. + internal override IPEndPoint ListenEndPoint => new IPEndPoint(_listenEndPoint.Address, _listenEndPoint.Port); + + internal override async ValueTask AcceptConnectionAsync(CancellationToken cancellationToken = default) + { + CheckDisposed(); + + Socket socket = await _tcpListener.AcceptSocketAsync().ConfigureAwait(false); + socket.NoDelay = true; + + // Read first 4 bytes to get client listen port + byte[] buffer = new byte[4]; + int bytesRead = 0; + do + { + bytesRead += await socket.ReceiveAsync(buffer.AsMemory().Slice(bytesRead), SocketFlags.None).ConfigureAwait(false); + } while (bytesRead != buffer.Length); + + int peerListenPort = BinaryPrimitives.ReadInt32LittleEndian(buffer); + IPEndPoint peerListenEndPoint = new IPEndPoint(((IPEndPoint)socket.RemoteEndPoint).Address, peerListenPort); + + // Listen on a new local endpoint for inbound streams + TcpListener inboundListener = new TcpListener(_listenEndPoint.Address, 0); + inboundListener.Start(); + int inboundListenPort = ((IPEndPoint)inboundListener.LocalEndpoint).Port; + + // Write inbound listen port to socket so client can read it + BinaryPrimitives.WriteInt32LittleEndian(buffer, inboundListenPort); + await socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false); + + return new MockConnection(socket, peerListenEndPoint, inboundListener); + } + + internal override void Close() + { + Dispose(); + } + + private void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(QuicListener)); + } + } + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _tcpListener?.Stop(); + _tcpListener = null; + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + _disposed = true; + } + } + + ~MockListener() + { + Dispose(false); + } + + public override void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs new file mode 100644 index 0000000..30869a1 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs @@ -0,0 +1,165 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic.Implementations.Mock +{ + internal sealed class MockStream : QuicStreamProvider + { + private bool _disposed = false; + private readonly long _streamId; + private bool _canRead; + private bool _canWrite; + + private MockConnection _connection; + + private Socket _socket = null; + + // Constructor for outbound streams + internal MockStream(MockConnection connection, long streamId, bool bidirectional) + { + _connection = connection; + _streamId = streamId; + _canRead = bidirectional; + _canWrite = true; + } + + // Constructor for inbound streams + internal MockStream(Socket socket, long streamId, bool bidirectional) + { + _socket = socket; + _streamId = streamId; + _canRead = true; + _canWrite = bidirectional; + } + + private async ValueTask ConnectAsync(CancellationToken cancellationToken = default) + { + Debug.Assert(_connection != null, "Stream not connected but no connection???"); + + _socket = await _connection.CreateOutboundMockStreamAsync(_streamId).ConfigureAwait(false); + + // Don't need to hold on to the connection any longer. + _connection = null; + } + + internal override long StreamId + { + get + { + CheckDisposed(); + return _streamId; + } + } + + internal override bool CanRead => _canRead; + + internal override int Read(Span buffer) + { + CheckDisposed(); + + if (!_canRead) + { + throw new NotSupportedException(); + } + + return _socket.Receive(buffer); + } + + internal override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + CheckDisposed(); + + if (!_canRead) + { + throw new NotSupportedException(); + } + + if (_socket == null) + { + await ConnectAsync(cancellationToken).ConfigureAwait(false); + } + + return await _socket.ReceiveAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false); + } + + internal override bool CanWrite => _canWrite; + + internal override void Write(ReadOnlySpan buffer) + { + CheckDisposed(); + + if (!_canWrite) + { + throw new NotSupportedException(); + } + + _socket.Send(buffer); + } + + internal override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + CheckDisposed(); + + if (!_canWrite) + { + throw new NotSupportedException(); + } + + if (_socket == null) + { + await ConnectAsync(cancellationToken).ConfigureAwait(false); + } + + await _socket.SendAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false); + } + + internal override void Flush() + { + CheckDisposed(); + } + + internal override Task FlushAsync(CancellationToken cancellationToken) + { + CheckDisposed(); + + return Task.CompletedTask; + } + + internal override void ShutdownRead() + { + throw new NotImplementedException(); + } + + internal override void ShutdownWrite() + { + CheckDisposed(); + + _socket.Shutdown(SocketShutdown.Send); + } + + private void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(QuicStream)); + } + } + + public override void Dispose() + { + if (!_disposed) + { + _disposed = true; + + _socket?.Dispose(); + _socket = null; + } + } + } +} 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 new file mode 100644 index 0000000..7c0af3d --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicConnectionProvider.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic.Implementations +{ + internal abstract class QuicConnectionProvider : IDisposable + { + internal abstract bool Connected { get; } + + internal abstract IPEndPoint LocalEndPoint { get; } + + internal abstract IPEndPoint RemoteEndPoint { get; } + + internal abstract ValueTask ConnectAsync(CancellationToken cancellationToken = default); + + internal abstract QuicStreamProvider OpenUnidirectionalStream(); + + internal abstract QuicStreamProvider OpenBidirectionalStream(); + + internal abstract ValueTask AcceptStreamAsync(CancellationToken cancellationToken = default); + + internal abstract void Close(); + + public abstract void Dispose(); + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicImplementationProvider.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicImplementationProvider.cs new file mode 100644 index 0000000..d250a2c --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicImplementationProvider.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Net.Security; + +namespace System.Net.Quic.Implementations +{ + public abstract class QuicImplementationProvider + { + internal QuicImplementationProvider() { } + + internal abstract QuicListenerProvider CreateListener(IPEndPoint listenEndPoint, SslServerAuthenticationOptions sslServerAuthenticationOptions); + + internal abstract QuicConnectionProvider CreateConnection(IPEndPoint remoteEndPoint, SslClientAuthenticationOptions sslClientAuthenticationOptions, IPEndPoint localEndPoint); + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicListenerProvider.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicListenerProvider.cs new file mode 100644 index 0000000..7d6819d --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicListenerProvider.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic.Implementations +{ + internal abstract class QuicListenerProvider : IDisposable + { + internal abstract IPEndPoint ListenEndPoint { get; } + + internal abstract ValueTask AcceptConnectionAsync(CancellationToken cancellationToken = default); + + internal abstract void Close(); + + public abstract void Dispose(); + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicStreamProvider.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicStreamProvider.cs new file mode 100644 index 0000000..32956e2 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicStreamProvider.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. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Quic.Implementations +{ + internal abstract class QuicStreamProvider : IDisposable + { + internal abstract long StreamId { get; } + + internal abstract bool CanRead { get; } + + internal abstract int Read(Span buffer); + + internal abstract ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default); + + internal abstract void ShutdownRead(); + + internal abstract bool CanWrite { get; } + + internal abstract void Write(ReadOnlySpan buffer); + + internal abstract ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default); + + internal abstract void ShutdownWrite(); + + internal abstract void Flush(); + + internal abstract Task FlushAsync(CancellationToken cancellationToken); + + public abstract void Dispose(); + } +} 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 0180b79..0832867 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 @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Buffers.Binary; -using System.Diagnostics; +using System.Net.Quic.Implementations; using System.Net.Security; -using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; @@ -13,19 +11,7 @@ namespace System.Net.Quic { public sealed class QuicConnection : IDisposable { - private readonly bool _isClient; - private bool _disposed = false; - private IPEndPoint _remoteEndPoint; - private IPEndPoint _localEndPoint; - private object _syncObject = new object(); - - // !!! TEMPORARY FOR QUIC MOCK SUPPORT - private readonly bool _mock = false; - private Socket _socket = null; - private IPEndPoint _peerListenEndPoint = null; - private TcpListener _inboundListener = null; - private long _nextOutboundBidirectionalStream; - private long _nextOutboundUnidirectionalStream; + private readonly QuicConnectionProvider _provider; /// /// Create an outbound QUIC connection. @@ -33,260 +19,61 @@ namespace System.Net.Quic /// The remote endpoint to connect to. /// TLS options /// The local endpoint to connect from. - /// Use mock QUIC implementation. - // !!! TEMPORARY FOR QUIC MOCK SUPPORT: Remove "mock" parameter before shipping - public QuicConnection(IPEndPoint remoteEndPoint, SslClientAuthenticationOptions sslClientAuthenticationOptions, IPEndPoint localEndPoint = null, bool mock = false) + public QuicConnection(IPEndPoint remoteEndPoint, SslClientAuthenticationOptions sslClientAuthenticationOptions, IPEndPoint localEndPoint = null) + : this(remoteEndPoint, sslClientAuthenticationOptions, localEndPoint, implementationProvider: null) { - // TODO: TLS handling - - _mock = mock; - _remoteEndPoint = remoteEndPoint; - _localEndPoint = localEndPoint; + } - _isClient = true; - _nextOutboundBidirectionalStream = 0; - _nextOutboundUnidirectionalStream = 2; + // !!! TEMPORARY: Remove "implementationProvider" before shipping + public QuicConnection(IPEndPoint remoteEndPoint, SslClientAuthenticationOptions sslClientAuthenticationOptions, IPEndPoint localEndPoint = null, QuicImplementationProvider implementationProvider = null) + { + _provider = implementationProvider.CreateConnection(remoteEndPoint, sslClientAuthenticationOptions, localEndPoint); } - // Constructor for accepted inbound QuicConnections - // !!! TEMPORARY FOR QUIC MOCK SUPPORT - internal QuicConnection(Socket socket, IPEndPoint peerListenEndPoint, TcpListener inboundListener) + internal QuicConnection(QuicConnectionProvider provider) { - _mock = true; - _isClient = false; - _nextOutboundBidirectionalStream = 1; - _nextOutboundUnidirectionalStream = 3; - _socket = socket; - _peerListenEndPoint = peerListenEndPoint; - _inboundListener = inboundListener; - _localEndPoint = (IPEndPoint)socket.LocalEndPoint; - _remoteEndPoint = (IPEndPoint)socket.RemoteEndPoint; + _provider = provider; } /// /// Indicates whether the QuicConnection is connected. /// - public bool Connected - { - get - { - CheckDisposed(); - - if (_mock) - { - return _socket != null; - } - else - { - throw new NotImplementedException(); - } - } - } + public bool Connected => _provider.Connected; - public IPEndPoint LocalEndPoint => new IPEndPoint(_localEndPoint.Address, _localEndPoint.Port); + public IPEndPoint LocalEndPoint => _provider.LocalEndPoint; - public IPEndPoint RemoteEndPoint => new IPEndPoint(_remoteEndPoint.Address, _remoteEndPoint.Port); + public IPEndPoint RemoteEndPoint => _provider.RemoteEndPoint; /// /// Connect to the remote endpoint. /// /// /// - public async ValueTask ConnectAsync(CancellationToken cancellationToken = default) - { - CheckDisposed(); - - if (_mock) - { - if (Connected) - { - // TODO: Exception text - throw new InvalidOperationException("Already connected"); - } - - Socket socket = new Socket(_remoteEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - await socket.ConnectAsync(_remoteEndPoint).ConfigureAwait(false); - socket.NoDelay = true; - - _localEndPoint = (IPEndPoint)socket.LocalEndPoint; - - // Listen on a new local endpoint for inbound streams - TcpListener inboundListener = new TcpListener(_localEndPoint.Address, 0); - inboundListener.Start(); - int inboundListenPort = ((IPEndPoint)inboundListener.LocalEndpoint).Port; - - // Write inbound listen port to socket so server can read it - byte[] buffer = new byte[4]; - BinaryPrimitives.WriteInt32LittleEndian(buffer, inboundListenPort); - await socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false); - - // Read first 4 bytes to get server listen port - int bytesRead = 0; - do - { - bytesRead += await socket.ReceiveAsync(buffer.AsMemory().Slice(bytesRead), SocketFlags.None).ConfigureAwait(false); - } while (bytesRead != buffer.Length); - - int peerListenPort = BinaryPrimitives.ReadInt32LittleEndian(buffer); - IPEndPoint peerListenEndPoint = new IPEndPoint(((IPEndPoint)socket.RemoteEndPoint).Address, peerListenPort); - - _socket = socket; - _peerListenEndPoint = peerListenEndPoint; - _inboundListener = inboundListener; - } - else - { - throw new NotImplementedException(); - } - } + public ValueTask ConnectAsync(CancellationToken cancellationToken = default) => _provider.ConnectAsync(cancellationToken); /// /// Create an outbound unidirectional stream. /// /// - public QuicStream OpenUnidirectionalStream() - { - if (_mock) - { - long streamId; - lock (_syncObject) - { - streamId = _nextOutboundUnidirectionalStream; - _nextOutboundUnidirectionalStream += 4; - } - - return new QuicStream(this, streamId, bidirectional: false); - } - else - { - throw new NotImplementedException(); - } - } + public QuicStream OpenUnidirectionalStream() => new QuicStream(_provider.OpenUnidirectionalStream()); /// /// Create an outbound bidirectional stream. /// /// - public QuicStream OpenBidirectionalStream() - { - if (_mock) - { - long streamId; - lock (_syncObject) - { - streamId = _nextOutboundBidirectionalStream; - _nextOutboundBidirectionalStream += 4; - } - - return new QuicStream(this, streamId, bidirectional: true); - } - else - { - throw new NotImplementedException(); - } - } - - // !!! TEMPORARY FOR QUIC MOCK SUPPORT - internal async Task CreateOutboundMockStreamAsync(long streamId) - { - Debug.Assert(_mock); - Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp); - await socket.ConnectAsync(_peerListenEndPoint).ConfigureAwait(false); - socket.NoDelay = true; - - // Write stream ID to socket so server can read it - byte[] buffer = new byte[8]; - BinaryPrimitives.WriteInt64LittleEndian(buffer, streamId); - await socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false); - - return socket; - } + public QuicStream OpenBidirectionalStream() => new QuicStream(_provider.OpenBidirectionalStream()); /// /// Accept an incoming stream. /// /// - public async ValueTask AcceptStreamAsync(CancellationToken cancellationToken = default) - { - CheckDisposed(); - - if (_mock) - { - Socket socket = await _inboundListener.AcceptSocketAsync().ConfigureAwait(false); - - // Read first bytes to get stream ID - byte[] buffer = new byte[8]; - int bytesRead = 0; - do - { - bytesRead += await socket.ReceiveAsync(buffer.AsMemory().Slice(bytesRead), SocketFlags.None).ConfigureAwait(false); - } while (bytesRead != buffer.Length); - - long streamId = BinaryPrimitives.ReadInt64LittleEndian(buffer); - - bool clientInitiated = ((streamId & 0b01) == 0); - if (clientInitiated == _isClient) - { - throw new Exception($"Wrong initiator on accepted stream??? streamId={streamId}, _isClient={_isClient}"); - } - - bool bidirectional = ((streamId & 0b10) == 0); - return new QuicStream(socket, streamId, bidirectional: bidirectional); - } - else - { - throw new NotImplementedException(); - } - } + public async ValueTask AcceptStreamAsync(CancellationToken cancellationToken = default) => new QuicStream(await _provider.AcceptStreamAsync(cancellationToken).ConfigureAwait(false)); /// /// Close the connection and terminate any active streams. /// - public void Close() - { - Dispose(); - } + public void Close() => _provider.Close(); - private void CheckDisposed() - { - if (_disposed) - { - throw new ObjectDisposedException(nameof(QuicConnection)); - } - } - - private void Dispose(bool disposing) - { - if (!_disposed) - { - if (disposing) - { - if (_mock) - { - _socket?.Dispose(); - _socket = null; - - _inboundListener?.Stop(); - _inboundListener = null; - } - } - - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - _disposed = true; - } - } - - ~QuicConnection() - { - Dispose(false); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() => _provider.Dispose(); } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicImplementationProviders.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicImplementationProviders.cs new file mode 100644 index 0000000..7cc5ef2 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicImplementationProviders.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Quic +{ + public static class QuicImplementationProviders + { + public static Implementations.QuicImplementationProvider Mock { get; } = new Implementations.Mock.MockImplementationProvider(); + } +} 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 fc36baf..7a3348b 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 @@ -2,150 +2,47 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Net.Sockets; +using System.Net.Quic.Implementations; using System.Net.Security; -using System.Threading.Tasks; using System.Threading; -using System.Buffers.Binary; +using System.Threading.Tasks; namespace System.Net.Quic { public sealed class QuicListener : IDisposable { - private bool _disposed = false; - private SslServerAuthenticationOptions _sslOptions; - private IPEndPoint _listenEndPoint; - - // !!! TEMPORARY FOR QUIC MOCK SUPPORT - private bool _mock = false; - private TcpListener _tcpListener = null; + private readonly QuicListenerProvider _provider; /// /// Create a QUIC listener on the specified local endpoint and start listening. /// /// The local endpoint to listen on. /// TLS options for the listener. - /// Use mock QUIC implementation. - // !!! TEMPORARY FOR QUIC MOCK SUPPORT: Remove "mock" parameter before shipping - public QuicListener(IPEndPoint listenEndPoint, SslServerAuthenticationOptions sslServerAuthenticationOptions, bool mock = false) + public QuicListener(IPEndPoint listenEndPoint, SslServerAuthenticationOptions sslServerAuthenticationOptions) + : this(listenEndPoint, sslServerAuthenticationOptions, implementationProvider: null) { - if (sslServerAuthenticationOptions == null && !mock) - { - throw new ArgumentNullException(nameof(sslServerAuthenticationOptions)); - } - - if (listenEndPoint == null) - { - throw new ArgumentNullException(nameof(listenEndPoint)); - } - - _sslOptions = sslServerAuthenticationOptions; - _listenEndPoint = listenEndPoint; - - _mock = mock; - if (mock) - { - _tcpListener = new TcpListener(listenEndPoint); - _tcpListener.Start(); + } - if (listenEndPoint.Port == 0) - { - // Get auto-assigned port - _listenEndPoint = (IPEndPoint)_tcpListener.LocalEndpoint; - } - } - else - { - throw new NotImplementedException(); - } + // !!! TEMPORARY: Remove "implementationProvider" before shipping + public QuicListener(IPEndPoint listenEndPoint, SslServerAuthenticationOptions sslServerAuthenticationOptions, QuicImplementationProvider implementationProvider = null) + { + _provider = implementationProvider.CreateListener(listenEndPoint, sslServerAuthenticationOptions); } - // IPEndPoint is mutable, so we must create a new instance every time this is retrieved. - public IPEndPoint ListenEndPoint => new IPEndPoint(_listenEndPoint.Address, _listenEndPoint.Port); + public IPEndPoint ListenEndPoint => _provider.ListenEndPoint; /// /// Accept a connection. /// /// - public async ValueTask AcceptConnectionAsync(CancellationToken cancellationToken = default) - { - CheckDisposed(); - - if (_mock) - { - Socket socket = await _tcpListener.AcceptSocketAsync().ConfigureAwait(false); - socket.NoDelay = true; - - // Read first 4 bytes to get client listen port - byte[] buffer = new byte[4]; - int bytesRead = 0; - do - { - bytesRead += await socket.ReceiveAsync(buffer.AsMemory().Slice(bytesRead), SocketFlags.None).ConfigureAwait(false); - } while (bytesRead != buffer.Length); - - int peerListenPort = BinaryPrimitives.ReadInt32LittleEndian(buffer); - IPEndPoint peerListenEndPoint = new IPEndPoint(((IPEndPoint)socket.RemoteEndPoint).Address, peerListenPort); - - // Listen on a new local endpoint for inbound streams - TcpListener inboundListener = new TcpListener(_listenEndPoint.Address, 0); - inboundListener.Start(); - int inboundListenPort = ((IPEndPoint)inboundListener.LocalEndpoint).Port; - - // Write inbound listen port to socket so client can read it - BinaryPrimitives.WriteInt32LittleEndian(buffer, inboundListenPort); - await socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false); - - return new QuicConnection(socket, peerListenEndPoint, inboundListener); - } - else - { - throw new NotImplementedException(); - } - } + public async ValueTask AcceptConnectionAsync(CancellationToken cancellationToken = default) => + new QuicConnection(await _provider.AcceptConnectionAsync(cancellationToken).ConfigureAwait(false)); /// /// Stop listening and close the listener. /// - public void Close() - { - Dispose(); - } - - private void CheckDisposed() - { - if (_disposed) - { - throw new ObjectDisposedException(nameof(QuicListener)); - } - } - - private void Dispose(bool disposing) - { - if (!_disposed) - { - if (disposing) - { - _tcpListener?.Stop(); - _tcpListener = null; - } - - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. + public void Close() => _provider.Close(); - _disposed = true; - } - } - - ~QuicListener() - { - Dispose(false); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() => _provider.Dispose(); } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs index 22df00c..ac1643f 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics; using System.IO; -using System.Net.Security; -using System.Net.Sockets; +using System.Net.Quic.Implementations; using System.Threading; using System.Threading.Tasks; @@ -13,37 +11,16 @@ namespace System.Net.Quic { public sealed class QuicStream : Stream { - private bool _disposed = false; - private readonly long _streamId; - private bool _canRead; - private bool _canWrite; - private QuicConnection _connection; + private readonly QuicStreamProvider _provider; - // !!! TEMPORARY FOR QUIC MOCK SUPPORT - private readonly bool _mock = false; - private Socket _socket = null; - - // Constructor for outbound streams - // !!! TEMPORARY FOR QUIC MOCK SUPPORT - internal QuicStream(QuicConnection connection, long streamId, bool bidirectional) + internal QuicStream(QuicStreamProvider provider) { - _mock = true; - _connection = connection; - _streamId = streamId; - _canRead = bidirectional; - _canWrite = true; + _provider = provider; } - // Constructor for inbound streams - // !!! TEMPORARY FOR QUIC MOCK SUPPORT - internal QuicStream(Socket socket, long streamId, bool bidirectional) - { - _mock = true; - _socket = socket; - _streamId = streamId; - _canRead = true; - _canWrite = bidirectional; - } + // + // Boilerplate implementation stuff + // public override bool CanSeek => false; public override long Length => throw new NotSupportedException(); @@ -105,172 +82,37 @@ namespace System.Net.Quic return WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); } - private async ValueTask ConnectAsync(CancellationToken cancellationToken = default) - { - Debug.Assert(_mock); - Debug.Assert(_connection != null, "Stream not connected but no connection???"); - - _socket = await _connection.CreateOutboundMockStreamAsync(_streamId).ConfigureAwait(false); - - // Don't need to hold on to the connection any longer. - _connection = null; - } - /// /// QUIC stream ID. /// - public long StreamId - { - get - { - CheckDisposed(); - return _streamId; - } - } + public long StreamId => _provider.StreamId; - public override bool CanRead => _canRead; + public override bool CanRead => _provider.CanRead; - public override int Read(Span buffer) - { - CheckDisposed(); - - if (!_canRead) - { - throw new NotSupportedException(); - } - - if (_mock) - { - return _socket.Receive(buffer); - } - else - { - throw new NotImplementedException(); - } - } + public override int Read(Span buffer) => _provider.Read(buffer); - public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) - { - CheckDisposed(); + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) => _provider.ReadAsync(buffer, cancellationToken); - if (!_canRead) - { - throw new NotSupportedException(); - } + public override bool CanWrite => _provider.CanWrite; - if (_mock) - { - if (_socket == null) - { - await ConnectAsync(cancellationToken).ConfigureAwait(false); - } + public override void Write(ReadOnlySpan buffer) => _provider.Write(buffer); - return await _socket.ReceiveAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false); - } - else - { - throw new NotImplementedException(); - } - } + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => _provider.WriteAsync(buffer, cancellationToken); - public override bool CanWrite => _canWrite; + public override void Flush() => _provider.Flush(); - public override void Write(ReadOnlySpan buffer) - { - CheckDisposed(); + public override Task FlushAsync(CancellationToken cancellationToken) => _provider.FlushAsync(cancellationToken); - if (!_canWrite) - { - throw new NotSupportedException(); - } + public void ShutdownRead() => _provider.ShutdownRead(); - if (_mock) - { - _socket.Send(buffer); - } - else - { - throw new NotImplementedException(); - } - } - - public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { - CheckDisposed(); - - if (!_canWrite) - { - throw new NotSupportedException(); - } - - if (_mock) - { - if (_socket == null) - { - await ConnectAsync(cancellationToken).ConfigureAwait(false); - } - - await _socket.SendAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false); - } - else - { - throw new NotImplementedException(); - } - } - - public override void Flush() - { - CheckDisposed(); - } - - public override Task FlushAsync(CancellationToken cancellationToken) - { - CheckDisposed(); - - return Task.CompletedTask; - } - - public void ShutdownRead() - { - throw new NotImplementedException(); - } - - public void ShutdownWrite() - { - CheckDisposed(); - - if (_mock) - { - _socket.Shutdown(SocketShutdown.Send); - } - else - { - throw new NotImplementedException(); - } - } - - private void CheckDisposed() - { - if (_disposed) - { - throw new ObjectDisposedException(nameof(QuicStream)); - } - } + public void ShutdownWrite() => _provider.ShutdownWrite(); protected override void Dispose(bool disposing) { - if (!_disposed) + if (disposing) { - _disposed = true; - - if (_mock) - { - _socket?.Dispose(); - _socket = null; - } + _provider.Dispose(); } - - base.Dispose(disposing); } } } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs index a13a353..c049fa8 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs @@ -18,7 +18,7 @@ namespace System.Net.Quic.Tests [Fact] public async Task BasicTest() { - using (QuicListener listener = new QuicListener(new IPEndPoint(IPAddress.Loopback, 0), sslServerAuthenticationOptions: null, mock: true)) + using (QuicListener listener = new QuicListener(new IPEndPoint(IPAddress.Loopback, 0), sslServerAuthenticationOptions: null, implementationProvider: QuicImplementationProviders.Mock)) { IPEndPoint listenEndPoint = listener.ListenEndPoint; @@ -26,7 +26,7 @@ namespace System.Net.Quic.Tests Task.Run(async () => { // Client code - using (QuicConnection connection = new QuicConnection(listenEndPoint, sslClientAuthenticationOptions: null, mock: true)) + using (QuicConnection connection = new QuicConnection(listenEndPoint, sslClientAuthenticationOptions: null, implementationProvider: QuicImplementationProviders.Mock)) { await connection.ConnectAsync(); using (QuicStream stream = connection.OpenBidirectionalStream()) @@ -55,11 +55,11 @@ namespace System.Net.Quic.Tests [Fact] public async Task TestStreams() { - using (QuicListener listener = new QuicListener(new IPEndPoint(IPAddress.Loopback, 0), sslServerAuthenticationOptions: null, mock: true)) + using (QuicListener listener = new QuicListener(new IPEndPoint(IPAddress.Loopback, 0), sslServerAuthenticationOptions: null, implementationProvider: QuicImplementationProviders.Mock)) { IPEndPoint listenEndPoint = listener.ListenEndPoint; - using (QuicConnection clientConnection = new QuicConnection(listenEndPoint, sslClientAuthenticationOptions: null, mock: true)) + using (QuicConnection clientConnection = new QuicConnection(listenEndPoint, sslClientAuthenticationOptions: null, implementationProvider: QuicImplementationProviders.Mock)) { Assert.False(clientConnection.Connected); Assert.Equal(listenEndPoint, clientConnection.RemoteEndPoint); -- 2.7.4