0

Based on this article I decided to create single instance for my HttpClient in my project, where are sent two requests to my WebAPI from its consumer. Also to kill the connection aftrewards I decided to use solution from this article. After sending two requests using the singleton I was receiving this exception:

System.InvalidOperationException: This instance has already started one or more requests.

So I decided to use the interface and configuration (similar) from this SO answer. Everything seems to work fine, BUT after running netstat.exe I noticed, that for my API consumer are opened two (2) different connection with various port numbers:

netstat.exe

Note that [::1]:49153 is my WebAPI port, so I assume that [::1]:57612 and [::1]:57614 are opened for my consumer. Why? If they supposed to use the same client?

Should not it be like in the first mentioned article?

My HttpClientFactory:

public interface IHttpClientFactory
    {
        HttpClient CreateClient();
    }

    public class HttpClientFactory : IHttpClientFactory
    {
        static AppConfig config = new AppConfig();

        static string baseAddress = config.ConnectionAPI;

        public HttpClient CreateClient()
        {
            var client = new HttpClient();
            SetupClientDefaults(client);
            return client;
        }

        protected virtual void SetupClientDefaults(HttpClient client)
        {
            //client.Timeout = TimeSpan.FromSeconds(30);
            client.BaseAddress = new Uri(baseAddress);
            client.DefaultRequestHeaders.ConnectionClose = true;
        }
    }

And my two request sending methods:

public async Task<bool> SendPostRequestAsync(string serializedData)
        {
            CallerName = _messageService.GetCallerName();
            HttpResponseMessage response = new HttpResponseMessage();

            Console.Write(MessagesInfo[CallerName]);

            try
            {
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, Config.ApiPostUri);
                request.Content = new StringContent(serializedData, Encoding.UTF8, "application/json");

                response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);

                if (response.IsSuccessStatusCode)
                {
                    Console.WriteLine(MessagesResult[CallerName]); //success status

                    return true;
                }
                else
                {
                    throw new Exception("Status code: " + response.StatusCode.ToString());
                }
            }
            catch (Exception e)
            {
                _logger.Error(e.Message.ToString() + ", " + MessagesError[CallerName]);

                CloseConnection();

                return false;
            }

        }

and

public async Task<bool> SendGetRequestAsync()
        {
            CallerName = _messageService.GetCallerName();
            HttpResponseMessage response = new HttpResponseMessage();

            Console.Write(MessagesInfo[CallerName]);

            try
            {
                response = await _httpClient.GetAsync(Config.ApiGetUri, HttpCompletionOption.ResponseHeadersRead);

                if (response.IsSuccessStatusCode)
                {
                    Console.WriteLine(MessagesResult[CallerName]); //success status

                    return true;
                }
                else
                {
                    throw new Exception("Status code: " + response.StatusCode.ToString());
                }
            }
            catch (Exception e)
            {
                _logger.Error(e.Message.ToString() + ", " + MessagesError[CallerName]);

                CloseConnection();

                return false;
            }
        }

Connection is closed with:

public void CloseConnection()
        {
            CallerName = _messageService.GetCallerName();

            var sp = ServicePointManager.FindServicePoint(new Uri(Config.ConnectionAPI));
            sp.ConnectionLeaseTimeout = 1 * 1000;

            Console.WriteLine();
            Console.WriteLine(MessagesResult[CallerName]);
        }
  • You may wish to consider changing `HttpClientFactory` to **store** the `HttpClient` (in a `static Lazy`) so it provides the **same** `HttpClient` rather than spinning up new ones. – mjwills Jul 28 '19 at 12:52
  • can you update the code which worked for you please? facing the same issue now – user3067170 Jan 14 '22 at 22:00

1 Answers1

1

You need to Dispose of response, as per this discussion. You may wish to use using.

Additionally (from that link):

It's expected that the connections stay open. You don't want them to stay open at all? By default connections are kept alive in a connection pool for several minutes in order to avoid the expense of creating and tearing them down for every request.

In other words, connections are pooled for future performance (for a short window of time).

Additionally, your:

var sp = ServicePointManager.FindServicePoint(new Uri(Config.ConnectionAPI));
sp.ConnectionLeaseTimeout = 1 * 1000;

should be run once at startup, not repeatedly. After doing this, you could thus remove the CloseConnection method.

mjwills
  • 23,389
  • 6
  • 40
  • 63
  • Regarding `var sp ...` it is called only for `catch` blocks and after proper exec of both requests in the program. Is it disposing the client? ... Ill check in documentation... –  Jul 28 '19 at 12:49
  • No it is not disposing. Read the docs for `ConnectionLeaseTimeout` to see what it does. – mjwills Jul 28 '19 at 12:50