On all platforms except Windows 7 when host name could not be resolved we get HttpRequestException with inner SocketException with SocketException.SocketErrorCode set to SocketError.HostNotFound while on Windows 7 we get SocketErrorCode.NoData.
This is problematic in Wcf scenarios where they wrap our exception and would like to throw System.ServiceModel.EndpointNotFoundException but the inconsistency makes it more difficult.
Simple repro is (PostAsync has identical result):
var r = (new HttpClient()).GetAsync("http://nonexistenthostname/foo/bar").Result;
Here is the exact exception being thrown on Windows7
System.AggregateException : One or more errors occurred. (The requested name is valid, but no data of the requested type was found)
---- System.Net.Http.HttpRequestException : The requested name is valid, but no data of the requested type was found
-------- System.Net.Sockets.SocketException : The requested name is valid, but no data of the requested type was found
Stack Trace:
E:\A\_work\104\s\src\mscorlib\src\System\Threading\Tasks\future.cs(493,0): at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
C:\Users\dotnet-bot\Desktop\src\corefx\src\System.Net.Http\tests\FunctionalTests\HttpClientHandlerTest.cs(3180,0): at System.Net.Http.Functional.Tests.HttpClientHandlerTest.HttpClientNonExistingHostnameTest()
----- Inner Stack Trace -----
C:\Users\dotnet-bot\Desktop\src\corefx\src\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\ConnectHelper.cs(86,0): at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
E:\A\_work\104\s\src\mscorlib\shared\System\Threading\Tasks\ValueTask.cs(723,0): at System.Threading.Tasks.ValueTask`1.get_Result()
C:\Users\dotnet-bot\Desktop\src\corefx\src\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\HttpConnectionPool.cs(338,0): at System.Net.Http.HttpConnectionPool.CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
E:\A\_work\104\s\src\mscorlib\shared\System\Threading\Tasks\ValueTask.cs(723,0): at System.Threading.Tasks.ValueTask`1.get_Result()
C:\Users\dotnet-bot\Desktop\src\corefx\src\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\HttpConnectionPool.cs(480,0): at System.Net.Http.HttpConnectionPool.WaitForCreatedConnectionAsync(ValueTask`1 creationTask)
E:\A\_work\104\s\src\mscorlib\shared\System\Threading\Tasks\ValueTask.cs(723,0): at System.Threading.Tasks.ValueTask`1.get_Result()
C:\Users\dotnet-bot\Desktop\src\corefx\src\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\HttpConnectionPool.cs(271,0): at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
C:\Users\dotnet-bot\Desktop\src\corefx\src\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\RedirectHandler.cs(30,0): at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
C:\Users\dotnet-bot\Desktop\src\corefx\src\System.Net.Http\src\System\Net\Http\HttpClient.cs(479,0): at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
----- Inner Stack Trace -----
C:\Users\dotnet-bot\Desktop\src\corefx\src\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\ConnectHelper.cs(67,0): at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
cc: @mconnew @stephentoub @davidsh @karelz
@krwq what is the behavior on Win7 on .NET Core 2.0?
@karelz I do not have data but can check if that is important. I do not expect 2.0 to have any special casing for different OS behavior.
I do not expect 2.0 to have any special casing for different OS behavior.
It would have different behavior because .NET Core 2.0 would use a different HTTP stack (WinHTTP) on Windows.
If this ends up working ok on Win7 with .NET Core 2.0 but broken on Win7 with .NET Core 2.1, then this would be an app-compat behavior difference (due to using the new SocketsHttpHandler by default). So, we should consider that as part of triaging this issue.
@davidsh, I'll try to get some info on that soon in this case
@davidsh on 2.0.0 exception is following:
System.AggregateException : One or more errors occurred. (An error occurred while sending the request.)
---- System.Net.Http.HttpRequestException : An error occurred while sending the request.
-------- System.Net.Http.WinHttpException : The server name or address could not be resolved
so we don't even get SocketException. The only common behavior is HttpRequestException
.NET Core 2.0 exception on Win10 is the same as on 2.0 Win7:
System.AggregateException: One or more errors occurred. (An error occurred while sending the request.)
---> System.Net.Http.HttpRequestException: An error occurred while sending the request.
---> System.Net.Http.WinHttpException: The server name or address could not be resolved
For .Net Core 2.0:
Linux: Inner exception has HResult value ERROR_INVALID_HANDLE (0x6)
Windows: Inner exception has HResult value WININET_E_NAME_NOT_RESOLVED (0x80072EE7)
UWP: Inner exception has HResult value WININET_E_NAME_NOT_RESOLVED (0x80072EE7)
For .Net Core 2.1:
Linux: Inner exception is SocketException with SocketError HostNotFound
Windows 8+ : Inner exception is SocketException with SocketError HostNotFound
Windows 7: Inner exception is SocketException with SocketError NoData
UWP: Inner exception has HResult value WININET_E_NAME_NOT_RESOLVED (0x80072EE7)
@krwq, in 2.0, you didn't need to worry about what type the inner exception was as the error value was always available on HError. With 2.1, as the app developer can make things revert to 2.0 implementations, we need to maintain error handling code for 2.0 Http handlers. This means we need to switch logic on the inner exception type and retrieve the error from different places depending on the inner exception type. What's worse is SocketException derives from Win32Exception but the value of HResult in the host not found scenario is SUCCESS.
@mconnew @davidsh
On 2.0 Win7 HResult set to WININET_E_NAME_NOT_RESOLVED (80072EE7).
On 2.1 Win7 HResult is set to E_FAIL (80004005).
On one hand translating errors is probably not something we want to do but on other assuming this is expected behavior from the OS and user cares how HttpClient failed it might be something we'd want to consider fixing as this logic will have to be somewhere.
Do we have any precedence in other places of framework? Also I'm unsure how important is this difference on Windows 7 considering the workaround exists (check for both errors).
cc: @stephentoub @karelz
@mconnew if HResult is SUCCESS in case of a failure, please file a new bug with repro and details. Let's not mix it in here.
@krwq where is the HResult from on 2.1? The exceptions on each version are different.
For background on HResult, we did explicit work to set the HResult field of WinHttpException so that we would have something that WCF could use on .NET Core so that their UAP vs. non-UAP code could have similar behavior.
By default, HResult is either SUCCESS or E_FAIL. Most customer code doesn't even look at it. We used it, though, in order to provide something that WCF can use when inspecting inner exceptions.
OK, I see - both exceptions inherit from Win32Exception - SocketException and WinHttpException (interestingly apisof.net is lying).
I don't see the difference in HResult as a big deal - it is representing underlying implementation details error IMO. Not something we would necessarily need to keep fully compatible (it's almost like being bug-by-bug compatible).
I understand the general desire to have platform independent error codes. That is sadly not something the stack is currently designed for (not even HResult).
@karelz, I think we should close this or move it to Future for reconsideration.
@davidsh can you please work with @mconnew to make a recommendation?
After researching this, it is valid for a SocketException to have SocketErrorCode of either HostNotFound or NoData values for "host not found" scenarios.
We actually test for this:
https://github.com/dotnet/corefx/blob/2e82c748da654905e29609804393e028a06d78fa/src/System.Net.Sockets/tests/FunctionalTests/DnsEndPointTest.cs#L67
The recommendation is for WCF to change their code and add a check for SocketErrorCode.NoData in addition to their current check for SocketErrorCode.HostNotFound:
Based on offline email discussion, we consider this good enough for 2.1. Closing.
For future we should explore improvements in surfacing errors - it was not design goal so far, due to lower customer demand. It is tracked also by dotnet/runtime#15567.
Most helpful comment
After researching this, it is valid for a SocketException to have SocketErrorCode of either HostNotFound or NoData values for "host not found" scenarios.
We actually test for this:
https://github.com/dotnet/corefx/blob/2e82c748da654905e29609804393e028a06d78fa/src/System.Net.Sockets/tests/FunctionalTests/DnsEndPointTest.cs#L67
The recommendation is for WCF to change their code and add a check for
SocketErrorCode.NoDatain addition to their current check forSocketErrorCode.HostNotFound:See:
https://github.com/dotnet/wcf/blob/46f14d00bfaed9bcb3867e09073e9ac52592079c/src/System.Private.ServiceModel/src/System/ServiceModel/Channels/HttpChannelHelpers.cs#L142