How do I configure timeouts for HttpClient (C#) requests?

Configuring timeouts for HttpClient requests in C# is essential to prevent applications from hanging when servers don't respond or are slow. This guide covers multiple approaches to handle different timeout scenarios.

Basic HttpClient Timeout Configuration

The simplest way to configure timeouts is using the HttpClient.Timeout property:

using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        using var httpClient = new HttpClient()
        {
            Timeout = TimeSpan.FromSeconds(30) // Set 30-second timeout
        };

        try
        {
            var response = await httpClient.GetAsync("https://api.example.com/data");

            if (response.IsSuccessStatusCode)
            {
                var content = await response.Content.ReadAsStringAsync();
                Console.WriteLine($"Response: {content}");
            }
            else
            {
                Console.WriteLine($"HTTP Error: {response.StatusCode}");
            }
        }
        catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
        {
            Console.WriteLine("Request timed out.");
        }
        catch (TaskCanceledException ex)
        {
            Console.WriteLine("Request was canceled.");
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($"HTTP request failed: {ex.Message}");
        }
    }
}

Per-Request Timeout with CancellationToken

For more granular control, use CancellationToken with individual requests:

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public class HttpService
{
    private readonly HttpClient _httpClient;

    public HttpService()
    {
        _httpClient = new HttpClient();
    }

    public async Task<string> GetWithTimeoutAsync(string url, int timeoutSeconds)
    {
        using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));

        try
        {
            var response = await _httpClient.GetAsync(url, cts.Token);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsStringAsync();
        }
        catch (OperationCanceledException) when (cts.Token.IsCancellationRequested)
        {
            throw new TimeoutException($"Request to {url} timed out after {timeoutSeconds} seconds");
        }
    }

    public void Dispose()
    {
        _httpClient?.Dispose();
    }
}

Advanced Timeout Configuration with HttpClientHandler

For separate connection and read timeouts, configure HttpClientHandler:

using System;
using System.Net.Http;
using System.Threading.Tasks;

public class AdvancedHttpClient
{
    public static HttpClient CreateWithCustomTimeouts()
    {
        var handler = new HttpClientHandler()
        {
            // Connection timeout is handled differently in .NET 5+
            ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
        };

        var client = new HttpClient(handler)
        {
            Timeout = TimeSpan.FromMinutes(5) // Overall request timeout
        };

        // Set default headers
        client.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");

        return client;
    }
}

// Usage example
class Program
{
    static async Task Main()
    {
        using var httpClient = AdvancedHttpClient.CreateWithCustomTimeouts();

        try
        {
            // This request will timeout after 5 minutes total
            var response = await httpClient.GetAsync("https://slow-api.example.com/data");
            var content = await response.Content.ReadAsStringAsync();
            Console.WriteLine(content);
        }
        catch (TaskCanceledException)
        {
            Console.WriteLine("Request exceeded timeout limit");
        }
    }
}

Multiple Requests with Different Timeouts

When you need different timeout values for various operations:

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public class ApiClient
{
    private readonly HttpClient _httpClient;

    public ApiClient()
    {
        _httpClient = new HttpClient()
        {
            BaseAddress = new Uri("https://api.example.com/"),
            Timeout = Timeout.InfiniteTimeSpan // Disable global timeout
        };
    }

    public async Task<string> GetQuickDataAsync()
    {
        // Short timeout for quick operations
        using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
        var response = await _httpClient.GetAsync("quick-data", cts.Token);
        return await response.Content.ReadAsStringAsync();
    }

    public async Task<string> GetLargeDataAsync()
    {
        // Longer timeout for large data transfers
        using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(10));
        var response = await _httpClient.GetAsync("large-data", cts.Token);
        return await response.Content.ReadAsStringAsync();
    }

    public async Task<string> PostDataAsync(string jsonData)
    {
        using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
        var content = new StringContent(jsonData, System.Text.Encoding.UTF8, "application/json");
        var response = await _httpClient.PostAsync("data", content, cts.Token);
        return await response.Content.ReadAsStringAsync();
    }
}

Important Timeout Considerations

Default Values

  • Default HttpClient.Timeout: 100 seconds
  • Infinite timeout: TimeSpan.FromMilliseconds(Timeout.Infinite) or Timeout.InfiniteTimeSpan

Scope of Timeouts

The HttpClient.Timeout property applies to the entire HTTP operation: - DNS resolution - TCP connection establishment - SSL handshake (if HTTPS) - Sending request headers and body - Receiving response headers and body

Exception Handling

try
{
    var response = await httpClient.GetAsync(url);
}
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
{
    // Timeout occurred
}
catch (TaskCanceledException ex) when (ex.CancellationToken.IsCancellationRequested)
{
    // Request was explicitly canceled
}
catch (TaskCanceledException)
{
    // Could be timeout or cancellation - check the context
}

Best Practices

  1. Reuse HttpClient instances - Create once and reuse throughout application lifecycle
  2. Use dependency injection - Register HttpClient as a singleton or use IHttpClientFactory
  3. Set appropriate timeouts - Balance between user experience and server capabilities
  4. Handle timeouts gracefully - Provide meaningful error messages to users
  5. Consider retry policies - Implement exponential backoff for transient failures

HttpClientFactory Example

// In Startup.cs or Program.cs
services.AddHttpClient("api", client =>
{
    client.BaseAddress = new Uri("https://api.example.com/");
    client.Timeout = TimeSpan.FromSeconds(30);
    client.DefaultRequestHeaders.Add("Accept", "application/json");
});

// In your service
public class MyService
{
    private readonly HttpClient _httpClient;

    public MyService(IHttpClientFactory httpClientFactory)
    {
        _httpClient = httpClientFactory.CreateClient("api");
    }
}

Proper timeout configuration ensures your application remains responsive and provides a better user experience when dealing with slow or unresponsive web services.

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon