Skip to content

Commit

Permalink
Change proxy detection to log a message (#2087)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK committed Apr 6, 2023
1 parent b82c0c7 commit 54d7f29
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 39 deletions.
20 changes: 12 additions & 8 deletions src/Grpc.Net.Client/GrpcChannel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#region Copyright notice and license
#region Copyright notice and license

// Copyright 2019 The gRPC Authors
//
Expand Down Expand Up @@ -114,14 +114,15 @@ internal GrpcChannel(Uri address, GrpcChannelOptions channelOptions) : base(addr
LoggerFactory = channelOptions.LoggerFactory ?? channelOptions.ResolveService<ILoggerFactory>(NullLoggerFactory.Instance);
OperatingSystem = channelOptions.ResolveService<IOperatingSystem>(Internal.OperatingSystem.Instance);
RandomGenerator = channelOptions.ResolveService<IRandomGenerator>(new RandomGenerator());
Logger = LoggerFactory.CreateLogger<GrpcChannel>();

#if SUPPORT_LOAD_BALANCING
InitialReconnectBackoff = channelOptions.InitialReconnectBackoff;
MaxReconnectBackoff = channelOptions.MaxReconnectBackoff;

var resolverFactory = GetResolverFactory(channelOptions);
ResolveCredentials(channelOptions, out _isSecure, out _callCredentials);
(HttpHandlerType, ConnectTimeout) = CalculateHandlerContext(address, _isSecure, channelOptions);
(HttpHandlerType, ConnectTimeout) = CalculateHandlerContext(Logger, address, _isSecure, channelOptions);

SubchannelTransportFactory = channelOptions.ResolveService<ISubchannelTransportFactory>(new SubChannelTransportFactory(this));

Expand Down Expand Up @@ -150,7 +151,7 @@ internal GrpcChannel(Uri address, GrpcChannelOptions channelOptions) : base(addr
throw new ArgumentException($"Address '{address.OriginalString}' doesn't have a host. Address should include a scheme, host, and optional port. For example, 'https://localhost:5001'.");
}
ResolveCredentials(channelOptions, out _isSecure, out _callCredentials);
(HttpHandlerType, ConnectTimeout) = CalculateHandlerContext(address, _isSecure, channelOptions);
(HttpHandlerType, ConnectTimeout) = CalculateHandlerContext(Logger, address, _isSecure, channelOptions);
#endif

HttpInvoker = channelOptions.HttpClient ?? CreateInternalHttpInvoker(channelOptions.HttpHandler);
Expand All @@ -161,7 +162,6 @@ internal GrpcChannel(Uri address, GrpcChannelOptions channelOptions) : base(addr
MaxRetryBufferPerCallSize = channelOptions.MaxRetryBufferPerCallSize;
CompressionProviders = ResolveCompressionProviders(channelOptions.CompressionProviders);
MessageAcceptEncoding = GrpcProtocolHelpers.GetMessageAcceptEncoding(CompressionProviders);
Logger = LoggerFactory.CreateLogger<GrpcChannel>();
ThrowOperationCanceledOnCancellation = channelOptions.ThrowOperationCanceledOnCancellation;
UnsafeUseInsecureChannelCallCredentials = channelOptions.UnsafeUseInsecureChannelCallCredentials;
_createMethodInfoFunc = CreateMethodInfo;
Expand Down Expand Up @@ -220,7 +220,7 @@ private static bool IsHttpOrHttpsAddress(Uri address)
return address.Scheme == Uri.UriSchemeHttps || address.Scheme == Uri.UriSchemeHttp;
}

private static HttpHandlerContext CalculateHandlerContext(Uri address, bool isSecure, GrpcChannelOptions channelOptions)
private static HttpHandlerContext CalculateHandlerContext(ILogger logger, Uri address, bool isSecure, GrpcChannelOptions channelOptions)
{
if (channelOptions.HttpHandler == null)
{
Expand Down Expand Up @@ -267,10 +267,14 @@ private static HttpHandlerContext CalculateHandlerContext(Uri address, bool isSe
// Proxy can be specified via:
// - SocketsHttpHandler.Proxy. Set via app code.
// - HttpClient.DefaultProxy. Set via environment variables, e.g. HTTPS_PROXY.
if (IsProxied(socketsHttpHandler, address, isSecure))
if (type == HttpHandlerType.SocketsHttpHandler)
{
type = HttpHandlerType.Custom;
connectTimeout = null;
if (IsProxied(socketsHttpHandler, address, isSecure))
{
logger.LogInformation("Proxy configuration is detected. How the gRPC client creates connections can cause unexpected behavior when a proxy is configured. " +
"To ensure the client correctly uses a proxy, configure GrpcChannelOptions.HttpHandler to use HttpClientHandler. " +
"Note that HttpClientHandler isn't compatible with load balancing.");
}
}
#else
type = HttpHandlerType.SocketsHttpHandler;
Expand Down
63 changes: 32 additions & 31 deletions test/FunctionalTests/Client/ConnectionTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#region Copyright notice and license
#region Copyright notice and license

// Copyright 2019 The gRPC Authors
//
Expand Down Expand Up @@ -159,45 +159,46 @@ Task<HelloReply> Unary(HelloRequest request, ServerCallContext context)
Assert.AreEqual("World", reply.Message);
}

[Test]
public async Task ConfiguredProxy_SslProxyTunnel()
{
using var proxyServer = LoopbackProxyServer.Create(new LoopbackProxyServer.Options());

Task<HelloReply> Unary(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply { Message = request.Name });
}
[Test]
[Ignore("Test disabled while figuring out the best solution for https://github.com/grpc/grpc-dotnet/issues/2075")]
public async Task ConfiguredProxy_SslProxyTunnel()
{
using var proxyServer = LoopbackProxyServer.Create(new LoopbackProxyServer.Options());

// Arrange
var method = Fixture.DynamicGrpc.AddUnaryMethod<HelloRequest, HelloReply>(Unary);
Task<HelloReply> Unary(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply { Message = request.Name });
}

var http = Fixture.CreateHandler(TestServerEndpointName.Http2WithTls,
configureHandler: handler =>
{
handler.Proxy = new WebProxy(proxyServer.Uri);
});
// Arrange
var method = Fixture.DynamicGrpc.AddUnaryMethod<HelloRequest, HelloReply>(Unary);

using var channel = GrpcChannel.ForAddress(http.address, new GrpcChannelOptions
var http = Fixture.CreateHandler(TestServerEndpointName.Http2WithTls,
configureHandler: handler =>
{
LoggerFactory = LoggerFactory,
HttpHandler = http.handler,
DisposeHttpClient = true
handler.Proxy = new WebProxy(proxyServer.Uri);
});

var client = TestClientFactory.Create(channel, method);
using var channel = GrpcChannel.ForAddress(http.address, new GrpcChannelOptions
{
LoggerFactory = LoggerFactory,
HttpHandler = http.handler,
DisposeHttpClient = true
});

var client = TestClientFactory.Create(channel, method);

// Act
var reply = await client.UnaryCall(new HelloRequest { Name = "World" }).ResponseAsync.DefaultTimeout();
// Act
var reply = await client.UnaryCall(new HelloRequest { Name = "World" }).ResponseAsync.DefaultTimeout();

// Assert
Assert.AreEqual("World", reply.Message);
// Assert
Assert.AreEqual("World", reply.Message);

Assert.AreEqual(1, proxyServer.Connections);
Assert.AreEqual(1, proxyServer.Requests.Count);
Assert.AreEqual(1, proxyServer.Connections);
Assert.AreEqual(1, proxyServer.Requests.Count);

var expected = $"CONNECT {http.address.Host}:{http.address.Port} HTTP/1.1";
Assert.AreEqual(expected, proxyServer.Requests[0].RequestLine);
}
var expected = $"CONNECT {http.address.Host}:{http.address.Port} HTTP/1.1";
Assert.AreEqual(expected, proxyServer.Requests[0].RequestLine);
}
#endif
}

0 comments on commit 54d7f29

Please sign in to comment.