我正在使用.NET Framework的HttpClient(4.5.1 +,4.6.1和4.7.2)遇到一些有趣的行为。由于TCP端口使用率较高的已知问题,我在工作中的项目中提出了一些更改,以免每次使用时都丢弃HttpClient,请参见https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/。
我研究了这些更改,以检查一切是否按预期进行,并发现我们仍然遇到与以前相同的TIME_WAIT端口。
为确认我建议的更改是正确的,我向应用程序添加了一些额外的跟踪,以确认我在整个应用程序中使用的是HttpClient的相同实例。此后,我使用了简单的测试应用程序(摘自上面链接的aspnetmonsters网站。
using System;
using System.Net.Http;
namespace ConsoleApplication
{
public class Program
{
private static HttpClientHandler { UseDefaultCredentials = true };
private static HttpClient Client = new HttpClient(handler);
public static async Task Main(string[] args)
{
Console.WriteLine("Starting connections");
for(int i = 0; i<10; i++)
{
var result = await Client.GetAsync("http://localhost:51000");
Console.WriteLine(result.StatusCode);
}
Console.WriteLine("Connections done");
Console.ReadLine();
}
}
}
仅当使用Windows身份验证连接到IIS中托管的站点时,才会出现此问题。通过将身份验证设置为匿名(问题消失)并重新设置为Windows身份验证(问题再次出现),我可以轻松地重现该问题。
Windows身份验证的问题似乎并不限于提供程序的范围。如果您与我们进行谈判或与NTLM协商,则会遇到相同的问题。如果计算机只是工作站或域的一部分,也会发生此问题。
出于兴趣,我创建了一个dotnet core 2.1.0控制台应用程序,该问题根本不存在,并且可以正常工作。
TLDR:是否有人对如何解决此问题有任何想法,或者可能是一个错误?
答案 0 :(得分:5)
简短版本
如果要通过NTLM身份验证重用连接,请使用.NET Core 2.1
长版
当使用NTLM身份验证时,“旧的” HttpClient确实为每个请求使用了不同的连接,我感到非常惊讶。这不是错误-在.NET Core 2.1 HttpClient使用HttpWebRequest之前,它将在每个NTLM身份验证调用之后关闭连接。
在HttpWebRequest.UnsafeAuthenticatedConnectionSharing属性的文档中对此进行了描述,该属性可用于启用连接共享:
此属性的默认值为false,这将导致在请求完成后关闭当前连接。您的应用程序每次发出新请求时都必须经过身份验证序列。
如果将此属性设置为true,则执行身份验证后,用于检索响应的连接将保持打开状态。在这种情况下,其他将此属性设置为true的请求可以使用该连接,而无需重新进行身份验证。
风险在于:
如果已为用户A验证了连接,则用户B可以重用A的连接;用户B的请求是根据用户A的凭据完成的。
如果人们了解风险,并且应用程序不使用模拟,则可以使用WebRequestHandler配置HttpClient并设置UnsafeAuthenticatedConnectionSharing,例如:
HttpClient _client;
public void InitTheClient()
{
var handler=new WebRequestHandler
{
UseDefaultCredentials=true,
UnsafeAuthenticatedConnectionSharing =true
};
_client=new HttpClient(handler);
}
WebRequestHandler 不不会公开HttpWebRequest.ConnectionGroupName,该ID允许通过ID对连接进行分组,因此它无法处理模拟。
.NET Core 2.1
HttpClient在.NET Core 2.1中进行了重写,并实现了所有HTTP,使用套接字的网络功能,最小分配,连接池等。它还单独处理the NTLM challenge/response flow,因此同一套接字连接可用于提供不同的身份验证要求。
如果有兴趣的人,可以跟踪从HttpClient到SocketsHttpHanlder到HttpConnectionPoolManager,HttpConnectionPool,HttpConnection,AuthenticationHelper.NtAuth的调用,然后再返回到HttpConnection以发送原始字节。