Skip to content

Commit 6f72158

Browse files
authored
fix: Specify a non-infinite timeout when creating HttpClient and SocketsHttpHandler instances. (#3084)
1 parent 12e1743 commit 6f72158

2 files changed

Lines changed: 17 additions & 12 deletions

File tree

src/Agent/NewRelic/Agent/Core/DataTransport/Client/NRHttpClient.cs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Reflection;
1010
using NewRelic.Agent.Configuration;
1111
using NewRelic.Agent.Core.DataTransport.Client.Interfaces;
12+
using NewRelic.Agent.Core.Utilities;
1213
using NewRelic.Agent.Extensions.Logging;
1314

1415
namespace NewRelic.Agent.Core.DataTransport.Client
@@ -18,20 +19,22 @@ namespace NewRelic.Agent.Core.DataTransport.Client
1819
/// </summary>
1920
public class NRHttpClient : HttpClientBase
2021
{
21-
private readonly IConfiguration _configuration;
2222
private IHttpClientWrapper _httpClientWrapper;
23+
private readonly TimeSpan _timeout;
24+
private readonly HttpMethod _httpMethod;
2325

2426
public NRHttpClient(IWebProxy proxy, IConfiguration configuration) : base(proxy)
2527
{
26-
_configuration = configuration;
28+
_timeout = TimeSpan.FromMilliseconds((int)configuration.CollectorTimeout);
29+
_httpMethod = configuration.PutForDataSend ? HttpMethod.Put : HttpMethod.Post;
2730

28-
// set the default timeout to "infinite", but specify the configured collector timeout as the actual timeout for SendAsync() calls
2931
var httpHandler = GetHttpHandler(proxy);
3032

31-
var httpClient = new HttpClient(httpHandler, true) { Timeout = System.Threading.Timeout.InfiniteTimeSpan };
32-
_httpClientWrapper = new HttpClientWrapper(httpClient, (int)configuration.CollectorTimeout);
33+
var httpClient = new HttpClient(httpHandler, true) { Timeout = _timeout };
34+
_httpClientWrapper = new HttpClientWrapper(httpClient, (int)_timeout.TotalMilliseconds);
3335
}
3436

37+
[NrExcludeFromCodeCoverage]
3538
private dynamic GetHttpHandler(IWebProxy proxy)
3639
{
3740
// check whether the application is running .NET 6 or later
@@ -42,30 +45,31 @@ private dynamic GetHttpHandler(IWebProxy proxy)
4245
var pooledConnectionLifetime = TimeSpan.FromMinutes(5); // an in-use connection will be closed and recycled after 5 minutes
4346
var pooledConnectionIdleTimeout = TimeSpan.FromMinutes(1); // a connection that is idle for 1 minute will be closed and recycled
4447

45-
Log.Info($"Creating a SocketsHttpHandler with PooledConnectionLifetime set to {pooledConnectionLifetime} and PooledConnectionIdleTimeout set to {pooledConnectionIdleTimeout}");
48+
Log.Info("Creating a SocketsHttpHandler with PooledConnectionLifetime {ConnectionLifetime}, PooledConnectionIdleTimeout {ConnectionIdleTimeout} and ConnectTimeout {ConnectTimeout}", pooledConnectionLifetime, pooledConnectionIdleTimeout, _timeout);
4649

47-
// use reflection to create a SocketsHttpHandler instance and set the PooledConnectionLifetime to 1 minute
50+
// use reflection to create a SocketsHttpHandler instance and set the timeout values
4851
var assembly = Assembly.Load("System.Net.Http");
4952
var handlerType = assembly.GetType("System.Net.Http.SocketsHttpHandler");
5053
dynamic handler = Activator.CreateInstance(handlerType);
5154

5255
handler.PooledConnectionLifetime = pooledConnectionLifetime;
5356
handler.PooledConnectionIdleTimeout = pooledConnectionIdleTimeout;
57+
handler.ConnectTimeout = _timeout;
5458

5559
handler.Proxy = proxy;
5660

57-
Log.Info("Current SocketsHttpHandler TLS Configuration (SocketsHttpHandler.SslOptions): {0}", handler.SslOptions.EnabledSslProtocols);
61+
Log.Info("Current SocketsHttpHandler TLS Configuration (SocketsHttpHandler.SslOptions): {SslOptions}", handler.SslOptions.EnabledSslProtocols);
5862
return handler;
5963
}
6064
catch (Exception e)
6165
{
62-
Log.Info(e, "Application is running .NET 6+ but an exception occurred trying to create SocketsHttpHandler. Falling back to HttpHandler.");
66+
Log.Info(e, "Application runtime is .NET 6+ but an exception occurred trying to create SocketsHttpHandler. Falling back to HttpHandler.");
6367
}
6468
}
6569

6670
// if the application is not running .NET 6 or later, use the default HttpClientHandler
67-
var httpClientHandler = new HttpClientHandler { Proxy = proxy };
68-
Log.Info("Current HttpClientHandler TLS Configuration (HttpClientHandler.SslProtocols): {0}", httpClientHandler.SslProtocols.ToString());
71+
var httpClientHandler = new HttpClientHandler { Proxy = proxy};
72+
Log.Info("Current HttpClientHandler TLS Configuration (HttpClientHandler.SslProtocols): {SslProtocols}", httpClientHandler.SslProtocols.ToString());
6973

7074
return httpClientHandler;
7175
}
@@ -77,7 +81,7 @@ public override IHttpResponse Send(IHttpRequest request)
7781
{
7882
using var req = new HttpRequestMessage();
7983
req.RequestUri = request.Uri;
80-
req.Method = _configuration.PutForDataSend ? HttpMethod.Put : HttpMethod.Post;
84+
req.Method = _httpMethod;
8185
req.Headers.Add("User-Agent", $"NewRelic-DotNetAgent/{AgentInstallConfiguration.AgentVersion}");
8286
req.Headers.Add("Connection", "keep-alive");
8387
req.Headers.Add("Keep-Alive", "true");

tests/Agent/UnitTests/Core.UnitTest/DataTransport/Client/NRHttpClientFactoryTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public void SetUp()
2727
Mock.Arrange(() => _mockConfiguration.AgentLicenseKey).Returns("12345");
2828
Mock.Arrange(() => _mockConfiguration.AgentRunId).Returns("123");
2929
Mock.Arrange(() => _mockConfiguration.CollectorMaxPayloadSizeInBytes).Returns(int.MaxValue);
30+
Mock.Arrange(() => _mockConfiguration.CollectorTimeout).Returns(12345);
3031

3132
_mockProxy = Mock.Create<IWebProxy>();
3233

0 commit comments

Comments
 (0)