未验证 提交 aa156dbb 编写于 作者: R Radek Zikmund 提交者: GitHub

Implement new QuicException proposal (#71432)

* Add QuicException as in proposal

* Map other MsQuic errors to QuicException

* Fix System.Net.Http

* Always set HResult in QUIC-related exception

* Fix possible incorrect exception when writing into aborted stream

* Code review feedback

* Fix ref files

* Use latest generated interop

* Remove MsQuicException

* Code review feedback

* Remove TODO

* move ThrowHelper to Internal

* Code review changes

* Minor change
上级 2948bb9a
...@@ -251,7 +251,7 @@ public async Task ShutdownAsync(bool failCurrentRequest = false) ...@@ -251,7 +251,7 @@ public async Task ShutdownAsync(bool failCurrentRequest = false)
long firstInvalidStreamId = failCurrentRequest ? _currentStreamId : _currentStreamId + 4; long firstInvalidStreamId = failCurrentRequest ? _currentStreamId : _currentStreamId + 4;
await _outboundControlStream.SendGoAwayFrameAsync(firstInvalidStreamId); await _outboundControlStream.SendGoAwayFrameAsync(firstInvalidStreamId);
} }
catch (QuicConnectionAbortedException abortException) when (abortException.ErrorCode == H3_NO_ERROR) catch (QuicException abortException) when (abortException.QuicError == QuicError.ConnectionAborted && abortException.ApplicationErrorCode == H3_NO_ERROR)
{ {
// Client must have closed the connection already because the HttpClientHandler instance was disposed. // Client must have closed the connection already because the HttpClientHandler instance was disposed.
// So nothing to do. // So nothing to do.
...@@ -288,7 +288,7 @@ public async Task WaitForClientDisconnectAsync(bool refuseNewRequests = true) ...@@ -288,7 +288,7 @@ public async Task WaitForClientDisconnectAsync(bool refuseNewRequests = true)
throw new Exception("Unexpected request stream received while waiting for client disconnect"); throw new Exception("Unexpected request stream received while waiting for client disconnect");
} }
} }
catch (QuicConnectionAbortedException abortException) when (abortException.ErrorCode == H3_NO_ERROR) catch (QuicException abortException) when (abortException.QuicError == QuicError.ConnectionAborted && abortException.ApplicationErrorCode == H3_NO_ERROR)
{ {
break; break;
} }
...@@ -301,7 +301,8 @@ public async Task WaitForClientDisconnectAsync(bool refuseNewRequests = true) ...@@ -301,7 +301,8 @@ public async Task WaitForClientDisconnectAsync(bool refuseNewRequests = true)
// The client's control stream should throw QuicConnectionAbortedException, indicating that it was // The client's control stream should throw QuicConnectionAbortedException, indicating that it was
// aborted because the connection was closed (and was not explicitly closed or aborted prior to the connection being closed) // aborted because the connection was closed (and was not explicitly closed or aborted prior to the connection being closed)
await Assert.ThrowsAsync<QuicConnectionAbortedException>(async () => await _inboundControlStream.ReadFrameAsync()); QuicException ex = await Assert.ThrowsAsync<QuicException>(async () => await _inboundControlStream.ReadFrameAsync());
Assert.Equal(QuicError.ConnectionAborted, ex.QuicError);
await CloseAsync(H3_NO_ERROR); await CloseAsync(H3_NO_ERROR);
} }
......
...@@ -379,7 +379,7 @@ async Task WaitForReadCancellation() ...@@ -379,7 +379,7 @@ async Task WaitForReadCancellation()
} }
} }
} }
catch (QuicStreamAbortedException ex) when (ex.ErrorCode == Http3LoopbackConnection.H3_REQUEST_CANCELLED) catch (QuicException ex) when (ex.QuicError == QuicError.StreamAborted && ex.ApplicationErrorCode == Http3LoopbackConnection.H3_REQUEST_CANCELLED)
{ {
readCanceled = true; readCanceled = true;
} }
...@@ -391,7 +391,7 @@ async Task WaitForWriteCancellation() ...@@ -391,7 +391,7 @@ async Task WaitForWriteCancellation()
{ {
await _stream.WaitForWriteCompletionAsync(); await _stream.WaitForWriteCompletionAsync();
} }
catch (QuicStreamAbortedException ex) when (ex.ErrorCode == Http3LoopbackConnection.H3_REQUEST_CANCELLED) catch (QuicException ex) when (ex.QuicError == QuicError.StreamAborted && ex.ApplicationErrorCode == Http3LoopbackConnection.H3_REQUEST_CANCELLED)
{ {
writeCanceled = true; writeCanceled = true;
} }
......
...@@ -196,7 +196,7 @@ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, lon ...@@ -196,7 +196,7 @@ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, lon
// Swallow any exceptions caused by the connection being closed locally or even disposed due to a race. // Swallow any exceptions caused by the connection being closed locally or even disposed due to a race.
// Since quicStream will stay `null`, the code below will throw appropriate exception to retry the request. // Since quicStream will stay `null`, the code below will throw appropriate exception to retry the request.
catch (ObjectDisposedException) { } catch (ObjectDisposedException) { }
catch (QuicException e) when (!(e is QuicConnectionAbortedException)) { } catch (QuicException e) when (e.QuicError != QuicError.OperationAborted) { }
finally finally
{ {
if (HttpTelemetry.Log.IsEnabled() && queueStartingTimestamp != 0) if (HttpTelemetry.Log.IsEnabled() && queueStartingTimestamp != 0)
...@@ -232,11 +232,13 @@ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, lon ...@@ -232,11 +232,13 @@ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, lon
return await responseTask.ConfigureAwait(false); return await responseTask.ConfigureAwait(false);
} }
catch (QuicConnectionAbortedException ex) catch (QuicException ex) when (ex.QuicError == QuicError.ConnectionAborted)
{ {
Debug.Assert(ex.ApplicationErrorCode.HasValue);
// This will happen if we aborted _connection somewhere. // This will happen if we aborted _connection somewhere.
Abort(ex); Abort(ex);
throw new HttpRequestException(SR.Format(SR.net_http_http3_connection_error, ex.ErrorCode), ex, RequestRetryType.RetryOnConnectionFailure); throw new HttpRequestException(SR.Format(SR.net_http_http3_connection_error, ex.ApplicationErrorCode.Value), ex, RequestRetryType.RetryOnConnectionFailure);
} }
finally finally
{ {
...@@ -417,7 +419,7 @@ private async Task AcceptStreamsAsync() ...@@ -417,7 +419,7 @@ private async Task AcceptStreamsAsync()
_ = ProcessServerStreamAsync(stream); _ = ProcessServerStreamAsync(stream);
} }
} }
catch (QuicOperationAbortedException) catch (QuicException ex) when (ex.QuicError == QuicError.OperationAborted)
{ {
// Shutdown initiated by us, no need to abort. // Shutdown initiated by us, no need to abort.
} }
...@@ -452,7 +454,7 @@ await using (stream.ConfigureAwait(false)) ...@@ -452,7 +454,7 @@ await using (stream.ConfigureAwait(false))
{ {
bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false); bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);
} }
catch (QuicStreamAbortedException) catch (QuicException ex) when (ex.QuicError == QuicError.StreamAborted)
{ {
// Treat identical to receiving 0. See below comment. // Treat identical to receiving 0. See below comment.
bytesRead = 0; bytesRead = 0;
......
...@@ -228,23 +228,27 @@ public async Task<HttpResponseMessage> SendAsync(CancellationToken cancellationT ...@@ -228,23 +228,27 @@ public async Task<HttpResponseMessage> SendAsync(CancellationToken cancellationT
shouldCancelBody = false; shouldCancelBody = false;
return response; return response;
} }
catch (QuicStreamAbortedException ex) when (ex.ErrorCode == (long)Http3ErrorCode.VersionFallback) catch (QuicException ex) when (ex.QuicError == QuicError.StreamAborted)
{ {
// The server is requesting us fall back to an older HTTP version. Debug.Assert(ex.ApplicationErrorCode.HasValue);
throw new HttpRequestException(SR.net_http_retry_on_older_version, ex, RequestRetryType.RetryOnLowerHttpVersion);
} switch ((Http3ErrorCode)ex.ApplicationErrorCode.Value)
catch (QuicStreamAbortedException ex) when (ex.ErrorCode == (long)Http3ErrorCode.RequestRejected) {
{ case Http3ErrorCode.VersionFallback:
// The server is rejecting the request without processing it, retry it on a different connection. // The server is requesting us fall back to an older HTTP version.
throw new HttpRequestException(SR.net_http_request_aborted, ex, RequestRetryType.RetryOnConnectionFailure); throw new HttpRequestException(SR.net_http_retry_on_older_version, ex, RequestRetryType.RetryOnLowerHttpVersion);
}
catch (QuicStreamAbortedException ex) case Http3ErrorCode.RequestRejected:
{ // The server is rejecting the request without processing it, retry it on a different connection.
// Our stream was reset. throw new HttpRequestException(SR.net_http_request_aborted, ex, RequestRetryType.RetryOnConnectionFailure);
Exception? abortException = _connection.AbortException;
throw new HttpRequestException(SR.net_http_client_execution_error, abortException ?? ex); default:
// Our stream was reset.
Exception? abortException = _connection.AbortException;
throw new HttpRequestException(SR.net_http_client_execution_error, abortException ?? ex);
}
} }
catch (QuicConnectionAbortedException ex) catch (QuicException ex) when (ex.QuicError == QuicError.ConnectionAborted)
{ {
// Our connection was reset. Start shutting down the connection. // Our connection was reset. Start shutting down the connection.
Exception abortException = _connection.Abort(ex); Exception abortException = _connection.Abort(ex);
...@@ -1185,12 +1189,10 @@ private void HandleReadResponseContentException(Exception ex, CancellationToken ...@@ -1185,12 +1189,10 @@ private void HandleReadResponseContentException(Exception ex, CancellationToken
{ {
switch (ex) switch (ex)
{ {
// Peer aborted the stream case QuicException e when (e.QuicError == QuicError.StreamAborted || e.QuicError == QuicError.OperationAborted):
case QuicStreamAbortedException: // Peer or user aborted the stream
// User aborted the stream
case QuicOperationAbortedException:
throw new IOException(SR.net_http_client_execution_error, new HttpRequestException(SR.net_http_client_execution_error, ex)); throw new IOException(SR.net_http_client_execution_error, new HttpRequestException(SR.net_http_client_execution_error, ex));
case QuicConnectionAbortedException: case QuicException e when (e.QuicError == QuicError.ConnectionAborted):
// Our connection was reset. Start aborting the connection. // Our connection was reset. Start aborting the connection.
Exception abortException = _connection.Abort(ex); Exception abortException = _connection.Abort(ex);
throw new IOException(SR.net_http_client_execution_error, new HttpRequestException(SR.net_http_client_execution_error, abortException)); throw new IOException(SR.net_http_client_execution_error, new HttpRequestException(SR.net_http_client_execution_error, abortException));
...@@ -1202,10 +1204,10 @@ private void HandleReadResponseContentException(Exception ex, CancellationToken ...@@ -1202,10 +1204,10 @@ private void HandleReadResponseContentException(Exception ex, CancellationToken
_stream.AbortRead((long)Http3ErrorCode.RequestCancelled); _stream.AbortRead((long)Http3ErrorCode.RequestCancelled);
ExceptionDispatchInfo.Throw(ex); // Rethrow. ExceptionDispatchInfo.Throw(ex); // Rethrow.
return; // Never reached. return; // Never reached.
default:
_stream.AbortRead((long)Http3ErrorCode.InternalError);
throw new IOException(SR.net_http_client_execution_error, new HttpRequestException(SR.net_http_client_execution_error, ex));
} }
_stream.AbortRead((long)Http3ErrorCode.InternalError);
throw new IOException(SR.net_http_client_execution_error, new HttpRequestException(SR.net_http_client_execution_error, ex));
} }
private async ValueTask<bool> ReadNextDataFrameAsync(HttpResponseMessage response, CancellationToken cancellationToken) private async ValueTask<bool> ReadNextDataFrameAsync(HttpResponseMessage response, CancellationToken cancellationToken)
......
...@@ -277,18 +277,18 @@ public async Task GetAsync_EmptyResponseHeader_Success() ...@@ -277,18 +277,18 @@ public async Task GetAsync_EmptyResponseHeader_Success()
[ActiveIssue("https://github.com/dotnet/runtime/issues/69870", TestPlatforms.Android)] [ActiveIssue("https://github.com/dotnet/runtime/issues/69870", TestPlatforms.Android)]
public async Task GetAsync_MissingExpires_ReturnNull() public async Task GetAsync_MissingExpires_ReturnNull()
{ {
await LoopbackServerFactory.CreateClientAndServerAsync(async uri => await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
{ {
using (HttpClient client = CreateHttpClient()) using (HttpClient client = CreateHttpClient())
{ {
HttpResponseMessage response = await client.GetAsync(uri); HttpResponseMessage response = await client.GetAsync(uri);
Assert.Null(response.Content.Headers.Expires); Assert.Null(response.Content.Headers.Expires);
} }
}, },
async server => async server =>
{ {
await server.HandleRequestAsync(HttpStatusCode.OK); await server.HandleRequestAsync(HttpStatusCode.OK);
}); });
} }
[Theory] [Theory]
...@@ -434,7 +434,6 @@ public async Task SendAsync_WithZeroLengthHeaderName_Throws() ...@@ -434,7 +434,6 @@ public async Task SendAsync_WithZeroLengthHeaderName_Throws()
}); });
} }
catch (IOException) { } catch (IOException) { }
catch (QuicConnectionAbortedException) { }
}); });
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
using System.Net.Sockets; using System.Net.Sockets;
using System.Net.Test.Common; using System.Net.Test.Common;
using System.Reflection; using System.Reflection;
using System.Security.Authentication;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -277,13 +278,13 @@ public async Task ReservedFrameType_Throws() ...@@ -277,13 +278,13 @@ public async Task ReservedFrameType_Throws()
await stream.SendFrameAsync(ReservedHttp2PriorityFrameId, new byte[8]); await stream.SendFrameAsync(ReservedHttp2PriorityFrameId, new byte[8]);
QuicConnectionAbortedException ex = await Assert.ThrowsAsync<QuicConnectionAbortedException>(async () => QuicException ex = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, async () =>
{ {
await stream.HandleRequestAsync(); await stream.HandleRequestAsync();
using Http3LoopbackStream stream2 = await connection.AcceptRequestStreamAsync(); using Http3LoopbackStream stream2 = await connection.AcceptRequestStreamAsync();
}); });
Assert.Equal(UnexpectedFrameErrorCode, ex.ErrorCode); Assert.Equal(UnexpectedFrameErrorCode, ex.ApplicationErrorCode);
}); });
Task clientTask = Task.Run(async () => Task clientTask = Task.Run(async () =>
...@@ -325,13 +326,13 @@ public async Task RequestSentResponseDisposed_ThrowsOnServer() ...@@ -325,13 +326,13 @@ public async Task RequestSentResponseDisposed_ThrowsOnServer()
{ {
await stream.SendResponseBodyAsync(data, isFinal: false); await stream.SendResponseBodyAsync(data, isFinal: false);
} }
catch (QuicStreamAbortedException) catch (QuicException ex) when (ex.QuicError == QuicError.StreamAborted)
{ {
hasFailed = true; hasFailed = true;
break; break;
} }
} }
Assert.True(hasFailed, $"Expected {nameof(QuicStreamAbortedException)}, instead ran successfully for {sw.Elapsed}"); Assert.True(hasFailed, $"Expected {nameof(QuicException)} with {nameof(QuicError.StreamAborted)}, instead ran successfully for {sw.Elapsed}");
}); });
Task clientTask = Task.Run(async () => Task clientTask = Task.Run(async () =>
...@@ -384,13 +385,13 @@ public async Task RequestSendingResponseDisposed_ThrowsOnServer() ...@@ -384,13 +385,13 @@ public async Task RequestSendingResponseDisposed_ThrowsOnServer()
var (frameType, payload) = await stream.ReadFrameAsync(); var (frameType, payload) = await stream.ReadFrameAsync();
Assert.Equal(Http3LoopbackStream.DataFrame, frameType); Assert.Equal(Http3LoopbackStream.DataFrame, frameType);
} }
catch (QuicStreamAbortedException) catch (QuicException ex) when (ex.QuicError == QuicError.StreamAborted)
{ {
hasFailed = true; hasFailed = true;
break; break;
} }
} }
Assert.True(hasFailed, $"Expected {nameof(QuicStreamAbortedException)}, instead ran successfully for {sw.Elapsed}"); Assert.True(hasFailed, $"Expected {nameof(QuicException)} with {nameof(QuicError.StreamAborted)}, instead ran successfully for {sw.Elapsed}");
}); });
Task clientTask = Task.Run(async () => Task clientTask = Task.Run(async () =>
...@@ -682,8 +683,8 @@ public async Task ResponseCancellation_ServerReceivesCancellation(CancellationTy ...@@ -682,8 +683,8 @@ public async Task ResponseCancellation_ServerReceivesCancellation(CancellationTy
// In that case even with synchronization via semaphores, first writes after peer aborting may "succeed" (get SEND_COMPLETE event) // In that case even with synchronization via semaphores, first writes after peer aborting may "succeed" (get SEND_COMPLETE event)
// We are asserting that PEER_RECEIVE_ABORTED would still arrive eventually // We are asserting that PEER_RECEIVE_ABORTED would still arrive eventually
var ex = await Assert.ThrowsAsync<QuicStreamAbortedException>(() => SendDataForever(stream).WaitAsync(TimeSpan.FromSeconds(10))); var ex = await AssertThrowsQuicExceptionAsync(QuicError.StreamAborted, () => SendDataForever(stream).WaitAsync(TimeSpan.FromSeconds(10)));
Assert.Equal(268, ex.ErrorCode); Assert.Equal(268, ex.ApplicationErrorCode);
serverDone.Release(); serverDone.Release();
}); });
...@@ -724,7 +725,8 @@ public async Task ResponseCancellation_ServerReceivesCancellation(CancellationTy ...@@ -724,7 +725,8 @@ public async Task ResponseCancellation_ServerReceivesCancellation(CancellationTy
{ {
var ioe = Assert.IsType<IOException>(ex); var ioe = Assert.IsType<IOException>(ex);
var hre = Assert.IsType<HttpRequestException>(ioe.InnerException); var hre = Assert.IsType<HttpRequestException>(ioe.InnerException);
Assert.IsType<QuicOperationAbortedException>(hre.InnerException); var qex = Assert.IsType<QuicException>(hre.InnerException);
Assert.Equal(QuicError.OperationAborted, qex.QuicError);
} }
clientDone.Release(); clientDone.Release();
...@@ -762,9 +764,9 @@ public async Task ResponseCancellation_BothCancellationTokenAndDispose_Success() ...@@ -762,9 +764,9 @@ public async Task ResponseCancellation_BothCancellationTokenAndDispose_Success()
// In that case even with synchronization via semaphores, first writes after peer aborting may "succeed" (get SEND_COMPLETE event) // In that case even with synchronization via semaphores, first writes after peer aborting may "succeed" (get SEND_COMPLETE event)
// We are asserting that PEER_RECEIVE_ABORTED would still arrive eventually // We are asserting that PEER_RECEIVE_ABORTED would still arrive eventually
var ex = await Assert.ThrowsAsync<QuicStreamAbortedException>(() => SendDataForever(stream).WaitAsync(TimeSpan.FromSeconds(20))); QuicException ex = await AssertThrowsQuicExceptionAsync(QuicError.StreamAborted, () => SendDataForever(stream).WaitAsync(TimeSpan.FromSeconds(20)));
// exact error code depends on who won the race // exact error code depends on who won the race
Assert.True(ex.ErrorCode == 268 /* cancellation */ || ex.ErrorCode == 0xffffffff /* disposal */, $"Expected 268 or 0xffffffff, got {ex.ErrorCode}"); Assert.True(ex.ApplicationErrorCode == 268 /* cancellation */ || ex.ApplicationErrorCode == 0xffffffff /* disposal */, $"Expected 268 or 0xffffffff, got {ex.ApplicationErrorCode}");
serverDone.Release(); serverDone.Release();
}); });
...@@ -797,7 +799,8 @@ public async Task ResponseCancellation_BothCancellationTokenAndDispose_Success() ...@@ -797,7 +799,8 @@ public async Task ResponseCancellation_BothCancellationTokenAndDispose_Success()
{ {
var ioe = Assert.IsType<IOException>(ex); var ioe = Assert.IsType<IOException>(ex);
var hre = Assert.IsType<HttpRequestException>(ioe.InnerException); var hre = Assert.IsType<HttpRequestException>(ioe.InnerException);
Assert.IsType<QuicOperationAbortedException>(hre.InnerException); var qex = Assert.IsType<QuicException>(hre.InnerException);
Assert.Equal(QuicError.OperationAborted, qex.QuicError);
} }
clientDone.Release(); clientDone.Release();
...@@ -877,7 +880,7 @@ public async Task Alpn_NonH3_NegotiationFailure() ...@@ -877,7 +880,7 @@ public async Task Alpn_NonH3_NegotiationFailure()
}; };
HttpRequestException ex = await Assert.ThrowsAsync<HttpRequestException>(() => client.SendAsync(request).WaitAsync(TimeSpan.FromSeconds(10))); HttpRequestException ex = await Assert.ThrowsAsync<HttpRequestException>(() => client.SendAsync(request).WaitAsync(TimeSpan.FromSeconds(10)));
Assert.Contains("ALPN_NEG_FAILURE", ex.Message); Assert.IsType<AuthenticationException>(ex.InnerException);
clientDone.Release(); clientDone.Release();
}); });
...@@ -1096,8 +1099,8 @@ public async Task RequestContentStreaming_Timeout_BothClientAndServerReceiveCanc ...@@ -1096,8 +1099,8 @@ public async Task RequestContentStreaming_Timeout_BothClientAndServerReceiveCanc
await clientTask.WaitAsync(TimeSpan.FromSeconds(120)); await clientTask.WaitAsync(TimeSpan.FromSeconds(120));
// server receives cancellation // server receives cancellation
var ex = await Assert.ThrowsAsync<QuicStreamAbortedException>(() => serverTask.WaitAsync(TimeSpan.FromSeconds(120))); QuicException ex = await AssertThrowsQuicExceptionAsync(QuicError.StreamAborted, () => serverTask.WaitAsync(TimeSpan.FromSeconds(120)));
Assert.Equal(268 /*H3_REQUEST_CANCELLED (0x10C)*/, ex.ErrorCode); Assert.Equal(268 /*H3_REQUEST_CANCELLED (0x10C)*/, ex.ApplicationErrorCode);
Assert.NotNull(serverStream); Assert.NotNull(serverStream);
serverStream.Dispose(); serverStream.Dispose();
...@@ -1161,8 +1164,8 @@ public async Task RequestContentStreaming_Cancellation_BothClientAndServerReceiv ...@@ -1161,8 +1164,8 @@ public async Task RequestContentStreaming_Cancellation_BothClientAndServerReceiv
await clientTask.WaitAsync(TimeSpan.FromSeconds(120)); await clientTask.WaitAsync(TimeSpan.FromSeconds(120));
// server receives cancellation // server receives cancellation
var ex = await Assert.ThrowsAsync<QuicStreamAbortedException>(() => serverTask.WaitAsync(TimeSpan.FromSeconds(120))); QuicException ex = await AssertThrowsQuicExceptionAsync(QuicError.StreamAborted, () => serverTask.WaitAsync(TimeSpan.FromSeconds(120)));
Assert.Equal(268 /*H3_REQUEST_CANCELLED (0x10C)*/, ex.ErrorCode); Assert.Equal(268 /*H3_REQUEST_CANCELLED (0x10C)*/, ex.ApplicationErrorCode);
Assert.NotNull(serverStream); Assert.NotNull(serverStream);
serverStream.Dispose(); serverStream.Dispose();
...@@ -1337,6 +1340,13 @@ public async Task DuplexStreaming_AbortByServer_StreamingCancelled(bool graceful ...@@ -1337,6 +1340,13 @@ public async Task DuplexStreaming_AbortByServer_StreamingCancelled(bool graceful
connection.Dispose(); connection.Dispose();
} }
private static async Task<QuicException> AssertThrowsQuicExceptionAsync(QuicError expectedError, Func<Task> testCode)
{
QuicException ex = await Assert.ThrowsAsync<QuicException>(testCode);
Assert.Equal(expectedError, ex.QuicError);
return ex;
}
public static TheoryData<HttpStatusCode, bool> StatusCodesTestData() public static TheoryData<HttpStatusCode, bool> StatusCodesTestData()
{ {
var statuses = Enum.GetValues(typeof(HttpStatusCode)).Cast<HttpStatusCode>().Where(s => s >= HttpStatusCode.OK); // exclude informational var statuses = Enum.GetValues(typeof(HttpStatusCode)).Cast<HttpStatusCode>().Where(s => s >= HttpStatusCode.OK); // exclude informational
......
...@@ -32,11 +32,6 @@ public sealed partial class QuicConnection : System.IDisposable ...@@ -32,11 +32,6 @@ public sealed partial class QuicConnection : System.IDisposable
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> 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<System.Net.Quic.QuicStream> OpenUnidirectionalStreamAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
} }
public partial class QuicConnectionAbortedException : System.Net.Quic.QuicException
{
public QuicConnectionAbortedException(string message, long errorCode) : base (default(string)) { }
public long ErrorCode { get { throw null; } }
}
public abstract partial class QuicConnectionOptions public abstract partial class QuicConnectionOptions
{ {
internal QuicConnectionOptions() { } internal QuicConnectionOptions() { }
...@@ -44,11 +39,27 @@ public abstract partial class QuicConnectionOptions ...@@ -44,11 +39,27 @@ public abstract partial class QuicConnectionOptions
public int MaxBidirectionalStreams { get { throw null; } set { } } public int MaxBidirectionalStreams { get { throw null; } set { } }
public int MaxUnidirectionalStreams { get { throw null; } set { } } public int MaxUnidirectionalStreams { get { throw null; } set { } }
} }
public partial class QuicException : System.Exception public enum QuicError
{
Success = 0,
InternalError = 1,
ConnectionAborted = 2,
StreamAborted = 3,
AddressInUse = 4,
InvalidAddress = 5,
ConnectionTimeout = 6,
HostUnreachable = 7,
ConnectionRefused = 8,
VersionNegotiationError = 9,
ConnectionIdle = 10,
ProtocolError = 11,
OperationAborted = 12,
}
public sealed partial class QuicException : System.IO.IOException
{ {
public QuicException(string? message) { } public QuicException(System.Net.Quic.QuicError error, long? applicationErrorCode, string message) { }
public QuicException(string? message, System.Exception? innerException) { } public long? ApplicationErrorCode { get { throw null; } }
public QuicException(string? message, System.Exception? innerException, int result) { } public System.Net.Quic.QuicError QuicError { get { throw null; } }
} }
public sealed partial class QuicListener : System.IAsyncDisposable public sealed partial class QuicListener : System.IAsyncDisposable
{ {
...@@ -68,10 +79,6 @@ public sealed partial class QuicListenerOptions ...@@ -68,10 +79,6 @@ public sealed partial class QuicListenerOptions
public int ListenBacklog { get { throw null; } set { } } public int ListenBacklog { get { throw null; } set { } }
public required System.Net.IPEndPoint ListenEndPoint { 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 sealed partial class QuicServerConnectionOptions : System.Net.Quic.QuicConnectionOptions public sealed partial class QuicServerConnectionOptions : System.Net.Quic.QuicConnectionOptions
{ {
public QuicServerConnectionOptions() { } public QuicServerConnectionOptions() { }
...@@ -118,9 +125,4 @@ public sealed partial class QuicStream : System.IO.Stream ...@@ -118,9 +125,4 @@ public sealed partial class QuicStream : System.IO.Stream
public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory<byte> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } 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 override void WriteByte(byte value) { }
} }
public partial class QuicStreamAbortedException : System.Net.Quic.QuicException
{
public QuicStreamAbortedException(string message, long errorCode) : base (default(string)) { }
public long ErrorCode { get { throw null; } }
}
} }
...@@ -129,8 +129,11 @@ ...@@ -129,8 +129,11 @@
<data name="net_quic_reading_notallowed" xml:space="preserve"> <data name="net_quic_reading_notallowed" xml:space="preserve">
<value>Reading is not allowed on stream.</value> <value>Reading is not allowed on stream.</value>
</data> </data>
<data name="net_quic_sending_aborted" xml:space="preserve"> <data name="net_quic_writing_aborted" xml:space="preserve">
<value>Sending has already been aborted on the stream</value> <value>Writing has been aborted on the stream.</value>
</data>
<data name="net_quic_reading_aborted" xml:space="preserve">
<value>Reading has been aborted on the stream.</value>
</data> </data>
<data name="net_quic_streamaborted" xml:space="preserve"> <data name="net_quic_streamaborted" xml:space="preserve">
<value>Stream aborted by peer ({0}).</value> <value>Stream aborted by peer ({0}).</value>
...@@ -148,7 +151,7 @@ ...@@ -148,7 +151,7 @@
<value>Timeout can only be set to 'System.Threading.Timeout.Infinite' or a value &gt; 0.</value> <value>Timeout can only be set to 'System.Threading.Timeout.Infinite' or a value &gt; 0.</value>
</data> </data>
<data name="net_quic_timeout" xml:space="preserve"> <data name="net_quic_timeout" xml:space="preserve">
<value>Connection timed out.</value> <value>Connection timed out waiting for a response from the peer.</value>
</data> </data>
<data name="net_quic_ssl_option" xml:space="preserve"> <data name="net_quic_ssl_option" xml:space="preserve">
<value>'{0}' is not supported by System.Net.Quic.</value> <value>'{0}' is not supported by System.Net.Quic.</value>
...@@ -162,6 +165,9 @@ ...@@ -162,6 +165,9 @@
<data name="net_quic_not_connected" xml:space="preserve"> <data name="net_quic_not_connected" xml:space="preserve">
<value>Connection is not connected.</value> <value>Connection is not connected.</value>
</data> </data>
<data name="net_quic_internal_error" xml:space="preserve">
<value>An internal error has occured. {0}</value>
</data>
<data name="net_ssl_app_protocols_invalid" xml:space="preserve"> <data name="net_ssl_app_protocols_invalid" xml:space="preserve">
<value>The application protocol list is invalid.</value> <value>The application protocol list is invalid.</value>
</data> </data>
...@@ -171,8 +177,39 @@ ...@@ -171,8 +177,39 @@
<data name="net_quic_empty_cipher_suite" xml:space="preserve"> <data name="net_quic_empty_cipher_suite" xml:space="preserve">
<value>CipherSuitePolicy must specify at least one cipher supported by QUIC.</value> <value>CipherSuitePolicy must specify at least one cipher supported by QUIC.</value>
</data> </data>
<data name="net_quic_address_in_use" xml:space="preserve">
<value>The local address is already in use.</value>
</data>
<data name="net_quic_host_unreachable" xml:space="preserve">
<value>The server is currnetly unreachable.</value>
</data>
<data name="net_quic_connection_refused" xml:space="preserve">
<value>The server refused the connection.</value>
</data>
<data name="net_quic_protocol_error" xml:space="preserve">
<value>A QUIC protocol error was encountered</value>
</data>
<data name="net_quic_ver_neg_error" xml:space="preserve">
<value>A version negotiation error was encountered.</value>
</data>
<data name="net_quic_alpn_neg_error" xml:space="preserve">
<value>Application layer protocol negotiation error was encountered.</value>
</data>
<data name="net_quic_connection_idle" xml:space="preserve">
<value>The connection timed out from inactivity.</value>
</data>
<data name="net_quic_invalid_address" xml:space="preserve">
<value>Binding to socket failed, likely caused by a family mismatch between local and remote address.</value>
</data>
<data name="net_quic_auth" xml:space="preserve">
<value>Authentication failed. {0}</value>
</data>
<!-- Same as in System.Net.Security -->
<data name="net_io_invalidnestedcall" xml:space="preserve"> <data name="net_io_invalidnestedcall" xml:space="preserve">
<value> This method may not be called when another {0} operation is pending.</value> <value>This method may not be called when another {0} operation is pending.</value>
</data>
<data name="net_auth_tls_alert" xml:space="preserve">
<value>Authentication failed because the remote party sent a TLS alert: '{0}'.</value>
</data> </data>
<!-- Referenced in shared IPEndPointExtensions.cs--> <!-- Referenced in shared IPEndPointExtensions.cs-->
<data name="net_InvalidAddressFamily" xml:space="preserve"> <data name="net_InvalidAddressFamily" xml:space="preserve">
......
...@@ -45,7 +45,7 @@ private MsQuicApi(QUIC_API_TABLE* apiTable) ...@@ -45,7 +45,7 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
}; };
QUIC_HANDLE* handle; QUIC_HANDLE* handle;
ThrowIfFailure(ApiTable->RegistrationOpen(&cfg, &handle), "RegistrationOpen failed"); ThrowHelper.ThrowIfMsQuicError(ApiTable->RegistrationOpen(&cfg, &handle), "RegistrationOpen failed");
Registration = new MsQuicSafeHandle(handle, apiTable->RegistrationClose, SafeHandleType.Registration); Registration = new MsQuicSafeHandle(handle, apiTable->RegistrationClose, SafeHandleType.Registration);
} }
......
...@@ -19,7 +19,7 @@ internal static unsafe IPEndPoint GetIPEndPointParam(MsQuicApi api, MsQuicSafeHa ...@@ -19,7 +19,7 @@ internal static unsafe IPEndPoint GetIPEndPointParam(MsQuicApi api, MsQuicSafeHa
fixed (byte* paddress = &MemoryMarshal.GetReference(address)) fixed (byte* paddress = &MemoryMarshal.GetReference(address))
{ {
ThrowIfFailure(api.ApiTable->GetParam( ThrowHelper.ThrowIfMsQuicError(api.ApiTable->GetParam(
nativeObject.QuicHandle, nativeObject.QuicHandle,
param, param,
&valueLen, &valueLen,
...@@ -42,7 +42,7 @@ internal static unsafe void SetIPEndPointParam(MsQuicApi api, MsQuicSafeHandle n ...@@ -42,7 +42,7 @@ internal static unsafe void SetIPEndPointParam(MsQuicApi api, MsQuicSafeHandle n
fixed (byte* paddress = &MemoryMarshal.GetReference(address)) fixed (byte* paddress = &MemoryMarshal.GetReference(address))
{ {
ThrowIfFailure(api.ApiTable->SetParam( ThrowHelper.ThrowIfMsQuicError(api.ApiTable->SetParam(
nativeObject.QuicHandle, nativeObject.QuicHandle,
param, param,
(uint)address.Length, (uint)address.Length,
...@@ -55,7 +55,7 @@ internal static unsafe ushort GetUShortParam(MsQuicApi api, MsQuicSafeHandle nat ...@@ -55,7 +55,7 @@ internal static unsafe ushort GetUShortParam(MsQuicApi api, MsQuicSafeHandle nat
ushort value; ushort value;
uint valueLen = (uint)sizeof(ushort); uint valueLen = (uint)sizeof(ushort);
ThrowIfFailure(api.ApiTable->GetParam( ThrowHelper.ThrowIfMsQuicError(api.ApiTable->GetParam(
nativeObject.QuicHandle, nativeObject.QuicHandle,
param, param,
&valueLen, &valueLen,
...@@ -67,7 +67,7 @@ internal static unsafe ushort GetUShortParam(MsQuicApi api, MsQuicSafeHandle nat ...@@ -67,7 +67,7 @@ internal static unsafe ushort GetUShortParam(MsQuicApi api, MsQuicSafeHandle nat
internal static unsafe void SetUShortParam(MsQuicApi api, MsQuicSafeHandle nativeObject, uint param, ushort value) internal static unsafe void SetUShortParam(MsQuicApi api, MsQuicSafeHandle nativeObject, uint param, ushort value)
{ {
ThrowIfFailure(api.ApiTable->SetParam( ThrowHelper.ThrowIfMsQuicError(api.ApiTable->SetParam(
nativeObject.QuicHandle, nativeObject.QuicHandle,
param, param,
sizeof(ushort), sizeof(ushort),
...@@ -79,7 +79,7 @@ internal static unsafe ulong GetULongParam(MsQuicApi api, MsQuicSafeHandle nativ ...@@ -79,7 +79,7 @@ internal static unsafe ulong GetULongParam(MsQuicApi api, MsQuicSafeHandle nativ
ulong value; ulong value;
uint valueLen = (uint)sizeof(ulong); uint valueLen = (uint)sizeof(ulong);
ThrowIfFailure(api.ApiTable->GetParam( ThrowHelper.ThrowIfMsQuicError(api.ApiTable->GetParam(
nativeObject.QuicHandle, nativeObject.QuicHandle,
param, param,
&valueLen, &valueLen,
...@@ -91,7 +91,7 @@ internal static unsafe ulong GetULongParam(MsQuicApi api, MsQuicSafeHandle nativ ...@@ -91,7 +91,7 @@ internal static unsafe ulong GetULongParam(MsQuicApi api, MsQuicSafeHandle nativ
internal static unsafe void SetULongParam(MsQuicApi api, MsQuicSafeHandle nativeObject, uint param, ulong value) internal static unsafe void SetULongParam(MsQuicApi api, MsQuicSafeHandle nativeObject, uint param, ulong value)
{ {
ThrowIfFailure(api.ApiTable->SetParam( ThrowHelper.ThrowIfMsQuicError(api.ApiTable->SetParam(
nativeObject.QuicHandle, nativeObject.QuicHandle,
param, param,
sizeof(ulong), sizeof(ulong),
......
// 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.Net.Quic;
using System.Net.Sockets;
using static Microsoft.Quic.MsQuic;
namespace Microsoft.Quic
{
internal sealed class MsQuicException : QuicException
{
public int Status { get; }
public MsQuicException(int status, string? message = null, Exception? innerException = null)
: base($"{(message ?? nameof(MsQuicException))}: {GetErrorCodeForStatus(status)}", innerException, MapMsQuicStatusToHResult(status))
{
Status = status;
}
public static string GetErrorCodeForStatus(int status)
{
if (status == MsQuic.QUIC_STATUS_SUCCESS) return "QUIC_STATUS_SUCCESS";
else if (status == MsQuic.QUIC_STATUS_PENDING) return "QUIC_STATUS_PENDING";
else if (status == MsQuic.QUIC_STATUS_CONTINUE) return "QUIC_STATUS_CONTINUE";
else if (status == MsQuic.QUIC_STATUS_OUT_OF_MEMORY) return "QUIC_STATUS_OUT_OF_MEMORY";
else if (status == MsQuic.QUIC_STATUS_INVALID_PARAMETER) return "QUIC_STATUS_INVALID_PARAMETER";
else if (status == MsQuic.QUIC_STATUS_INVALID_STATE) return "QUIC_STATUS_INVALID_STATE";
else if (status == MsQuic.QUIC_STATUS_NOT_SUPPORTED) return "QUIC_STATUS_NOT_SUPPORTED";
else if (status == MsQuic.QUIC_STATUS_NOT_FOUND) return "QUIC_STATUS_NOT_FOUND";
else if (status == MsQuic.QUIC_STATUS_BUFFER_TOO_SMALL) return "QUIC_STATUS_BUFFER_TOO_SMALL";
else if (status == MsQuic.QUIC_STATUS_HANDSHAKE_FAILURE) return "QUIC_STATUS_HANDSHAKE_FAILURE";
else if (status == MsQuic.QUIC_STATUS_ABORTED) return "QUIC_STATUS_ABORTED";
else if (status == MsQuic.QUIC_STATUS_ADDRESS_IN_USE) return "QUIC_STATUS_ADDRESS_IN_USE";
else if (status == MsQuic.QUIC_STATUS_CONNECTION_TIMEOUT) return "QUIC_STATUS_CONNECTION_TIMEOUT";
else if (status == MsQuic.QUIC_STATUS_CONNECTION_IDLE) return "QUIC_STATUS_CONNECTION_IDLE";
else if (status == MsQuic.QUIC_STATUS_UNREACHABLE) return "QUIC_STATUS_UNREACHABLE";
else if (status == MsQuic.QUIC_STATUS_INTERNAL_ERROR) return "QUIC_STATUS_INTERNAL_ERROR";
else if (status == MsQuic.QUIC_STATUS_CONNECTION_REFUSED) return "QUIC_STATUS_CONNECTION_REFUSED";
else if (status == MsQuic.QUIC_STATUS_PROTOCOL_ERROR) return "QUIC_STATUS_PROTOCOL_ERROR";
else if (status == MsQuic.QUIC_STATUS_VER_NEG_ERROR) return "QUIC_STATUS_VER_NEG_ERROR";
else if (status == MsQuic.QUIC_STATUS_TLS_ERROR) return "QUIC_STATUS_TLS_ERROR";
else if (status == MsQuic.QUIC_STATUS_USER_CANCELED) return "QUIC_STATUS_USER_CANCELED";
else if (status == MsQuic.QUIC_STATUS_ALPN_NEG_FAILURE) return "QUIC_STATUS_ALPN_NEG_FAILURE";
else if (status == MsQuic.QUIC_STATUS_STREAM_LIMIT_REACHED) return "QUIC_STATUS_STREAM_LIMIT_REACHED";
else if (status == MsQuic.QUIC_STATUS_CLOSE_NOTIFY) return "QUIC_STATUS_CLOSE_NOTIFY";
else if (status == MsQuic.QUIC_STATUS_BAD_CERTIFICATE) return "QUIC_STATUS_BAD_CERTIFICATE";
else if (status == MsQuic.QUIC_STATUS_UNSUPPORTED_CERTIFICATE) return "QUIC_STATUS_UNSUPPORTED_CERTIFICATE";
else if (status == MsQuic.QUIC_STATUS_REVOKED_CERTIFICATE) return "QUIC_STATUS_REVOKED_CERTIFICATE";
else if (status == MsQuic.QUIC_STATUS_EXPIRED_CERTIFICATE) return "QUIC_STATUS_EXPIRED_CERTIFICATE";
else if (status == MsQuic.QUIC_STATUS_UNKNOWN_CERTIFICATE) return "QUIC_STATUS_UNKNOWN_CERTIFICATE";
else if (status == MsQuic.QUIC_STATUS_CERT_EXPIRED) return "QUIC_STATUS_CERT_EXPIRED";
else if (status == MsQuic.QUIC_STATUS_CERT_UNTRUSTED_ROOT) return "QUIC_STATUS_CERT_UNTRUSTED_ROOT";
else return $"Unknown status '{status}'";
}
public static int MapMsQuicStatusToHResult(int status)
{
if (status == QUIC_STATUS_CONNECTION_REFUSED) return (int)SocketError.ConnectionRefused; // 0x8007274D - WSAECONNREFUSED
else if (status == QUIC_STATUS_CONNECTION_TIMEOUT) return (int)SocketError.TimedOut; // 0x8007274C - WSAETIMEDOUT
else if (status == QUIC_STATUS_UNREACHABLE) return (int)SocketError.HostUnreachable;
else return 0;
}
}
}
...@@ -167,7 +167,7 @@ private static unsafe SafeMsQuicConfigurationHandle Create(QuicConnectionOptions ...@@ -167,7 +167,7 @@ private static unsafe SafeMsQuicConfigurationHandle Create(QuicConnectionOptions
QUIC_HANDLE* handle; QUIC_HANDLE* handle;
using var msquicBuffers = new MsQuicBuffers(); using var msquicBuffers = new MsQuicBuffers();
msquicBuffers.Initialize(alpnProtocols, alpnProtocol => alpnProtocol.Protocol); msquicBuffers.Initialize(alpnProtocols, alpnProtocol => alpnProtocol.Protocol);
ThrowIfFailure(MsQuicApi.Api.ApiTable->ConfigurationOpen( ThrowHelper.ThrowIfMsQuicError(MsQuicApi.Api.ApiTable->ConfigurationOpen(
MsQuicApi.Api.Registration.QuicHandle, MsQuicApi.Api.Registration.QuicHandle,
msquicBuffers.Buffers, msquicBuffers.Buffers,
(uint)alpnProtocols.Count, (uint)alpnProtocols.Count,
...@@ -247,11 +247,11 @@ private static unsafe SafeMsQuicConfigurationHandle Create(QuicConnectionOptions ...@@ -247,11 +247,11 @@ private static unsafe SafeMsQuicConfigurationHandle Create(QuicConnectionOptions
#if TARGET_WINDOWS #if TARGET_WINDOWS
if ((Interop.SECURITY_STATUS)status == Interop.SECURITY_STATUS.AlgorithmMismatch && (isServer ? MsQuicApi.Tls13ServerMayBeDisabled : MsQuicApi.Tls13ClientMayBeDisabled)) if ((Interop.SECURITY_STATUS)status == Interop.SECURITY_STATUS.AlgorithmMismatch && (isServer ? MsQuicApi.Tls13ServerMayBeDisabled : MsQuicApi.Tls13ClientMayBeDisabled))
{ {
throw new MsQuicException(status, SR.net_quic_tls_version_notsupported); throw new PlatformNotSupportedException(SR.net_quic_tls_version_notsupported);
} }
#endif #endif
ThrowIfFailure(status, "ConfigurationLoadCredential failed"); ThrowHelper.ThrowIfMsQuicError(status, "ConfigurationLoadCredential failed");
} }
catch catch
{ {
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Net.Quic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
...@@ -26,28 +25,6 @@ internal unsafe partial struct QUIC_BUFFER ...@@ -26,28 +25,6 @@ internal unsafe partial struct QUIC_BUFFER
internal partial class MsQuic internal partial class MsQuic
{ {
public static unsafe QUIC_API_TABLE* Open()
{
QUIC_API_TABLE* ApiTable;
int Status = MsQuicOpenVersion(2, (void**)&ApiTable);
ThrowIfFailure(Status);
return ApiTable;
}
public static unsafe void Close(QUIC_API_TABLE* ApiTable)
{
MsQuicClose(ApiTable);
}
public static void ThrowIfFailure(int status, string? message = null)
{
if (StatusFailed(status))
{
// TODO make custom exception, and maybe throw helpers
throw new MsQuicException(status, message);
}
}
public static bool StatusSucceeded(int status) public static bool StatusSucceeded(int status)
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
...@@ -84,6 +61,7 @@ public static bool StatusFailed(int status) ...@@ -84,6 +61,7 @@ public static bool StatusFailed(int status)
public static int QUIC_STATUS_HANDSHAKE_FAILURE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_HANDSHAKE_FAILURE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_HANDSHAKE_FAILURE : MsQuic_Linux.QUIC_STATUS_HANDSHAKE_FAILURE; public static int QUIC_STATUS_HANDSHAKE_FAILURE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_HANDSHAKE_FAILURE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_HANDSHAKE_FAILURE : MsQuic_Linux.QUIC_STATUS_HANDSHAKE_FAILURE;
public static int QUIC_STATUS_ABORTED => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_ABORTED : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_ABORTED : MsQuic_Linux.QUIC_STATUS_ABORTED; public static int QUIC_STATUS_ABORTED => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_ABORTED : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_ABORTED : MsQuic_Linux.QUIC_STATUS_ABORTED;
public static int QUIC_STATUS_ADDRESS_IN_USE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_ADDRESS_IN_USE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_ADDRESS_IN_USE : MsQuic_Linux.QUIC_STATUS_ADDRESS_IN_USE; public static int QUIC_STATUS_ADDRESS_IN_USE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_ADDRESS_IN_USE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_ADDRESS_IN_USE : MsQuic_Linux.QUIC_STATUS_ADDRESS_IN_USE;
public static int QUIC_STATUS_INVALID_ADDRESS => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_INVALID_ADDRESS : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_INVALID_ADDRESS : MsQuic_Linux.QUIC_STATUS_INVALID_ADDRESS;
public static int QUIC_STATUS_CONNECTION_TIMEOUT => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_CONNECTION_TIMEOUT : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_CONNECTION_TIMEOUT : MsQuic_Linux.QUIC_STATUS_CONNECTION_TIMEOUT; public static int QUIC_STATUS_CONNECTION_TIMEOUT => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_CONNECTION_TIMEOUT : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_CONNECTION_TIMEOUT : MsQuic_Linux.QUIC_STATUS_CONNECTION_TIMEOUT;
public static int QUIC_STATUS_CONNECTION_IDLE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_CONNECTION_IDLE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_CONNECTION_IDLE : MsQuic_Linux.QUIC_STATUS_CONNECTION_IDLE; public static int QUIC_STATUS_CONNECTION_IDLE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_CONNECTION_IDLE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_CONNECTION_IDLE : MsQuic_Linux.QUIC_STATUS_CONNECTION_IDLE;
public static int QUIC_STATUS_UNREACHABLE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_UNREACHABLE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_UNREACHABLE : MsQuic_Linux.QUIC_STATUS_UNREACHABLE; public static int QUIC_STATUS_UNREACHABLE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_UNREACHABLE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_UNREACHABLE : MsQuic_Linux.QUIC_STATUS_UNREACHABLE;
...@@ -101,8 +79,10 @@ public static bool StatusFailed(int status) ...@@ -101,8 +79,10 @@ public static bool StatusFailed(int status)
public static int QUIC_STATUS_REVOKED_CERTIFICATE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_REVOKED_CERTIFICATE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_REVOKED_CERTIFICATE : MsQuic_Linux.QUIC_STATUS_REVOKED_CERTIFICATE; public static int QUIC_STATUS_REVOKED_CERTIFICATE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_REVOKED_CERTIFICATE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_REVOKED_CERTIFICATE : MsQuic_Linux.QUIC_STATUS_REVOKED_CERTIFICATE;
public static int QUIC_STATUS_EXPIRED_CERTIFICATE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_EXPIRED_CERTIFICATE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_EXPIRED_CERTIFICATE : MsQuic_Linux.QUIC_STATUS_EXPIRED_CERTIFICATE; public static int QUIC_STATUS_EXPIRED_CERTIFICATE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_EXPIRED_CERTIFICATE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_EXPIRED_CERTIFICATE : MsQuic_Linux.QUIC_STATUS_EXPIRED_CERTIFICATE;
public static int QUIC_STATUS_UNKNOWN_CERTIFICATE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_UNKNOWN_CERTIFICATE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_UNKNOWN_CERTIFICATE : MsQuic_Linux.QUIC_STATUS_UNKNOWN_CERTIFICATE; public static int QUIC_STATUS_UNKNOWN_CERTIFICATE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_UNKNOWN_CERTIFICATE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_UNKNOWN_CERTIFICATE : MsQuic_Linux.QUIC_STATUS_UNKNOWN_CERTIFICATE;
public static int QUIC_STATUS_REQUIRED_CERTIFICATE => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_REQUIRED_CERTIFICATE : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_REQUIRED_CERTIFICATE : MsQuic_Linux.QUIC_STATUS_REQUIRED_CERTIFICATE;
public static int QUIC_STATUS_CERT_EXPIRED => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_CERT_EXPIRED : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_CERT_EXPIRED : MsQuic_Linux.QUIC_STATUS_CERT_EXPIRED; public static int QUIC_STATUS_CERT_EXPIRED => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_CERT_EXPIRED : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_CERT_EXPIRED : MsQuic_Linux.QUIC_STATUS_CERT_EXPIRED;
public static int QUIC_STATUS_CERT_UNTRUSTED_ROOT => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_CERT_UNTRUSTED_ROOT : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_CERT_UNTRUSTED_ROOT : MsQuic_Linux.QUIC_STATUS_CERT_UNTRUSTED_ROOT; public static int QUIC_STATUS_CERT_UNTRUSTED_ROOT => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_CERT_UNTRUSTED_ROOT : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_CERT_UNTRUSTED_ROOT : MsQuic_Linux.QUIC_STATUS_CERT_UNTRUSTED_ROOT;
public static int QUIC_STATUS_CERT_NO_CERT => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_STATUS_CERT_NO_CERT : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_STATUS_CERT_NO_CERT : MsQuic_Linux.QUIC_STATUS_CERT_NO_CERT;
public static int QUIC_ADDRESS_FAMILY_UNSPEC => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_ADDRESS_FAMILY_UNSPEC : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_ADDRESS_FAMILY_UNSPEC : MsQuic_Linux.QUIC_ADDRESS_FAMILY_UNSPEC; public static int QUIC_ADDRESS_FAMILY_UNSPEC => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_ADDRESS_FAMILY_UNSPEC : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_ADDRESS_FAMILY_UNSPEC : MsQuic_Linux.QUIC_ADDRESS_FAMILY_UNSPEC;
public static int QUIC_ADDRESS_FAMILY_INET => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_ADDRESS_FAMILY_INET : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_ADDRESS_FAMILY_INET : MsQuic_Linux.QUIC_ADDRESS_FAMILY_INET; public static int QUIC_ADDRESS_FAMILY_INET => OperatingSystem.IsWindows() ? MsQuic_Windows.QUIC_ADDRESS_FAMILY_INET : (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid()) ? MsQuic_Linux.QUIC_ADDRESS_FAMILY_INET : MsQuic_Linux.QUIC_ADDRESS_FAMILY_INET;
......
...@@ -22,6 +22,12 @@ internal partial struct QUIC_HANDLE ...@@ -22,6 +22,12 @@ internal partial struct QUIC_HANDLE
{ {
} }
internal enum QUIC_TLS_PROVIDER
{
SCHANNEL = 0x0000,
OPENSSL = 0x0001,
}
internal enum QUIC_EXECUTION_PROFILE internal enum QUIC_EXECUTION_PROFILE
{ {
LOW_LATENCY, LOW_LATENCY,
...@@ -816,13 +822,13 @@ internal enum QUIC_PERFORMANCE_COUNTERS ...@@ -816,13 +822,13 @@ internal enum QUIC_PERFORMANCE_COUNTERS
internal unsafe partial struct QUIC_VERSION_SETTINGS internal unsafe partial struct QUIC_VERSION_SETTINGS
{ {
[NativeTypeName("uint32_t *")] [NativeTypeName("const uint32_t *")]
internal uint* AcceptableVersions; internal uint* AcceptableVersions;
[NativeTypeName("uint32_t *")] [NativeTypeName("const uint32_t *")]
internal uint* OfferedVersions; internal uint* OfferedVersions;
[NativeTypeName("uint32_t *")] [NativeTypeName("const uint32_t *")]
internal uint* FullyDeployedVersions; internal uint* FullyDeployedVersions;
[NativeTypeName("uint32_t")] [NativeTypeName("uint32_t")]
...@@ -2029,6 +2035,9 @@ internal partial struct _SHUTDOWN_INITIATED_BY_TRANSPORT_e__Struct ...@@ -2029,6 +2035,9 @@ internal partial struct _SHUTDOWN_INITIATED_BY_TRANSPORT_e__Struct
{ {
[NativeTypeName("HRESULT")] [NativeTypeName("HRESULT")]
internal int Status; internal int Status;
[NativeTypeName("QUIC_UINT62")]
internal ulong ErrorCode;
} }
internal partial struct _SHUTDOWN_INITIATED_BY_PEER_e__Struct internal partial struct _SHUTDOWN_INITIATED_BY_PEER_e__Struct
...@@ -2401,19 +2410,36 @@ internal byte AppCloseInProgress ...@@ -2401,19 +2410,36 @@ internal byte AppCloseInProgress
} }
} }
[NativeTypeName("BOOLEAN : 7")] [NativeTypeName("BOOLEAN : 1")]
internal byte ConnectionShutdownByPeer
{
get
{
return (byte)((_bitfield >> 1) & 0x1u);
}
set
{
_bitfield = (byte)((_bitfield & ~(0x1u << 1)) | ((value & 0x1u) << 1));
}
}
[NativeTypeName("BOOLEAN : 6")]
internal byte RESERVED internal byte RESERVED
{ {
get get
{ {
return (byte)((_bitfield >> 1) & 0x7Fu); return (byte)((_bitfield >> 2) & 0x3Fu);
} }
set set
{ {
_bitfield = (byte)((_bitfield & ~(0x7Fu << 1)) | ((value & 0x7Fu) << 1)); _bitfield = (byte)((_bitfield & ~(0x3Fu << 2)) | ((value & 0x3Fu) << 2));
} }
} }
[NativeTypeName("QUIC_UINT62")]
internal ulong ConnectionErrorCode;
} }
internal partial struct _IDEAL_SEND_BUFFER_SIZE_e__Struct internal partial struct _IDEAL_SEND_BUFFER_SIZE_e__Struct
...@@ -2592,6 +2618,9 @@ internal static unsafe partial class MsQuic ...@@ -2592,6 +2618,9 @@ internal static unsafe partial class MsQuic
[NativeTypeName("#define QUIC_PARAM_GLOBAL_DATAPATH_PROCESSORS 0x01000009")] [NativeTypeName("#define QUIC_PARAM_GLOBAL_DATAPATH_PROCESSORS 0x01000009")]
internal const uint QUIC_PARAM_GLOBAL_DATAPATH_PROCESSORS = 0x01000009; internal const uint QUIC_PARAM_GLOBAL_DATAPATH_PROCESSORS = 0x01000009;
[NativeTypeName("#define QUIC_PARAM_GLOBAL_TLS_PROVIDER 0x0100000A")]
internal const uint QUIC_PARAM_GLOBAL_TLS_PROVIDER = 0x0100000A;
[NativeTypeName("#define QUIC_PARAM_CONFIGURATION_SETTINGS 0x03000000")] [NativeTypeName("#define QUIC_PARAM_CONFIGURATION_SETTINGS 0x03000000")]
internal const uint QUIC_PARAM_CONFIGURATION_SETTINGS = 0x03000000; internal const uint QUIC_PARAM_CONFIGURATION_SETTINGS = 0x03000000;
......
...@@ -45,6 +45,9 @@ internal static unsafe partial class MsQuic_Linux ...@@ -45,6 +45,9 @@ internal static unsafe partial class MsQuic_Linux
[NativeTypeName("#define QUIC_STATUS_ADDRESS_IN_USE ((QUIC_STATUS)EADDRINUSE)")] [NativeTypeName("#define QUIC_STATUS_ADDRESS_IN_USE ((QUIC_STATUS)EADDRINUSE)")]
public const int QUIC_STATUS_ADDRESS_IN_USE = ((int)(98)); public const int QUIC_STATUS_ADDRESS_IN_USE = ((int)(98));
[NativeTypeName("#define QUIC_STATUS_INVALID_ADDRESS ((QUIC_STATUS)EAFNOSUPPORT)")]
public const int QUIC_STATUS_INVALID_ADDRESS = ((int)(97));
[NativeTypeName("#define QUIC_STATUS_CONNECTION_TIMEOUT ((QUIC_STATUS)ETIMEDOUT)")] [NativeTypeName("#define QUIC_STATUS_CONNECTION_TIMEOUT ((QUIC_STATUS)ETIMEDOUT)")]
public const int QUIC_STATUS_CONNECTION_TIMEOUT = ((int)(110)); public const int QUIC_STATUS_CONNECTION_TIMEOUT = ((int)(110));
...@@ -96,12 +99,18 @@ internal static unsafe partial class MsQuic_Linux ...@@ -96,12 +99,18 @@ internal static unsafe partial class MsQuic_Linux
[NativeTypeName("#define QUIC_STATUS_UNKNOWN_CERTIFICATE QUIC_STATUS_TLS_ALERT(46)")] [NativeTypeName("#define QUIC_STATUS_UNKNOWN_CERTIFICATE QUIC_STATUS_TLS_ALERT(46)")]
public const int QUIC_STATUS_UNKNOWN_CERTIFICATE = ((int)(0xff & 46) + 256 + 200000000); public const int QUIC_STATUS_UNKNOWN_CERTIFICATE = ((int)(0xff & 46) + 256 + 200000000);
[NativeTypeName("#define QUIC_STATUS_REQUIRED_CERTIFICATE QUIC_STATUS_TLS_ALERT(116)")]
public const int QUIC_STATUS_REQUIRED_CERTIFICATE = ((int)(0xff & 116) + 256 + 200000000);
[NativeTypeName("#define QUIC_STATUS_CERT_EXPIRED QUIC_STATUS_CERT_ERROR(1)")] [NativeTypeName("#define QUIC_STATUS_CERT_EXPIRED QUIC_STATUS_CERT_ERROR(1)")]
public const int QUIC_STATUS_CERT_EXPIRED = ((int)(1) + 512 + 200000000); public const int QUIC_STATUS_CERT_EXPIRED = ((int)(1) + 512 + 200000000);
[NativeTypeName("#define QUIC_STATUS_CERT_UNTRUSTED_ROOT QUIC_STATUS_CERT_ERROR(2)")] [NativeTypeName("#define QUIC_STATUS_CERT_UNTRUSTED_ROOT QUIC_STATUS_CERT_ERROR(2)")]
public const int QUIC_STATUS_CERT_UNTRUSTED_ROOT = ((int)(2) + 512 + 200000000); public const int QUIC_STATUS_CERT_UNTRUSTED_ROOT = ((int)(2) + 512 + 200000000);
[NativeTypeName("#define QUIC_STATUS_CERT_NO_CERT QUIC_STATUS_CERT_ERROR(3)")]
public const int QUIC_STATUS_CERT_NO_CERT = ((int)(3) + 512 + 200000000);
public const int QUIC_ADDRESS_FAMILY_UNSPEC = 0; public const int QUIC_ADDRESS_FAMILY_UNSPEC = 0;
public const int QUIC_ADDRESS_FAMILY_INET = 2; public const int QUIC_ADDRESS_FAMILY_INET = 2;
public const int QUIC_ADDRESS_FAMILY_INET6 = 10; public const int QUIC_ADDRESS_FAMILY_INET6 = 10;
......
...@@ -45,6 +45,9 @@ internal static unsafe partial class MsQuic_MacOS ...@@ -45,6 +45,9 @@ internal static unsafe partial class MsQuic_MacOS
[NativeTypeName("#define QUIC_STATUS_ADDRESS_IN_USE ((QUIC_STATUS)EADDRINUSE)")] [NativeTypeName("#define QUIC_STATUS_ADDRESS_IN_USE ((QUIC_STATUS)EADDRINUSE)")]
public const int QUIC_STATUS_ADDRESS_IN_USE = ((int)(48)); public const int QUIC_STATUS_ADDRESS_IN_USE = ((int)(48));
[NativeTypeName("#define QUIC_STATUS_INVALID_ADDRESS ((QUIC_STATUS)EAFNOSUPPORT)")]
public const int QUIC_STATUS_INVALID_ADDRESS = ((int)(47));
[NativeTypeName("#define QUIC_STATUS_CONNECTION_TIMEOUT ((QUIC_STATUS)ETIMEDOUT)")] [NativeTypeName("#define QUIC_STATUS_CONNECTION_TIMEOUT ((QUIC_STATUS)ETIMEDOUT)")]
public const int QUIC_STATUS_CONNECTION_TIMEOUT = ((int)(60)); public const int QUIC_STATUS_CONNECTION_TIMEOUT = ((int)(60));
...@@ -96,12 +99,18 @@ internal static unsafe partial class MsQuic_MacOS ...@@ -96,12 +99,18 @@ internal static unsafe partial class MsQuic_MacOS
[NativeTypeName("#define QUIC_STATUS_UNKNOWN_CERTIFICATE QUIC_STATUS_TLS_ALERT(46)")] [NativeTypeName("#define QUIC_STATUS_UNKNOWN_CERTIFICATE QUIC_STATUS_TLS_ALERT(46)")]
public const int QUIC_STATUS_UNKNOWN_CERTIFICATE = ((int)(0xff & 46) + 256 + 200000000); public const int QUIC_STATUS_UNKNOWN_CERTIFICATE = ((int)(0xff & 46) + 256 + 200000000);
[NativeTypeName("#define QUIC_STATUS_REQUIRED_CERTIFICATE QUIC_STATUS_TLS_ALERT(116)")]
public const int QUIC_STATUS_REQUIRED_CERTIFICATE = ((int)(0xff & 116) + 256 + 200000000);
[NativeTypeName("#define QUIC_STATUS_CERT_EXPIRED QUIC_STATUS_CERT_ERROR(1)")] [NativeTypeName("#define QUIC_STATUS_CERT_EXPIRED QUIC_STATUS_CERT_ERROR(1)")]
public const int QUIC_STATUS_CERT_EXPIRED = ((int)(1) + 512 + 200000000); public const int QUIC_STATUS_CERT_EXPIRED = ((int)(1) + 512 + 200000000);
[NativeTypeName("#define QUIC_STATUS_CERT_UNTRUSTED_ROOT QUIC_STATUS_CERT_ERROR(2)")] [NativeTypeName("#define QUIC_STATUS_CERT_UNTRUSTED_ROOT QUIC_STATUS_CERT_ERROR(2)")]
public const int QUIC_STATUS_CERT_UNTRUSTED_ROOT = ((int)(2) + 512 + 200000000); public const int QUIC_STATUS_CERT_UNTRUSTED_ROOT = ((int)(2) + 512 + 200000000);
[NativeTypeName("#define QUIC_STATUS_CERT_NO_CERT QUIC_STATUS_CERT_ERROR(3)")]
public const int QUIC_STATUS_CERT_NO_CERT = ((int)(3) + 512 + 200000000);
public const int QUIC_ADDRESS_FAMILY_UNSPEC = 0; public const int QUIC_ADDRESS_FAMILY_UNSPEC = 0;
public const int QUIC_ADDRESS_FAMILY_INET = 2; public const int QUIC_ADDRESS_FAMILY_INET = 2;
public const int QUIC_ADDRESS_FAMILY_INET6 = 30; public const int QUIC_ADDRESS_FAMILY_INET6 = 30;
......
...@@ -45,6 +45,9 @@ internal static partial class MsQuic_Windows ...@@ -45,6 +45,9 @@ internal static partial class MsQuic_Windows
[NativeTypeName("#define QUIC_STATUS_ADDRESS_IN_USE QUIC_STATUS_HRESULT_FROM_WIN32(WSAEADDRINUSE)")] [NativeTypeName("#define QUIC_STATUS_ADDRESS_IN_USE QUIC_STATUS_HRESULT_FROM_WIN32(WSAEADDRINUSE)")]
public const int QUIC_STATUS_ADDRESS_IN_USE = unchecked((int)(10048) <= 0 ? ((int)(10048)) : ((int)(((10048) & 0x0000FFFF) | (7 << 16) | 0x80000000))); public const int QUIC_STATUS_ADDRESS_IN_USE = unchecked((int)(10048) <= 0 ? ((int)(10048)) : ((int)(((10048) & 0x0000FFFF) | (7 << 16) | 0x80000000)));
[NativeTypeName("#define QUIC_STATUS_INVALID_ADDRESS QUIC_STATUS_HRESULT_FROM_WIN32(WSAEADDRNOTAVAIL)")]
public const int QUIC_STATUS_INVALID_ADDRESS = unchecked((int)(10049) <= 0 ? ((int)(10049)) : ((int)(((10049) & 0x0000FFFF) | (7 << 16) | 0x80000000)));
[NativeTypeName("#define QUIC_STATUS_CONNECTION_TIMEOUT ERROR_QUIC_CONNECTION_TIMEOUT")] [NativeTypeName("#define QUIC_STATUS_CONNECTION_TIMEOUT ERROR_QUIC_CONNECTION_TIMEOUT")]
public const int QUIC_STATUS_CONNECTION_TIMEOUT = unchecked((int)(0x80410006)); public const int QUIC_STATUS_CONNECTION_TIMEOUT = unchecked((int)(0x80410006));
...@@ -96,12 +99,18 @@ internal static partial class MsQuic_Windows ...@@ -96,12 +99,18 @@ internal static partial class MsQuic_Windows
[NativeTypeName("#define QUIC_STATUS_UNKNOWN_CERTIFICATE QUIC_STATUS_TLS_ALERT(46)")] [NativeTypeName("#define QUIC_STATUS_UNKNOWN_CERTIFICATE QUIC_STATUS_TLS_ALERT(46)")]
public const int QUIC_STATUS_UNKNOWN_CERTIFICATE = unchecked(((int)(0x80410100)) | (0xff & 46)); public const int QUIC_STATUS_UNKNOWN_CERTIFICATE = unchecked(((int)(0x80410100)) | (0xff & 46));
[NativeTypeName("#define QUIC_STATUS_REQUIRED_CERTIFICATE QUIC_STATUS_TLS_ALERT(116)")]
public const int QUIC_STATUS_REQUIRED_CERTIFICATE = unchecked(((int)(0x80410100)) | (0xff & 116));
[NativeTypeName("#define QUIC_STATUS_CERT_EXPIRED CERT_E_EXPIRED")] [NativeTypeName("#define QUIC_STATUS_CERT_EXPIRED CERT_E_EXPIRED")]
public const int QUIC_STATUS_CERT_EXPIRED = unchecked((int)(0x800B0101)); public const int QUIC_STATUS_CERT_EXPIRED = unchecked((int)(0x800B0101));
[NativeTypeName("#define QUIC_STATUS_CERT_UNTRUSTED_ROOT CERT_E_UNTRUSTEDROOT")] [NativeTypeName("#define QUIC_STATUS_CERT_UNTRUSTED_ROOT CERT_E_UNTRUSTEDROOT")]
public const int QUIC_STATUS_CERT_UNTRUSTED_ROOT = unchecked((int)(0x800B0109)); public const int QUIC_STATUS_CERT_UNTRUSTED_ROOT = unchecked((int)(0x800B0109));
[NativeTypeName("#define QUIC_STATUS_CERT_NO_CERT SEC_E_NO_CREDENTIALS")]
public const int QUIC_STATUS_CERT_NO_CERT = unchecked((int)(0x8009030E));
public const int QUIC_ADDRESS_FAMILY_UNSPEC = 0; public const int QUIC_ADDRESS_FAMILY_UNSPEC = 0;
public const int QUIC_ADDRESS_FAMILY_INET = 2; public const int QUIC_ADDRESS_FAMILY_INET = 2;
public const int QUIC_ADDRESS_FAMILY_INET6 = 23; public const int QUIC_ADDRESS_FAMILY_INET6 = 23;
......
...@@ -173,7 +173,7 @@ public unsafe MsQuicConnection(QuicClientConnectionOptions options) ...@@ -173,7 +173,7 @@ public unsafe MsQuicConnection(QuicClientConnectionOptions options)
{ {
QUIC_HANDLE* handle; QUIC_HANDLE* handle;
Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)");
ThrowIfFailure(MsQuicApi.Api.ApiTable->ConnectionOpen( ThrowHelper.ThrowIfMsQuicError(MsQuicApi.Api.ApiTable->ConnectionOpen(
MsQuicApi.Api.Registration.QuicHandle, MsQuicApi.Api.Registration.QuicHandle,
&NativeCallback, &NativeCallback,
(void*)GCHandle.ToIntPtr(_state.StateGCHandle), (void*)GCHandle.ToIntPtr(_state.StateGCHandle),
...@@ -236,7 +236,7 @@ private static int HandleEventShutdownInitiatedByTransport(State state, ref QUIC ...@@ -236,7 +236,7 @@ private static int HandleEventShutdownInitiatedByTransport(State state, ref QUIC
state.Connection = null; state.Connection = null;
} }
state.ConnectTcs.TrySetException(new MsQuicException(connectionEvent.SHUTDOWN_INITIATED_BY_TRANSPORT.Status, "Connection has been shutdown by transport")); state.ConnectTcs.TrySetException(ThrowHelper.GetExceptionForMsQuicStatus(connectionEvent.SHUTDOWN_INITIATED_BY_TRANSPORT.Status, "Connection has been shutdown by transport"));
} }
// To throw QuicConnectionAbortedException (instead of QuicOperationAbortedException) out of AcceptStreamAsync() since // To throw QuicConnectionAbortedException (instead of QuicOperationAbortedException) out of AcceptStreamAsync() since
...@@ -545,7 +545,7 @@ internal unsafe ValueTask ConnectAsync(CancellationToken cancellationToken = def ...@@ -545,7 +545,7 @@ internal unsafe ValueTask ConnectAsync(CancellationToken cancellationToken = def
try try
{ {
Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)");
ThrowIfFailure(MsQuicApi.Api.ApiTable->ConnectionStart( ThrowHelper.ThrowIfMsQuicError(MsQuicApi.Api.ApiTable->ConnectionStart(
_state.Handle.QuicHandle, _state.Handle.QuicHandle,
_configuration.QuicHandle, _configuration.QuicHandle,
af, af,
...@@ -584,7 +584,7 @@ internal unsafe ValueTask FinishHandshakeAsync(QuicServerConnectionOptions optio ...@@ -584,7 +584,7 @@ internal unsafe ValueTask FinishHandshakeAsync(QuicServerConnectionOptions optio
_state.RevocationMode = options.ServerAuthenticationOptions.CertificateRevocationCheckMode; _state.RevocationMode = options.ServerAuthenticationOptions.CertificateRevocationCheckMode;
_state.RemoteCertificateValidationCallback = options.ServerAuthenticationOptions.RemoteCertificateValidationCallback; _state.RemoteCertificateValidationCallback = options.ServerAuthenticationOptions.RemoteCertificateValidationCallback;
_configuration = SafeMsQuicConfigurationHandle.Create(options, options.ServerAuthenticationOptions, targetHost); _configuration = SafeMsQuicConfigurationHandle.Create(options, options.ServerAuthenticationOptions, targetHost);
ThrowIfFailure(MsQuicApi.Api.ApiTable->ConnectionSetConfiguration( ThrowHelper.ThrowIfMsQuicError(MsQuicApi.Api.ApiTable->ConnectionSetConfiguration(
_state.Handle.QuicHandle, _state.Handle.QuicHandle,
_configuration.QuicHandle)); _configuration.QuicHandle));
} }
......
...@@ -182,7 +182,7 @@ internal unsafe MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM ...@@ -182,7 +182,7 @@ internal unsafe MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM
throw ThrowHelper.GetConnectionAbortedException(connectionState.AbortErrorCode); throw ThrowHelper.GetConnectionAbortedException(connectionState.AbortErrorCode);
} }
ThrowIfFailure(status, "Failed to open stream to peer"); ThrowHelper.ThrowIfMsQuicError(status, "Failed to open stream to peer");
_state.Handle = new SafeMsQuicStreamHandle(handle); _state.Handle = new SafeMsQuicStreamHandle(handle);
} }
catch catch
...@@ -307,11 +307,11 @@ private async ValueTask WriteAsync<TBuffer>(Action<State, TBuffer> stateSetup, T ...@@ -307,11 +307,11 @@ private async ValueTask WriteAsync<TBuffer>(Action<State, TBuffer> stateSetup, T
if (_state.SendErrorCode != -1) if (_state.SendErrorCode != -1)
{ {
// aborted by peer // aborted by peer
throw new QuicStreamAbortedException(_state.SendErrorCode); throw ThrowHelper.GetStreamAbortedException(_state.SendErrorCode);
} }
// aborted locally // aborted locally
throw new QuicOperationAbortedException(SR.net_quic_sending_aborted); throw ThrowHelper.GetOperationAbortedException(SR.net_quic_writing_aborted);
} }
// if token was already cancelled, this would execute synchronously // if token was already cancelled, this would execute synchronously
...@@ -345,11 +345,11 @@ private async ValueTask WriteAsync<TBuffer>(Action<State, TBuffer> stateSetup, T ...@@ -345,11 +345,11 @@ private async ValueTask WriteAsync<TBuffer>(Action<State, TBuffer> stateSetup, T
if (_state.SendErrorCode != -1) if (_state.SendErrorCode != -1)
{ {
// aborted by peer // aborted by peer
throw new QuicStreamAbortedException(_state.SendErrorCode); throw ThrowHelper.GetStreamAbortedException(_state.SendErrorCode);
} }
// aborted locally // aborted locally
throw new QuicOperationAbortedException(SR.net_quic_sending_aborted); throw ThrowHelper.GetOperationAbortedException(SR.net_quic_writing_aborted);
} }
if (_state.SendState == SendState.ConnectionClosed) if (_state.SendState == SendState.ConnectionClosed)
{ {
...@@ -413,9 +413,13 @@ private unsafe ValueTask WriteAsyncCore<TBuffer>(Action<State, TBuffer> stateSet ...@@ -413,9 +413,13 @@ private unsafe ValueTask WriteAsyncCore<TBuffer>(Action<State, TBuffer> stateSet
if (status == QUIC_STATUS_ABORTED) if (status == QUIC_STATUS_ABORTED)
{ {
if (_state.SendErrorCode != -1)
{
throw ThrowHelper.GetStreamAbortedException(_state.SendErrorCode);
}
throw ThrowHelper.GetConnectionAbortedException(_state.ConnectionState.AbortErrorCode); throw ThrowHelper.GetConnectionAbortedException(_state.ConnectionState.AbortErrorCode);
} }
ThrowIfFailure(status, "Could not send data to peer."); ThrowHelper.ThrowIfMsQuicError(status, "Could not send data to peer.");
} }
return _state.SendResettableCompletionSource.GetTypelessValueTask(); return _state.SendResettableCompletionSource.GetTypelessValueTask();
...@@ -618,7 +622,7 @@ internal void AbortRead(long errorCode) ...@@ -618,7 +622,7 @@ internal void AbortRead(long errorCode)
if (shouldComplete) if (shouldComplete)
{ {
_state.ReceiveResettableCompletionSource.CompleteException( _state.ReceiveResettableCompletionSource.CompleteException(
ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException("Read was aborted"))); ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetOperationAbortedException(SR.net_quic_reading_aborted)));
} }
StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_RECEIVE, errorCode); StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_RECEIVE, errorCode);
...@@ -659,13 +663,13 @@ internal void AbortWrite(long errorCode) ...@@ -659,13 +663,13 @@ internal void AbortWrite(long errorCode)
if (shouldComplete) if (shouldComplete)
{ {
_state.ShutdownWriteCompletionSource.SetException( _state.ShutdownWriteCompletionSource.SetException(
ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException("Write was aborted."))); ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetOperationAbortedException(SR.net_quic_writing_aborted)));
} }
if (shouldCompleteSends) if (shouldCompleteSends)
{ {
_state.SendResettableCompletionSource.CompleteException( _state.SendResettableCompletionSource.CompleteException(
ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException("Write was aborted."))); ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetOperationAbortedException(SR.net_quic_writing_aborted)));
} }
StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_SEND, errorCode); StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_SEND, errorCode);
...@@ -674,7 +678,7 @@ internal void AbortWrite(long errorCode) ...@@ -674,7 +678,7 @@ internal void AbortWrite(long errorCode)
private unsafe void StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS flags, long errorCode) private unsafe void StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS flags, long errorCode)
{ {
Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)");
ThrowIfFailure(MsQuicApi.Api.ApiTable->StreamShutdown( ThrowHelper.ThrowIfMsQuicError(MsQuicApi.Api.ApiTable->StreamShutdown(
_state.Handle.QuicHandle, _state.Handle.QuicHandle,
flags, flags,
(uint)errorCode), "StreamShutdown failed"); (uint)errorCode), "StreamShutdown failed");
...@@ -897,7 +901,7 @@ private void Dispose(bool disposing) ...@@ -897,7 +901,7 @@ private void Dispose(bool disposing)
if (completeRead) if (completeRead)
{ {
_state.ReceiveResettableCompletionSource.CompleteException( _state.ReceiveResettableCompletionSource.CompleteException(
ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException("Read was canceled"))); ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetOperationAbortedException()));
} }
...@@ -914,7 +918,7 @@ private void Dispose(bool disposing) ...@@ -914,7 +918,7 @@ private void Dispose(bool disposing)
private unsafe void EnableReceive() private unsafe void EnableReceive()
{ {
Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)");
ThrowIfFailure(MsQuicApi.Api.ApiTable->StreamReceiveSetEnabled(_state.Handle.QuicHandle, 1), "StreamReceiveSetEnabled failed"); ThrowHelper.ThrowIfMsQuicError(MsQuicApi.Api.ApiTable->StreamReceiveSetEnabled(_state.Handle.QuicHandle, 1), "StreamReceiveSetEnabled failed");
} }
/// <summary> /// <summary>
...@@ -1136,13 +1140,13 @@ private static int HandleEventPeerRecvAborted(State state, ref QUIC_STREAM_EVENT ...@@ -1136,13 +1140,13 @@ private static int HandleEventPeerRecvAborted(State state, ref QUIC_STREAM_EVENT
if (shouldSendComplete) if (shouldSendComplete)
{ {
state.SendResettableCompletionSource.CompleteException( state.SendResettableCompletionSource.CompleteException(
ExceptionDispatchInfo.SetCurrentStackTrace(new QuicStreamAbortedException(state.SendErrorCode))); ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetStreamAbortedException(state.SendErrorCode)));
} }
if (shouldShutdownWriteComplete) if (shouldShutdownWriteComplete)
{ {
state.ShutdownWriteCompletionSource.SetException( state.ShutdownWriteCompletionSource.SetException(
ExceptionDispatchInfo.SetCurrentStackTrace(new QuicStreamAbortedException(state.SendErrorCode))); ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetStreamAbortedException(state.SendErrorCode)));
} }
return QUIC_STATUS_SUCCESS; return QUIC_STATUS_SUCCESS;
...@@ -1187,7 +1191,7 @@ private static int HandleEventStartComplete(State state, ref QUIC_STREAM_EVENT s ...@@ -1187,7 +1191,7 @@ private static int HandleEventStartComplete(State state, ref QUIC_STREAM_EVENT s
// TODO: Should we throw QuicOperationAbortedException when status is InvalidState? // TODO: Should we throw QuicOperationAbortedException when status is InvalidState?
// [ActiveIssue("https://github.com/dotnet/runtime/issues/55619")] // [ActiveIssue("https://github.com/dotnet/runtime/issues/55619")]
state.StartCompletionSource.TrySetException( state.StartCompletionSource.TrySetException(
ExceptionDispatchInfo.SetCurrentStackTrace(new MsQuicException(status, "StreamStart failed"))); ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetExceptionForMsQuicStatus(status, "StreamStart failed")));
} }
} }
...@@ -1271,7 +1275,7 @@ private static int HandleEventShutdownComplete(State state, ref QUIC_STREAM_EVEN ...@@ -1271,7 +1275,7 @@ private static int HandleEventShutdownComplete(State state, ref QUIC_STREAM_EVEN
else else
{ {
state.ReceiveResettableCompletionSource.CompleteException( state.ReceiveResettableCompletionSource.CompleteException(
ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException($"Stream start failed"))); ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetOperationAbortedException($"Stream start failed")));
} }
} }
...@@ -1284,7 +1288,7 @@ private static int HandleEventShutdownComplete(State state, ref QUIC_STREAM_EVEN ...@@ -1284,7 +1288,7 @@ private static int HandleEventShutdownComplete(State state, ref QUIC_STREAM_EVEN
else else
{ {
state.ShutdownWriteCompletionSource.SetException( state.ShutdownWriteCompletionSource.SetException(
ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException($"Stream start failed"))); ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetOperationAbortedException($"Stream start failed")));
} }
} }
...@@ -1325,7 +1329,7 @@ private static int HandleEventPeerSendAborted(State state, ref QUIC_STREAM_EVENT ...@@ -1325,7 +1329,7 @@ private static int HandleEventPeerSendAborted(State state, ref QUIC_STREAM_EVENT
if (shouldComplete) if (shouldComplete)
{ {
state.ReceiveResettableCompletionSource.CompleteException( state.ReceiveResettableCompletionSource.CompleteException(
ExceptionDispatchInfo.SetCurrentStackTrace(new QuicStreamAbortedException(state.ReadErrorCode))); ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetStreamAbortedException(state.ReadErrorCode)));
} }
return QUIC_STATUS_SUCCESS; return QUIC_STATUS_SUCCESS;
...@@ -1543,7 +1547,7 @@ internal async ValueTask StartAsync(CancellationToken cancellationToken) ...@@ -1543,7 +1547,7 @@ internal async ValueTask StartAsync(CancellationToken cancellationToken)
if (!StatusSucceeded(status)) if (!StatusSucceeded(status))
{ {
Exception exception = new MsQuicException(status, "Could not start stream"); Exception exception = ThrowHelper.GetExceptionForMsQuicStatus(status, "Could not start stream");
_state.StartCompletionSource.TrySetException(ExceptionDispatchInfo.SetCurrentStackTrace(exception)); _state.StartCompletionSource.TrySetException(ExceptionDispatchInfo.SetCurrentStackTrace(exception));
throw exception; throw exception;
} }
......
// 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 static class ThrowHelper
{
internal static Exception GetConnectionAbortedException(long errorCode)
{
return errorCode switch
{
-1 => new QuicOperationAbortedException(), // Shutdown initiated by us.
long err => new QuicConnectionAbortedException(err) // Shutdown initiated by peer.
};
}
internal static Exception GetStreamAbortedException(long errorCode)
{
return errorCode switch
{
-1 => new QuicOperationAbortedException(), // Shutdown initiated by us.
long err => new QuicStreamAbortedException(err) // Shutdown initiated by peer.
};
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Quic;
using System.Security.Authentication;
using static Microsoft.Quic.MsQuic;
namespace System.Net.Quic.Implementations.MsQuic
{
internal static class ThrowHelper
{
internal static QuicException GetConnectionAbortedException(long errorCode)
{
return errorCode switch
{
-1 => GetOperationAbortedException(), // Shutdown initiated by us.
long err => new QuicException(QuicError.ConnectionAborted, err, SR.Format(SR.net_quic_connectionaborted, err)) // Shutdown initiated by peer.
};
}
internal static QuicException GetStreamAbortedException(long errorCode)
{
return errorCode switch
{
-1 => GetOperationAbortedException(), // Shutdown initiated by us.
long err => new QuicException(QuicError.StreamAborted, err, SR.Format(SR.net_quic_streamaborted, err)) // Shutdown initiated by peer.
};
}
internal static QuicException GetOperationAbortedException(string? message = null)
{
return new QuicException(QuicError.OperationAborted, null, message ?? SR.net_quic_operationaborted);
}
internal static Exception GetExceptionForMsQuicStatus(int status, string? message = null)
{
Exception ex = GetExceptionInternal(status, message);
if (status != 0)
{
// Include the raw MsQuic status in the HResult property for better diagnostics
ex.HResult = status;
}
return ex;
static Exception GetExceptionInternal(int status, string? message)
{
//
// Start by checking for statuses mapped to QuicError enum
//
if (status == QUIC_STATUS_ADDRESS_IN_USE) return new QuicException(QuicError.AddressInUse, null, SR.net_quic_address_in_use);
if (status == QUIC_STATUS_UNREACHABLE) return new QuicException(QuicError.HostUnreachable, null, SR.net_quic_host_unreachable);
if (status == QUIC_STATUS_CONNECTION_REFUSED) return new QuicException(QuicError.ConnectionRefused, null, SR.net_quic_connection_refused);
if (status == QUIC_STATUS_VER_NEG_ERROR) return new QuicException(QuicError.VersionNegotiationError, null, SR.net_quic_ver_neg_error);
if (status == QUIC_STATUS_INVALID_ADDRESS) return new QuicException(QuicError.InvalidAddress, null, SR.net_quic_invalid_address);
if (status == QUIC_STATUS_CONNECTION_IDLE) return new QuicException(QuicError.ConnectionIdle, null, SR.net_quic_connection_idle);
if (status == QUIC_STATUS_PROTOCOL_ERROR) return new QuicException(QuicError.ProtocolError, null, SR.net_quic_protocol_error);
if (status == QUIC_STATUS_TLS_ERROR ||
status == QUIC_STATUS_CERT_EXPIRED ||
status == QUIC_STATUS_CERT_UNTRUSTED_ROOT ||
status == QUIC_STATUS_CERT_NO_CERT)
{
return new AuthenticationException(SR.Format(SR.net_quic_auth, GetErrorMessageForStatus(status, message)));
}
//
// Although ALPN negotiation failure is triggered by a TLS Alert, it is mapped differently
//
if (status == QUIC_STATUS_ALPN_NEG_FAILURE)
{
return new AuthenticationException(SR.net_quic_alpn_neg_error);
}
//
// other TLS Alerts: MsQuic maps TLS alerts by offsetting them by a
// certain value. CloseNotify is the TLS Alert with value 0x00, so
// all TLS Alert codes are mapped to [QUIC_STATUS_CLOSE_NOTIFY,
// QUIC_STATUS_CLOSE_NOTIFY + 255]
//
// Mapped TLS alerts include following statuses:
// - QUIC_STATUS_CLOSE_NOTIFY
// - QUIC_STATUS_BAD_CERTIFICATE
// - QUIC_STATUS_UNSUPPORTED_CERTIFICATE
// - QUIC_STATUS_REVOKED_CERTIFICATE
// - QUIC_STATUS_EXPIRED_CERTIFICATE
// - QUIC_STATUS_UNKNOWN_CERTIFICATE
// - QUIC_STATUS_REQUIRED_CERTIFICATE
//
if ((uint)status >= (uint)QUIC_STATUS_CLOSE_NOTIFY && (uint)status < (uint)QUIC_STATUS_CLOSE_NOTIFY + 256)
{
int alert = status - QUIC_STATUS_CLOSE_NOTIFY;
return new AuthenticationException(SR.Format(SR.net_auth_tls_alert, alert));
}
//
// for everything else, use general InternalError
//
return new QuicException(QuicError.InternalError, null, SR.Format(SR.net_quic_internal_error, GetErrorMessageForStatus(status, message)));
}
}
internal static void ThrowIfMsQuicError(int status, string? message = null)
{
if (StatusFailed(status))
{
throw GetExceptionForMsQuicStatus(status, message);
}
}
internal static string GetErrorMessageForStatus(int status, string? message)
{
return (message ?? "Status code") + ": " + GetErrorMessageForStatus(status);
}
internal static string GetErrorMessageForStatus(int status)
{
if (status == QUIC_STATUS_SUCCESS) return "QUIC_STATUS_SUCCESS";
else if (status == QUIC_STATUS_PENDING) return "QUIC_STATUS_PENDING";
else if (status == QUIC_STATUS_CONTINUE) return "QUIC_STATUS_CONTINUE";
else if (status == QUIC_STATUS_OUT_OF_MEMORY) return "QUIC_STATUS_OUT_OF_MEMORY";
else if (status == QUIC_STATUS_INVALID_PARAMETER) return "QUIC_STATUS_INVALID_PARAMETER";
else if (status == QUIC_STATUS_INVALID_STATE) return "QUIC_STATUS_INVALID_STATE";
else if (status == QUIC_STATUS_NOT_SUPPORTED) return "QUIC_STATUS_NOT_SUPPORTED";
else if (status == QUIC_STATUS_NOT_FOUND) return "QUIC_STATUS_NOT_FOUND";
else if (status == QUIC_STATUS_BUFFER_TOO_SMALL) return "QUIC_STATUS_BUFFER_TOO_SMALL";
else if (status == QUIC_STATUS_HANDSHAKE_FAILURE) return "QUIC_STATUS_HANDSHAKE_FAILURE";
else if (status == QUIC_STATUS_ABORTED) return "QUIC_STATUS_ABORTED";
else if (status == QUIC_STATUS_ADDRESS_IN_USE) return "QUIC_STATUS_ADDRESS_IN_USE";
else if (status == QUIC_STATUS_INVALID_ADDRESS) return "QUIC_STATUS_INVALID_ADDRESS";
else if (status == QUIC_STATUS_CONNECTION_TIMEOUT) return "QUIC_STATUS_CONNECTION_TIMEOUT";
else if (status == QUIC_STATUS_CONNECTION_IDLE) return "QUIC_STATUS_CONNECTION_IDLE";
else if (status == QUIC_STATUS_UNREACHABLE) return "QUIC_STATUS_UNREACHABLE";
else if (status == QUIC_STATUS_INTERNAL_ERROR) return "QUIC_STATUS_INTERNAL_ERROR";
else if (status == QUIC_STATUS_CONNECTION_REFUSED) return "QUIC_STATUS_CONNECTION_REFUSED";
else if (status == QUIC_STATUS_PROTOCOL_ERROR) return "QUIC_STATUS_PROTOCOL_ERROR";
else if (status == QUIC_STATUS_VER_NEG_ERROR) return "QUIC_STATUS_VER_NEG_ERROR";
else if (status == QUIC_STATUS_TLS_ERROR) return "QUIC_STATUS_TLS_ERROR";
else if (status == QUIC_STATUS_USER_CANCELED) return "QUIC_STATUS_USER_CANCELED";
else if (status == QUIC_STATUS_ALPN_NEG_FAILURE) return "QUIC_STATUS_ALPN_NEG_FAILURE";
else if (status == QUIC_STATUS_STREAM_LIMIT_REACHED) return "QUIC_STATUS_STREAM_LIMIT_REACHED";
else if (status == QUIC_STATUS_CLOSE_NOTIFY) return "QUIC_STATUS_CLOSE_NOTIFY";
else if (status == QUIC_STATUS_BAD_CERTIFICATE) return "QUIC_STATUS_BAD_CERTIFICATE";
else if (status == QUIC_STATUS_UNSUPPORTED_CERTIFICATE) return "QUIC_STATUS_UNSUPPORTED_CERTIFICATE";
else if (status == QUIC_STATUS_REVOKED_CERTIFICATE) return "QUIC_STATUS_REVOKED_CERTIFICATE";
else if (status == QUIC_STATUS_EXPIRED_CERTIFICATE) return "QUIC_STATUS_EXPIRED_CERTIFICATE";
else if (status == QUIC_STATUS_UNKNOWN_CERTIFICATE) return "QUIC_STATUS_UNKNOWN_CERTIFICATE";
else if (status == QUIC_STATUS_REQUIRED_CERTIFICATE) return "QUIC_STATUS_REQUIRED_CERTIFICATE";
else if (status == QUIC_STATUS_CERT_EXPIRED) return "QUIC_STATUS_CERT_EXPIRED";
else if (status == QUIC_STATUS_CERT_UNTRUSTED_ROOT) return "QUIC_STATUS_CERT_UNTRUSTED_ROOT";
else if (status == QUIC_STATUS_CERT_NO_CERT) return "QUIC_STATUS_CERT_NO_CERT";
else return $"Unknown (0x{status:x})";
}
}
}
// 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
{
public class QuicConnectionAbortedException : QuicException
{
internal QuicConnectionAbortedException(long errorCode)
: this(SR.Format(SR.net_quic_connectionaborted, errorCode), errorCode)
{
}
public QuicConnectionAbortedException(string message, long errorCode)
: base (message)
{
ErrorCode = errorCode;
}
public long ErrorCode { get; }
}
}
// 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
{
/// <summary>
/// Defines the various error conditions for <see cref="QuicListener"/>, <see cref="QuicConnection"/> and <see cref="QuicStream"/> operations.
/// </summary>
public enum QuicError
{
/// <summary>
/// No error.
/// </summary>
Success,
/// <summary>
/// An internal implementation error has occured.
/// </summary>
InternalError,
/// <summary>
/// The connection was aborted by the peer. This error is associated with an application-level error code.
/// </summary>
ConnectionAborted,
/// <summary>
/// The read or write direction of the stream was aborted by the peer. This error is associated with an application-level error code.
/// </summary>
StreamAborted,
/// <summary>
/// The local address is already in use.
/// </summary>
AddressInUse,
/// <summary>
/// Binding to socket failed, likely caused by a family mismatch between local and remote address.
/// </summary>
InvalidAddress,
/// <summary>
/// The connection timed out waiting for a response from the peer.
/// </summary>
ConnectionTimeout,
/// <summary>
/// The server is currently unreachable.
/// </summary>
HostUnreachable,
/// <summary>
/// The server refused the connection.
/// </summary>
ConnectionRefused,
/// <summary>
/// A version negotiation error was encountered.
/// </summary>
VersionNegotiationError,
/// <summary>
/// The connection timed out from inactivity.
/// </summary>
ConnectionIdle,
/// <summary>
/// A QUIC protocol error was encountered.
/// </summary>
ProtocolError,
/// <summary>
/// The operation has been aborted.
/// </summary>
OperationAborted,
}
}
// Licensed to the .NET Foundation under one or more agreements. // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using System.IO;
namespace System.Net.Quic namespace System.Net.Quic
{ {
public class QuicException : Exception /// <summary>
/// The exception that is thrown when a QUIC error occurs.
/// </summary>
public sealed class QuicException : IOException
{ {
public QuicException(string? message) /// <summary>
/// Initializes a new instance of the <see cref='QuicException'/> class.
/// </summary>
public QuicException(QuicError error, long? applicationErrorCode, string message)
: base(message) : base(message)
{ {
} QuicError = error;
public QuicException(string? message, Exception? innerException) ApplicationErrorCode = applicationErrorCode;
: base(message, innerException)
{
} }
public QuicException(string? message, Exception? innerException, int result) /// <summary>
: base(message, innerException) /// Gets the error which is associated with this exception.
{ /// </summary>
// HResult 0 means OK, so do not override the default value set by Exception ctor, public QuicError QuicError { get; }
// because in this case we don't have an HResult.
if (result != 0) /// <summary>
{ /// The application protocol error code associated with the error.
HResult = result; /// </summary>
} /// <remarks>
} /// This property contains the error code set by the application layer when closing the connection (<see cref="QuicError.ConnectionAborted"/>) or closing a read/write direction of a QUIC stream (<see cref="QuicError.StreamAborted"/>). Contains null for all other errors.
/// </remarks>
public long? ApplicationErrorCode { get; }
} }
} }
...@@ -109,7 +109,7 @@ private unsafe QuicListener(QuicListenerOptions options) ...@@ -109,7 +109,7 @@ private unsafe QuicListener(QuicListenerOptions options)
try try
{ {
QUIC_HANDLE* handle; QUIC_HANDLE* handle;
ThrowIfFailure(MsQuicApi.Api.ApiTable->ListenerOpen( ThrowHelper.ThrowIfMsQuicError(MsQuicApi.Api.ApiTable->ListenerOpen(
MsQuicApi.Api.Registration.QuicHandle, MsQuicApi.Api.Registration.QuicHandle,
&NativeCallback, &NativeCallback,
(void*)GCHandle.ToIntPtr(context), (void*)GCHandle.ToIntPtr(context),
...@@ -138,7 +138,7 @@ private unsafe QuicListener(QuicListenerOptions options) ...@@ -138,7 +138,7 @@ private unsafe QuicListener(QuicListenerOptions options)
// Using the Unspecified family makes MsQuic handle connections from all IP addresses. // Using the Unspecified family makes MsQuic handle connections from all IP addresses.
address.Family = QUIC_ADDRESS_FAMILY_UNSPEC; address.Family = QUIC_ADDRESS_FAMILY_UNSPEC;
} }
ThrowIfFailure(MsQuicApi.Api.ApiTable->ListenerStart( ThrowHelper.ThrowIfMsQuicError(MsQuicApi.Api.ApiTable->ListenerStart(
_handle.QuicHandle, _handle.QuicHandle,
alpnBuffers.Buffers, alpnBuffers.Buffers,
(uint)alpnBuffers.Count, (uint)alpnBuffers.Count,
...@@ -279,7 +279,7 @@ public async ValueTask DisposeAsync() ...@@ -279,7 +279,7 @@ public async ValueTask DisposeAsync()
_handle.Dispose(); _handle.Dispose();
// Flush the queue and dispose all remaining connections. // Flush the queue and dispose all remaining connections.
_acceptQueue.Writer.TryComplete(ExceptionDispatchInfo.SetCurrentStackTrace(new QuicOperationAbortedException())); _acceptQueue.Writer.TryComplete(ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetOperationAbortedException()));
while (_acceptQueue.Reader.TryRead(out PendingConnection? pendingConnection)) while (_acceptQueue.Reader.TryRead(out PendingConnection? pendingConnection))
{ {
await pendingConnection.DisposeAsync().ConfigureAwait(false); await pendingConnection.DisposeAsync().ConfigureAwait(false);
......
// 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
{
public class QuicOperationAbortedException : QuicException
{
internal QuicOperationAbortedException()
: base(SR.net_quic_operationaborted)
{
}
public QuicOperationAbortedException(string message) : base(message)
{
}
}
}
// 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
{
public class QuicStreamAbortedException : QuicException
{
internal QuicStreamAbortedException(long errorCode)
: this(SR.Format(SR.net_quic_streamaborted, errorCode), errorCode)
{
}
public QuicStreamAbortedException(string message, long errorCode)
: base(message)
{
ErrorCode = errorCode;
}
public long ErrorCode { get; }
}
}
...@@ -604,7 +604,7 @@ public async Task OpenStreamAsync_ConnectionAbort_Throws(bool unidirectional, bo ...@@ -604,7 +604,7 @@ public async Task OpenStreamAsync_ConnectionAbort_Throws(bool unidirectional, bo
else else
{ {
await serverConnection.CloseAsync(0); await serverConnection.CloseAsync(0);
await Assert.ThrowsAsync<QuicConnectionAbortedException>(() => waitTask.AsTask().WaitAsync(TimeSpan.FromSeconds(3))); await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, () => waitTask.AsTask().WaitAsync(TimeSpan.FromSeconds(3)));
} }
clientConnection.Dispose(); clientConnection.Dispose();
...@@ -624,7 +624,7 @@ public async Task SetListenerTimeoutWorksWithSmallTimeout() ...@@ -624,7 +624,7 @@ public async Task SetListenerTimeoutWorksWithSmallTimeout()
}; };
(QuicConnection clientConnection, QuicConnection serverConnection) = await CreateConnectedQuicConnection(null, listenerOptions); (QuicConnection clientConnection, QuicConnection serverConnection) = await CreateConnectedQuicConnection(null, listenerOptions);
await Assert.ThrowsAsync<QuicConnectionAbortedException>(async () => await serverConnection.AcceptStreamAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(100))); await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, async () => await serverConnection.AcceptStreamAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(100)));
serverConnection.Dispose(); serverConnection.Dispose();
clientConnection.Dispose(); clientConnection.Dispose();
} }
...@@ -746,7 +746,7 @@ public async Task CloseAsync_ByServer_AcceptThrows() ...@@ -746,7 +746,7 @@ public async Task CloseAsync_ByServer_AcceptThrows()
var acceptTask = serverConnection.AcceptStreamAsync(); var acceptTask = serverConnection.AcceptStreamAsync();
await serverConnection.CloseAsync(errorCode: 0); await serverConnection.CloseAsync(errorCode: 0);
// make sure we throw // make sure we throw
await Assert.ThrowsAsync<QuicOperationAbortedException>(() => acceptTask.AsTask()); await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, () => acceptTask.AsTask());
} }
} }
...@@ -966,8 +966,8 @@ public async Task Read_ConnectionAbortedByPeer_Throws() ...@@ -966,8 +966,8 @@ public async Task Read_ConnectionAbortedByPeer_Throws()
await clientConnection.CloseAsync(ExpectedErrorCode); await clientConnection.CloseAsync(ExpectedErrorCode);
byte[] buffer = new byte[100]; byte[] buffer = new byte[100];
QuicConnectionAbortedException ex = await Assert.ThrowsAsync<QuicConnectionAbortedException>(() => serverStream.ReadAsync(buffer).AsTask()); QuicException ex = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, () => serverStream.ReadAsync(buffer).AsTask());
Assert.Equal(ExpectedErrorCode, ex.ErrorCode); Assert.Equal(ExpectedErrorCode, ex.ApplicationErrorCode);
}).WaitAsync(TimeSpan.FromMilliseconds(PassingTestTimeoutMilliseconds)); }).WaitAsync(TimeSpan.FromMilliseconds(PassingTestTimeoutMilliseconds));
} }
...@@ -987,7 +987,7 @@ public async Task Read_ConnectionAbortedByUser_Throws() ...@@ -987,7 +987,7 @@ public async Task Read_ConnectionAbortedByUser_Throws()
await serverConnection.CloseAsync(0); await serverConnection.CloseAsync(0);
byte[] buffer = new byte[100]; byte[] buffer = new byte[100];
await Assert.ThrowsAsync<QuicOperationAbortedException>(() => serverStream.ReadAsync(buffer).AsTask()); await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, () => serverStream.ReadAsync(buffer).AsTask());
}).WaitAsync(TimeSpan.FromMilliseconds(PassingTestTimeoutMilliseconds)); }).WaitAsync(TimeSpan.FromMilliseconds(PassingTestTimeoutMilliseconds));
} }
......
...@@ -74,7 +74,7 @@ public async Task CloseAsync_WithPendingAcceptAndConnect_PendingAndSubsequentThr ...@@ -74,7 +74,7 @@ public async Task CloseAsync_WithPendingAcceptAndConnect_PendingAndSubsequentThr
sync.Release(); sync.Release();
// Pending ops should fail // Pending ops should fail
await Assert.ThrowsAsync<QuicOperationAbortedException>(() => acceptTask); await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, () => acceptTask);
// TODO: This may not always throw QuicOperationAbortedException due to a data race with MsQuic worker threads // TODO: This may not always throw QuicOperationAbortedException due to a data race with MsQuic worker threads
// (CloseAsync may be processed before OpenStreamAsync as it is scheduled to the front of the operation queue) // (CloseAsync may be processed before OpenStreamAsync as it is scheduled to the front of the operation queue)
// To be revisited once we standartize on exceptions. // To be revisited once we standartize on exceptions.
...@@ -83,7 +83,7 @@ public async Task CloseAsync_WithPendingAcceptAndConnect_PendingAndSubsequentThr ...@@ -83,7 +83,7 @@ public async Task CloseAsync_WithPendingAcceptAndConnect_PendingAndSubsequentThr
// Subsequent attempts should fail // Subsequent attempts should fail
// TODO: Which exception is correct? // TODO: Which exception is correct?
await Assert.ThrowsAsync<QuicOperationAbortedException>(async () => await serverConnection.AcceptStreamAsync()); await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, async () => await serverConnection.AcceptStreamAsync());
await Assert.ThrowsAnyAsync<QuicException>(() => OpenAndUseStreamAsync(serverConnection)); await Assert.ThrowsAnyAsync<QuicException>(() => OpenAndUseStreamAsync(serverConnection));
}); });
} }
...@@ -111,13 +111,13 @@ public async Task Dispose_WithPendingAcceptAndConnect_PendingAndSubsequentThrowO ...@@ -111,13 +111,13 @@ public async Task Dispose_WithPendingAcceptAndConnect_PendingAndSubsequentThrowO
sync.Release(); sync.Release();
// Pending ops should fail // Pending ops should fail
await Assert.ThrowsAsync<QuicOperationAbortedException>(() => acceptTask); await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, () => acceptTask);
// TODO: This may not always throw QuicOperationAbortedException due to a data race with MsQuic worker threads // TODO: This may not always throw QuicOperationAbortedException due to a data race with MsQuic worker threads
// (CloseAsync may be processed before OpenStreamAsync as it is scheduled to the front of the operation queue) // (CloseAsync may be processed before OpenStreamAsync as it is scheduled to the front of the operation queue)
// To be revisited once we standartize on exceptions. // To be revisited once we standartize on exceptions.
// [ActiveIssue("https://github.com/dotnet/runtime/issues/55619")] // [ActiveIssue("https://github.com/dotnet/runtime/issues/55619")]
await Assert.ThrowsAnyAsync<QuicException>(() => connectTask); await Assert.ThrowsAsync<QuicException>(() => connectTask);
// Subsequent attempts should fail // Subsequent attempts should fail
// TODO: Should these be QuicOperationAbortedException, to match above? Or vice-versa? // TODO: Should these be QuicOperationAbortedException, to match above? Or vice-versa?
...@@ -149,18 +149,18 @@ public async Task ConnectionClosedByPeer_WithPendingAcceptAndConnect_PendingAndS ...@@ -149,18 +149,18 @@ public async Task ConnectionClosedByPeer_WithPendingAcceptAndConnect_PendingAndS
sync.Release(); sync.Release();
// Pending ops should fail // Pending ops should fail
QuicConnectionAbortedException ex; QuicException ex;
ex = await Assert.ThrowsAsync<QuicConnectionAbortedException>(() => acceptTask); ex = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, () => acceptTask);
Assert.Equal(ExpectedErrorCode, ex.ErrorCode); Assert.Equal(ExpectedErrorCode, ex.ApplicationErrorCode);
ex = await Assert.ThrowsAsync<QuicConnectionAbortedException>(() => connectTask); ex = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, () => connectTask);
Assert.Equal(ExpectedErrorCode, ex.ErrorCode); Assert.Equal(ExpectedErrorCode, ex.ApplicationErrorCode);
// Subsequent attempts should fail // Subsequent attempts should fail
ex = await Assert.ThrowsAsync<QuicConnectionAbortedException>(() => serverConnection.AcceptStreamAsync().AsTask()); ex = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, () => serverConnection.AcceptStreamAsync().AsTask());
Assert.Equal(ExpectedErrorCode, ex.ErrorCode); Assert.Equal(ExpectedErrorCode, ex.ApplicationErrorCode);
ex = await Assert.ThrowsAsync<QuicConnectionAbortedException>(() => OpenAndUseStreamAsync(serverConnection)); ex = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, () => OpenAndUseStreamAsync(serverConnection));
Assert.Equal(ExpectedErrorCode, ex.ErrorCode); Assert.Equal(ExpectedErrorCode, ex.ApplicationErrorCode);
}); });
} }
...@@ -199,8 +199,8 @@ public async Task CloseAsync_WithOpenStream_LocalAndPeerStreamsFailWithQuicOpera ...@@ -199,8 +199,8 @@ public async Task CloseAsync_WithOpenStream_LocalAndPeerStreamsFailWithQuicOpera
await clientConnection.CloseAsync(ExpectedErrorCode); await clientConnection.CloseAsync(ExpectedErrorCode);
await Assert.ThrowsAsync<QuicOperationAbortedException>(async () => await clientStream.ReadAsync(new byte[1])); await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, async () => await clientStream.ReadAsync(new byte[1]));
await Assert.ThrowsAsync<QuicOperationAbortedException>(async () => await clientStream.WriteAsync(new byte[1])); await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, async () => await clientStream.WriteAsync(new byte[1]));
}, },
async serverConnection => async serverConnection =>
{ {
...@@ -210,11 +210,11 @@ public async Task CloseAsync_WithOpenStream_LocalAndPeerStreamsFailWithQuicOpera ...@@ -210,11 +210,11 @@ public async Task CloseAsync_WithOpenStream_LocalAndPeerStreamsFailWithQuicOpera
sync.Release(); sync.Release();
// Since the peer did the abort, we should receive the abort error code in the exception. // Since the peer did the abort, we should receive the abort error code in the exception.
QuicConnectionAbortedException ex; QuicException ex;
ex = await Assert.ThrowsAsync<QuicConnectionAbortedException>(async () => await serverStream.ReadAsync(new byte[1])); ex = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, async () => await serverStream.ReadAsync(new byte[1]));
Assert.Equal(ExpectedErrorCode, ex.ErrorCode); Assert.Equal(ExpectedErrorCode, ex.ApplicationErrorCode);
ex = await Assert.ThrowsAsync<QuicConnectionAbortedException>(async () => await serverStream.WriteAsync(new byte[1])); ex = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, async () => await serverStream.WriteAsync(new byte[1]));
Assert.Equal(ExpectedErrorCode, ex.ErrorCode); Assert.Equal(ExpectedErrorCode, ex.ApplicationErrorCode);
}); });
} }
...@@ -240,8 +240,8 @@ public async Task Dispose_WithOpenLocalStream_LocalStreamFailsWithQuicOperationA ...@@ -240,8 +240,8 @@ public async Task Dispose_WithOpenLocalStream_LocalStreamFailsWithQuicOperationA
clientConnection.Dispose(); clientConnection.Dispose();
await Assert.ThrowsAsync<QuicOperationAbortedException>(async () => await clientStream.ReadAsync(new byte[1])); await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, async () => await clientStream.ReadAsync(new byte[1]));
await Assert.ThrowsAsync<QuicOperationAbortedException>(async () => await clientStream.WriteAsync(new byte[1])); await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, async () => await clientStream.WriteAsync(new byte[1]));
}, },
async serverConnection => async serverConnection =>
{ {
...@@ -252,8 +252,8 @@ public async Task Dispose_WithOpenLocalStream_LocalStreamFailsWithQuicOperationA ...@@ -252,8 +252,8 @@ public async Task Dispose_WithOpenLocalStream_LocalStreamFailsWithQuicOperationA
// The client has done an abortive shutdown of the connection, which means we are not notified that the connection has closed. // The client has done an abortive shutdown of the connection, which means we are not notified that the connection has closed.
// But the connection idle timeout should kick in and eventually we will get exceptions. // But the connection idle timeout should kick in and eventually we will get exceptions.
await Assert.ThrowsAsync<QuicConnectionAbortedException>(async () => await serverStream.ReadAsync(new byte[1])); await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, async () => await serverStream.ReadAsync(new byte[1]));
await Assert.ThrowsAsync<QuicConnectionAbortedException>(async () => await serverStream.WriteAsync(new byte[1])); await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, async () => await serverStream.WriteAsync(new byte[1]));
}, listenerOptions: listenerOptions); }, listenerOptions: listenerOptions);
} }
} }
......
...@@ -459,8 +459,8 @@ public async Task Read_WriteAborted_Throws() ...@@ -459,8 +459,8 @@ public async Task Read_WriteAborted_Throws()
sem.Release(); sem.Release();
byte[] buffer = new byte[100]; byte[] buffer = new byte[100];
QuicStreamAbortedException ex = await Assert.ThrowsAsync<QuicStreamAbortedException>(() => serverStream.ReadAsync(buffer).AsTask()); QuicException ex = await AssertThrowsQuicExceptionAsync(QuicError.StreamAborted, () => serverStream.ReadAsync(buffer).AsTask());
Assert.Equal(ExpectedErrorCode, ex.ErrorCode); Assert.Equal(ExpectedErrorCode, ex.ApplicationErrorCode);
}); });
} }
...@@ -519,7 +519,7 @@ public async Task ReadOutstanding_ReadAborted_Throws() ...@@ -519,7 +519,7 @@ public async Task ReadOutstanding_ReadAborted_Throws()
using (clientStream) using (clientStream)
using (serverStream) using (serverStream)
{ {
Task exTask = Assert.ThrowsAsync<QuicOperationAbortedException>(() => serverStream.ReadAsync(new byte[1]).AsTask()); Task exTask = AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, () => serverStream.ReadAsync(new byte[1]).AsTask());
Assert.False(exTask.IsCompleted); Assert.False(exTask.IsCompleted);
serverStream.AbortRead(ExpectedErrorCode); serverStream.AbortRead(ExpectedErrorCode);
...@@ -546,8 +546,8 @@ public async Task WriteAbortedWithoutWriting_ReadThrows() ...@@ -546,8 +546,8 @@ public async Task WriteAbortedWithoutWriting_ReadThrows()
byte[] buffer = new byte[1]; byte[] buffer = new byte[1];
QuicStreamAbortedException ex = await Assert.ThrowsAsync<QuicStreamAbortedException>(() => ReadAll(stream, buffer)); QuicException ex = await AssertThrowsQuicExceptionAsync(QuicError.StreamAborted, () => ReadAll(stream, buffer));
Assert.Equal(expectedErrorCode, ex.ErrorCode); Assert.Equal(expectedErrorCode, ex.ApplicationErrorCode);
// We should still return true from CanRead, even though the read has been aborted. // We should still return true from CanRead, even though the read has been aborted.
Assert.True(stream.CanRead); Assert.True(stream.CanRead);
...@@ -570,8 +570,8 @@ public async Task ReadAbortedWithoutReading_WriteThrows() ...@@ -570,8 +570,8 @@ public async Task ReadAbortedWithoutReading_WriteThrows()
{ {
await using QuicStream stream = await connection.AcceptStreamAsync(); await using QuicStream stream = await connection.AcceptStreamAsync();
QuicStreamAbortedException ex = await Assert.ThrowsAsync<QuicStreamAbortedException>(() => WriteForever(stream)); QuicException ex = await AssertThrowsQuicExceptionAsync(QuicError.StreamAborted, () => WriteForever(stream));
Assert.Equal(expectedErrorCode, ex.ErrorCode); Assert.Equal(expectedErrorCode, ex.ApplicationErrorCode);
// We should still return true from CanWrite, even though the write has been aborted. // We should still return true from CanWrite, even though the write has been aborted.
Assert.True(stream.CanWrite); Assert.True(stream.CanWrite);
...@@ -595,7 +595,7 @@ public async Task WritePreCanceled_Throws() ...@@ -595,7 +595,7 @@ public async Task WritePreCanceled_Throws()
await Assert.ThrowsAsync<OperationCanceledException>(() => stream.WriteAsync(new byte[1], cts.Token).AsTask()); await Assert.ThrowsAsync<OperationCanceledException>(() => stream.WriteAsync(new byte[1], cts.Token).AsTask());
// aborting write causes the write direction to throw on subsequent operations // aborting write causes the write direction to throw on subsequent operations
await Assert.ThrowsAsync<QuicOperationAbortedException>(() => stream.WriteAsync(new byte[1]).AsTask()); await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, () => stream.WriteAsync(new byte[1]).AsTask());
// manual write abort is still required // manual write abort is still required
stream.AbortWrite(expectedErrorCode); stream.AbortWrite(expectedErrorCode);
...@@ -608,7 +608,7 @@ public async Task WritePreCanceled_Throws() ...@@ -608,7 +608,7 @@ public async Task WritePreCanceled_Throws()
byte[] buffer = new byte[1024 * 1024]; byte[] buffer = new byte[1024 * 1024];
QuicStreamAbortedException ex = await Assert.ThrowsAsync<QuicStreamAbortedException>(() => ReadAll(stream, buffer)); await AssertThrowsQuicExceptionAsync(QuicError.StreamAborted, () => ReadAll(stream, buffer));
await stream.ShutdownCompleted(); await stream.ShutdownCompleted();
} }
...@@ -640,7 +640,7 @@ async Task WriteUntilCanceled() ...@@ -640,7 +640,7 @@ async Task WriteUntilCanceled()
await Assert.ThrowsAsync<OperationCanceledException>(() => WriteUntilCanceled().WaitAsync(TimeSpan.FromSeconds(3))); await Assert.ThrowsAsync<OperationCanceledException>(() => WriteUntilCanceled().WaitAsync(TimeSpan.FromSeconds(3)));
// next write would also throw // next write would also throw
await Assert.ThrowsAsync<QuicOperationAbortedException>(() => stream.WriteAsync(new byte[1]).AsTask()); await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, () => stream.WriteAsync(new byte[1]).AsTask());
// manual write abort is still required // manual write abort is still required
stream.AbortWrite(expectedErrorCode); stream.AbortWrite(expectedErrorCode);
...@@ -664,7 +664,7 @@ async Task ReadUntilAborted() ...@@ -664,7 +664,7 @@ async Task ReadUntilAborted()
} }
} }
QuicStreamAbortedException ex = await Assert.ThrowsAsync<QuicStreamAbortedException>(() => ReadUntilAborted()); await AssertThrowsQuicExceptionAsync(QuicError.StreamAborted, () => ReadUntilAborted());
await stream.ShutdownCompleted(); await stream.ShutdownCompleted();
} }
...@@ -772,9 +772,9 @@ async ValueTask ReleaseOnWriteCompletionAsync() ...@@ -772,9 +772,9 @@ async ValueTask ReleaseOnWriteCompletionAsync()
await serverStream.WaitForWriteCompletionAsync(); await serverStream.WaitForWriteCompletionAsync();
waitForAbortTcs.SetException(new Exception("WaitForWriteCompletionAsync didn't throw stream aborted.")); waitForAbortTcs.SetException(new Exception("WaitForWriteCompletionAsync didn't throw stream aborted."));
} }
catch (QuicStreamAbortedException ex) catch (QuicException ex) when (ex.QuicError == QuicError.StreamAborted)
{ {
waitForAbortTcs.SetResult(ex.ErrorCode); waitForAbortTcs.SetResult(ex.ApplicationErrorCode.Value);
} }
catch (Exception ex) catch (Exception ex)
{ {
...@@ -805,7 +805,7 @@ public async Task WriteAsync_LocalAbort_Throws() ...@@ -805,7 +805,7 @@ public async Task WriteAsync_LocalAbort_Throws()
var writeTask = WriteForever(serverStream, 1024 * 1024); var writeTask = WriteForever(serverStream, 1024 * 1024);
serverStream.AbortWrite(ExpectedErrorCode); serverStream.AbortWrite(ExpectedErrorCode);
await Assert.ThrowsAsync<QuicOperationAbortedException>(() => writeTask.WaitAsync(TimeSpan.FromSeconds(3))); await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, () => writeTask.WaitAsync(TimeSpan.FromSeconds(3)));
sem.Release(); sem.Release();
}); });
} }
...@@ -845,7 +845,7 @@ async ValueTask ReleaseOnWriteCompletionAsync() ...@@ -845,7 +845,7 @@ async ValueTask ReleaseOnWriteCompletionAsync()
await serverStream.WaitForWriteCompletionAsync(); await serverStream.WaitForWriteCompletionAsync();
waitForAbortTcs.SetException(new Exception("WaitForWriteCompletionAsync didn't throw stream aborted.")); waitForAbortTcs.SetException(new Exception("WaitForWriteCompletionAsync didn't throw stream aborted."));
} }
catch (QuicOperationAbortedException) catch (QuicException ex) when (ex.QuicError == QuicError.OperationAborted)
{ {
waitForAbortTcs.SetResult(); waitForAbortTcs.SetResult();
} }
...@@ -956,9 +956,9 @@ async ValueTask ReleaseOnWriteCompletionAsync() ...@@ -956,9 +956,9 @@ async ValueTask ReleaseOnWriteCompletionAsync()
await stream.WaitForWriteCompletionAsync(); await stream.WaitForWriteCompletionAsync();
waitForAbortTcs.SetException(new Exception("WaitForWriteCompletionAsync didn't throw connection aborted.")); waitForAbortTcs.SetException(new Exception("WaitForWriteCompletionAsync didn't throw connection aborted."));
} }
catch (QuicConnectionAbortedException ex) catch (QuicException ex) when (ex.QuicError == QuicError.ConnectionAborted)
{ {
waitForAbortTcs.SetResult(ex.ErrorCode); waitForAbortTcs.SetResult(ex.ApplicationErrorCode.Value);
} }
}; };
}, },
......
...@@ -42,6 +42,13 @@ public bool RemoteCertificateValidationCallback(object sender, X509Certificate? ...@@ -42,6 +42,13 @@ public bool RemoteCertificateValidationCallback(object sender, X509Certificate?
return true; return true;
} }
public async Task<QuicException> AssertThrowsQuicExceptionAsync(QuicError expectedError, Func<Task> testCode)
{
QuicException ex = await Assert.ThrowsAsync<QuicException>(testCode);
Assert.Equal(expectedError, ex.QuicError);
return ex;
}
public QuicServerConnectionOptions CreateQuicServerOptions() public QuicServerConnectionOptions CreateQuicServerOptions()
{ {
return new QuicServerConnectionOptions() return new QuicServerConnectionOptions()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册