I agree with Todd Menier's answer. But if you use .Net core I suggest to read this and this articles where Microsoft says:
Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. That issue will result in SocketException errors.
It's sad, but they provide a solution:
To address those mentioned issues and make the management of HttpClient instances easier, .NET Core 2.1 introduced a new HttpClientFactory that can also be used to implement resilient HTTP calls by integrating Polly with it.
I looked at IHttpClientFactory
summary block and see that:
Each call to System.Net.Http.IHttpClientFactory.CreateClient(System.String) is
guaranteed to return a new System.Net.Http.HttpClient instance. Callers may cache
the returned System.Net.Http.HttpClient instance indefinitely or surround its
use in a using block to dispose it when desired.
The default System.Net.Http.IHttpClientFactory implementation may cache the underlying
System.Net.Http.HttpMessageHandler instances to improve performance.
Callers are also free to mutate the returned System.Net.Http.HttpClient instance's
public properties as desired.
Let's look at picture
IHttpClientFactory
implementation injecting into some service (CatalogueService or whatever you made) and then HttpClient
instantiated via IHttpClientFactory
every time when you need to make request (you can even wrap it into using(...)
block), but HttpMessageHandler
will be cached in some kind of connection pool.
So you can use HttpClientFactory
to create so many HttpClient
instances as you need and set proxy before you make call.
I'd be glad if it helps.
UPDATE:
I tried it out and it not actually what you need.
You can implement own IHttpClientFactory
like this:
public class Program
{
public interface IHttpClientFactory
{
HttpClient CreateClientWithProxy(IWebProxy webProxy);
}
internal class HttpClientFactory : IHttpClientFactory
{
private readonly Func<HttpClientHandler> makeHandler;
public HttpClientFactory(Func<HttpClientHandler> makeHandler)
{
this.makeHandler = makeHandler;
}
public HttpClient CreateClientWithProxy(IWebProxy webProxy)
{
var handler = this.makeHandler();
handler.Proxy = webProxy;
return new HttpClient(handler, true);
}
}
internal class CachedHttpClientFactory : IHttpClientFactory
{
private readonly IHttpClientFactory httpClientFactory;
private readonly Dictionary<int, HttpClient> cache = new Dictionary<int, HttpClient>();
public CachedHttpClientFactory(IHttpClientFactory httpClientFactory)
{
this.httpClientFactory = httpClientFactory;
}
public HttpClient CreateClientWithProxy(IWebProxy webProxy)
{
var key = webProxy.GetHashCode();
lock (this.cache)
{
if (this.cache.ContainsKey(key))
{
return this.cache[key];
}
var result = this.httpClientFactory.CreateClientWithProxy(webProxy);
this.cache.Add(key, result);
return result;
}
}
}
public static void Main(string[] args)
{
var httpClientFactory = new HttpClientFactory(() => new HttpClientHandler
{
UseCookies = true,
UseDefaultCredentials = true,
});
var cachedhttpClientFactory = new CachedHttpClientFactory(httpClientFactory);
var proxies = new[] {
new WebProxy()
{
Address = new Uri("https://contoso.com"),
},
new WebProxy()
{
Address = new Uri("https://microsoft.com"),
},
};
foreach (var item in proxies)
{
var client = cachedhttpClientFactory.CreateClientWithProxy(item);
client.GetAsync("http://someAddress.com");
}
}
}
But be careful with large collections of WebProxy that can occupy all connections in pool.