-
Notifications
You must be signed in to change notification settings - Fork 779
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
How to create HttpClientHandler later than configuration time #521
Comments
…master [automated] Merge branch 'release/2.2' => 'master'
Seems to have relevance #932. |
We also have this issue. We act as a provider for our partners and we wrap calls to a 3rd party service that requires us to have a unique client certificate for each partner we are sending the request on behalf of. The problem is that the client certificate is set in the We could of course register a named client for each partner we have (about 180 today) and resolve the httpclient by name. This however requires us to restart the service each time a new partner is onboarded. What are your recommendations on how to design this? |
Any thoughts on this @Eilon? |
Sorry @davidhagg I'm not an expert in this area. @rynowak - can you take a look? |
I have the exact same situation...I need to load a cert at runtime from a secret vault based on context. The solution of using the service provider inside I would also love some recommendations around this. |
@davidfowl any suggestions/ideas? |
@rynowak it would be very helpful to get your insights on this issue. If there is a work around or if we simply must live with it because it is by design. I appreciate any answer :-) |
We don't have a feature that does this yet sorry. I'm interested in your ideas about how this would behave, be implemented, and how you'd want to use it. What I think you're asking for the is the ability to pass additional information besides the name to a call to The problem that this creates is that the name is the key. This is fundamental unit of both configuration and caching for HttpClient factory. So if you want to use a single name and multiple client-certificates, then the HttpClient factory system has to be aware of how that works - basically we would need to do have a compound key. I think the reason why we're arriving here is that you want to create a separate I want to try and nail down the requirements of this in more detail and also loop in some HttpClient experts. |
Thanks for the update @rynowak. I understand that the current design makes it hard to change this since the pooling of handlers is one of the good things that were introduced with the new HttpClientFactory. I'll try to explain our problem. We communicate with a 3rd party service where we basically start a "session" on behalf of our customers. Each of our customers give us the client certificate that must be provided in order to communicate with the 3rd party service. The communication with the 3rd party service is done through a class in our solution that takes a HttpClient injected into the constructor. We keep all our customers client certificates in an Azure keyvault and the intention is to read the correct certificate depending on the incoming request data (i.e. which customers cert we should be using) So the issue we are facing is that the factory to create a HttpClientHandler is setup at startup and this is where we set the client certificate to use. Since we are using the same class to call our 3rd party service for many different customers we can't use the option to use the standard HttpClientFactory with a delegate to create a messagehandler like: This pretty much leaves us with the option to have a named client for every customer. This would result in about 180 named clients (and hopefully more over time). Each with their own pool of handlers connected to a client certificate for that customer. This might be feasible but the big issue with this solution is that we will have to restart our application each time we onboard a new customer since that would require this code to be run again:
|
Honestly I'd rather not have to deal with that. Speaking purely from the perspective of a developer wanting to make HTTP calls on an API, ignoring any preconceived notions or valid architecture issues that make this a bad idea, it sure is nice the way curl does it. curl -X GET \
{url} \
--key pk.pem \
--cert cert.pem \
--cacert ca.pem \
-H 'Content-Type: application/json' In curl if I need to include a private key or specific cert(s), I just add a flag. In dotnet, I would see this as similar to This would require some framework logic (in the handler I assume) to recognize that I'm providing explicit certs and use them. But, it would allow the developer to implement the logic for finding and assigning the cert(s) inside a Typed Client, the ideal place to do this in my opinion. Once loaded from wherever the data is stored, it's as simple as adding a header or replacing a portion of the URL to include the certs. I was perhaps misguided and am admittedly not a certificate expert, but this is how I assumed it would work when I started digging in, that I'd find and load certs from my Typed Client. Obviously curl and dotnet are not equal, but being able to execute a semi-basic curl and have my request work vs what it takes to make the same request happen in dotnet core is frustrating. Thanks for the help @rynowak (and everyone else) on this. |
Having similar (albeit milder) requirements as @davidhagg, I am using a custom implementation in production. I will post it as inspiration for a suitable solution. Before I go into details, I will argue that we are wrestling with the design of Anyway, things being as they are, here is the solution I am using to both prevent running out of sockets and still have configurability at any time:
|
We are having a similar issue, we are utilizing the Our requirement is to use Digest authentication when the actual credentials should be set at run-time at each request rather at configure time. See below:
The problem is that once Can you please advise how to overcome this issue? |
@tsaluszewski Beware that if you you use pooling, you probably want new clients every now and then, because otherwise DNS changes will not be taken into account. Also, I'm not sure if long-lived could break, for example if a firewall ends a TCP connection after 15 idle minutes. (But perhaps they know how to deal with that.) I am quite satisfied with the results of the solution I posted just above your comment. It has been in used for months. I recommend a careful read of that post. It might just help complete your implementation. |
Thank you @Timovzl for you input! However I do not want to new clients every now and then, as I had it before and ran into too many open handles (over 13K) in linux container in AWS ECS :(
Note that
|
@tsaluszewski Thanks for sharing your solution as well.
My solution specifically uses a strategy to avoid that. Before, we were simply creating and disposing an Now, each handler lives for 2 * 240 minutes. For each distinct configuration (i.e. for each endpoint in the very worst case, but most endpoints simply share the default configuration in practice), there are at most two handlers at any given time. |
We're going to look into solve these problems via some mechanism in 5.0. The problem as I understand it:
These three points are in conflict 😆 |
Would you please explain more details? For me, reusing handlers with IHttpClientFactory should be a must. The users shouldn't worry about the underlying problems such as 'Socket Exhaustion'. |
I would summarize the problem as: To avoid socket exhaustion, |
Can someone summarize for me why this is believed to be the case? Is it because a ton of distinct components all want to share the same instance and can't otherwise? |
Keeping an instance around forever wasn't a good solution in 2.0 when this feature was added because you'd end up with stale DNS. Keep in mind that we're talking about environments where you keep HTTP connections open for days. Now that we have
Sure. A client handler ( Features like client certificates or proxy settings describe how those connections are made. You cannot share the same connections to the same host with two different client certificates - you're asking to do something impossible. Since in .NET a client handler represents a pool of connections, that is where settings related to client certificates and proxy servers life. You could argue that it's inconvenient for you, but that's the design .NET has. So back to my three statements.
These desires conflict with how all of this is designed. |
Right. That's why I was asking :) Thanks. |
It would be great to see improved CoreFx docs on this, or something else to help get the word about about how users should using HttpClient in 2019 (blog) |
I agree. cc: @karelz |
@rynowak Thank you very much.
That's the scenario we meet and that's why we're here to discuss this question.
Certainly we hope to code up easily, but not define that this problem must be resolved by using client factory. I thought some implementation of IHttpClientFactory could help, but now I understand I'm wrong.
That's the goal. Now we have to create a lot of HttpClient instances for each proxy which may cause 'Socket Exhaustion' or else. Also, if we want to be as efficient as possible currently, the instances have to be manually managed, maybe in a dictionary(Key-Value : Proxy/Cert-HttpClient). When the proxy/cert is not available(maybe out of date or else), we need to remember that the HttpClient instance should be removed and disposed. I think it's not elegant, and there should be some official best practice to inform the developers how to deal with this problem. We hope our codes run safely and efficiently in this scenario. Glad to see that DNC notices the problem, and I'm looking forward to a better solution. |
@rynowak Thank you for clearing that up! That definitely helps solve the problem. I had no idea that could serve as an alternative to the factory. A few follow-up questions just to make sure that we use this correctly: From the updated docs:
|
Correct. Just keep in mind that it is the handler that owns its own connection pooling resources, so a) you still want to limit the number you create to as few as you need (if you need another because you want different settings applied to the pooled connections, that's a good reason to create another), and b) you want to Dispose of the handler when you're done with it if the app is going to keep on running after, as otherwise connections might end up staying open longer than you'd like.
Correct. And in fact in the situation you cite, you really should strive to use the same instance. Otherwise, you may end up with more connections than is your ideal minimum.
Using the pooled connection lifetime to avoid stale DNS, you'd want to factor in how common it is for your target server's DNS information to change. In general with DNS TTL, an hour is considered short and a day is considered long, though in the wild TTLs can be found to range anywhere from a minute to a few days. At the same time, connection pooling is used in order to minimize the costs of establishing new connections, but even though establishing connections is relatively expensive when compared to just using an existing one, the overhead we're generally talking about is measured anywhere from milliseconds to seconds, so even with a really short connection lifetime, you're unlikely to see the overheads show up, and you can tune your app as you see fit without feeling badly about it. In fact, if memory serves, HttpClientFactory defaults to something around 2 minutes.
Infinite, as by default SocketsHttpHandler wants to avoid tearing down connections that are actively being used over and over. However, there's also PooledConnectionIdleTimeout, which is how long a connection can remain open in the pool unused before it's closed, and that defaults to 2 minutes. |
@stephentoub Many thanks! @LeaFrock I'm closing this issue, as I consider it adequately covered. If you have any unanswered follow-ups, we can always re-open it. |
As dicussed briefly in #71:
Looking at the usage of
services.AddHttpClient().ConfigurePrimaryHttpMessageHandler()
, I'm wondering how to configure it when the necessary information is not available at configuration time.Say that each time we want to use the HttpClient, we load a certificate (perhaps based on a fingerprint), with the intention of getting an HttpClientHandler that uses that particular certificate. Such information may not be available (or even remotely convenient) at configuration time, let alone for each endpoint.
How can we get a properly managed HttpClientHandler at a later stage, when we have all the information (including potential certificates) about a particular endpoint we are connecting to?
Note that we want the client handler's lifetime to be handled for us: don't create a new one for every request, to avoid opening tons of TCP connections, but don't keep one around indefinitely either, because DNS changes must be accounted for.
The text was updated successfully, but these errors were encountered: