Reputation: 31
I've noticed that using HttpClient is NOT thread safe when modifying HttpClient.DefaultRequestHeaders but I want to make as many requests as possible. I need a custom header each request (2 other headers are always the same). Also the URL changes a bit
Currently I'm creating a new HttpClient for every request but I feel like creating 10k+ HttpClients isn't the best choice here.
I'd like to make one static HttpClient with 2 DefaultRequestHeaders and use this HttpClient for every request but also add one custom header.
I want to make this as fast as possible so if you have something else I'll take it.
Parallel.ForEach(Requests, Request =>
{
var Client = new HttpClient();
Client.DefaultRequestHeaders.Clear();
Client.DefaultRequestHeaders.Add("Header1", "Value1");
Client.DefaultRequestHeaders.Add("Header2", "Value2");
Client.DefaultRequestHeaders.Add("Header3", "Value for exact this request");
var response = Client.PutAsync(new Uri($"http://example.com/books/1234/readers/837"), null); //.Result (?)
Client.Dispose();
});
Upvotes: 3
Views: 10793
Reputation: 15226
The standard scenario of using HttpClient since .NET Core 2.1 is:
Straightforward example with direct passing all headers and without batch-processing:
var tasks = Enumerable.Range(1, 10000)
.Select(v => HttpClientFactory.Create().SendAsync(new HttpRequestMessage(HttpMethod.Put, new Uri($"http://example.com/books/1234/readers/837"))
{
Headers =
{
{"Header1", "Value1"},
{"Header2", "Value2"},
{"Header3", v.ToString()},
},
Content = new StringContent("{test:\"hello\"}", Encoding.UTF8, "application/json")
}));
var responses = await Task.WhenAll(tasks).ConfigureAwait(false);
// ..
Remarks:
Upvotes: 2
Reputation: 8335
Don’t use DefaultRequestHeaders
for headers that don’t apply to all requests the HttpClient
sends.
Also, don’t create an HttpClient
per request.
You can do this easily by instead creating one HttpRequestMessage
for each request, applying whatever headers you need to it, and using the same HttpClient
throughout to .SendAsync()
them:
using (var request = new HttpRequestMessage(HttpMethod.Put, url)
{
request.Headers.<add here>;
// optionally set .Content
using (var response = await httpClient.SendAsync(request))
{
// ... process response
}
}
Upvotes: 12
Reputation: 7375
I think the usage of a DelegatingHandler
would be able to accomplish what you are looking for. You would stop using DefaultRequestHeaders and instead set them within the DelegatingHandler
.
Also note, HttpClient
is threadsafe for requests, and should not be disposed of. This is because it disposes of the underlying HttpMessageHandler
which causes some lower level inefficiencies. More detail is here http://www.nimaara.com/2016/11/01/beware-of-the-net-httpclient/.
Sample.cs
// .Net Framework, careful here because the sockets will be cached, network changes will break your app
// see http://www.nimaara.com/2016/11/01/beware-of-the-net-httpclient/ for more details
var client = new HttpClient(new MyCustomHandler() { InnerHandler = new HttpClientHandler() });
// .Net Core
client = new HttpClient(new MyCustomHandler() { InnerHandler = new SocketsHttpHandler() { PooledConnectionLifetime = TimeSpan.FromMinutes(1) } });
Parallel.ForEach(Requests, Request =>
{
var response = client.PutAsync(new Uri($"http://example.com/books/1234/readers/837"), null); //.Result (?)
// do not dispose of httpclient!
});
MyCustomHandler.cs
public class MyCustomHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// the static headers
request.Headers.Add("Header1", "Value1");
request.Headers.Add("Header2", "Value2");
// the unique header
SignRequest(request);
return base.SendAsync(request, cancellationToken);
}
public void SignRequest(HttpRequestMessage message)
{
// usually the pattern of a unique header per request is for an authorization header based on some signature of the request
// this logic would be here
// generate the signature
string signature = new Random().Next(int.MaxValue).ToString();
message.Headers.Add("Header3", signature);
}
}
Upvotes: 2