In an event triggered Azure Function using .NET 7, I'm trying to create an HttpClient using HttpClientFactory:
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
I need to add client certificates in requests. To do that, I created my own HttpMessageHandlerBuilder which loads the certificates then adds them in the handler it returns. I then add the services and when I later inject the IHttpClientFactory, it uses this handler that has all certificates:
custom handler builder : HttpMessageHandlerBuilder
public override HttpMessageHandler Build()
{
var handler = new HttpClientHandler();
var certificates = certificateService
.GetAllCertificates(); // this fetches certificates in Azure Keyvault
foreach (var certificate in certificates)
{
handler.ClientCertificates.Add(certificate);
}
return handler;
}
program.cs (startup.cs)
serviceCollection.AddCustomCertificateServices();
serviceCollection.AddHttpClient();
function.cs
using var client = httpClientFactory.CreateClient();
This works. However, I now need to send only one certificate per request based on some condition. But I cannot manage to access the handler in the client returned by the factory, nor the certificate collection. The only way I managed to do this is to get rid of the factory and create a new instance of the handler and client as follows (summarized):
function.cs
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(x509Certificate);
using var client = new HttpClient(handler);
(also see Add client certificate to .NET Core HttpClient)
Question
According to the MSDN article this can lead to port exhaustion, however this is not a high load function so this will not be an issue in this case. But in case it was, is there a way (and if yes, how) to manage handler/client certificates per request using the factory + DI? In other words, is there a way to tell the factory to provide a customer that has a handler containing only the certificate needed for the current request? Maybe something like this answer?
This can be done with named clients, i.e.
// at startup
services.AddHttpClient("Cert1", client =>
{
client.AddCertificateClient(cert1);
});
// then you can
using var httpClient = _httpClientFactory.CreateClient("Cert1");
and then for other certificates you just create more named clients, i.e.
// at startup
services.AddHttpClient("Cert2", client =>
{
client.AddCertificateClient(cert2);
});
// then you can
using var httpClient = _httpClientFactory.CreateClient("Cert2");
When in AddHttpClient this is a good place to configure how to do retries, configure timeout or handle transient errors. Each according to the named client
For full reference Make HTTP requests using IHttpClientFactory in ASP.NET Core
Here is what I ended up doing.
Create an extension method:
public static IServiceCollection AddNamedHandlers(
this IServiceCollection serviceCollection,
List<Certificate> certificates) // custom Certificate object, adapt to fit your needs
{
// Create one handler per certificate.
// Then add one client per certificate using the created handler.
foreach (var certificate in certificates)
{
var httpClientHandler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
SslProtocols = SslProtocols.Tls12
};
httpClientHandler.ClientCertificates.Add(certificate);
serviceCollection
.AddHttpClient(certificate.Name) // whatever you want to name it to be able to use it later
.ConfigurePrimaryHttpMessageHandler(() => httpClientHandler);
}
return serviceCollection;
}
Add the handlers and clients during startup:
serviceCollection.AddNamedHandlers(certificates); // pass your certificates here somehow
Usage with HttpClientFactory:
using var client = httpClientFactory.CreateClient(certificateName);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With