diff --git a/src/Grpc.Net.Client/GrpcChannel.cs b/src/Grpc.Net.Client/GrpcChannel.cs index 4153e74e7..a64641e63 100644 --- a/src/Grpc.Net.Client/GrpcChannel.cs +++ b/src/Grpc.Net.Client/GrpcChannel.cs @@ -431,13 +431,15 @@ private HttpMessageInvoker CreateInternalHttpInvoker(HttpMessageHandler? handler // in advanced gRPC scenarios. We want Android to use SocketsHttpHandler. Throw an error if: // 1. Client is running on Android. // 2. Channel is created with HttpClientHandler. - // 3. UseNativeHttpHandler switch is true. + // 3. Channel is not using GrpcWebHandler. grpc-web is compatible with the native handler. + // 4. UseNativeHttpHandler switch is true. if (OperatingSystem.IsAndroid) { // GetHttpHandlerType recurses through DelegatingHandlers that may wrap the HttpClientHandler. var httpClientHandler = HttpRequestHelpers.GetHttpHandlerType<HttpClientHandler>(handler); + var grpcWebHandler = HttpRequestHelpers.GetHttpHandlerType(handler, "Grpc.Net.Client.Web.GrpcWebHandler"); - if (httpClientHandler != null && RuntimeHelpers.QueryRuntimeSettingSwitch("System.Net.Http.UseNativeHttpHandler", defaultValue: false)) + if (httpClientHandler != null && grpcWebHandler == null && RuntimeHelpers.QueryRuntimeSettingSwitch("System.Net.Http.UseNativeHttpHandler", defaultValue: false)) { throw new InvalidOperationException("The channel configuration isn't valid on Android devices. " + "The channel is configured to use HttpClientHandler and Android's native HTTP/2 library. " + diff --git a/test/Grpc.Net.Client.Tests/Grpc.Net.Client.Tests.csproj b/test/Grpc.Net.Client.Tests/Grpc.Net.Client.Tests.csproj index a2c890199..370eafae7 100644 --- a/test/Grpc.Net.Client.Tests/Grpc.Net.Client.Tests.csproj +++ b/test/Grpc.Net.Client.Tests/Grpc.Net.Client.Tests.csproj @@ -30,6 +30,7 @@ <ItemGroup> <ProjectReference Include="..\..\src\Grpc.Net.Client\Grpc.Net.Client.csproj" /> + <ProjectReference Include="..\..\src\Grpc.Net.Client.Web\Grpc.Net.Client.Web.csproj" /> <PackageReference Include="Google.Protobuf" Version="$(GoogleProtobufPackageVersion)" /> <PackageReference Include="Grpc.Tools" Version="$(GrpcToolsPackageVersion)" PrivateAssets="All" /> diff --git a/test/Grpc.Net.Client.Tests/GrpcChannelTests.cs b/test/Grpc.Net.Client.Tests/GrpcChannelTests.cs index 16128ef16..5dbc7a747 100644 --- a/test/Grpc.Net.Client.Tests/GrpcChannelTests.cs +++ b/test/Grpc.Net.Client.Tests/GrpcChannelTests.cs @@ -26,7 +26,7 @@ using Microsoft.Extensions.Logging.Testing; using NUnit.Framework; using Grpc.Net.Client.Internal; -using System.Net; +using Grpc.Net.Client.Web; #if SUPPORT_LOAD_BALANCING using Grpc.Net.Client.Balancer; using Grpc.Net.Client.Balancer.Internal; @@ -402,7 +402,7 @@ public async Task Dispose_CalledWhileActiveCalls_ActiveCallsDisposed() Assert.AreEqual(0, channel.ActiveCalls.Count); } - [TestCase(null)] + [TestCase(true)] [TestCase(false)] public void HttpHandler_HttpClientHandlerOverNativeOnAndroid_ThrowError(bool useDelegatingHandlers) { @@ -415,6 +415,8 @@ public void HttpHandler_HttpClientHandlerOverNativeOnAndroid_ThrowError(bool use services.AddSingleton<IOperatingSystem>(new TestOperatingSystem { IsAndroid = true }); HttpMessageHandler handler = new HttpClientHandler(); + + // Add an extra handler to verify that test successfully recurses down custom handlers. if (useDelegatingHandlers) { handler = new TestDelegatingHandler(handler); @@ -443,6 +445,43 @@ public void HttpHandler_HttpClientHandlerOverNativeOnAndroid_ThrowError(bool use } } + [TestCase(true)] + [TestCase(false)] + public void HttpHandler_HttpClientHandlerOverNativeOnAndroid_HasGrpcWebHandler_ThrowError(bool useDelegatingHandlers) + { + // Arrange + AppContext.SetSwitch("System.Net.Http.UseNativeHttpHandler", true); + + try + { + var services = new ServiceCollection(); + services.AddSingleton<IOperatingSystem>(new TestOperatingSystem { IsAndroid = true }); + + HttpMessageHandler handler = new HttpClientHandler(); + handler = new GrpcWebHandler(handler); + + // Add an extra handler to verify that test successfully recurses down custom handlers. + if (useDelegatingHandlers) + { + handler = new TestDelegatingHandler(handler); + } + + var channel = GrpcChannel.ForAddress("https://localhost", new GrpcChannelOptions + { + HttpHandler = handler, + ServiceProvider = services.BuildServiceProvider() + }); + + // Assert + Assert.IsTrue(channel.OperatingSystem.IsAndroid); + } + finally + { + // Reset switch for other tests. + AppContext.SetSwitch("System.Net.Http.UseNativeHttpHandler", false); + } + } + private class TestDelegatingHandler : DelegatingHandler { public TestDelegatingHandler(HttpMessageHandler innerHandler) : base(innerHandler)