-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Async capability when using ConfigurePrimaryHttpMessageHandler
to configure certificates
#71861
Comments
Tagging subscribers to this area: @dotnet/ncl Issue DetailsHey @davidfowl (apologies for the direct tag), but in Unable to use dynamic client certificate with HttpClientFactory #932 you asked if we figured it out in the last post. We didn't (well we did sort of with a hack). We are a shop with hundreds of certificates, and which one to use is decided by the context of the current request. In order to get the certificate, we need to talk to the database and we would like to do that in an asynchronous way. We sort of have a work around that is a bit dirty and it looks something like this: public class TransportPrimaryMessageHandler : HttpClientHandler
{
private readonly ICertificateDb _db;
private readonly Lazy<Task<int>> _triggerOnceOnlyInit;
public TransportPrimaryMessageHandler(ICertificateDb db)
{
_db = db;
_triggerOnceOnlyInit = new Lazy<Task<int>>(InitializeTransportCertificate);
}
private async Task<int> InitializeTransportCertificate()
{
var secretInfo = await _db.GetCurrentCertificate();
var certificate = new X509Certificate2(Convert.FromBase64String(secretInfo.TransportCertificate), "pwd");
return ClientCertificates.Add(certificate);
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
await _triggerOnceOnlyInit.Value;
return await base.SendAsync(request, cancellationToken);
}
} The lazy initiation ensures that the certificate will only be fetched and added at the first call and it is done asynchronously. But this kinda doesn't feel right. Are there any details available for using
|
How are you managing client instances? Do you have one per tenant? The problem is you can’t safely mutate the handler like this. You need one per customer to avoid crossing the streams by mistake |
Thanks for the heads up. I incorrectly assumed that this is solved by registering
Our use case is a bit more complex. The selection of the certificate depends on a compound key of 3 values : This means we would need to traverse all the possibilities at the startup to create all the possible http clients? I think we definitely need to approach this in a different way. |
I guess my question is not just about async context for configuring HttpClientHandler, but is the same as this How to create HttpClientHandler later than configuration time #521 - (without pre-creating all the possible named http clients). I will read the above thread a couple more times since I still don't understand why #521 was closed as completed. |
triage: we should look into how to integrate it with the factory. Not obvious how to do that at the moment. |
Wrt to How to create HttpClientHandler later than configuration time #521 am I right thinking that you could actually add HttpClient's later? When checking the default implementation of Looking at the implementation of public virtual TOptions Get(string name)
{
name = name ?? Options.DefaultName;
return _cache.GetOrAdd(name, () => _factory.Create(name));
} Doesn't this mean I can create new named instances of |
I can also see that |
Would you create a unique name based on |
Still brainstorming but this is what I'm thinking:
services.AddHttpClient("template-destinationA-",
httpClient => { httpClient.BaseAddress = new Uri("https://www.destinationA.com"); })
.AddHttpMessageHandler<SomeMessageHandlerA>()
.AddHttpMessageHandler<SomeMessageHandlerB>()
.ConfigurePrimaryHttpMessageHandler<PrimaryMessageHandler>();
I would still use the same trick to initialise the primary with the certificate at the first call to |
Thanks for the ideas @jernejg. I was actually also thinking in the similar direction. About initializing primary certificates -- services.AddHttpClient(...)
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new SocketsHttpHandler()
{
SslOptions = new SslClientAuthenticationOptions()
{
LocalCertificateSelectionCallback = (object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate? remoteCertificate, string[] acceptableIssuers) =>
{
// extract certificate from database or whatever cache you have and return it here
}
}
};
});
|
Thank you @CarnaViire I'll take a look. |
The cert callback does work if you can use the host name (sni) but it’s not enough because of the other cache keys required. The other problem with the callback is the lack of async |
Hey @davidfowl (apologies for the direct tag), but in Unable to use dynamic client certificate with HttpClientFactory #932 you asked if we figured it out in the last post.
We didn't (well we did sort of with a hack).
We are a shop with hundreds of certificates, and which one to use is decided by the context of the current request. In order to get the certificate, we need to talk to the database and we would like to do that in an asynchronous way.
We sort of have a work around that is a bit dirty and it looks something like this:
The lazy initiation ensures that the certificate will only be fetched and added at the first call and it is done asynchronously.
But this kinda doesn't feel right. Are there any details available for using
SocketsHttpHandler
as you suggested in your post?Thanks!
The text was updated successfully, but these errors were encountered: