0

We have implemented IHttpClientFactory to make the third party calls using HttpClient in .NET Core. However, we are still getting the below errors.

System.IO.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.

System.Net.Sockets.SocketException (995): The I/O operation has been aborted because of either a thread exit or an application request

System.Net.Http.HttpRequestException: An error occurred while sending the request.   ---> System.IO.IOException: The response ended prematurely

This is the code in which we have configured the things:

Startup.cs

builder.Services
       .AddHttpClient<IRequestHandler, RequestHandler>()
       .SetHandlerLifetime(TimeSpan.FromMinutes(5))
       .ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler
       {
           SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
       })
       .AddPolicyHandler(retryPolicyHttp);

services.AddTransient<IRequestHandler, RequestHandler>();

RequestHandler.cs

public class RequestHandler: IRequestHandler
{
    private readonly IHttpClientFactory _httpClientFactory;

    public RequestHandler(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public virtual HttpResponseMessage ExecuteThirdPartyServices(HttpMethod httpMethod, string 
                           postData, RequestModel requestModel)
    {
        using HttpRequestMessage request = new(httpMethod, requestModel.RequestUri);
        
        HttpResponseMessage httpMessageResponse;

        using (var httpClient = _httpClientFactory.CreateClient())
        {
            httpClient.Timeout = TimeSpan.FromMinutes(5);
            httpMessageResponse = httpClient.SendAsync(request).Result;
        }

        return httpMessageResponse;
    }
}

I have tried all sort of options to resolve these errors but still we are not able to pass these errors and we keep on getting these errors.

Any help on this is appreciated!

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
XamDev
  • 3,377
  • 12
  • 58
  • 97
  • It looks like you are confusing `IHttpClientFactory` with `Typed HttpClient`, check [this doc](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-7.0) for the various ways you can instantiate one. But generally you should either use the `IHttpClientFactory` and the AddTransient registration, or use the typed AddHttpClient registration and an injected `HttpClient` in your RequestHandler. – Angel Yordanov Apr 10 '23 at 07:48
  • @AngelYordanov Thanks for the response. But, I have exactly implemented in the way explained in the doc. Can you update what are changes OR if i missing something? – XamDev Apr 10 '23 at 08:10
  • @XamDev : When you have followed "the doc" then add a link. – H H Apr 10 '23 at 08:16
  • @XamDev based on the usage I would say the the strongly typed approach would suit you more (which is also mentioned [in the docs](https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#how-to-use-typed-clients-with-ihttpclientfactory) or [in this one](https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#how-to-use-typed-clients-with-ihttpclientfactory)) . – Guru Stron Apr 10 '23 at 08:17
  • httpClient.SendAsync(request).**Result** - do not do this. Use `async`-`await`. – Guru Stron Apr 10 '23 at 08:19
  • @GuruStron Thanks for the reply. As this [link](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-7.0#basic-usage) we are using basic usage. And to achive strongly type i need to inject HttpClient only instead of IHttpClientFactory?. It will be good if you show some dummy example in context to my question. Thanks again – XamDev Apr 10 '23 at 08:38
  • Mayne this doc would help:https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-6.0#outgoing-request-middleware – Ruikai Feng Apr 12 '23 at 09:55
  • As I can see you are using a `retryPolicyHttp`. Please bear in mind that NOT all http requests are retryable. The called endpoint might not be implemented in an idempotent way. To learn more please read [this](https://github.com/peter-csala/resilience-service-design/blob/main/resilience.md#retry-) – Peter Csala Jun 29 '23 at 11:05
  • @PeterCsala We have added this for performance test for concurrent users. Does it going to help during performance test? – XamDev Jun 30 '23 at 14:55
  • @XamDev I don't think so. I would rather suggest to use [bombardier](https://github.com/codesenberg/bombardier) to issue current requests. – Peter Csala Jun 30 '23 at 17:24

2 Answers2

1

Not sure about the actual reason of the errors but I suspect the line (because of using Result)

httpClient.SendAsync(request).Result;

What about changing this method to async and awaiting the SendAsync() line?

await httpClient.SendAsync(request);
Mustafa Özçetin
  • 1,893
  • 1
  • 14
  • 16
0

First about your Startup.cs. You have two registrations for the same service .AddHttpClient<IRequestHandler, RequestHandler>() and .AddTransient<IRequestHandler, RequestHandler>();. The second one is the one being used if you try to inject an IRequestHandler. Which means your setup for the SSL is not being used and may be the reason for the error.

I would suggest you remove the second registration and use the first one. I also doubt you need this SetHandlerLifetime and would suggest that you should remove it.

Than there is the .Result blocking in your code. Generally you should never use .Result or .Wait() and do async all the way, but if you really want a blocking call you better do GetAwaiter().GetResult(), check Stephen Cleary's writings on the matter, he should be your go-to guide on all things async.

// Startup.cs

services
    .AddHttpClient<IRequestHandler, RequestHandler>(httpClient =>
    {  
        httpClient.Timeout = TimeSpan.FromMinutes(5);
    })
    .ConfigurePrimaryHttpMessageHandler(_ =>
        new HttpClientHandler
        {
            SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
        })
    .AddPolicyHandler(retryPolicyHttp);
// RequestHandler.cs

public class RequestHandler: IRequestHandler
{
    private readonly HttpClient _httpClient;

    public RequestHandler(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public virtual HttpResponseMessage ExecuteThirdPartyServices(
        HttpMethod httpMethod,
        string postData,
        RequestModel requestModel)
    {
        using HttpRequestMessage request = new(httpMethod, requestModel.RequestUri);
        
        return _httpClient.SendAsync(request).GetAwaiter().GetResult();
    }
}
Angel Yordanov
  • 3,112
  • 1
  • 22
  • 19
  • Thanks for the response. However, we have some methods in IRequestHandler interface which are get called from respective classes to execute third party services. By removing second option that it is giving Dependency Injection error – XamDev Apr 11 '23 at 16:58
  • Nothing is stopping you from doing that, you probably have some other misconfiguration. – Angel Yordanov Apr 12 '23 at 08:17
  • So, can you suggest what misconfiguration we might have based on above code? – XamDev May 04 '23 at 05:55