When requesting a JSON off of a Gravatar API endpoint, the call to SendAsync ends unsuccessfully with the above System.Net.Http.HttpRequestException.
Repro:
```c#
using (HttpClient httpClient = new HttpClient())
{
HttpRequestMessage request = new HttpRequestMessage
{
RequestUri = new Uri(@"https://www.gravatar.com/caaa1c76de5cc0ceed0e32f3d4377f31.json"),
Method = HttpMethod.Get,
Headers = { { "User-Agent", "csharp", "Connection", "close" } }
};
httpClient.Timeout = new TimeSpan(0, 0, 0, 5);
using (HttpResponseMessage response = await httpClient.SendAsync(request))
{
string responseContent = response.Content.ReadAsStringAsync();
}
}
Expected result: The `responseContent` points to a string with the JSON content.
Environment: System.Net.Http 4.3.0, netcoreapp1.0, runtime framework version 1.0.4
Notes: The Gravatar endpoint works in a way that it returns a 302 response to redirect to a more specific endpoint for the JSON data. When inspected with Fiddler, the above code throws without even doing the second HTTP request (after the redirect). It might seem that the root cause is that HttpClient does not follow the 302 status code and the 'Location' response header. But no, even with the below code it throws the same kind of exception.
```c#
using (HttpClient httpClient = new HttpClient())
{
HttpRequestMessage request = new HttpRequestMessage
{
RequestUri = new Uri(@"https://www.gravatar.com/caaa1c76de5cc0ceed0e32f3d4377f31.json"),
Method = HttpMethod.Get,
Headers = { { "User-Agent", "csharp" } }
};
httpClient.Timeout = new TimeSpan(0, 0, 0, 5);
string responseContent;
using (HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
{
if (response.StatusCode == System.Net.HttpStatusCode.Found && !string.IsNullOrEmpty(response.Headers.Location.PathAndQuery))
{
HttpRequestMessage redirectRequest = new HttpRequestMessage
{
RequestUri = response.Headers.Location,
Method = HttpMethod.Get,
Headers = { { "User-Agent", "csharp" }, { "Connection", "close" } }
};
using (HttpResponseMessage redirectResponse = await httpClient.SendAsync(redirectRequest, HttpCompletionOption.ResponseContentRead))
{
var bytes = await response.Content.ReadAsByteArrayAsync();
responseContent = System.Text.Encoding.UTF8.GetString(bytes);
}
}
}
}
@JanLenoch thanks for your report and small repro, we apprectiate that ;).
Could you please try it on .NET Core 2.0 as well? Here's jow to dogfood: https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/dogfooding.md ... it would be very helpful to confirm it is existing problem. Thanks!
@JanLenoch, a few things:
1) There are a few mistakes in the repro. First, it's not compilable, e.g. the Headers assignment line doesn't work. Second, your second example has problems with what objects are being used, e.g. the response.Content.ReadAsByteArrayAsync
line should be using redirectResponse
rather than response
.
2) I believe you're hitting a security restriction: HttpClient blocks attempts to redirect from an https site to an http site, which the link you're using is trying to do. That'll prevent your first example from working.
3) It appears that WinHTTP cancels the request when it attempts to do an invalid redirect like this. The following code runs as you're hoping it will on both desktop and .NET Core on Linux, but fails with the error you're seeing on .NET Core on Windows:
```C#
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Test
{
public static void Main() => MainAsync().GetAwaiter().GetResult();
private static async Task MainAsync()
{
using (var httpClient = new HttpClient() { Timeout = new TimeSpan(0, 0, 0, 5) })
{
var request = new HttpRequestMessage
{
RequestUri = new Uri(@"https://www.gravatar.com/caaa1c76de5cc0ceed0e32f3d4377f31.json"),
Method = HttpMethod.Get
};
request.Headers.Add("User-Agent", "csharp");
using (HttpResponseMessage response = await httpClient.SendAsync(request))
{
if (response.StatusCode == System.Net.HttpStatusCode.Found)
{
var redirectRequest = new HttpRequestMessage
{
RequestUri = response.Headers.Location,
Method = HttpMethod.Get,
};
redirectRequest.Headers.Add("User-Agent", "csharp");
using (HttpResponseMessage redirectResponse = await httpClient.SendAsync(redirectRequest, HttpCompletionOption.ResponseContentRead))
{
Console.WriteLine(await redirectResponse.Content.ReadAsStringAsync());
}
}
else
{
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}
}
}
}
```
cc: @davidsh
I believe you're hitting a security restriction: HttpClient blocks attempts to redirect from an https site to an http site, which the link you're using is trying to do. That'll prevent your first example from working.
That is correct. HTTPS -> HTTP redirection in not allowed. If you need that for a specific purpose, then you should turn off automatic redirection (HttpClientHandler.AllowAutoRedirect = false) and handle the 3xx responses yourself.
HTTPS -> HTTP redirection in not allowed
@davidsh, is it by-design on .NET Core on Windows that such redirections result in the request failing with an OperationCanceledException (that's then wrapped in an HttpRequestException) rather than just not doing the redirect?
@davidsh, is it by-design on .NET Core on Windows that such redirections result in the request failing with an OperationCanceledException (that's then wrapped in an HttpRequestException) rather than just not doing the redirect?
My answer to this question would be to first ask what is the behavior on .NET Framework (Desktop). But I believe the answer is that on .NET Framework HTTPS -> HTTP was allowed. We choise to use a more secure default for HttpClientHandler on .NET Core.
The question, then, is what is a better design choice. An error or simply to ignore the redirection? We went with an error which is what happens internally with WinHTTP. I'm not sure that simply ignoring the redirect is the best choice.
Although, in comparison, we have the issue with max number of redirects behavior between .NET Framework and .NET Core. In that case, .NET Framework stop doing redirects whereas .NET Framework returns an error.
We currently have three different behaviors:
To me the behavior of not doing the redirect makes more sense. But regardless we have a choice:
Make .NET Core on Windows not error out
This will be difficult because you can't change WinHTTP auto-redirect behavior like that. So, we would end up having to turn off WinHTTP auto-redirect and try to "do it ourselves" in the handler by parsing the 3xx response, follow the Location: url response header and then create a new http request (internally) and submit the new one etc. Lots of code and would be messy.
Allow the https->http redirect on core
We could decide to do this on the basis of "match .NET Framework behavior'. But then it would also go against "be more secure" behavior.
This will be difficult because you can't change WinHTTP auto-redirect behavior like that.
Same is true in reverse on Unix. We would likely need to implement redirects in the handler rather than relying on libcurl's implementation.
@danmosemsft, @karelz, @bartonjs, do you have an opinion on .NET Core's blocking of https->http redirects whereas they're allowed on desktop?
Sounds like a case where we're becoming more secure by default, which can be a good reason to make a breaking change. But ideally we'd have a flag code could set to get desktop behavior. It sounds like implementing such a flag would be messy, but presumably it's better we handle that mess than the library.
I agree with Dan. I like the not redirecting behavior. Whether it ends at a 302 or throws isn't something I'm familiar enough with the usage to have a strong opinion about.
Since we probably can't insert a switch in a meaningful way, exception might be better...
Agreed as well - let's be more secure. If we have enough people hitting it who want Desktop behavior, we could potentially expose it later under a flag (based on demand) as 'less secure' option.
Can someone please summarize the difference here? https://github.com/dotnet/corefx/wiki/ApiCompat
Not sure we should flag the API, but at least we should document the behavioral difference ...
Moving to 2.0 to document the behavioral difference.
(If I did miss any code change needed here, please feel free flip it back to 'bug')
I updated the API compat page.
Sounds like we want to keep the behavior of not following https->http redirects and that we can't readily change the behavior from either WinHTTP or libcurl, so we'll keep it as-is for now. For a replacement managed implementation, we'll need to decide what behavior we want here (at least by default, potentially exposing it as a knob)... I'd vote for behaving in this case as if AllowAutoRedirect was false, as if MaxAutomaticRedirections was 1, etc., basically just not following the redirect and ending with the previous 302 response, as happens on Unix today.
cc: @geoffkizer
Thanks @stephentoub! I also added link back to this issue in case we need more details/reference later.
Thanks @karelz , @stephentoub and others for pointing me to the HTTPS > HTTP issue. Sorry for mistakes in my code. I've just botched these examples from a different working code, without actually compiling them.
Upon experimenting with request headers, I've managed to avoid that Gravatar's HTTPS > HTTP issue. Without such redirect, HttpClient did its job perfectly!
I'm having exactly same problem using Azure SDK on .Net Core, on Mac OSX, so no, it is not a Windows-only issue:
exc.ToString()
System.Net.Http.HttpRequestException: Error while copying content to a stream. --->
System.IO.IOException: The read operation failed, see inner exception. --->
System.Net.Http.CurlException: Failure when receiving data from the peer
at System.Net.Http.CurlHandler.ThrowIfCURLEError(CURLcode error)
at System.Net.Http.CurlHandler.MultiAgent.FinishRequest(StrongToWeakReference`1 easyWrapper, CURLcode messageResult)
--- End of inner exception stack trace ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at System.Net.Http.StreamToStreamCopy.<CopyAsyncAnyStreamToAnyStreamCore>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
at System.Net.Http.HttpContent.<LoadIntoBufferAsyncCore>d__48.MoveNext()
--- End of inner exception stack trace ---
at System.Net.Http.HttpContent.<LoadIntoBufferAsyncCore>d__48.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()\n at System.Net.Http.HttpClient.<FinishSendAsync>d__58.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Microsoft.Rest.Azure.AzureClientExtensions.<GetRawAsync>d__16.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Microsoft.Rest.Azure.AzureClientExtensions.<GetAsync>d__15`2.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Microsoft.Rest.Azure.AzureClientExtensions.<UpdateStateFromAzureAsyncOperationHeader>d__14`2.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Microsoft.Rest.Azure.AzureClientExtensions.<GetLongRunningOperationResultAsync>d__1`2.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Microsoft.Rest.Azure.AzureClientExtensions.<GetLongRunningOperationResultAsync>d__0`1.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Microsoft.Rest.Azure.AzureClientExtensions.<GetPutOrPatchOperationResultAsync>d__4`1.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Microsoft.Azure.Management.Compute.Fluent.VirtualMachinesOperations.<CreateOrUpdateWithHttpMessagesAsync>d__6.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Microsoft.Azure.Management.Compute.Fluent.VirtualMachinesOperationsExtensions.<CreateOrUpdateAsync>d__3.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Microsoft.Azure.Management.Compute.Fluent.VirtualMachineImpl.<CreateResourceAsync>d__151.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Microsoft.Azure.Management.ResourceManager.Fluent.Core.ResourceActions.Creatable`4.<Microsoft-Azure-Management-ResourceManager-Fluent-Core-ResourceActions-IResourceCreator<IResourceT>-CreateResourceAsync>d__15.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Microsoft.Azure.Management.ResourceManager.Fluent.Core.DAG.CreatorTaskItem`1.<ExecuteAsync>d__6.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at Microsoft.Azure.Management.ResourceManager.Fluent.Core.DAG.TaskGroupBase`1.<ExecuteNodeTaskAsync>d__14.MoveNext()\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\n at Orchestrator.AzureContext.<CreateHosts>d__24.MoveNext() in /Users/guto/dev/repos/Orleans-Geo/OrleansBenchmark/Orchestrator/Azure/AzureContext.cs:line 83\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\n at Orchestrator.AzureContext.<DeployResourceGroup>d__23.MoveNext() in /Users/guto/dev/repos/Orleans-Geo/OrleansBenchmark/Orchestrator/Azure/AzureContext.cs:line 63\n--- End of stack trace from previous location where exception was thrown ---\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n at System.Runtime.CompilerServices.TaskAwaiter.GetResult()\n at Orchestrator.Projects.ProjectGrain.<DeployProject>d__8.MoveNext() in /Users/guto/dev/repos/Orleans-Geo/OrleansBenchmark/Orchestrator/Projects/ProjectGrain.cs:line 74"
Discussed offline with @galvesribeiro that this might not be a dupe. Please file a new issue (with readable callstack ;)) and let's discuss it there. If your app is not doing https -> http redirect, this may be similar symptom with different root cause.
For people coming through Google:
I had the same error message, I solved it by adding a HttpClientHandler to my HttpClient with the property UseProxy set to false:
new HttpClient(new HttpClientHandler() {
UseProxy = false
}))
We are running into exact same issue on core 2.0 app.
Can someone explain the workaround or fix in simple terms?
I tried:
UseProxy = false while creating HttpClientHandler
AllowAutoRedirect=true and MaximumAutomaticRedirections to more than 1.
Both of them did not resolve the issue.
@manums if you are truly hitting HTTPS->HTTP redirection, then it is not supported in .NET Core as insecure.
BTW: .NET Core 2.0 is out of support as of 10/1 (4 days ago), I would recommend to upgrade to .NET Core 2.1.
I am getting a similar exception, using 2.1.4, without any redirects and not using HTTPS. My call stack is:
System.Net.Http.HttpRequestException: Error while copying content to a stream.
---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
--- End of inner exception stack trace ---
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error)
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
at System.Net.Http.HttpConnection.WriteAsync(ReadOnlyMemory`1 source)
at System.IO.Stream.CopyToAsyncInternal(Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
at System.Net.Http.HttpContent.CopyToAsyncCore(ValueTask copyTask)
--- End of inner exception stack trace ---
at System.Net.Http.HttpContent.CopyToAsyncCore(ValueTask copyTask)
at System.Net.Http.MultipartContent.SerializeToStreamAsync(Stream stream, TransportContext context)
at System.Net.Http.HttpContent.CopyToAsyncCore(ValueTask copyTask)
at System.Net.Http.HttpConnection.SendRequestContentAsync(HttpRequestMessage request, HttpContentWriteStream stream, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at Controller.PostAsync(String alg)
I have a front-end service running behind IIS in Azure that forwards requests to a back-end kestrel service running on a Linux VM. My HttpClient on the front end is initialized like this:
c#
var httpMessageHandler = new SocketsHttpHandler() { UseProxy = false };
var httpClient = new HttpClient(httpMessageHandler, true);
httpClient.DefaultRequestHeaders.ConnectionClose = true; // Close connection after request
httpClient.DefaultRequestHeaders.ExpectContinue = false; // Do not expect continue
httpClient.Timeout = Timeout.InfiniteTimeSpan;
In a perf test of 8K requests which ran over 19 hours (each request takes about 7-8 seconds between front-end and back-end) there were 17 of these exceptions, all exactly the same. It is very perplexing considering the test uses identical request payloads each time, and there is no timing pattern to the exceptions. The requests to the front end that fail with this exception end in about 600-700 ms.
@ti82 how is your exception similar to this issue? It seems like something unrelated to me - we should take the discussion into new issue.
If you have evidence that it is not server issue (the server or network gadgets on the way might have closed the connection forcibly as the exception says), then it would be worth creating a repro we can look at.
@karelz The issue is similar because the title is the exact same error result I got in my code. There is no call stack from the original issue, but there could potentially be a lot of overlap. Note in my stack trace that the handler is System.Net.Http.RedirectHandler, so despite the fact that I may not be performing a redirect there is likely some overlap here.
The server and "network gadgets" are all Azure components - front end is a App Service S2 written in net core 2.1.4 and the backend is an NV12 using the Ubuntu 16.04 LTS image for Azure in a vNet. Thus, there is a VPN Gateway to allow the App Service to connect with the vNet. I wouldn't know nearly as much about the network communications as someone from the Azure team.
@ti82 OK, there are some similarities in symptoms. The root-cause seems to be different though - HTTPS->HTTP redirection vs. server closing the connection.
As next best step I would recommend to confirm if it is truly server closing the connection, or if it is a bug in .NET Core (e.g. by analyzing Wireshark/Fiddler traces when the error happens). If there are signs that it is a .NET Core bug (i.e. the server did NOT close the connection), please file a new issue and we can continue further troubleshooting there.
@karelz I was able to do a packet capture:
So, one the one hand, the backend server did close the connection, and it wouldn't be a bug from the perspective of the web service that I hit the exception from. On the other hand, the backend service is also written in AspNetCore 2.1.4, but without a reverse proxy (e.g. Kestrel listens directly on port 5020) and it sent the premature FIN, ACK packet. So it still could be a bug in Kestrel, albeit on the listening side. There are no log entries regarding this connection in my backend server logs so I'm not sure where to look next.
It would be best to analyze the server I assume - you likely need to get some logs from there. ASP.NET repos might be able to help you if you need guidance.
I use proxy in my code but it doesn't work !
```c#
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_config["MicroService:DataAccess"]);
var _url = _config["MicroService:DataAccess"] + "/something";
client.DefaultRequestHeaders.Accept.Clear();
HttpRequestMessage requestMessage = new HttpRequestMessage();
requestMessage.Method = HttpMethod.Post;
requestMessage.RequestUri = new Uri(_config["MicroService:DataAccess"] + "/something");
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json");
var response = client.SendAsync(requestMessage).Result;//(I have Error in hear exactly)
if (!response.IsSuccessStatusCode)
return Task.FromCanceled<model>(new System.Threading.CancellationToken(true));
var content = response.Content.ReadAsStringAsync().Result;
return Task.FromResult((model)JsonConvert.DeserializeObject(content,
typeof(model)));
}
```
other api work corectly but just this has error!
@SaraZakizadeh it would be best to file a new issue. There is a mix of unrelated problems here already.
First, please use latest 2.1 release (2.1.5 at the moment) where we have bunch of proxy fixes already.
Second, please create a minimal repro (ideally console app with hardcoded values, etc.) - find out how often it fails (100% or just sometimes with some frequency?). Try to reproduce it on another machine in the same environment. Try to reproduce it on another machine in different environment.
I am still seeing this in 2.2.106, curious if I should be trying some of these options or if there is still a fix pending.
@firebellys please check my last reply - it calls out there is multiple problems with same symptoms. And that we need isolated repro in separate issue to make it actionable.
I'm seeing the exception thrown too using SendAsync with HttpCompletionOption.ResponseContentRead on .netcoreapp3.1 app.
My endpoint is a Azure Function (local).
However, when I publish my endpoint to azure, it works fine. Maybe of the https? I examine both response with Postman, they are returning the same.
I made a repro here
When I run the azure function locally, it throws. When deploy on azure, no problem.
This is still an issue. A very easy way to reproduce is to pass the call through a nginx which have no access to its cache files.
Most helpful comment
Thanks @karelz , @stephentoub and others for pointing me to the HTTPS > HTTP issue. Sorry for mistakes in my code. I've just botched these examples from a different working code, without actually compiling them.
Upon experimenting with request headers, I've managed to avoid that Gravatar's HTTPS > HTTP issue. Without such redirect, HttpClient did its job perfectly!