为什么HttpClientHandler无法正确选择并使用默认代理设置?

时间:2019-07-30 15:53:50

标签: c# .net-core proxy

我们有许多.NET Core 2.1服务,以及一些在客户的Windows Server(我认为是2016)上运行的.NET Framework 4.7.1应用程序。客户有一个代理设置(通过控制面板>代理设置> LAN设置),所有流量都需要使用。所有这些服务都在向云发出呼叫。

我们看到的是来自.NET Core服务的某些调用正在到达云(例如,我们能够查看的日志记录),但是大多数其他调用正在超时。 .NET Framework应用程序没有任何这些问题。

所有服务/应用程序都将HttpClient(每个应用程序中的单个实例)与HttpRequestMessage请求一起使用,而不是与HttpWebRequest对象一起使用。我已经读过,如果您确实使用HttpClientHandler初始化HttpClient并将UseProxy属性设置为true,并且Proxy属性设置为null,则它应该使用Windows中设置的默认代理设置,并且我相信如果不初始化带有HttpClientHandler的HttpClient,无论如何它将使用默认值,该效果应该相同。

我们通过制作一个测试.Net Core控制台应用程序并调用了许多云服务,设法找到了HttpClient如何使用默认代理设置的问题。有趣的是,似乎使用了默认代理设置,因为第一个调用按预期进行。但是,此后(针对不同的云服务)的每个调用都以完全不使用代理设置的相同方式失败。

我们设法通过为传递给HttpClient的HttpClientHandler对象提供一个配置为使用正确代理的WebProxy对象来设法解决了这个问题。设置好之后,所有呼叫都会成功。尽管这不是首选的解决方案,因为它将涉及客户必须为每种服务提供额外的配置。他们也不一定知道这些服务将在哪台计算机上运行,​​并且在配置服务时可能不知道代理设置。

我们还通过另一种方式解决了该问题,方法是在每次调用后都处理HttpClient并实例化一个新的调用。显然,这不是解决方案,因为这是一种不好的做法,并且可能导致其他问题(并且在涉及多个依赖项的过程中涉及大量代码更改)。

当我们将.NET Core 2.1控制台应用程序转换为.NET Framework 4.7.1应用程序时,我们也没有看到此问题。

如果我们使用HttpWebRequest对象发出请求,我们也看不到任何问题。

这是当前失败的控制台应用程序代码。我们在服务中正确使用了异步,而不是调用Result。这似乎没有什么不同。

var httpClient = new HttpClient();

Console.WriteLine("Hitting service 1 API");

var request = new HttpRequestMessage(HttpMethod.Get, $"{baseUrl}/service1/serviceinfo");

var result = httpClient.SendAsync(request).Result;

Console.WriteLine($"Response code: {result.StatusCode}");           

var responseMessage = result.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseMessage);

Thread.Sleep(2000);

Console.WriteLine("Hitting service 2 API");

request = new HttpRequestMessage(HttpMethod.Get, $"{baseUrl}/service2/serviceinfo");

result = httpClient.SendAsync(request).Result;

Console.WriteLine($"Response code: {result.StatusCode}");          

responseMessage = result.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseMessage);            

Thread.Sleep(2000);

Console.WriteLine("Hitting service 3 API");

request = new HttpRequestMessage(HttpMethod.Get, $"{baseUrl}/service3/serviceinfo");

result = httpClient.SendAsync(request).Result;

Console.WriteLine($"Response code: {result.StatusCode}");            

responseMessage = result.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseMessage);

预期结果:无论您是否配置了代理,所有三个呼叫都应通过并收到200响应。

实际结果:不使用代理时,我们会获得预期的结果,尽管使用代理时,第一个调用按预期成功,但是下一个失败,在我们的本地测试计算机上出现SocketException,或者在客户计算机上出现GatewayTimeout (我相信这只是我们阻止所有不通过代理的流量的方式。)

如果每个调用使用新的HttpClient,则会发生预期的结果。

如果使用HttpWebRequest而不是HttpClient,则会发生预期的结果

如果使用已明确设置了代理设置的HttpClientHandler初始化HttpClient,则会出现预期的结果。

1 个答案:

答案 0 :(得分:1)

经过进一步的挖掘,我们找到了以下解决方案:

AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);

显然,在.NET Core 2.1中,HttpClient被迫使用新的HttpClientHandler。您可以使用上面的AppContext开关,也可以使用旧的处理程序初始化HttpClient。这似乎是.NET Core的明显错误/回归,但我知道我似乎找不到其他在线上遇到过此确切问题的人。欢迎提出任何建议。也许错误需要报告?