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 QuicConnection #71783

Merged
merged 23 commits into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ public async Task CloseAsync(long errorCode)

public async ValueTask<Http3LoopbackStream> OpenUnidirectionalStreamAsync()
{
return new Http3LoopbackStream(await _connection.OpenUnidirectionalStreamAsync());
return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional));
}

public async ValueTask<Http3LoopbackStream> OpenBidirectionalStreamAsync()
{
return new Http3LoopbackStream(await _connection.OpenBidirectionalStreamAsync());
return new Http3LoopbackStream(await _connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional));
}

public static int GetRequestId(QuicStream stream)
Expand Down Expand Up @@ -131,7 +131,7 @@ async Task EnsureControlStreamAcceptedInternalAsync()

while (true)
{
QuicStream quicStream = await _connection.AcceptStreamAsync().ConfigureAwait(false);
QuicStream quicStream = await _connection.AcceptInboundStreamAsync().ConfigureAwait(false);

if (!quicStream.CanWrite)
{
Expand Down Expand Up @@ -165,7 +165,7 @@ public async Task<Http3LoopbackStream> AcceptRequestStreamAsync()

if (!_delayedStreams.TryDequeue(out QuicStream quicStream))
{
quicStream = await _connection.AcceptStreamAsync().ConfigureAwait(false);
quicStream = await _connection.AcceptInboundStreamAsync().ConfigureAwait(false);
}

var stream = new Http3LoopbackStream(quicStream);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ public Http3LoopbackServer(Http3Options options = null)
{
var serverOptions = new QuicServerConnectionOptions()
{
MaxBidirectionalStreams = options.MaxBidirectionalStreams,
MaxUnidirectionalStreams = options.MaxUnidirectionalStreams,
DefaultStreamErrorCode = Http3LoopbackConnection.H3_REQUEST_CANCELLED,
DefaultCloseErrorCode = Http3LoopbackConnection.H3_NO_ERROR,
MaxInboundBidirectionalStreams = options.MaxInboundBidirectionalStreams,
MaxInboundUnidirectionalStreams = options.MaxInboundUnidirectionalStreams,
ServerAuthenticationOptions = new SslServerAuthenticationOptions
{
EnabledSslProtocols = options.SslProtocols,
Expand Down Expand Up @@ -136,16 +138,16 @@ private static Http3Options CreateOptions(GenericLoopbackOptions options)
}
public class Http3Options : GenericLoopbackOptions
{
public int MaxUnidirectionalStreams { get; set; }
public int MaxInboundUnidirectionalStreams { get; set; }

public int MaxBidirectionalStreams { get; set; }
public int MaxInboundBidirectionalStreams { get; set; }

public string Alpn { get; set; }

public Http3Options()
{
MaxUnidirectionalStreams = 10;
MaxBidirectionalStreams = 100;
MaxInboundUnidirectionalStreams = 10;
MaxInboundBidirectionalStreams = 100;
Alpn = SslApplicationProtocol.Http3.ToString();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,22 +107,22 @@ public static async ValueTask<SslStream> EstablishSslConnectionAsync(SslClientAu
public static async ValueTask<QuicConnection> ConnectQuicAsync(HttpRequestMessage request, DnsEndPoint endPoint, TimeSpan idleTimeout, SslClientAuthenticationOptions clientAuthenticationOptions, CancellationToken cancellationToken)
{
clientAuthenticationOptions = SetUpRemoteCertificateValidationCallback(clientAuthenticationOptions, request);
QuicConnection connection = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions()
{
MaxBidirectionalStreams = 0, // Client doesn't support inbound streams: https://www.rfc-editor.org/rfc/rfc9114.html#name-bidirectional-streams. An extension might change this.
MaxUnidirectionalStreams = 5, // Minimum is 3: https://www.rfc-editor.org/rfc/rfc9114.html#unidirectional-streams (1x control stream + 2x QPACK). Set to 100 if/when support for PUSH streams is added.
IdleTimeout = idleTimeout,
RemoteEndPoint = endPoint,
ClientAuthenticationOptions = clientAuthenticationOptions
}, cancellationToken).ConfigureAwait(false);

try
{
await connection.ConnectAsync(cancellationToken).ConfigureAwait(false);
return connection;
return await QuicConnection.ConnectAsync(new QuicClientConnectionOptions()
{
MaxInboundBidirectionalStreams = 0, // Client doesn't support inbound streams: https://www.rfc-editor.org/rfc/rfc9114.html#name-bidirectional-streams. An extension might change this.
MaxInboundUnidirectionalStreams = 5, // Minimum is 3: https://www.rfc-editor.org/rfc/rfc9114.html#unidirectional-streams (1x control stream + 2x QPACK). Set to 100 if/when support for PUSH streams is added.
IdleTimeout = idleTimeout,
DefaultStreamErrorCode = (long)Http3ErrorCode.RequestCancelled,
DefaultCloseErrorCode = (long)Http3ErrorCode.NoError,
RemoteEndPoint = endPoint,
ClientAuthenticationOptions = clientAuthenticationOptions
}, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
connection.Dispose();
throw CreateWrappedException(ex, endPoint.Host, endPoint.Port, cancellationToken);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private void CheckForShutdown()
QuicConnection connection = _connection;
_connection = null;

_ = _connectionClosedTask.ContinueWith(closeTask =>
_ = _connectionClosedTask.ContinueWith(async closeTask =>
{
if (closeTask.IsFaulted && NetEventSource.Log.IsEnabled())
{
Expand All @@ -141,7 +141,7 @@ private void CheckForShutdown()

try
{
connection.Dispose();
await connection.DisposeAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -184,7 +184,7 @@ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, lon
queueStartingTimestamp = Stopwatch.GetTimestamp();
}

quicStream = await conn.OpenBidirectionalStreamAsync(cancellationToken).ConfigureAwait(false);
quicStream = await conn.OpenOutboundStreamAsync(QuicStreamType.Bidirectional, cancellationToken).ConfigureAwait(false);

requestStream = new Http3RequestStream(request, this, quicStream);
lock (SyncObj)
Expand Down Expand Up @@ -366,7 +366,7 @@ private async Task SendSettingsAsync()
{
try
{
_clientControl = await _connection!.OpenUnidirectionalStreamAsync().ConfigureAwait(false);
_clientControl = await _connection!.OpenOutboundStreamAsync(QuicStreamType.Unidirectional).ConfigureAwait(false);
await _clientControl.WriteAsync(_pool.Settings.Http3SettingsFrame, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
Expand Down Expand Up @@ -410,7 +410,7 @@ private async Task AcceptStreamsAsync()
}

// No cancellation token is needed here; we expect the operation to cancel itself when _connection is disposed.
streamTask = _connection!.AcceptStreamAsync(CancellationToken.None);
streamTask = _connection!.AcceptInboundStreamAsync(CancellationToken.None);
}

QuicStream stream = await streamTask.ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public async Task ClientSettingsReceived_Success(int headerSizeLimit)
[InlineData(1000)]
public async Task SendMoreThanStreamLimitRequests_Succeeds(int streamLimit)
{
using Http3LoopbackServer server = CreateHttp3LoopbackServer(new Http3Options() { MaxBidirectionalStreams = streamLimit });
using Http3LoopbackServer server = CreateHttp3LoopbackServer(new Http3Options() { MaxInboundBidirectionalStreams = streamLimit });

Task serverTask = Task.Run(async () =>
{
Expand Down Expand Up @@ -119,7 +119,7 @@ public async Task SendMoreThanStreamLimitRequests_Succeeds(int streamLimit)
[InlineData(1000)]
public async Task SendStreamLimitRequestsConcurrently_Succeeds(int streamLimit)
{
using Http3LoopbackServer server = CreateHttp3LoopbackServer(new Http3Options() { MaxBidirectionalStreams = streamLimit });
using Http3LoopbackServer server = CreateHttp3LoopbackServer(new Http3Options() { MaxInboundBidirectionalStreams = streamLimit });

Task serverTask = Task.Run(async () =>
{
Expand Down Expand Up @@ -165,7 +165,7 @@ public async Task SendStreamLimitRequestsConcurrently_Succeeds(int streamLimit)
[InlineData(1000)]
public async Task SendMoreThanStreamLimitRequestsConcurrently_LastWaits(int streamLimit)
{
using Http3LoopbackServer server = CreateHttp3LoopbackServer(new Http3Options() { MaxBidirectionalStreams = streamLimit });
using Http3LoopbackServer server = CreateHttp3LoopbackServer(new Http3Options() { MaxInboundBidirectionalStreams = streamLimit });
var lastRequestContentStarted = new TaskCompletionSource();

Task serverTask = Task.Run(async () =>
Expand Down Expand Up @@ -890,31 +890,10 @@ public async Task Alpn_NonH3_NegotiationFailure()

private SslApplicationProtocol ExtractMsQuicNegotiatedAlpn(Http3LoopbackConnection loopbackConnection)
{
// TODO: rewrite after object structure change
// current structure:
// Http3LoopbackConnection -> private QuicConnection _connection
// QuicConnection -> private QuicConnectionProvider _provider (= MsQuicConnection)
// MsQuicConnection -> private SslApplicationProtocol _negotiatedAlpnProtocol

FieldInfo quicConnectionField = loopbackConnection.GetType().GetField("_connection", BindingFlags.Instance | BindingFlags.NonPublic);
Assert.NotNull(quicConnectionField);
object quicConnection = quicConnectionField.GetValue(loopbackConnection);
Assert.NotNull(quicConnection);
Assert.Equal("QuicConnection", quicConnection.GetType().Name);

FieldInfo msQuicConnectionField = quicConnection.GetType().GetField("_provider", BindingFlags.Instance | BindingFlags.NonPublic);
Assert.NotNull(msQuicConnectionField);
object msQuicConnection = msQuicConnectionField.GetValue(quicConnection);
Assert.NotNull(msQuicConnection);
Assert.Equal("MsQuicConnection", msQuicConnection.GetType().Name);

FieldInfo alpnField = msQuicConnection.GetType().GetField("_negotiatedAlpnProtocol", BindingFlags.Instance | BindingFlags.NonPublic);
Assert.NotNull(alpnField);
object alpn = alpnField.GetValue(msQuicConnection);
Assert.NotNull(alpn);
Assert.IsType<SslApplicationProtocol>(alpn);

return (SslApplicationProtocol)alpn;
QuicConnection quicConnection = Assert.IsType<QuicConnection>(quicConnectionField.GetValue(loopbackConnection));
return quicConnection.NegotiatedApplicationProtocol;
}

[Theory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ await GetFactoryForVersion(version).CreateClientAndServerAsync(
await connection.ReadRequestDataAsync(readBody: false);
await connection.SendResponseAsync();
};
}, options: new Http3Options { MaxBidirectionalStreams = 1 });
}, options: new Http3Options { MaxInboundBidirectionalStreams = 1 });

await WaitForEventCountersAsync(events);
});
Expand Down
41 changes: 22 additions & 19 deletions src/libraries/System.Net.Quic/ref/System.Net.Quic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,33 @@ namespace System.Net.Quic
public sealed partial class QuicClientConnectionOptions : System.Net.Quic.QuicConnectionOptions
{
public QuicClientConnectionOptions() { }
public required System.Net.Security.SslClientAuthenticationOptions ClientAuthenticationOptions { get { throw null; } set { } }
public System.Net.Security.SslClientAuthenticationOptions ClientAuthenticationOptions { get { throw null; } set { } }
public System.Net.IPEndPoint? LocalEndPoint { get { throw null; } set { } }
public required System.Net.EndPoint RemoteEndPoint { get { throw null; } set { } }
public System.Net.EndPoint RemoteEndPoint { get { throw null; } set { } }
}
public sealed partial class QuicConnection : System.IDisposable
public sealed partial class QuicConnection : System.IAsyncDisposable
{
internal QuicConnection() { }
public bool Connected { get { throw null; } }
public static bool IsSupported { 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.Security.Cryptography.X509Certificates.X509Certificate? RemoteCertificate { get { throw null; } }
public System.Net.EndPoint RemoteEndPoint { get { throw null; } }
public System.Threading.Tasks.ValueTask<System.Net.Quic.QuicStream> AcceptStreamAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public System.Net.IPEndPoint RemoteEndPoint { get { throw null; } }
public System.Threading.Tasks.ValueTask<System.Net.Quic.QuicStream> AcceptInboundStreamAsync(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 static System.Threading.Tasks.ValueTask<System.Net.Quic.QuicConnection> ConnectAsync(System.Net.Quic.QuicClientConnectionOptions options, 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 int GetRemoteAvailableBidirectionalStreamCount() { throw null; }
public int GetRemoteAvailableUnidirectionalStreamCount() { throw null; }
public System.Threading.Tasks.ValueTask<System.Net.Quic.QuicStream> OpenBidirectionalStreamAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public System.Threading.Tasks.ValueTask<System.Net.Quic.QuicStream> OpenUnidirectionalStreamAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public System.Threading.Tasks.ValueTask DisposeAsync() { throw null; }
public System.Threading.Tasks.ValueTask<System.Net.Quic.QuicStream> OpenOutboundStreamAsync(System.Net.Quic.QuicStreamType type, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public override string ToString() { throw null; }
}
public abstract partial class QuicConnectionOptions
{
internal QuicConnectionOptions() { }
public long DefaultCloseErrorCode { get { throw null; } set { } }
public long DefaultStreamErrorCode { get { throw null; } set { } }
public System.TimeSpan IdleTimeout { get { throw null; } set { } }
public int MaxBidirectionalStreams { get { throw null; } set { } }
public int MaxUnidirectionalStreams { get { throw null; } set { } }
public int MaxInboundBidirectionalStreams { get { throw null; } set { } }
public int MaxInboundUnidirectionalStreams { get { throw null; } set { } }
}
public enum QuicError
{
Expand Down Expand Up @@ -74,15 +72,15 @@ internal QuicListener() { }
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 System.Collections.Generic.List<System.Net.Security.SslApplicationProtocol> ApplicationProtocols { get { throw null; } set { } }
public 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 required System.Net.IPEndPoint ListenEndPoint { get { throw null; } set { } }
public System.Net.IPEndPoint ListenEndPoint { get { throw null; } set { } }
}
public sealed partial class QuicServerConnectionOptions : System.Net.Quic.QuicConnectionOptions
{
public QuicServerConnectionOptions() { }
public required System.Net.Security.SslServerAuthenticationOptions ServerAuthenticationOptions { get { throw null; } set { } }
public System.Net.Security.SslServerAuthenticationOptions ServerAuthenticationOptions { get { throw null; } set { } }
}
public sealed partial class QuicStream : System.IO.Stream
{
Expand Down Expand Up @@ -125,4 +123,9 @@ public override void Write(System.ReadOnlySpan<byte> buffer) { }
public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory<byte> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public override void WriteByte(byte value) { }
}
public enum QuicStreamType
{
Unidirectional = 0,
Bidirectional = 1,
}
}
Loading