Skip to content

Commit c0971e5

Browse files
AlexRadchManickaP
andauthored
Added QuicException.TransportErrorCode (#88550)
* Added QuicException.TransportErrorCode * Code review by Tomas Weinfurt @wfurt * @ManickaP Marie Píchová code review * @ManickaP Marie Píchová code review * @ManickaP Marie Píchová code review * Feedback about error code casts --------- Co-authored-by: ManickaP <[email protected]>
1 parent fad5bd9 commit c0971e5

File tree

6 files changed

+53
-35
lines changed

6 files changed

+53
-35
lines changed

src/libraries/System.Net.Quic/ref/System.Net.Quic.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public sealed partial class QuicException : System.IO.IOException
6464
{
6565
public QuicException(System.Net.Quic.QuicError error, long? applicationErrorCode, string message) { }
6666
public long? ApplicationErrorCode { get { throw null; } }
67+
public long? TransportErrorCode { get { throw null; } }
6768
public System.Net.Quic.QuicError QuicError { get { throw null; } }
6869
}
6970
public sealed partial class QuicListener : System.IAsyncDisposable

src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicHelpers.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,7 @@ internal static unsafe T GetMsQuicParameter<T>(MsQuicSafeHandle handle, uint par
6767
&length,
6868
(byte*)&value);
6969

70-
if (StatusFailed(status))
71-
{
72-
throw ThrowHelper.GetExceptionForMsQuicStatus(status, $"GetParam({handle}, {parameter}) failed");
73-
}
74-
70+
ThrowHelper.ThrowIfMsQuicError(status, $"GetParam({handle}, {parameter}) failed");
7571
return value;
7672
}
7773

@@ -84,9 +80,6 @@ internal static unsafe void SetMsQuicParameter<T>(MsQuicSafeHandle handle, uint
8480
(uint)sizeof(T),
8581
(byte*)&value);
8682

87-
if (StatusFailed(status))
88-
{
89-
throw ThrowHelper.GetExceptionForMsQuicStatus(status, $"SetParam({handle}, {parameter}) failed");
90-
}
83+
ThrowHelper.ThrowIfMsQuicError(status, $"SetParam({handle}, {parameter}) failed");
9184
}
9285
}

src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ThrowHelper.cs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using Microsoft.Quic;
54
using System.Security.Authentication;
65
using System.Net.Security;
76
using System.Net.Sockets;
87
using static Microsoft.Quic.MsQuic;
98
using System.Diagnostics.CodeAnalysis;
9+
using System.Runtime.CompilerServices;
1010

1111
namespace System.Net.Quic;
1212

@@ -38,21 +38,21 @@ internal static bool TryGetStreamExceptionForMsQuicStatus(int status, [NotNullWh
3838
else if (status == QUIC_STATUS_INVALID_STATE)
3939
{
4040
// If status == QUIC_STATUS_INVALID_STATE, we have closed the connection.
41-
exception = ThrowHelper.GetOperationAbortedException();
41+
exception = GetOperationAbortedException();
4242
return true;
4343
}
4444
else if (StatusFailed(status))
4545
{
46-
exception = ThrowHelper.GetExceptionForMsQuicStatus(status);
46+
exception = GetExceptionForMsQuicStatus(status);
4747
return true;
4848
}
4949
exception = null;
5050
return false;
5151
}
5252

53-
internal static Exception GetExceptionForMsQuicStatus(int status, string? message = null)
53+
internal static Exception GetExceptionForMsQuicStatus(int status, long? errorCode = default, string? message = null)
5454
{
55-
Exception ex = GetExceptionInternal(status, message);
55+
Exception ex = GetExceptionInternal(status, errorCode, message);
5656
if (status != 0)
5757
{
5858
// Include the raw MsQuic status in the HResult property for better diagnostics
@@ -61,17 +61,17 @@ internal static Exception GetExceptionForMsQuicStatus(int status, string? messag
6161

6262
return ex;
6363

64-
static Exception GetExceptionInternal(int status, string? message)
64+
static Exception GetExceptionInternal(int status, long? errorCode, string? message)
6565
{
6666
//
6767
// Start by checking for statuses mapped to QuicError enum
6868
//
69-
if (status == QUIC_STATUS_CONNECTION_REFUSED) return new QuicException(QuicError.ConnectionRefused, null, SR.net_quic_connection_refused);
70-
if (status == QUIC_STATUS_CONNECTION_TIMEOUT) return new QuicException(QuicError.ConnectionTimeout, null, SR.net_quic_timeout);
71-
if (status == QUIC_STATUS_VER_NEG_ERROR) return new QuicException(QuicError.VersionNegotiationError, null, SR.net_quic_ver_neg_error);
72-
if (status == QUIC_STATUS_CONNECTION_IDLE) return new QuicException(QuicError.ConnectionIdle, null, SR.net_quic_connection_idle);
73-
if (status == QUIC_STATUS_PROTOCOL_ERROR) return new QuicException(QuicError.TransportError, null, SR.net_quic_protocol_error);
74-
if (status == QUIC_STATUS_ALPN_IN_USE) return new QuicException(QuicError.AlpnInUse, null, SR.net_quic_protocol_error);
69+
if (status == QUIC_STATUS_CONNECTION_REFUSED) return new QuicException(QuicError.ConnectionRefused, null, errorCode, SR.net_quic_connection_refused);
70+
if (status == QUIC_STATUS_CONNECTION_TIMEOUT) return new QuicException(QuicError.ConnectionTimeout, null, errorCode, SR.net_quic_timeout);
71+
if (status == QUIC_STATUS_VER_NEG_ERROR) return new QuicException(QuicError.VersionNegotiationError, null, errorCode, SR.net_quic_ver_neg_error);
72+
if (status == QUIC_STATUS_CONNECTION_IDLE) return new QuicException(QuicError.ConnectionIdle, null, errorCode, SR.net_quic_connection_idle);
73+
if (status == QUIC_STATUS_PROTOCOL_ERROR) return new QuicException(QuicError.TransportError, null, errorCode, SR.net_quic_protocol_error);
74+
if (status == QUIC_STATUS_ALPN_IN_USE) return new QuicException(QuicError.AlpnInUse, null, errorCode, SR.net_quic_protocol_error);
7575

7676
//
7777
// Transport errors will throw SocketException
@@ -81,7 +81,7 @@ static Exception GetExceptionInternal(int status, string? message)
8181
if (status == QUIC_STATUS_UNREACHABLE) return new SocketException((int)SocketError.HostUnreachable);
8282

8383
//
84-
// TLS and certificate erros throw AuthenticationException to match SslStream
84+
// TLS and certificate errors throw AuthenticationException to match SslStream
8585
//
8686
if (status == QUIC_STATUS_TLS_ERROR ||
8787
status == QUIC_STATUS_CERT_EXPIRED ||
@@ -125,11 +125,12 @@ static Exception GetExceptionInternal(int status, string? message)
125125
}
126126
}
127127

128+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
128129
internal static void ThrowIfMsQuicError(int status, string? message = null)
129130
{
130131
if (StatusFailed(status))
131132
{
132-
throw GetExceptionForMsQuicStatus(status, message);
133+
throw GetExceptionForMsQuicStatus(status, message: message);
133134
}
134135
}
135136

src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public sealed partial class QuicConnection : IAsyncDisposable
4242
/// <summary>
4343
/// The actual secret structure wrapper passed to MsQuic.
4444
/// </summary>
45-
private MsQuicTlsSecret? _tlsSecret;
45+
private readonly MsQuicTlsSecret? _tlsSecret;
4646
#endif
4747

4848
/// <summary>
@@ -304,7 +304,7 @@ private async ValueTask FinishConnectAsync(QuicClientConnectionOptions options,
304304

305305
// RFC 6066 forbids IP literals
306306
// DNI mapping is handled by MsQuic
307-
var hostname = TargetHostNameHelper.IsValidAddress(options.ClientAuthenticationOptions.TargetHost)
307+
string hostname = TargetHostNameHelper.IsValidAddress(options.ClientAuthenticationOptions.TargetHost)
308308
? string.Empty
309309
: options.ClientAuthenticationOptions.TargetHost ?? string.Empty;
310310

@@ -492,9 +492,7 @@ private unsafe int HandleEventConnected(ref CONNECTED_DATA data)
492492
}
493493
private unsafe int HandleEventShutdownInitiatedByTransport(ref SHUTDOWN_INITIATED_BY_TRANSPORT_DATA data)
494494
{
495-
// TODO: we should propagate transport error code.
496-
// https://github.com/dotnet/runtime/issues/72666
497-
Exception exception = ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetExceptionForMsQuicStatus(data.Status));
495+
Exception exception = ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetExceptionForMsQuicStatus(data.Status, (long)data.ErrorCode));
498496
_connectedTcs.TrySetException(exception);
499497
_acceptQueue.Writer.TryComplete(exception);
500498
return QUIC_STATUS_SUCCESS;

src/libraries/System.Net.Quic/src/System/Net/Quic/QuicException.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,18 @@ public sealed class QuicException : IOException
1717
/// <param name="applicationErrorCode">The application protocol error code associated with the error.</param>
1818
/// <param name="message">The message for the exception.</param>
1919
public QuicException(QuicError error, long? applicationErrorCode, string message)
20-
: this(error, applicationErrorCode, message, null)
20+
: this(error, applicationErrorCode, null, message, null)
21+
{ }
22+
23+
/// <summary>
24+
/// Initializes a new instance of the <see cref='QuicException'/> class.
25+
/// </summary>
26+
/// <param name="error">The error associated with the exception.</param>
27+
/// <param name="applicationErrorCode">The application protocol error code associated with the error.</param>
28+
/// <param name="transportErrorCode">The transport protocol error code associated with the error.</param>
29+
/// <param name="message">The message for the exception.</param>
30+
internal QuicException(QuicError error, long? applicationErrorCode, long? transportErrorCode, string message)
31+
: this(error, applicationErrorCode, transportErrorCode, message, null)
2132
{ }
2233

2334
/// <summary>
@@ -28,10 +39,23 @@ public QuicException(QuicError error, long? applicationErrorCode, string message
2839
/// <param name="message">The message for the exception.</param>
2940
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
3041
internal QuicException(QuicError error, long? applicationErrorCode, string message, Exception? innerException)
42+
: this(error, applicationErrorCode, null, message, innerException)
43+
{ }
44+
45+
/// <summary>
46+
/// Initializes a new instance of the <see cref='QuicException'/> class.
47+
/// </summary>
48+
/// <param name="error">The error associated with the exception.</param>
49+
/// <param name="applicationErrorCode">The application protocol error code associated with the error.</param>
50+
/// <param name="transportErrorCode">The transport protocol error code associated with the error.</param>
51+
/// <param name="message">The message for the exception.</param>
52+
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
53+
internal QuicException(QuicError error, long? applicationErrorCode, long? transportErrorCode, string message, Exception? innerException)
3154
: base(message, innerException)
3255
{
3356
QuicError = error;
3457
ApplicationErrorCode = applicationErrorCode;
58+
TransportErrorCode = transportErrorCode;
3559
}
3660

3761
/// <summary>
@@ -46,4 +70,9 @@ internal QuicException(QuicError error, long? applicationErrorCode, string messa
4670
/// 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.
4771
/// </remarks>
4872
public long? ApplicationErrorCode { get; }
73+
74+
/// <summary>
75+
/// The transport protocol error code associated with the error.
76+
/// </summary>
77+
public long? TransportErrorCode { get; }
4978
}

src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -581,13 +581,9 @@ private unsafe int HandleEventShutdownComplete(ref SHUTDOWN_COMPLETE_DATA data)
581581
// It's local shutdown by app, this side called QuicConnection.CloseAsync, throw QuicError.OperationAborted.
582582
(shutdownByApp: true, closedRemotely: false) => ThrowHelper.GetOperationAbortedException(),
583583
// It's remote shutdown by transport, we received a CONNECTION_CLOSE frame with a QUIC transport error code, throw error based on the status.
584-
// TODO: we should propagate the transport error code
585-
// https://github.com/dotnet/runtime/issues/72666
586-
(shutdownByApp: false, closedRemotely: true) => ThrowHelper.GetExceptionForMsQuicStatus(data.ConnectionCloseStatus, $"Shutdown by transport {data.ConnectionErrorCode}"),
584+
(shutdownByApp: false, closedRemotely: true) => ThrowHelper.GetExceptionForMsQuicStatus(data.ConnectionCloseStatus, (long)data.ConnectionErrorCode, $"Shutdown by transport {data.ConnectionErrorCode}"),
587585
// It's local shutdown by transport, most likely due to a timeout, throw error based on the status.
588-
// TODO: we should propagate the transport error code
589-
// https://github.com/dotnet/runtime/issues/72666
590-
(shutdownByApp: false, closedRemotely: false) => ThrowHelper.GetExceptionForMsQuicStatus(data.ConnectionCloseStatus),
586+
(shutdownByApp: false, closedRemotely: false) => ThrowHelper.GetExceptionForMsQuicStatus(data.ConnectionCloseStatus, (long)data.ConnectionErrorCode),
591587
};
592588
_startedTcs.TrySetException(exception);
593589
_receiveTcs.TrySetException(exception, final: true);

0 commit comments

Comments
 (0)