[Breaking change]: HttpClientFactory now uses SocketsHttpHandler as a default primary handler #42824
Open
1 of 3 tasks
Labels
breaking-change
Indicates a .NET Core breaking change
doc-idea
Indicates issues that are suggestions for new topics [org][type][category]
Pri1
High priority, do before Pri2 and Pri3
⌚ Not Triaged
Not triaged
Description
HttpClientFactory
allows you to configureHttpMessageHandler
pipeline for named and typedHttpClient
s. The "inner-most" handler -- the one that actually sends the request on the wire -- is called a primary handler. If not configured, previously, this handler would always be anHttpClientHandler
. While the default primary handler is an implementation detail, as it is never specified in the docs, there were users who depended on it, for example, casting the primary handler toHttpClientHandler
to set properties likeClientCertificates
,UseCookies
,UseProxy
etc.The change makes the default primary handler to be a
SocketsHttpHandler
(on platforms that support it). Other platforms (e.g. .NET Framework) continue to useHttpClientHandler
.SocketsHttpHandler
will also have thePooledConnectionLifetime
property pre-set to match theHandlerLifetime
value (it will reflect the latest value, ifHandlerLifetime
was configured by the user).Introduced in .NET 9 Preview 6 (dotnet/runtime#101808)
Version
Other (please put exact version in description textbox)
Previous behavior
Default primary handler was
HttpClientHandler
. Casting it toHttpClientHandler
to update the properties happened to work.For example:
New behavior
On platforms where
SocketsHttpHandler
is supported, default primary handler will beSocketsHttpHandler
withPooledConnectionLifetime
set toHandlerLifetime
value. Casting it toHttpClientHandler
to update the properties will throw.For example, the same code as above
throws InvalidCastException
Type of breaking change
Reason for change
One of the most common problems
HttpClientFactory
users run into is when a Named or a Typed client erroneously gets captured in a Singleton service, or, in general, stored somewhere for a period of time that's longer than the specifiedHandlerLifetime
. BecauseHttpClientFactory
can't rotate such handlers, they might end up not respecting DNS changes.This can be mitigated by using
SocketsHttpHandler
, which has an option to controlPooledConnectionLifetime
. Similarly toHandlerLifetime
, it allows regularly recreating connections to pick up the DNS changes, but on lower level. A client withPooledConnectionLifetime
set up can be safely used as a Singleton.It is, unfortunately, very easy and seemingly "intuitive" to inject a Typed client into a singleton, but very hard to have any kind of check/analyzer to make sure
HttpClient
is not captured when it was not supposed to. It might be even harder to troubleshoot the resulting issues. So as a preventative measure -- to minimize the potential impact of such erroneous usage pattern -- theSocketsHttpHandler
mitigation mentioned above is now applied by default.This will only affect cases when the client was not configured by the end user to use a custom PrimaryHandler (via e.g.
ConfigurePrimaryHttpMessageHandler<T>()
)Recommended action
There are three options to workaround the breaking change.
ConfigureHttpClientDefaults
:HttpClientHandler
andSocketsHttpHandler
:Feature area
Extensions, Networking
Affected APIs
Microsoft.Extensions.Http.HttpMessageHandlerBuilder.PrimaryHandler
propertyMicrosoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.ConfigurePrimaryHttpMessageHandler(IHttpClientBuilder, Action<HttpMessageHandler,IServiceProvider>)
specific overload that allows you to configure existing primary handler (instead of supplying a new one, like in other overloads)The text was updated successfully, but these errors were encountered: