-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
[API Proposal]: HttpClient.Shared #65380
Comments
Tagging subscribers to this area: @dotnet/ncl Issue DetailsBackground and motivationMany uses of HttpClient require configuring either properties on HttpClient itself or on a customized underlying handler passed to HttpClient (e.g. Related: #65261 API Proposalnamespace System.Net.Http
{
public class HttpClient
{
+ public static HttpClient Shared { get; }
}
}
API Usagestring text = await HttpClient.Shared.GetStringAsync("https://dot.net/somefile.txt"); Alternative DesignsNo response Risks
|
It seems weird to me to have different defaults for the shared instance than a brand new instance. I'd expect that a new instance would in general have the same behavior as the shared instance, only limited to exactly the requests I use it for. |
Then it would still have the same problem people are solving with HttpClientFactory (DNS changes being ignored). And you will not be able to configure SocketsHttpHandler to set PooledConnectionLifetime. |
Currently the Cookies would then also be shared right? Isn't that a potential security risk? |
Triage: Looks like interesting idea, it may simplify lots of things - the easy usage. As mentioned above, we would need to decide on reasonable defaults ( We should sit on it for a while to think about it more. We need to be careful about interaction / overlap with Cookies might be problematic - technically a memory leak in waiting. |
DefaultProxy is static, nothing would change there as part of this proposal for a shared instance.
Cookies should already be segregated by domain so there shouldn't be much additional risk.
Might have to revisit cookie retention policies. E.g. Capacity limits and LRU disposal. This proposal seems orthogonal to HttpClientFactory. We may also want to consider adding HttpClientHandler.Shared that would come similarly pre-configured, but still allow you to add DelegatingHandlers, DefaultRequestHeaders, etc.. The handler has always been the real resource/lifetime issue, not the client. |
This Shared property doesn't seem ideal for usage everywhere as in your sample. I would prefer simpler static methods like Http.GetAsync for that. We might potentially optimize that better, and wouldn't have to worry about e.g. DefaultRequestHeaders. In addition to this API, consider: HttpClient.SharedHandler that gives you the underlying handler. This will allow use with libraries that take the abstract handler. Note we rejected this idea once before in a team meeting. I think it was due to pooled auth, but iirc we don't have that behavior anymore. |
I would like to combine this with the option of optionally changing the settings on the member of the cached instance here. If this can be done then I can nuke places in my code where I also do my own caching of HttpClient instances that live for the entire process lifetime (it would help me with remembering not to dispose of it). |
I assume that could be platform handler as well on mobile devices, right? To me the biggest challenges are around connection management as that impact OS resources and network behavior. |
Yes it would need to be a platform handler. |
A using alias that worked for static properties would be a nice way to have similar shortness but keep all the extension methods still naturally working. Hmm. |
Triage: Marking as ready to review since a decision about the exact defaults we would want such an instance to use is unlikely to affect the API shape. |
namespace System.Net.Http;
public class HttpClient
{
public static HttpClient Shared { get; }
} |
@dotnet/ncl, I implemented this and started writing tests for it, and realized there are a variety of members that don't lend themselves well to this. DefaultRequestHeaders, for example, as it is not thread-safe, or CancelPendingRequests, as it'll end up canceling everything anyone scheduled to the instance. Do we still think it's a good idea to add, or should we close this? |
Yeah, most of the properties (7) don't make sense, but most of the methods and extensions do (dozens). Since the properties are only designed to be set by the person creating it anyways, and the user isn't creating this instance, would they expect to set these? It might be an FAQ, but one that could be answered by throwing a good exception? |
I think this makes a lot of sense. 99% if uses that I see in code reviews are just using After watching the stream on YouTube about this issue, I agree with @bartonjs that the defaults should be changed, although I understand that this would work for everyone and might be confusing, so I'd like to suggest another static member So I think it's important to keep this feature and document the issues well. (or even have analyzers for that?) |
In the PR i put up they end up throwing either NotSupportedException (getting the default headers instance) or InvalidOperationException (setting any of the properties). |
I sure hope that in the impementation that the shared instance is not created until the property is first used (so that way we do not end up in a situation where the application would end up with 2 HttpClient instances where one is an advanced instance, and the other from HttpClient.Shared). |
There will be a need to configure things like the protocol version and policy, but not in code, maybe via AppContext switches? E.g. when library code is written using the shared instance, how do you tell it to use HTTP/2 instead? |
You can still create a custom
It will be lazy to save a little of bit of startup time when not used, but even if it weren't, creating an HttpClient instance is almost free if you never actually use it. |
That is the problem, Microsoft themselves recommend that only a single instance of HttpClient be used globally due to the implementation of Windows (and unix websockets) having a hard coded limit until all sockets are exhausted. Also, some people like me do custom things like changing the base domain for the entire HttpClient as well, I guess a custom HttpRequestMessage implementation might handle that as well? The thing I can think of currently is when people use HttpClient for things like connecting to discord's apis endpoints to make rest calls to it. |
What is the problem? Nothing here changes any of that. If you want to completely ignore
Yes, you can set an absolute url in the request message. |
IIRC HttpClient pools all the sockets internally, which means that the pool itself will be created lazily when used. |
I still think this is valuable, but there's enough consternation over this that I'm going to close it. |
I am not against this; my main point of view is that I think the shared instance should be created after it is first accessed so that way it will not cause problems for applications using advanced HttpClient logic. I also think it is valuable, however I also think that all possible ways that applications could be using HttpClient should be in mind as well at the same time (Basically only pay for what we use). Also another thing to consider:
|
That's what the PR did.
Yes. HttpClient is in general. |
Ah I see, well then I see no point in not doing it now that it is now cleared up on the confusion. |
Background and motivation
Many uses of HttpClient require configuring either properties on HttpClient itself or on a customized underlying handler passed to HttpClient (e.g.
new HttpClient(new SocketsHttpHandler() { ... })
). But many uses are also really simple, with tests, utilities, and even simple product source wanting to make simple HTTP requests. Today each such use needs to either create and destroy a new HttpClient instance, or it needs to manage its own cached instance, or it needs to use something like HttpClientFactory, which both involves DI and is rarely a simple scenario. For these simple scenarios, where no settings need to be configured, it'd be nice to have a one-liner ability to make HTTP requests and to use some of the simple helpers exposed on HttpClient.Related: #65261
API Proposal
namespace System.Net.Http { public class HttpClient { + public static HttpClient Shared { get; } } }
API Usage
Alternative Designs
No response
Risks
The text was updated successfully, but these errors were encountered: