ObjectDisposedException / SocketsHttpHandler in .NET 3.1

Viewed 3099

I am using asp.net core in one of my projects and I am making some https requests with a client certificate. To achieve this, I created a typed http client and injected it in my startup.cs like the following:

services.AddHttpClient<IClientService, ClientService>(c =>
            {
            }).ConfigurePrimaryHttpMessageHandler(() =>
            {
                var handler = new HttpClientHandler();
                handler.ClientCertificateOptions = ClientCertificateOption.Manual;
                handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls | SslProtocols.Tls11;
                handler.ClientCertificates.Add(clientCertificate);
                handler.ServerCertificateCustomValidationCallback = delegate { return true; };
                return handler;
            }
            );

My service is implemented like the following:

 public class ClientService : IClientService
    {
        public HttpClient _httpClient { get; private set; }
        private readonly string _remoteServiceBaseUrl;
        

        private readonly IOptions<AppSettings> _settings;


        public ClientService(HttpClient httpClient, IOptions<AppSettings> settings)
        {
            _httpClient = httpClient;
            _httpClient.Timeout = TimeSpan.FromSeconds(60);
            _settings = settings;
            _remoteServiceBaseUrl = $"{settings.Value.ClientUrl}";  /


        }

        async public Task<Model> GetInfo(string id)
        {
            var uri = ServiceAPI.API.GetOperation(_remoteServiceBaseUrl, id);
            var stream = await _httpClient.GetAsync(uri).Result.Content.ReadAsStreamAsync();
            var cbor = CBORObject.Read(stream);
            return JsonConvert.DeserializeObject<ModelDTO>(cbor.ToJSONString());
                
        }
    }

In my calling class, I am using this code:

public class CommandsApi 
    {
        IClientService _clientService;
       
        public CommandsApi( IclientService clientService)
           : base(applicationService, loggerFactory)
        {
            _clientService = clientService;
            _loggerfactory = loggerFactory;
        }
        public async Task<IActionResult> Post(V1.AddTransaction command)
        {

           
            var result = await _clientService.GetInfo(command.id);
        }
    }

It works just fine however after sending many requests I am receiving the foloowing error:

Cannot access a disposed object. Object name: 'SocketsHttpHandler'.
 at System.Net.Http.SocketsHttpHandler.CheckDisposed()
 at System.Net.Http.SocketsHttpHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 at System.Net.Http.DiagnosticsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)

I tried some solutions that I found in previous issues (asp.net core github )and stackoverflow but they did not work. Any idea ? Thank you

2 Answers

I suspect it's caused by resources not being disposed properly. There's an unnecessary call to .Result and you create a stream, but you don't dispose of it. If you use using statement, then the stream should be disposed. (you can always call stream.dispose() but I wouldn't recommend it).

var stream = await _httpClient.GetAsync(uri).Result.Content.ReadAsStreamAsync();

I've not run this, but consider:

public async Task<Model> GetInfo(string id)
{
    var uri = ServiceAPI.API.GetOperation(_remoteServiceBaseUrl, id);
    var response = await _httpClient.GetAsync(uri);

    using (var stream = await response.Content.ReadAsStreamAsync())
    {
        var cbor = CBORObject.Read(stream);
        return JsonConvert.DeserializeObject<ModelDTO>(cbor.ToJSONString());
    }
}

So after many tests I followed @Greg advice and implemented a class inheriting from HttpClientHandler and injected it like the following:

services.AddTransient<MyHttpClientHandler>();
services.AddHttpClient<IClientService, ClientService>().
                ConfigurePrimaryHttpMessageHandler<MyHttpClientHandler>();

this solved my problem. Thank you @Greg for the link How to use ConfigurePrimaryHttpMessageHandler generic

Related