Table of Contents

Managing HttpClient Lifetime

Improper HttpClient usage can cause issues:

Problem Cause
Socket exhaustion Creating many short-lived clients
Stale DNS Single long-lived client without connection recycling

Option 1: Long-lived Client with Connection Recycling

Create one client and set PooledConnectionLifetime:

var handler = new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(15)
};
var sharedClient = new HttpClient(handler);
Warning

SocketsHttpHandler is only available in .NET Core 2.1+ and .NET 5+. Not available for .NET Framework.

Note

Don't dispose this client—it's designed to live for the entire application lifetime.

Adding Polly Resilience

using Microsoft.Extensions.Http.Resilience;
using Polly;

var resiliencePipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()
    .AddTimeout(Gw2Resiliency.TotalTimeoutStrategy)
    .AddRetry(Gw2Resiliency.RetryStrategy)
    .AddCircuitBreaker(Gw2Resiliency.CircuitBreakerStrategy)
    .AddHedging(Gw2Resiliency.HedgingStrategy)
    .AddTimeout(Gw2Resiliency.AttemptTimeoutStrategy)
    .Build();

var primaryHandler = new SocketsHttpHandler
{
    PooledConnectionLifetime = TimeSpan.FromMinutes(15)
};

var resilienceHandler = new ResilienceHandler(resiliencePipeline)
{
    InnerHandler = primaryHandler,
};

var httpClient = new HttpClient(resilienceHandler);

See Resilience with static clients for more details.


Option 2: IHttpClientFactory

Let the factory manage connection lifetimes. Disposing the HttpClient returns connections to the pool.

using System.Drawing;

using GuildWars2;
using GuildWars2.Hero.Equipment.Dyes;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;

using Pastel;

HostApplicationBuilder appBuilder = Host.CreateApplicationBuilder(args);

// Set up the HTTP client factory
appBuilder.Services.AddHttpClient<Gw2Client>();

// Configure logging
appBuilder.Logging.AddSimpleConsole(options =>
    {
        options.ColorBehavior = LoggerColorBehavior.Enabled;
        options.SingleLine = true;
    }
);

IHost app = appBuilder.Build();

// Obtain a Gw2Client from the service provider, which uses the HTTP client factory
Gw2Client gw2 = app.Services.GetRequiredService<Gw2Client>();

// Some demo code to print dye colors, using Pastel to colorize the console output
foreach (DyeColor dye in await gw2.Hero.Equipment.Dyes.GetColors().ValueOnly().ConfigureAwait(false))
{
    PrintColor(dye.Name, dye.Cloth.Rgb, dye.Leather.Rgb, dye.Metal.Rgb);
}

// Helper method to print a row of colors
void PrintColor(string name, Color cloth, Color leather, Color metal)
{
    app.Services.GetRequiredService<ILogger<DyeColor>>()
        .LogInformation(
            "{Name,-20} {Cloth,-25} {Leather,-25} {Metal,-25}",
            name,
            "  C  ".Pastel(Invert(cloth)).PastelBg(cloth),
            "  L  ".Pastel(Invert(leather)).PastelBg(leather),
            "  M  ".Pastel(Invert(metal)).PastelBg(metal)
        );

    static Color Invert(Color color)
    {
        return Color.FromArgb(color.ToArgb() ^ 0xffffff);
    }
}

📚 Additional Resources

Resource Description
HttpClient Guidelines Microsoft's official best practices
IHttpClientFactory with .NET Factory pattern deep dive

Guidance for ASP.NET Core applications: