Runtime: Equivalent to WebRequestHandler in .net Core

Created on 8 Jan 2018  路  30Comments  路  Source: dotnet/runtime

In .net Framework we could use the WebRequestHandler, which has a UnsafeAuthenticatedConnectionSharing property. That will allow us to send multiple requests while keeping the connection open and without verifying credentials again. This is a huge performance boost when multiple requests are issued to the same host.
Is there similar functionality in .net Core ?

area-System.Net question

All 30 comments

@davidsh
I can see you added this to future milestone.
Does that mean that currently there is no equivalent for this in core?

Does that mean that currently there is no equivalent for this in core?

There is no current equivalent for this in .NET Core.

There is no current equivalent for this in .NET Core.

Does WinHTTP not pool such connections?

Does WinHTTP not pool such connections?

Yes, WinHTTP and WinINet pool and cache connections.

I probably should have clarified my response. While there is no direct replacement for that property (UnsafeAuthenticatedConnectionSharing) from .NET Framework's WebRequestHandler class, it is not clear that it is needed in .NET Core with how the HTTP stacks are already caching connections and HTTP authentication credentials.

It would be best if a small repro could be put together and a network trace to show any possible performance issue with how connections and credentials are currently being used by the HTTP stacks in .NET Core.

cc: @Tratcher who can provide some possible historical background on the use of the 'UnsafeAuthenticatedConnectionSharing' property from WebRequestHandler and whether it's value is still needed/missing from .NET Core HTTP stacks.

NTLM is connection bound authentication and requires that the connection be closed after every request for optimal security. This isn't optimal for performance so UnsafeAuthenticatedConnectionSharing allows overriding this closure and re-using the connection. When you do this you do not re-authenticate on subsequent requests, the server caches the credentials from the first one and re-uses them. Both the client and server have to have this setting enabled for it to work. IIS has this enabled by default. .NET HttpWebRequest and HttpListener did not.

Note this feature should only be used when you're sure you're handling requests for only one user, or properly channeling users to their own connection pools.

So yes the flag may still be relevant. Do you know what WinHttp's default behavior is?

Will there be a similar flag in .net Core ?

Will there be a similar flag in .net Core ?

cc: @geoffkizer @stephentoub @karelz

Perhaps this might be addressed as an extra setting in the new SocketHttpHandler:
https://github.com/dotnet/corefx/blob/master/src/System.Net.Http/ref/System.Net.Http.cs#L239

Do we actually need a setting here? Since the pooling is associated with a given handler, I think it's very reasonable to say that you should only share that handler with code you're comfortable sharing connections with. And then there's no need for such a property or the associated complexity in the implementation.

cc: @Tratcher

So yes the flag may still be relevant. Do you know what WinHttp's default behavior is?

At a minimum we need to confirm the defaults on various platforms and see if the experience is consistent. E.g. this feature is disabled by default on .NET Framework for HttpWebRequest & HttpClient.

Do we actually need a setting here? Since the pooling is associated with a given handler, I think it's very reasonable to say that you should only share that handler with code you're comfortable sharing connections with. And then there's no need for such a property or the associated complexity in the implementation.

Agreed. Plus, credentials are specified on the HttpClientHandler, not per-request as in HttpWebRequest. So all connections shared the same credentials, and there's no reason not to always reuse them.

I hear many clever suggestions.
Does that mean that there will be support for re-using NTLM-based http-connections in .net Core ?

System.Net.Http.WebRequestHandler seems to be bit more over System.Net.Http.HttpClientHandler than just UnsafeAuthenticatedConnectionSharing.

What's the recommendation for replacing System.Net.Http.WebRequestHandler with System.Net.Http.HttpClientHandler? What should the user look out for?

Does that mean that there will be support for re-using NTLM-based http-connections in .net Core ?

Yes, they are reused.

What should the user look out for?

What from WebRequestHandler are you concerned about?

There was a reason for WebRequestHandler, wasn't it? Apart from the NTLM thing, what should a user migrating to .NET Standard use instead of WebRequestHandler?

In net461 WebRequestHandler used the same implementation as HttpClientHandler, it only exposed more switches that we didn't think would be portable across different implementations (like UnsafeAuthenticatedConnectionSharing). If there wasn't a specific option on WebRequestHandler you were using then you should be able to move to HttpClientHandler.

