Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't reliably determine if connection was successful when using HttpClient #30896

Closed
wizofaus opened this issue Sep 19, 2019 · 10 comments
Closed
Labels
area-System.Net.Http enhancement Product code improvement that does NOT require public API changes/additions
Milestone

Comments

@wizofaus
Copy link

If an exception is thrown from a call to HttpClient.SendAsync( ) there's no way to be certain that the connection itself failed, vs. e.g., a timeout before trying to read the response.
While there are certain SocketException SocketError values that can only happen if the DNS lookup or connection fails, if there is a) a failure negotiating the SSL handshake or b) a timeout trying to connect, there's no reliable way to programmatically distinguish this from other possible errors.
This is important in many real world applications where if there's any possibility that the remote server did receive all or part of the request, but a network or system failure occurred after this point, it's necessary to attempt to contact that server again to determine whether it did process the request.
If the HttpClient had a way of determine how many bytes had been successfully sent to the remote server before the exception occurred, that would solve it nicely too.

@davidsh
Copy link
Contributor

davidsh commented Sep 19, 2019

This is similar to #15567 and related issues. Basically we need to enhance the top-level HttpRequestException and add a property that would be similar to the WebException.Status property. That property was of type WebExceptionStatus enum and made it easy to distinguish various error conditions.

@eiriktsarpalis
Copy link
Member

This feels related to #21965. A solution here should probably also address that issue.

@wizofaus
Copy link
Author

I notice that mentions OperationCanceledException - in the testing I'd done I was getting OperationCanceledException when the connection timed out, but just a regular TaskCanceledException when it timed out trying to read a response. Not really something I could rely on either way!

@qjw2bqn
Copy link

qjw2bqn commented Sep 27, 2019

I have the same error.But I publish the demo on ubuntu across docker.
The base image is microsoft/dotnet:2.2-aspnetcore-runtime.
when i use the httpclient request the remote api,sometimes work right ,but somethimes it get wrong with the error message:
the operation was canceled

but it doesn't occur on window environment!!
somebody have the same question with me?

@karelz
Copy link
Member

karelz commented Oct 4, 2019

Duplicate of #15567

@karelz karelz closed this as completed Oct 4, 2019
@msftgits msftgits transferred this issue from dotnet/corefx Feb 1, 2020
@msftgits msftgits added this to the 5.0 milestone Feb 1, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 12, 2020
@dotnet dotnet unlocked this conversation Feb 7, 2023
@antonfirsov
Copy link
Member

antonfirsov commented Feb 7, 2023

@wizofaus @qjw2bqn sorry for pinging you in this old discussion, but I wonder about your experience with .NET 5.0+ era error handling in HttpClient if you can comment on that. Note that HttpClient / SocketsHttpHandler throws specific exceptions for Socket/URI/DNS/SSL errors and #21965 has been addressed in #2281. Is this sufficient, or do you have scenarios we can improve?

@wizofaus
Copy link
Author

wizofaus commented Feb 8, 2023

I just tried with .NET 5.0, and with HttpClient.Timeout set to 5 seconds, HttpClient.SendAsync( ) throws a TaskCanceledException (with an inner exception of TimeoutException) if the connection is successful and the request is sent but it times out reading a response (tested with https://httpbin.org/delay/10), and exactly the same result if trying to connect to an unreachable IP address (e.g 2.2.2.2), so if anything it seems harder to distinguish now? There's no OperationCanceledException any more anyhow. However, I retried the latter without setting "HttpClient.Timeout" and it threw an HttpRequestException instead (presumably because the error happened at an OS level) - checking that the InnerException is a SocketException and that the ErrorCode is 10060 might suffice in that case but I'm not sure if it can be counted on in all scenarios. I also tried after disconnecting my wifi and got the same result except a different ErrorCode (10065).

I guess I was hoping for some simple way to determine "had the HTTP connection been established", and even better, had any attempt been made to send any bytes across the wire (obviously there's no reliable way to know if they'd reached the other side).

@antonfirsov
Copy link
Member

antonfirsov commented Feb 8, 2023

Thanks for the detailed answer! Summarizing the cases you mentioned:

  1. Connect to https://httpbin.org/delay/10 with a timeout of 5 seconds -> Throws TaskCanceledException + TimeoutException
  2. Connect to an unreachable host with a timeout -> Throws TaskCanceledException + TimeoutException
  3. Connect to an unreachable host without a timeout -> Throws HttpRequestException + SocketException

I believe 2. and 3. are really different error cases: 2. is an actual timeout (regardless if the request would be successful without it), while 3. is a TCP-level failure, which would always happen if the host is unreachable.

Distinguishing between 1. and 2. "to determine had the HTTP connection been established" might be a valid ask, though we haven't seen it coming up often. Is this needed to support troubleshooting, or does your code need to make runtime decisions after telling apart the two failure cases? If it's for troubleshooting, maybe telemetry could help you? The System.Net.Sockets counters would also tell the number of bytes sent/received.

@wizofaus
Copy link
Author

wizofaus commented Feb 8, 2023

Thanks, I no longer work on the product where it was an issue, but yes, it was making runtime decisions - basically if it knew the connection (or TLS session) had never been established (and no bytes sent across the wire), there was no need to go back to the server to check what had happened (the API in question was responsible for performing financial transactions) and the error could be presented to the user immediately. If not, it was necessary to attempt to get info from the server about what had happened (and if that wasn't possible, to let the user know that the operation would be retried and/or reversed where possible).

@antonfirsov
Copy link
Member

Thanks for the feedback, really appreciate!

@ghost ghost locked as resolved and limited conversation to collaborators Mar 11, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net.Http enhancement Product code improvement that does NOT require public API changes/additions
Projects
None yet
Development

No branches or pull requests

7 participants