-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[API Proposal]: Support error codes for HttpRequestException #76644
Comments
Tagging subscribers to this area: @dotnet/ncl Issue DetailsBackground and motivationHttpRequestException is thrown under various conditions but it does not provide an error code to differentiate those conditions.
The above strings are defined in https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/Resources/Strings.resx If we want to pragmatically handle exceptions based on the cause, it would be better dealing with error codes rather than dealing with string values. For example, when we create a dashboard for different exceptions encountered, aggregating by error codes would make more sense that doing by string values. And we could categorize some errors into a group for a special handling. Many other exceptions like SocketException and Win32Exception have an error code field already. API Proposalnamespace System.Collections.Generic;
public class HttpRequestException : Exception
{
.....
public HttpRequestError HttpRequestErrorCode;
.....
} API Usagetry {
await httpClient.SendAsync(...);
}
catch (HttpRequestException ex)
{
switch (ex.HttpRequestErrorCode)
{
case: HttpRequestError.InvalidStatus:
// do something
break;
default:
break;
}
} Alternative DesignsNo response RisksNo response
|
|
I'd love to see a comparison with other HTTP stack to see if they provide error codes like this or how do they report similar errors. |
Triage: we'll decide if/how to solve this in 8.0 timeframe and if we'll do something, it'll aim at 8.0 release. |
Another case where a more strongly-typed mechanism would be an improvement is the use of runtime/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs Line 441 in 96b849e
runtime/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs Line 1074 in 96b849e
Consuming this is rather ugly in YARP. |
We hit this same need in the .NET SDK built-in container tooling. To provide more actionable user feedback with AWS' container registry, we tried to detect server-side connection shutdowns that signaled a missing resource. To detect that case we had to compare on the message string, which felt very foreign and made everyone wonder if this was the correct way to accomplish the goal. So A+ idea to have a more structured representation - looking forward to having it available. |
@baronfel question for clarity: you need to distinguish the case when server sends an EOF from other "invalid server response cases" right? Note that the linked PR will miss some of those cases by doing runtime/src/libraries/System.Net.Http/src/Resources/Strings.resx Lines 312 to 320 in c84d95d
If we would define an error category (=error code) for the EOF case, it would most likely include all 3 of the above cases, therefore your catch block would convert exceptions in cases it doesn't do today. Any concerns with this? |
At least the cases found by Norm were specifically EOF, though I'm not sure if that's an exhaustive set of cases we would want to catch or just a point-in-time response. Since this particular handler is an attempt to detect and give the user a way to workaround a specific error scenario, I think any of the other response type you mention would be ok for us to catch as well. Any sort of hard-disconnect error from this particular API would require the same user mitigation - go create the missing resource through some other means. |
Android and iOS have their own native HTTP handlers and would need separate implementations to support this. |
@grendello would you be interested and able to implement this contract in the native handlers at some point to increase compatibility? Do you see any pitfalls looking at the proposed API? Related: dotnet/android#5761 |
@antonfirsov I can implement that for Xamarin.Android, absolutely. Regarding the API I have a handful of immediate thoughts:
|
Do you know who could provide some feedback about the iOS side?
Instead of defining a "generic" error category, I prefer to make the excpetion property
You can always convert the enum to an integer, but we may consider this as a convenience feature. |
@antonfirsov @rolfbjarne would probably be the person to talk to with regards to iOS (or he would be able to name the person who'd be able to work on it) |
@antonfirsov, thanks for updating the proposal. Overall it seems reasonable, but some questions / comments:
|
@stephentoub my reasoning was that this makes it very clear that the error value is optional and can be missing in many cases. But since it goes against our standards I will update the proposal to include a value of
The proposed API is only exposing errors on exceptions, and not on
Sorry, I didn't make it clear that |
public class HttpRequestException : Exception
{
public HttpRequestError? HttpRequestError { get; }
public HttpRequestException(string? message, Exception? inner = null, HttpStatusCode? statusCode = null, HttpRequestError? httpRequestError = null) {}
}
// IOException subtype to throw from response read streams
public class HttpIOException : IOException // ALTERNATIVE name: HttpResponseReadException
{
public HttpRequestError HttpRequestError { get; }
public HttpIOException(HttpRequestError httpRequestError, string? message = null, Exception? innerException = null) {}
}
// The EXISTING HttpProtocolException should be the subtype of this new exception type
public class HttpProtocolException : HttpIOException
{
}
// Defines disjoint error categories with high granularity.
public enum HttpRequestError
{
Unknown = 0, // Uncategorized/generic error
NameResolutionError, // DNS request failed
ConnectionError, // Transport-level error during connection
TransportError, // Transport-level error after connection
SecureConnectionError, // SSL/TLS error
HttpProtocolError, // HTTP 2.0/3.0 protocol error occurred
ExtendedConnectNotSupported, // Extended CONNECT for WebSockets over HTTP/2 is not supported.
// (SETTINGS_ENABLE_CONNECT_PROTOCOL has not been sent).
VersionNegotiationError, // Cannot negotiate the HTTP Version requested
UserAuthenticationError, // Authentication failed with the provided credentials
ProxyTunnelError,
InvalidResponse, // General error in response/malformed response
// OPTIONAL TO NOT INCLUDE: The one's below are specific cases of errors with the response.
// We may consider merging them into "InvalidResponse" and "InvalidResponseHeader"
ResponseEnded, // Received EOF
// These depend on SocketsHttpHandler configuration
ConfigurationLimitExceeded, // Response Content size exceeded MaxResponseContentBufferSize or
// Response Header length exceeded MaxResponseHeadersLength or
// any future limits are exceeded
} |
Edited by @antonfirsov
Update 1: changes based on feedback - #76644 (comment)
Background and motivation
HttpRequestException is thrown under various conditions but it does not provide an error code to differentiate those conditions.
Currently, it provides 'string Message' property inherited from Exception type and it can contain various error messages:
The above strings are defined in https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/Resources/Strings.resx
If we want to pragmatically handle exceptions based on the cause, it would be better dealing with error codes rather than dealing with string values. For example, when we create a dashboard for different exceptions encountered, aggregating by error codes would make more sense that doing by string values. And we could categorize some errors into a group for a special handling.
Many other exceptions like SocketException and Win32Exception have an error code field already.
Would it make sense to provide a new property for HttpRequestException to hold error codes?
API Proposal
API Usage
With
HttpClient
methods:With response read
Stream
:Risks
Any change to the error categorization is a breaking change, meaning that if we introduce new error codes for some users, we would break others.
Mobile handlers would need to implement their error mapping separately, meaning that we would lack platform consistency until that point. Whenever it's impossible to detect some error categories on those platforms, they would need to report a more generic error code eg.
InvalidResponse
instead ofResponseEnded
. This is a challenge for users who need to include mobile targets in their cross-platformHttpClient
code.Since requests and connections are decoupled, connection-establishment errors (
NameResolutionError
,ConnectionError
andSecureConnectionError
) will be only reported if the originating request is still in the queue, otherwise we will swallow them. Observing these error codes is not a reliable way to observe ALL connection-level errors happening under the hood. Some users may find this surprising.Alternative Designs
HttpResponseReadError
enum.HttpResponseReadException
would hold values from that separate enum.HttpClient
andStream
).TransportError
orProtocolError
can occur in bothe cases, meaning that enum values would be shared.ContentBufferSizeExceeded
, and threat them asInvalidResponse
.HttpRequestException
sealed, and define a class hierarchy underIOException
(which then should be awaysInnerException
ofHttpRequestException
).The text was updated successfully, but these errors were encountered: