diff --git a/src/Grpc.Net.Client/GrpcChannel.cs b/src/Grpc.Net.Client/GrpcChannel.cs index 7420482bd..5d80e9e31 100644 --- a/src/Grpc.Net.Client/GrpcChannel.cs +++ b/src/Grpc.Net.Client/GrpcChannel.cs @@ -113,7 +113,6 @@ internal GrpcChannel(Uri address, GrpcChannelOptions channelOptions) : base(addr Address = address; LoggerFactory = channelOptions.LoggerFactory ?? channelOptions.ResolveService(NullLoggerFactory.Instance); RandomGenerator = channelOptions.ResolveService(new RandomGenerator()); - (HttpHandlerType, ConnectTimeout) = CalculateHandlerContext(channelOptions); #if SUPPORT_LOAD_BALANCING InitialReconnectBackoff = channelOptions.InitialReconnectBackoff; @@ -121,10 +120,11 @@ internal GrpcChannel(Uri address, GrpcChannelOptions channelOptions) : base(addr var resolverFactory = GetResolverFactory(channelOptions); ResolveCredentials(channelOptions, out _isSecure, out _callCredentials); + (HttpHandlerType, ConnectTimeout) = CalculateHandlerContext(address, _isSecure, channelOptions); SubchannelTransportFactory = channelOptions.ResolveService(new SubChannelTransportFactory(this)); - if (!IsHttpOrHttpsAddress() || channelOptions.ServiceConfig?.LoadBalancingConfigs.Count > 0) + if (!IsHttpOrHttpsAddress(Address) || channelOptions.ServiceConfig?.LoadBalancingConfigs.Count > 0) { ValidateHttpHandlerSupportsConnectivity(); } @@ -149,6 +149,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); #endif HttpInvoker = channelOptions.HttpClient ?? CreateInternalHttpInvoker(channelOptions.HttpHandler); @@ -213,12 +214,12 @@ private void ResolveCredentials(GrpcChannelOptions channelOptions, out bool isSe } } - private bool IsHttpOrHttpsAddress() + private static bool IsHttpOrHttpsAddress(Uri address) { - return Address.Scheme == Uri.UriSchemeHttps || Address.Scheme == Uri.UriSchemeHttp; + return address.Scheme == Uri.UriSchemeHttps || address.Scheme == Uri.UriSchemeHttp; } - private static HttpHandlerContext CalculateHandlerContext(GrpcChannelOptions channelOptions) + private static HttpHandlerContext CalculateHandlerContext(Uri address, bool isSecure, GrpcChannelOptions channelOptions) { if (channelOptions.HttpHandler == null) { @@ -262,7 +263,10 @@ private static HttpHandlerContext CalculateHandlerContext(GrpcChannelOptions cha // If a proxy is specified then requests could be sent via an SSL tunnel. // A CONNECT request is made to the proxy to establish the transport stream and then // gRPC calls are sent via stream. This feature isn't supported by load balancer. - if (socketsHttpHandler.Proxy != null) + // 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)) { type = HttpHandlerType.Custom; connectTimeout = null; @@ -281,6 +285,33 @@ private static HttpHandlerContext CalculateHandlerContext(GrpcChannelOptions cha return new HttpHandlerContext(HttpHandlerType.Custom); } +#if NET5_0_OR_GREATER + private static readonly Uri HttpLoadBalancerTemporaryUri = new Uri("http://loadbalancer.temporary.invalid"); + private static readonly Uri HttpsLoadBalancerTemporaryUri = new Uri("https://loadbalancer.temporary.invalid"); + + private static bool IsProxied(SocketsHttpHandler socketsHttpHandler, Uri address, bool isSecure) + { + // Check standard address directly. + // When load balancing the channel doesn't know the final addresses yet so use temporary address. + Uri resolvedAddress; + if (IsHttpOrHttpsAddress(address)) + { + resolvedAddress = address; + } + else if (isSecure) + { + resolvedAddress = HttpsLoadBalancerTemporaryUri; + } + else + { + resolvedAddress = HttpLoadBalancerTemporaryUri; + } + + var proxy = socketsHttpHandler.Proxy ?? HttpClient.DefaultProxy; + return proxy.GetProxy(resolvedAddress) != null; + } +#endif + #if SUPPORT_LOAD_BALANCING private ResolverFactory GetResolverFactory(GrpcChannelOptions options) { @@ -289,7 +320,7 @@ private ResolverFactory GetResolverFactory(GrpcChannelOptions options) // // Even with just one address we still want to use the load balancing infrastructure. This enables // the connectivity APIs on channel like GrpcChannel.State and GrpcChannel.WaitForStateChanged. - if (IsHttpOrHttpsAddress()) + if (IsHttpOrHttpsAddress(Address)) { return new StaticResolverFactory(uri => new[] { new BalancerAddress(Address.Host, Address.Port) }); }