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)
orTimeout.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
- Reuse HttpClient instances - Create once and reuse throughout application lifecycle
- Use dependency injection - Register HttpClient as a singleton or use IHttpClientFactory
- Set appropriate timeouts - Balance between user experience and server capabilities
- Handle timeouts gracefully - Provide meaningful error messages to users
- 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.