From 0b4dcf2fdfa1b91fa10cc12183b40a8fcbdf65a1 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Tue, 18 Jul 2023 21:47:18 +0200 Subject: [PATCH 1/5] React to HttpRequestError API changes --- .../System.Net.Http/ref/System.Net.Http.cs | 4 ++-- .../src/System/Net/Http/HttpContent.cs | 7 +++--- .../System/Net/Http/HttpRequestException.cs | 13 +++++------ .../AuthenticationHelper.NtAuth.cs | 2 +- .../Http/SocketsHttpHandler/ConnectHelper.cs | 4 ++-- .../SocketsHttpHandler/Http2Connection.cs | 4 ++-- .../Http/SocketsHttpHandler/Http2Stream.cs | 17 +++++++------- .../SocketsHttpHandler/Http3RequestStream.cs | 18 +++++++-------- .../Http/SocketsHttpHandler/HttpConnection.cs | 22 +++++++++---------- .../SocketsHttpHandler/HttpConnectionBase.cs | 2 +- .../SocketsHttpHandler/HttpConnectionPool.cs | 10 ++++----- 11 files changed, 51 insertions(+), 52 deletions(-) diff --git a/src/libraries/System.Net.Http/ref/System.Net.Http.cs b/src/libraries/System.Net.Http/ref/System.Net.Http.cs index 3ac82158387eb4..a31f5e69ad8cf0 100644 --- a/src/libraries/System.Net.Http/ref/System.Net.Http.cs +++ b/src/libraries/System.Net.Http/ref/System.Net.Http.cs @@ -272,8 +272,8 @@ public HttpRequestException() { } public HttpRequestException(string? message) { } public HttpRequestException(string? message, System.Exception? inner) { } public HttpRequestException(string? message, System.Exception? inner, System.Net.HttpStatusCode? statusCode) { } - public HttpRequestException(string? message, System.Exception? inner = null, System.Net.HttpStatusCode? statusCode = null, System.Net.Http.HttpRequestError? httpRequestError = null) { } - public System.Net.Http.HttpRequestError? HttpRequestError { get { throw null; } } + public HttpRequestException(System.Net.Http.HttpRequestError httpRequestError, string? message = null, System.Exception? inner = null, System.Net.HttpStatusCode? statusCode = null) { } + public System.Net.Http.HttpRequestError HttpRequestError { get { throw null; } } public System.Net.HttpStatusCode? StatusCode { get { throw null; } } } public partial class HttpRequestMessage : System.IDisposable diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpContent.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpContent.cs index b1cc357f32a355..2523829550e517 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpContent.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpContent.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.IO; using System.Net.Http.Headers; using System.Text; @@ -608,7 +609,7 @@ private bool CreateTemporaryBuffer(long maxBufferSize, out MemoryStream? tempBuf // This should only be hit when called directly; HttpClient/HttpClientHandler // will not exceed this limit. throw new ArgumentOutOfRangeException(nameof(maxBufferSize), maxBufferSize, - SR.Format(System.Globalization.CultureInfo.InvariantCulture, + SR.Format(CultureInfo.InvariantCulture, SR.net_http_content_buffersize_limit, HttpContent.MaxBufferSize)); } @@ -720,7 +721,7 @@ internal static Exception WrapStreamCopyException(Exception e) { Debug.Assert(StreamCopyExceptionNeedsWrapping(e)); HttpRequestError error = e is HttpIOException ioEx ? ioEx.HttpRequestError : HttpRequestError.Unknown; - return new HttpRequestException(SR.net_http_content_stream_copy_error, e, httpRequestError: error); + return new HttpRequestException(error, SR.net_http_content_stream_copy_error, e); } private static int GetPreambleLength(ArraySegment buffer, Encoding encoding) @@ -835,7 +836,7 @@ private static async Task WaitAndReturnAsync(Task wait private static HttpRequestException CreateOverCapacityException(long maxBufferSize) { - return new HttpRequestException(SR.Format(System.Globalization.CultureInfo.InvariantCulture, SR.net_http_content_buffersize_exceeded, maxBufferSize), httpRequestError: HttpRequestError.ConfigurationLimitExceeded); + return new HttpRequestException(HttpRequestError.ConfigurationLimitExceeded, SR.Format(CultureInfo.InvariantCulture, SR.net_http_content_buffersize_exceeded, maxBufferSize)); } internal sealed class LimitMemoryStream : MemoryStream diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestException.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestException.cs index 5623387adfbee8..4ee9c11bc8639a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestException.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestException.cs @@ -38,11 +38,11 @@ public HttpRequestException(string? message, Exception? inner, HttpStatusCode? s /// /// Initializes a new instance of the class with a specific message an inner exception, and an HTTP status code and an . /// + /// The that caused the exception. /// A message that describes the current exception. /// The inner exception. /// The HTTP status code. - /// The that caused the exception. - public HttpRequestException(string? message, Exception? inner = null, HttpStatusCode? statusCode = null, HttpRequestError? httpRequestError = null) + public HttpRequestException(HttpRequestError httpRequestError, string? message = null, Exception? inner = null, HttpStatusCode? statusCode = null) : this(message, inner, statusCode) { HttpRequestError = httpRequestError; @@ -51,10 +51,7 @@ public HttpRequestException(string? message, Exception? inner = null, HttpStatus /// /// Gets the that caused the exception. /// - /// - /// The or if the underlying did not provide it. - /// - public HttpRequestError? HttpRequestError { get; } + public HttpRequestError HttpRequestError { get; } /// /// Gets the HTTP status code to be returned with the exception. @@ -66,8 +63,8 @@ public HttpRequestException(string? message, Exception? inner = null, HttpStatus // This constructor is used internally to indicate that a request was not successfully sent due to an IOException, // and the exception occurred early enough so that the request may be retried on another connection. - internal HttpRequestException(string? message, Exception? inner, RequestRetryType allowRetry, HttpRequestError? httpRequestError = null) - : this(message, inner, httpRequestError: httpRequestError) + internal HttpRequestException(HttpRequestError httpRequestError, string? message, Exception? inner, RequestRetryType allowRetry) + : this(httpRequestError, message, inner) { AllowRetry = allowRetry; } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs index 7ac830be4b889a..eee8c617b5345e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs @@ -209,7 +209,7 @@ private static async Task SendWithNtAuthAsync(HttpRequestMe { isNewConnection = false; connection.Dispose(); - throw new HttpRequestException(SR.Format(SR.net_http_authvalidationfailure, statusCode), null, HttpStatusCode.Unauthorized, HttpRequestError.UserAuthenticationError); + throw new HttpRequestException(HttpRequestError.UserAuthenticationError, SR.Format(SR.net_http_authvalidationfailure, statusCode), statusCode: HttpStatusCode.Unauthorized); } break; } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs index 2dbe558d971661..8bf0f39e0f63a9 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs @@ -89,7 +89,7 @@ public static async ValueTask EstablishSslConnectionAsync(SslClientAu throw CancellationHelper.CreateOperationCanceledException(e, cancellationToken); } - HttpRequestException ex = new HttpRequestException(SR.net_http_ssl_connection_failed, e, httpRequestError: HttpRequestError.SecureConnectionError); + HttpRequestException ex = new HttpRequestException(HttpRequestError.SecureConnectionError, SR.net_http_ssl_connection_failed, e); if (request.IsExtendedConnectRequest) { // Extended connect request is negotiating strictly for ALPN = "h2" because HttpClient is unaware of a possible downgrade. @@ -139,7 +139,7 @@ internal static Exception CreateWrappedException(Exception exception, string hos { return CancellationHelper.ShouldWrapInOperationCanceledException(exception, cancellationToken) ? CancellationHelper.CreateOperationCanceledException(exception, cancellationToken) : - new HttpRequestException($"{exception.Message} ({host}:{port})", exception, RequestRetryType.RetryOnNextProxy, DeduceError(exception)); + new HttpRequestException(DeduceError(exception), $"{exception.Message} ({host}:{port})", exception, RequestRetryType.RetryOnNextProxy); static HttpRequestError DeduceError(Exception exception) { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs index 69563103e1e18f..c28eb5cb4fb326 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs @@ -2104,11 +2104,11 @@ await Task.WhenAny(requestBodyTask, responseHeadersTask).ConfigureAwait(false) = } catch (HttpIOException e) { - throw new HttpRequestException(e.Message, e, httpRequestError: e.HttpRequestError); + throw new HttpRequestException(e.HttpRequestError, e.Message, e); } catch (Exception e) when (e is IOException || e is ObjectDisposedException || e is InvalidOperationException) { - throw new HttpRequestException(SR.net_http_client_execution_error, e, httpRequestError: HttpRequestError.Unknown); + throw new HttpRequestException(HttpRequestError.Unknown, SR.net_http_client_execution_error, e); } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs index 2b770746218669..1b43fcabae1822 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs @@ -540,7 +540,7 @@ void IHttpStreamHeadersHandler.OnStaticIndexedHeader(int index) if (index <= LastHPackRequestPseudoHeaderId) { if (NetEventSource.Log.IsEnabled()) Trace($"Invalid request pseudo-header ID {index}."); - throw new HttpRequestException(SR.net_http_invalid_response, httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.net_http_invalid_response); } else if (index <= LastHPackStatusPseudoHeaderId) { @@ -563,7 +563,7 @@ void IHttpStreamHeadersHandler.OnStaticIndexedHeader(int index, ReadOnlySpan value) if (_responseProtocolState != ResponseProtocolState.ExpectingHeaders && _responseProtocolState != ResponseProtocolState.ExpectingTrailingHeaders) { if (NetEventSource.Log.IsEnabled()) Trace("Received header before status."); - throw new HttpRequestException(SR.net_http_invalid_response, httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.net_http_invalid_response); } Encoding? valueEncoding = _connection._pool.Settings._responseHeaderEncodingSelector?.Invoke(descriptor.Name, _request); @@ -725,7 +726,7 @@ public void OnHeader(ReadOnlySpan name, ReadOnlySpan value) else { if (NetEventSource.Log.IsEnabled()) Trace($"Invalid response pseudo-header '{Encoding.ASCII.GetString(name)}'."); - throw new HttpRequestException(SR.net_http_invalid_response, httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.net_http_invalid_response); } } else @@ -734,7 +735,7 @@ public void OnHeader(ReadOnlySpan name, ReadOnlySpan value) if (!HeaderDescriptor.TryGet(name, out HeaderDescriptor descriptor)) { // Invalid header name - throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_header_name, Encoding.ASCII.GetString(name)), httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.Format(SR.net_http_invalid_response_header_name, Encoding.ASCII.GetString(name))); } OnHeader(descriptor, value); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs index 828e0bb2ffc7e8..0923167eb5c1e8 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs @@ -257,13 +257,13 @@ await Task.WhenAny(sendContentTask, readResponseTask).ConfigureAwait(false) == s case Http3ErrorCode.RequestRejected: // The server is rejecting the request without processing it, retry it on a different connection. HttpProtocolException rejectedException = HttpProtocolException.CreateHttp3StreamException(code, ex); - throw new HttpRequestException(SR.net_http_request_aborted, rejectedException, RequestRetryType.RetryOnConnectionFailure, httpRequestError: HttpRequestError.HttpProtocolError); + throw new HttpRequestException(HttpRequestError.HttpProtocolError, SR.net_http_request_aborted, rejectedException, RequestRetryType.RetryOnConnectionFailure); default: // Our stream was reset. Exception innerException = _connection.AbortException ?? HttpProtocolException.CreateHttp3StreamException(code, ex); HttpRequestError httpRequestError = innerException is HttpProtocolException ? HttpRequestError.HttpProtocolError : HttpRequestError.Unknown; - throw new HttpRequestException(SR.net_http_client_execution_error, innerException, httpRequestError: httpRequestError); + throw new HttpRequestException(httpRequestError, SR.net_http_client_execution_error, innerException); } } catch (QuicException ex) when (ex.QuicError == QuicError.ConnectionAborted) @@ -273,12 +273,12 @@ await Task.WhenAny(sendContentTask, readResponseTask).ConfigureAwait(false) == s Http3ErrorCode code = (Http3ErrorCode)ex.ApplicationErrorCode.Value; Exception abortException = _connection.Abort(HttpProtocolException.CreateHttp3ConnectionException(code, SR.net_http_http3_connection_close)); - throw new HttpRequestException(SR.net_http_client_execution_error, abortException, httpRequestError: HttpRequestError.HttpProtocolError); + throw new HttpRequestException(HttpRequestError.HttpProtocolError, SR.net_http_client_execution_error); } catch (QuicException ex) when (ex.QuicError == QuicError.OperationAborted && _connection.AbortException != null) { // we close the connection, propagate the AbortException - throw new HttpRequestException(SR.net_http_client_execution_error, _connection.AbortException, httpRequestError: HttpRequestError.Unknown); + throw new HttpRequestException(HttpRequestError.Unknown, SR.net_http_client_execution_error, _connection.AbortException); } // It is possible for user's Content code to throw an unexpected OperationCanceledException. catch (OperationCanceledException ex) when (ex.CancellationToken == _requestBodyCancellationSource.Token || ex.CancellationToken == cancellationToken) @@ -292,13 +292,13 @@ await Task.WhenAny(sendContentTask, readResponseTask).ConfigureAwait(false) == s else { Debug.Assert(_requestBodyCancellationSource.IsCancellationRequested); - throw new HttpRequestException(SR.net_http_request_aborted, ex, RequestRetryType.RetryOnConnectionFailure, httpRequestError: HttpRequestError.Unknown); + throw new HttpRequestException(HttpRequestError.Unknown, SR.net_http_request_aborted, ex, RequestRetryType.RetryOnConnectionFailure); } } catch (HttpIOException ex) { _connection.Abort(ex); - throw new HttpRequestException(SR.net_http_client_execution_error, ex, httpRequestError: ex.HttpRequestError); + throw new HttpRequestException(ex.HttpRequestError, SR.net_http_client_execution_error, ex); } catch (Exception ex) { @@ -307,7 +307,7 @@ await Task.WhenAny(sendContentTask, readResponseTask).ConfigureAwait(false) == s { throw; } - throw new HttpRequestException(SR.net_http_client_execution_error, ex, httpRequestError: HttpRequestError.Unknown); + throw new HttpRequestException(HttpRequestError.Unknown, SR.net_http_client_execution_error, ex); } finally { @@ -870,7 +870,7 @@ private async ValueTask ReadHeadersAsync(long headersLength, CancellationToken c if (headersLength > _headerBudgetRemaining) { _stream.Abort(QuicAbortDirection.Read, (long)Http3ErrorCode.ExcessiveLoad); - throw new HttpRequestException(SR.Format(SR.net_http_response_headers_exceeded_length, _connection.Pool.Settings.MaxResponseHeadersByteLength), httpRequestError: HttpRequestError.ConfigurationLimitExceeded); + throw new HttpRequestException(HttpRequestError.ConfigurationLimitExceeded, SR.Format(SR.net_http_response_headers_exceeded_length, _connection.Pool.Settings.MaxResponseHeadersByteLength)); } _headerBudgetRemaining -= (int)headersLength; @@ -911,7 +911,7 @@ void IHttpStreamHeadersHandler.OnHeader(ReadOnlySpan name, ReadOnlySpan line, HttpResponseMessage res const int MinStatusLineLength = 12; // "HTTP/1.x 123" if (line.Length < MinStatusLineLength || line[8] != ' ') { - throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_status_line, Encoding.ASCII.GetString(line)), httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.Format(SR.net_http_invalid_response_status_line, Encoding.ASCII.GetString(line))); } ulong first8Bytes = BitConverter.ToUInt64(line); @@ -1044,7 +1044,7 @@ private static void ParseStatusLineCore(Span line, HttpResponseMessage res } else { - throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_status_line, Encoding.ASCII.GetString(line)), httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.Format(SR.net_http_invalid_response_status_line, Encoding.ASCII.GetString(line))); } } @@ -1052,7 +1052,7 @@ private static void ParseStatusLineCore(Span line, HttpResponseMessage res byte status1 = line[9], status2 = line[10], status3 = line[11]; if (!IsDigit(status1) || !IsDigit(status2) || !IsDigit(status3)) { - throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_status_code, Encoding.ASCII.GetString(line.Slice(9, 3))), httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.Format(SR.net_http_invalid_response_status_code, Encoding.ASCII.GetString(line.Slice(9, 3)))); } response.SetStatusCodeWithoutValidation((HttpStatusCode)(100 * (status1 - '0') + 10 * (status2 - '0') + (status3 - '0'))); @@ -1077,13 +1077,13 @@ private static void ParseStatusLineCore(Span line, HttpResponseMessage res } catch (FormatException formatEx) { - throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_status_reason, Encoding.ASCII.GetString(reasonBytes.ToArray())), formatEx, httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.Format(SR.net_http_invalid_response_status_reason, Encoding.ASCII.GetString(reasonBytes.ToArray())), formatEx); } } } else { - throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_status_line, Encoding.ASCII.GetString(line)), httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.Format(SR.net_http_invalid_response_status_line, Encoding.ASCII.GetString(line))); } } @@ -1182,7 +1182,7 @@ private bool ParseHeaders(HttpResponseMessage? response, bool isFromTrailer) } static void ThrowForInvalidHeaderLine(ReadOnlySpan buffer, int newLineIndex) => - throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_header_line, Encoding.ASCII.GetString(buffer.Slice(0, newLineIndex))), httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.Format(SR.net_http_invalid_response_header_line, Encoding.ASCII.GetString(buffer.Slice(0, newLineIndex)))); } private void AddResponseHeader(ReadOnlySpan name, ReadOnlySpan value, HttpResponseMessage response, bool isFromTrailer) @@ -1281,14 +1281,14 @@ private void AddResponseHeader(ReadOnlySpan name, ReadOnlySpan value Debug.Assert(added); static void ThrowForEmptyHeaderName() => - throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_header_name, ""), httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.Format(SR.net_http_invalid_response_header_name, "")); static void ThrowForInvalidHeaderName(ReadOnlySpan name) => - throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_header_name, Encoding.ASCII.GetString(name)), httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.Format(SR.net_http_invalid_response_header_name, Encoding.ASCII.GetString(name))); } private void ThrowExceededAllowedReadLineBytes() => - throw new HttpRequestException(SR.Format(SR.net_http_response_headers_exceeded_length, _pool.Settings.MaxResponseHeadersByteLength), httpRequestError: HttpRequestError.ConfigurationLimitExceeded); + throw new HttpRequestException(HttpRequestError.ConfigurationLimitExceeded, SR.Format(SR.net_http_response_headers_exceeded_length, _pool.Settings.MaxResponseHeadersByteLength)); private void ProcessKeepAliveHeader(string keepAlive) { @@ -2023,7 +2023,7 @@ public async ValueTask DrainResponseAsync(HttpResponseMessage response, Cancella if (_connectionClose) { - throw new HttpRequestException(SR.net_http_authconnectionfailure, httpRequestError: HttpRequestError.UserAuthenticationError); + throw new HttpRequestException(HttpRequestError.UserAuthenticationError, SR.net_http_authconnectionfailure); } Debug.Assert(response.Content != null); @@ -2039,7 +2039,7 @@ public async ValueTask DrainResponseAsync(HttpResponseMessage response, Cancella if (!await responseStream.DrainAsync(_pool.Settings._maxResponseDrainSize).ConfigureAwait(false) || _connectionClose) // Draining may have set this { - throw new HttpRequestException(SR.net_http_authconnectionfailure, httpRequestError: HttpRequestError.UserAuthenticationError); + throw new HttpRequestException(HttpRequestError.UserAuthenticationError, SR.net_http_authconnectionfailure); } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs index ed5091ec06f2f6..f44c893f8f53d5 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs @@ -164,7 +164,7 @@ internal static int ParseStatusCode(ReadOnlySpan value) !IsDigit(status2 = value[1]) || !IsDigit(status3 = value[2])) { - throw new HttpRequestException(SR.Format(SR.net_http_invalid_response_status_code, System.Text.Encoding.ASCII.GetString(value)), httpRequestError: HttpRequestError.InvalidResponse); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.Format(SR.net_http_invalid_response_status_code, Encoding.ASCII.GetString(value))); } return 100 * (status1 - '0') + 10 * (status2 - '0') + (status3 - '0'); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs index de967c4e769a7e..5e98fc1cdcad93 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs @@ -448,7 +448,7 @@ private static void ThrowGetVersionException(HttpRequestMessage request, int des { Debug.Assert(desiredVersion == 2 || desiredVersion == 3); - HttpRequestException ex = new(SR.Format(SR.net_http_requested_version_cannot_establish, request.Version, request.VersionPolicy, desiredVersion), inner, httpRequestError: HttpRequestError.VersionNegotiationError); + HttpRequestException ex = new(HttpRequestError.VersionNegotiationError, SR.Format(SR.net_http_requested_version_cannot_establish, request.Version, request.VersionPolicy, desiredVersion), inner); if (request.IsExtendedConnectRequest && desiredVersion == 2) { ex.Data["HTTP2_ENABLED"] = false; @@ -1095,7 +1095,7 @@ public async ValueTask SendWithVersionDetectionAndRetryAsyn await connection.InitialSettingsReceived.WaitWithCancellationAsync(cancellationToken).ConfigureAwait(false); if (!connection.IsConnectEnabled) { - HttpRequestException exception = new(SR.net_unsupported_extended_connect, httpRequestError: HttpRequestError.ExtendedConnectNotSupported); + HttpRequestException exception = new(HttpRequestError.ExtendedConnectNotSupported, SR.net_unsupported_extended_connect); exception.Data["SETTINGS_ENABLE_CONNECT_PROTOCOL"] = false; throw exception; } @@ -1162,7 +1162,7 @@ public async ValueTask SendWithVersionDetectionAndRetryAsyn // Throw if fallback is not allowed by the version policy. if (request.VersionPolicy != HttpVersionPolicy.RequestVersionOrLower) { - throw new HttpRequestException(SR.Format(SR.net_http_requested_version_server_refused, request.Version, request.VersionPolicy), e, httpRequestError: HttpRequestError.VersionNegotiationError); + throw new HttpRequestException(HttpRequestError.VersionNegotiationError, SR.Format(SR.net_http_requested_version_server_refused, request.Version, request.VersionPolicy), e); } if (NetEventSource.Log.IsEnabled()) @@ -1775,7 +1775,7 @@ private async ValueTask EstablishProxyTunnelAsync(bool async, Cancellati if (tunnelResponse.StatusCode != HttpStatusCode.OK) { tunnelResponse.Dispose(); - throw new HttpRequestException(SR.Format(SR.net_http_proxy_tunnel_returned_failure_status_code, _proxyUri, (int)tunnelResponse.StatusCode), httpRequestError: HttpRequestError.ProxyTunnelError); + throw new HttpRequestException(HttpRequestError.ProxyTunnelError, SR.Format(SR.net_http_proxy_tunnel_returned_failure_status_code, _proxyUri, (int)tunnelResponse.StatusCode)); } try @@ -1802,7 +1802,7 @@ private async ValueTask EstablishSocksTunnel(HttpRequestMessage request, catch (Exception e) when (!(e is OperationCanceledException)) { Debug.Assert(!(e is HttpRequestException)); - throw new HttpRequestException(SR.net_http_proxy_tunnel_error, e, httpRequestError: HttpRequestError.ProxyTunnelError); + throw new HttpRequestException(HttpRequestError.ProxyTunnelError, SR.net_http_proxy_tunnel_error, e); } return stream; From 2bee08ff525c6dadfa839e19a1bc26c8a6195402 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Tue, 18 Jul 2023 21:47:59 +0200 Subject: [PATCH 2/5] Use InvalidResponse for net_http_invalid_response_multiple_status_codes --- .../src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs index 1b43fcabae1822..de66b7cfa103d2 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs @@ -610,9 +610,8 @@ private void OnStatus(int statusCode) if (_responseProtocolState == ResponseProtocolState.ExpectingHeaders) { - // TODO: Fix if (NetEventSource.Log.IsEnabled()) Trace("Received extra status header."); - throw new HttpRequestException(HttpRequestError.ConfigurationLimitExceeded, SR.net_http_invalid_response_multiple_status_codes); + throw new HttpRequestException(HttpRequestError.InvalidResponse, SR.net_http_invalid_response_multiple_status_codes); } if (_responseProtocolState != ResponseProtocolState.ExpectingStatus) From eee6bba9327564028d85a961a765800dbd073dc4 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Tue, 18 Jul 2023 21:58:16 +0200 Subject: [PATCH 3/5] Use HttpRequestError.Unknown for the rest --- .../System/Net/Http/SocketsHttpHandler/Http2Connection.cs | 2 +- .../System/Net/Http/SocketsHttpHandler/Http3Connection.cs | 6 +++--- .../Net/Http/SocketsHttpHandler/Http3RequestStream.cs | 2 +- .../System/Net/Http/SocketsHttpHandler/HttpConnection.cs | 6 +++++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs index c28eb5cb4fb326..8f3093de88e2c7 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs @@ -2205,7 +2205,7 @@ internal void Trace(int streamId, string message, [CallerMemberName] string? mem [DoesNotReturn] private static void ThrowRetry(string message, Exception? innerException = null) => - throw new HttpRequestException(message, innerException, allowRetry: RequestRetryType.RetryOnConnectionFailure); + throw new HttpRequestException(HttpRequestError.Unknown, message, innerException, RequestRetryType.RetryOnConnectionFailure); private static Exception GetRequestAbortedException(Exception? innerException = null) => innerException as HttpIOException ?? new IOException(SR.net_http_request_aborted, innerException); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs index 2c6984162e55be..9744d02fc94f85 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs @@ -208,7 +208,7 @@ public async Task SendAsync(HttpRequestMessage request, lon if (quicStream == null) { - throw new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnConnectionFailure); + throw new HttpRequestException(HttpRequestError.Unknown, SR.net_http_request_aborted, null, RequestRetryType.RetryOnConnectionFailure); } requestStream!.StreamId = quicStream.Id; @@ -221,7 +221,7 @@ public async Task SendAsync(HttpRequestMessage request, lon if (goAway) { - throw new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnConnectionFailure); + throw new HttpRequestException(HttpRequestError.Unknown, SR.net_http_request_aborted, null, RequestRetryType.RetryOnConnectionFailure); } if (NetEventSource.Log.IsEnabled()) Trace($"Sending request: {request}"); @@ -237,7 +237,7 @@ public async Task SendAsync(HttpRequestMessage request, lon { // This will happen if we aborted _connection somewhere and we have pending OpenOutboundStreamAsync call. // note that _abortException may be null if we closed the connection in response to a GOAWAY frame - throw new HttpRequestException(SR.net_http_client_execution_error, _abortException, RequestRetryType.RetryOnConnectionFailure); + throw new HttpRequestException(HttpRequestError.Unknown, SR.net_http_client_execution_error, _abortException, RequestRetryType.RetryOnConnectionFailure); } finally { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs index 0923167eb5c1e8..031ecd7930e32a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs @@ -252,7 +252,7 @@ await Task.WhenAny(sendContentTask, readResponseTask).ConfigureAwait(false) == s { case Http3ErrorCode.VersionFallback: // The server is requesting us fall back to an older HTTP version. - throw new HttpRequestException(SR.net_http_retry_on_older_version, ex, RequestRetryType.RetryOnLowerHttpVersion); + throw new HttpRequestException(HttpRequestError.Unknown, SR.net_http_retry_on_older_version, ex, RequestRetryType.RetryOnLowerHttpVersion); case Http3ErrorCode.RequestRejected: // The server is rejecting the request without processing it, retry it on a different connection. diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs index 1d30d3d422da0b..617dd466ef1df0 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs @@ -882,19 +882,23 @@ private bool MapSendException(Exception exception, CancellationToken cancellatio mappedException = CancellationHelper.CreateOperationCanceledException(exception, cancellationToken); return true; } + if (exception is InvalidOperationException) { // For consistency with other handlers we wrap the exception in an HttpRequestException. mappedException = new HttpRequestException(SR.net_http_client_execution_error, exception); return true; } + if (exception is IOException ioe) { // For consistency with other handlers we wrap the exception in an HttpRequestException. // If the request is retryable, indicate that on the exception. - mappedException = new HttpRequestException(SR.net_http_client_execution_error, ioe, _canRetry ? RequestRetryType.RetryOnConnectionFailure : RequestRetryType.NoRetry); + HttpRequestError error = ioe is HttpIOException httpIoe ? httpIoe.HttpRequestError : HttpRequestError.Unknown; + mappedException = new HttpRequestException(error, SR.net_http_client_execution_error, ioe, _canRetry ? RequestRetryType.RetryOnConnectionFailure : RequestRetryType.NoRetry); return true; } + // Otherwise, just allow the original exception to propagate. mappedException = exception; return false; From 0edf122b02f3ca2b4e46378bd89aae96d8d5c560 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Tue, 18 Jul 2023 22:11:28 +0200 Subject: [PATCH 4/5] Add simple tests for the HttpRequestError property --- .../tests/UnitTests/HttpRequestExceptionTests.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libraries/System.Net.Http/tests/UnitTests/HttpRequestExceptionTests.cs b/src/libraries/System.Net.Http/tests/UnitTests/HttpRequestExceptionTests.cs index eadba6eca1a312..2eedfcd575f4b8 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/HttpRequestExceptionTests.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/HttpRequestExceptionTests.cs @@ -35,5 +35,20 @@ public void StoresNonStandardStatusCode() var exception = new HttpRequestException("message", null, statusCode); Assert.Equal(statusCode, exception.StatusCode); } + + [Fact] + public void DefaultsToRequestErrorUnknown() + { + Assert.Equal(HttpRequestError.Unknown, new HttpRequestException().HttpRequestError); + } + + [Fact] + public void StoresNonStandardRequestError() + { + var requestError = (HttpRequestError)999; + + var exception = new HttpRequestException(requestError); + Assert.Equal(requestError, exception.HttpRequestError); + } } } From 6f64e224f7d8225e5803cb8d60a93f9f6d4c0046 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Tue, 18 Jul 2023 23:55:03 +0200 Subject: [PATCH 5/5] Fix the inner exception being removed by mistake --- .../System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs index 031ecd7930e32a..f5e037cf1d4067 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs @@ -273,7 +273,7 @@ await Task.WhenAny(sendContentTask, readResponseTask).ConfigureAwait(false) == s Http3ErrorCode code = (Http3ErrorCode)ex.ApplicationErrorCode.Value; Exception abortException = _connection.Abort(HttpProtocolException.CreateHttp3ConnectionException(code, SR.net_http_http3_connection_close)); - throw new HttpRequestException(HttpRequestError.HttpProtocolError, SR.net_http_client_execution_error); + throw new HttpRequestException(HttpRequestError.HttpProtocolError, SR.net_http_client_execution_error, abortException); } catch (QuicException ex) when (ex.QuicError == QuicError.OperationAborted && _connection.AbortException != null) {