@Tratcher, I hope so. But I'd like some clear messaging on this.

@paulomorgado @Tratcher
In our company, we are required to use NTLM for identity authentication.
A move to HttpClientHandler would be a major performance degradation, as http connections cannot be reused.

as http connections cannot be reused

They can. In .NET Core, it's as if UnsafeAuthenticatedConnectionSharing is true. The assumption is that you're only giving out the client/handler to code you're ok reusing your connections. That's a different model from HttpWebRequest, where the pool of connections is essentially static and potentially accessed by all HttpWebRequests.

@stephentoub
Thanks Stephen. That is really great news. It is an enabler for our migration to .net Core
Also a surprise to me. Last time I tried it, I was not able to reuse connections.
So there is no property or anything else I should use? Just straight forward use of HttpClientHandler?

Yes, direct use of HttpClient/HttpClientHandler should achieve what you need.

Closing as the question seems to be answered.

If you have repro that shows different perf characteristics between .NET Core and .NET Framework on the same HW, minimize it and share it with us. We can try to reproduce it locally and take a look.

@FenrisWolfAtMiddleEarth have you had any luck?

I have not been able to reuse the NTLM authentication header across multiple requests, using the same instance of HttpClient. Every call requires re-authentication which means 2 calls for every request, rather than 1.

On full .NET Framework with WebRequest, setting UnsafeAuthenticatedConnectionSharing to true, would eliminate re-authentication, thus only needing 1 request.

This behavior is on .NET Core 2.0.

@tynor88 can you try .NET Core 2.1? It has new SocketsHttpHandler, which has this property. I am not sure about the other handlers (which were default in 2.0 and earlier).

@karelz That cannot be used from a .net standard library, is that correct?

@karelz yes exactly. That was my issue. I am doing this from a .NET Standard 2.0 library which is then consumed by a full .NET Framework project (entry point).

How would i be able to solve this issue with such a project structure?

@karelz yes exactly. That was my issue. I am doing this from a .NET Standard 2.0 library which is then consumed by a full .NET Framework project (entry point).
How would i be able to solve this issue with such a project structure?

Unfortunately, there is no easy solution to this. If your library is targeting .NET Standard 2.0, then you have to use HttpClientHandler as the handler type for your HttpClient. And that means the consumers of it will have different behaviors regarding Negotiate/NTLM credential caching depending on if the app consuming your library is running against .NET Framework vs. NET Core etc.

If you need to have the behavior equivalent of WebRequestHandler.UnsafeAuthenticatedConnectionSharing, then you will need to provide a library/NuGet package that has multiple implementations (i.e. netcoreapp, net47 etc.). And thus you will need to directly use WebRequestHandler for the .NET Framework implementation.

@davidsh

I can see .net Standard 2.1 is released with Core 3.0

Are we so lucky that SocketsHttpHandler is now available for .net Standard?
Or will it remain .net core only?

Are we so lucky that SocketsHttpHandler is now available for .net Standard?
Or will it remain .net core only?

SocketsHttpHandler is only in .NET Core. It is not part of .NETStandard.

To anyone struggling with wrapping an NTLM HttpClient in a .net Standard library, I just don't think it is possible.

So my solution was to build my library for both framework and core instead, and then add them to the same nuget-package
Also add the WebRequest package, but only if framework:

<TargetFrameworks>netcoreapp2.2;net461</TargetFrameworks>

<ItemGroup>
  <PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net4'))">
  <Reference Include="System.Net.Http.WebRequest" />
</ItemGroup>

You can then create your client pending the framework

        #if NETCOREAPP2_2
        var handler = new SocketsHttpHandler { Credentials = _credentials };
        #elif NET461
        var handler = new WebRequestHandler { Credentials = _credentials, UnsafeAuthenticatedConnectionSharing = true };
        #endif
        var httpClient = new HttpClient(handler) { BaseAddress = _uri, Timeout = _timeout };

That is far from an ideal solution.
One problem is that any other library referencing this library also cannot be Standard. It also has to be built for both framework and core.

If anyone has a better solution, I would appreciate a line dropped here.

Was this page helpful?
0 / 5 - 0 ratings