Exception when trying to use an existing instance of HttpClient in C# - c#

Update- The below issue was solved by setting the proxy for
HttpClientHandler. But I still wonder how the first request succeeded
without setting the proxy ever? Why would only subsequent
requests(after the first request) would fail because of proxy?
I have a simple class to reuse a single instance of "HttpClient" across different requests. The issue I am facing is that only the first call succeeds, while all the subsequent call fails with a SocketException.
public class MyHttpClient
{
private static readonly HttpClient client;
static MyHttpClient()
{
client = new HttpClient();
client.BaseAddress = new Uri("http://x.com");
}
public async Task<string> GetSecurityToken(LoginData data)
{
var reqMsg = JsonConvert.SerializeObject(data);
var reqContent = new StringContent(reqMsg, System.Text.Encoding.UTF8, "application/json");
var response= await client.PostAsync("/api/v1/security/token", reqContent)
var result = await response.Content.ReadAsStringAsync();
return result
}
}
// The above class is consumed as
var client = new MyHttpClient();
var login = new LoginData("abc#gmail.com", "xxxx");
var result = await client.GetSecurityToken(login); // this call succeeds !
var result1 = await client.GetSecurityToken(login); // this call fails with an exception, but why?
The exception I get in second call is - SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond

Related

The SSL connection could not be established when using httpclient in c# (.net core) but works in Postman and Browser

I have a .net core console app with a function that will do a simple http call and get the page content (this is for a testing). I am getting an issue "The SSL connection could not be established". But when I try the same url with Postman or a browser it works fine. Can anyone help me on this?
inner exception:"Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.."
private static async Task<string> CallUrl(string url)
{
using var client = new HttpClient();
using var response = await client.GetAsync(url);
using var content = response.Content;
var data = await content.ReadAsStringAsync();
return data;
}
I tried the suggestion in The SSL connection could not be established as well. like this but no luck.
private static async Task<string> CallUrl(string url)
{
var clientHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
{
return true;
}
};
using var client = new HttpClient(clientHandler);
using var response = await client.GetAsync(url);
using var content = response.Content;
var data = await content.ReadAsStringAsync();
return data;
}
I had the same issue calling a web service. I was able to fix it by updating "Load User Profile" to true on the IIS AppPool advanced properties.

Cannot assign requested address in Docker .NET Console client application

I am trying to learn some docker, i managed to setup API image/container and it works fine, I can get response from the API when i'll go through web browser. However my console client can't get a request to my API and I am not sure why. The only error i get is Cannot assign requested address in docker CLI.
class Program
{
static HttpClient client = new HttpClient();
static string apiUrl = "http://localhost:8080";
static void Main()
{
System.Console.WriteLine("Console started.");
RunAsync().GetAwaiter().GetResult();
}
static async Task RunAsync()
{
// Update port # in the following line.
client.BaseAddress = new Uri("http://localhost:64195/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
System.Console.WriteLine(client.BaseAddress);
try
{
var value = await GetValueAsync();
}
}
}
And the method that generate the error:
static async Task<Value> GetValueAsync()
{
Value value = null;
HttpResponseMessage response = await client.GetAsync(apiUrl + "/value");
System.Console.WriteLine("TEST");
if (response.IsSuccessStatusCode)
{
value = await response.Content.ReadAsAsync<Value>();
}
return value;
}
Program stops and returns an error on the client.GetAsync line, it never gets to the writeline("TEST"). Anyone knows what could be the problem? Everything else works until the request. On the request the Cannot assign requested address shows up and stops the container/program.

.NET Core 3.1 HttpClient throws exception intermittently: SocketException: The I/O operation has been aborted because of either a thread exit or an

Intermittently I get the exceptions below:
IOException: Unable to read data from the transport connection: The
I/O operation has been aborted because of either a thread exit or an
application request..
SocketException: The I/O operation has been aborted because of either
a thread exit or an application request.
The system is querying an external resource and from time to time the exceptions happen without anything seeming to be out of the ordinary. I have tried to set a longer timeout for HttpClient but it did not help. It could be anywhere from 5000-50000 searches before the exception happens but I would still like to mitigate it. If I retry the same search directly after exception it works so the receiving party does not seem to have a problem even though I can't access that applications logs. Runs on .NET Core 3.1.
MyService.cs
public class MyService
{
private readonly HttpClient _httpClient;
public MyService(HttpClient client)
{
client.BaseAddress = new Uri("https://example.com/");
client.Timeout = TimeSpan.FromMinutes(5);
_httpClient = client;
}
private async Task<List<string>> GetValuesFromSearch(string search)
{
//Exception is thrown here
var response = await _httpClient.GetAsync("search/" + search);
using var responseStream = await response.Content.ReadAsStreamAsync();
response.EnsureSuccessStatusCode();
var searchResultList = await JsonSerializer.DeserializeAsync
<List<string>>(responseStream);
return searchResultList;
}
}
Called like this:
var myService = new MyService(new HttpClient());
foreach (var search in listToIterate)
{
//Can be called up to 200 000 times
var result = await myService.GetValuesFromSearch(search);
}
The issue could be due to socket exhaustion. This is a known issue with HttpClient and the solution is to use HttpClientFactory. I haven't tested this but here's a quick re-write of your MyService class:
public class MyService
{
private readonly IHttpClientFactory _httpClientFactory;
public MyService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory ??
throw new ArgumentNullException(nameof(httpClientFactory));
}
private async Task<List<string>> GetValuesFromSearch(string search)
{
var _httpClient = _httpClientFactory.CreateClient("MyClient");
_httpClient.BaseAddress = new Uri("https://example.com/");
_httpClient.Timeout = TimeSpan.FromMinutes(5);
// You could also set the above in Startup.cs or wherever you add your services:
//services.AddHttpClient("MyClient", c => {
// c.BaseAddress = new Uri("https://example.com/");
// c.Timeout = TimeSpan.FromMinutes(5);
//});
//Exception is thrown here
var response = await _httpClient.GetAsync("search/" + search);
using var responseStream = await response.Content.ReadAsStreamAsync();
response.EnsureSuccessStatusCode();
var searchResultList = await JsonSerializer.DeserializeAsync
<List<string>>(responseStream);
return searchResultList;
}
}
If your request fails to return a HttpResponseMessage, HttpClient will throw this as the inner exception stack trace of type TaskCancelledException. To confirm, try using Polly to add a TimeoutAsync policy; the exception should change to a TimeOutRejectedException.
In a similar use case, the best solution I have found is to:
use HttpClientFactory, and skip the using statement when you use your client.
use Polly to build in some additional resiliency.
wrap the HttpClient code in a try/catch block so your process doesn't bomb out in the event there is no HttpResponseMessage to work with (this is the actual 'fix,' the other two pieces are to keep the exception from throwing as frequently):
private async Task<List<string>> GetValuesFromSearch(string search)
{
try
{
//Exception is thrown here
var response = await _httpClient.GetAsync("search/" + search);
var responseStream = await response.Content.ReadAsStreamAsync();
response.EnsureSuccessStatusCode();
var searchResultList = await JsonSerializer.DeserializeAsync
<List<string>>(responseStream);
return searchResultList;
}
catch (Exception ex)
{
// Log the exception.
// Do what you want, or return null and handle a dropped request in your calling method.
}
}

HttpClient changing request URI when called from WebAPI

I am implementing a transparent server-side proxy for an ASP.NET MVC application which wants to communicate with an API on another server; the code is fairly straightforward:
public class TransparentProxyDelegatingHandler : DelegatingHandler
{
private static readonly Uri ApiUri;
private static readonly HttpClient Client;
static TransparentProxyDelegatingHandler()
{
var apiServer = new Uri(ConfigurationManager.AppSettings["ApiUrl"]);
ApiUri = new Uri(apiServer);
Client = new HttpClient();
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-Forwarded-For", request.GetClientIpAddress());
request.RequestUri = TranslateIncomingRequestToUpstreamApi(request);
request.Headers.AcceptEncoding.Clear();
var response = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
return response;
}
private static Uri TranslateIncomingRequestToUpstreamApi(HttpRequestMessage request)
{
var forwardUri = new UriBuilder(request.RequestUri)
{
Host = ApiUri.Host,
Path = request.RequestUri.AbsolutePath.Replace("/Proxy", string.Empty)
};
return forwardUri.Uri;
}
}
So if I query GET https://ui.myserver.com/proxy/timesheets?from=2018-01-01, the request URI gets changed by the proxy to GET https://api.myserver.com/timesheets?from=2018-01-01, and I can verify this in the debugger; however, when the SendAsync method is invoked, the hostname part of the request URI is changed back to https://ui.myserver.com, and the call fails.
Why is it changing the value of request.RequestUri when I call SendAsync ? I've checked the source in GitHub (https://github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/System/Net/Http/HttpClient.cs), but none of the conditions for changing the value seem to apply in my case. Unfortunately the GitHub source does not align with the debug symbols, so I can't seem to step into the HttpClient source to figure out what's really going on.
OK, I found the cause of my problem; I needed to set change the Host header; the initial request to the proxy set it to the hostname of the UI (ui.myserver.com), and that overrides the hostname of the proxy that was set in the request. So if I add the following:
request.Headers.Host = $"{ApiUri.Host}:{ApiUri.Port}";
then everything magically works.

ASP .NET Core 2.1-preview2 HttpClient deadlock

I have ASP.NET Core 2.1 application hosted on Azure web app. I am sending photos base64 string over WebSockets and then by HttpClient to Azure Face API.
After some 150-250 requests HttpClient stops responding and I can't use HttpClient class in any part of my application.
In my localhost it works properly and I never get this problem.
public class FaceApiHttpClient
{
private HttpClient _client;
public FaceApiHttpClient(HttpClient client)
{
_client = client;
}
public async Task<string> GetStringAsync(byte[] byteData,string uri)
{
using (ByteArrayContent content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
HttpResponseMessage response = await _client.PostAsync(uri, content).ConfigureAwait(false);
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
}
DI:
services.AddHttpClient<FaceApiHttpClient>(
client => {
client.BaseAddress = new Uri("xxx");
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "xxx");
});
The method from FaceApiClient is invoke in a Scoped Service:
public interface IFaceAPIService
{
Task<DataServiceResult<List<Face>>> GetFacesDataFromImage(byte[] byteArray);
}
public class FaceAPIService: ServiceBase, IFaceAPIService
{
private readonly IServerLogger _serverLogger;
private FaceApiHttpClient _httpClient;
//Consts
public const string _APIKey = "xxx";
public const string _BaseURL = "xxx";
public FaceAPIService(IServerLogger serverLogger, FaceApiHttpClient client)
{
_serverLogger = serverLogger;
_httpClient = client;
}
public async Task<DataServiceResult<List<Face>>> GetFacesDataFromImage(byte[] byteData)
{
try
{
// Request parameters. A third optional parameter is "details".
string requestParameters = "returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=age,gender,headPose,smile,facialHair,glasses,emotion,hair,makeup,occlusion,accessories,blur,exposure,noise";
// Assemble the URI for the REST API Call.
string uri = _BaseURL + "/detect" + "?" + requestParameters;
var result = await _httpClient.GetStringAsync(byteData, uri).ConfigureAwait(false);
List<Face> faces = JsonConvert.DeserializeObject<List<Face>>(result);
return Success(faces);
}
catch (Exception ex)
{
_serverLogger.LogExceptionFromService(ex);
return DataServiceResult.ErrorResult<List<Face>>(ex.Message);
}
}
}
a) on localhost enviroment it works. I run 11 simulators with many request per seconds and it never broke (10 hours of simulators, over 20k requests).
b) HttpClient stops working in any part of application not only in one class.
How to fix this?
Consider changing up the design a bit.
Using a typed client the assumption is that its configuration is something that will not change frequently and that it should be added once when registering the typed client.
services.AddHttpClient<FaceApiHttpClient>(_ => {
_.BaseAddress = new Uri(Configuration["OcpApimBaseURL"]);
var apiKey = Configuration["OcpApimSubscriptionKey"];
_.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", apiKey);
_.Timeout = new TimeSpan(0, 0, 10);
});
Which would allow the typed client to no have to add the key for every call
public class FaceApiHttpClient {
private readonly HttpClient client;
public FaceApiHttpClient(HttpClient client) {
this.client = client;
}
public async Task<string> GetStringAsync(byte[] byteData, string uri) {
using (var content = new ByteArrayContent(byteData)) {
// This example uses content type "application/octet-stream".
// The other content types you can use are "application/json" and "multipart/form-data".
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
// Execute the REST API call.
HttpResponseMessage response; response = await _client.PostAsync(uri, content).ConfigureAwait(false);
// Get the JSON response.
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
}
It should be noted from ASP.NET Core 2.1-preview1: Introducing HTTPClient factory
A typed client is, effectively, a transient service, meaning that a new instance is created each time one is needed and it will receive a new HttpClient instance each time it is constructed. This means that the configuration func, in this case retrieving the URI from configuration, will run every time something needs a FaceApiHttpClient.
Based on previous documentations, having that many clients created can pose problems, but the assumption here is that the developers of this new feature took that into consideration when designing it.
I say this because the issues you described are similar to previous problem with the same cause.
After release version of ASP .NET CORE 2.1 RC1 the problem is fixed. I updated project to new version and now there is no problem with deadlock.
The problem with deadlock was only in ASP .NET CORE 2.1 Preview 2 version.

Categories

Resources