Skip to content

Commit 38335b0

Browse files
authored
Channel internal HttpClient created with no timeout (#595)
1 parent dde2e76 commit 38335b0

File tree

4 files changed

+61
-1
lines changed

4 files changed

+61
-1
lines changed

src/Grpc.Net.Client/GrpcChannel.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using System.Collections.Concurrent;
2121
using System.Collections.Generic;
2222
using System.Net.Http;
23+
using System.Threading;
2324
using Grpc.Core;
2425
using Grpc.Net.Client.Internal;
2526
using Grpc.Net.Compression;
@@ -67,7 +68,7 @@ internal GrpcChannel(Uri address, GrpcChannelOptions channelOptions) : base(addr
6768
_shouldDisposeHttpClient = channelOptions.HttpClient == null || channelOptions.DisposeHttpClient;
6869

6970
Address = address;
70-
HttpClient = channelOptions.HttpClient ?? new HttpClient();
71+
HttpClient = channelOptions.HttpClient ?? CreateInternalHttpClient();
7172
SendMaxMessageSize = channelOptions.MaxSendMessageSize;
7273
ReceiveMaxMessageSize = channelOptions.MaxReceiveMessageSize;
7374
CompressionProviders = ResolveCompressionProviders(channelOptions.CompressionProviders);
@@ -88,6 +89,23 @@ internal GrpcChannel(Uri address, GrpcChannelOptions channelOptions) : base(addr
8889
}
8990
}
9091

92+
private static HttpClient CreateInternalHttpClient()
93+
{
94+
var httpClient = new HttpClient();
95+
96+
// Long running server and duplex streaming gRPC requests may not
97+
// return any messages for over 100 seconds, triggering a cancellation
98+
// of HttpClient.SendAsync. Disable timeout in internally created
99+
// HttpClient for channel.
100+
//
101+
// gRPC deadline should be the recommended way to timeout gRPC calls.
102+
//
103+
// https://github.com/dotnet/corefx/issues/41650
104+
httpClient.Timeout = Timeout.InfiniteTimeSpan;
105+
106+
return httpClient;
107+
}
108+
91109
internal GrpcMethodInfo GetCachedGrpcMethodInfo(IMethod method)
92110
{
93111
return _methodInfoCache.GetOrAdd(method, _createMethodInfoFunc);

src/Grpc.Net.ClientFactory/GrpcClientServiceExtensions.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using System.Diagnostics;
2121
using System.Linq;
2222
using System.Net.Http;
23+
using System.Threading;
2324
using Grpc.Core;
2425
using Grpc.Net.ClientFactory;
2526
using Grpc.Net.ClientFactory.Internal;
@@ -305,6 +306,16 @@ private static IHttpClientBuilder AddGrpcClientCore<TClient>(this IServiceCollec
305306
var clientOptions = os.Get(name);
306307

307308
httpClient.BaseAddress = clientOptions.Address;
309+
310+
// Long running server and duplex streaming gRPC requests may not
311+
// return any messages for over 100 seconds, triggering a cancellation
312+
// of HttpClient.SendAsync. Disable timeout in internally created
313+
// HttpClient for channel.
314+
//
315+
// gRPC deadline should be the recommended way to timeout gRPC calls.
316+
//
317+
// https://github.com/dotnet/corefx/issues/41650
318+
httpClient.Timeout = Timeout.InfiniteTimeSpan;
308319
};
309320

310321
IHttpClientBuilder clientBuilder = services.AddGrpcHttpClient<TClient>(name, configureTypedClient);

test/Grpc.Net.Client.Tests/GrpcChannelTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ public void Build_SslCredentialsWithHttps_Success()
4242
Assert.IsTrue(channel.IsSecure);
4343
}
4444

45+
[Test]
46+
public void Build_NoHttpClient_InternalHttpClientHasInfiniteTimeout()
47+
{
48+
// Arrange & Act
49+
var channel = GrpcChannel.ForAddress("https://localhost");
50+
51+
// Assert
52+
Assert.AreEqual(Timeout.InfiniteTimeSpan, channel.HttpClient.Timeout);
53+
}
54+
4555
[Test]
4656
public void Build_SslCredentialsWithHttp_ThrowsError()
4757
{

test/Grpc.Net.ClientFactory.Tests/DefaultGrpcClientFactoryTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,27 @@ namespace Grpc.AspNetCore.Server.ClientFactory.Tests
4242
[TestFixture]
4343
public class DefaultGrpcClientFactoryTests
4444
{
45+
[Test]
46+
public void CreateClient_Default_InternalHttpClientHasInfiniteTimeout()
47+
{
48+
// Arrange
49+
var services = new ServiceCollection();
50+
services
51+
.AddGrpcClient<TestGreeterClient>(o => o.Address = new Uri("http://localhost"));
52+
53+
var serviceProvider = services.BuildServiceProvider(validateScopes: true);
54+
55+
var clientFactory = new DefaultGrpcClientFactory(
56+
serviceProvider,
57+
serviceProvider.GetRequiredService<IHttpClientFactory>());
58+
59+
// Act
60+
var client = clientFactory.CreateClient<TestGreeterClient>(nameof(TestGreeterClient));
61+
62+
// Assert
63+
Assert.AreEqual(Timeout.InfiniteTimeSpan, client.CallInvoker.Channel.HttpClient.Timeout);
64+
}
65+
4566
[Test]
4667
public void CreateClient_MatchingConfigurationBasedOnTypeName_ReturnConfiguration()
4768
{

0 commit comments

Comments
 (0)