From 53d97d47611c79cba2639f2ac778629e6194cdf1 Mon Sep 17 00:00:00 2001 From: John Cormie Date: Thu, 15 Jan 2026 15:45:11 -0800 Subject: [PATCH 1/4] Move FlagResetRule from core to testing --- core/src/test/java/io/grpc/internal/DnsNameResolverTest.java | 1 + .../java/io/grpc/internal/ManagedChannelImplBuilderTest.java | 1 + .../src/main/java/io/grpc/internal/testing}/FlagResetRule.java | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) rename {core/src/testFixtures/java/io/grpc/internal => testing/src/main/java/io/grpc/internal/testing}/FlagResetRule.java (98%) diff --git a/core/src/test/java/io/grpc/internal/DnsNameResolverTest.java b/core/src/test/java/io/grpc/internal/DnsNameResolverTest.java index c6067d305b5..6826ac82668 100644 --- a/core/src/test/java/io/grpc/internal/DnsNameResolverTest.java +++ b/core/src/test/java/io/grpc/internal/DnsNameResolverTest.java @@ -60,6 +60,7 @@ import io.grpc.internal.JndiResourceResolverFactory.JndiResourceResolver; import io.grpc.internal.JndiResourceResolverFactory.RecordFetcher; import io.grpc.internal.SharedResourceHolder.Resource; +import io.grpc.internal.testing.FlagResetRule; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; diff --git a/core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java b/core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java index 8bf10de3949..39224b60220 100644 --- a/core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java +++ b/core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java @@ -52,6 +52,7 @@ import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder; import io.grpc.internal.ManagedChannelImplBuilder.FixedPortProvider; import io.grpc.internal.ManagedChannelImplBuilder.UnsupportedClientTransportFactoryBuilder; +import io.grpc.internal.testing.FlagResetRule; import io.grpc.testing.GrpcCleanupRule; import java.net.InetSocketAddress; import java.net.SocketAddress; diff --git a/core/src/testFixtures/java/io/grpc/internal/FlagResetRule.java b/testing/src/main/java/io/grpc/internal/testing/FlagResetRule.java similarity index 98% rename from core/src/testFixtures/java/io/grpc/internal/FlagResetRule.java rename to testing/src/main/java/io/grpc/internal/testing/FlagResetRule.java index 96125cd0c8a..49fc964c4ad 100644 --- a/core/src/testFixtures/java/io/grpc/internal/FlagResetRule.java +++ b/testing/src/main/java/io/grpc/internal/testing/FlagResetRule.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.grpc.internal; +package io.grpc.internal.testing; import java.util.ArrayDeque; import java.util.Deque; From 7eee1287f73312c196c52d9daed249537560d3fc Mon Sep 17 00:00:00 2001 From: John Cormie Date: Thu, 15 Jan 2026 15:46:44 -0800 Subject: [PATCH 2/4] move GrpcUtil.getFlag to api --- .../java/io/grpc/InternalFeatureFlags.java | 39 +++++++++++++++++++ .../main/java/io/grpc/internal/GrpcUtil.java | 15 +------ 2 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 api/src/main/java/io/grpc/InternalFeatureFlags.java diff --git a/api/src/main/java/io/grpc/InternalFeatureFlags.java b/api/src/main/java/io/grpc/InternalFeatureFlags.java new file mode 100644 index 00000000000..60ef904ca51 --- /dev/null +++ b/api/src/main/java/io/grpc/InternalFeatureFlags.java @@ -0,0 +1,39 @@ +/* + * Copyright 2026 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc; + +import com.google.common.base.Strings; + +@Internal +public class InternalFeatureFlags { + public static boolean getFlag(String envVarName, boolean enableByDefault) { + String envVar = System.getenv(envVarName); + if (envVar == null) { + envVar = System.getProperty(envVarName); + } + if (envVar != null) { + envVar = envVar.trim(); + } + if (enableByDefault) { + return Strings.isNullOrEmpty(envVar) || Boolean.parseBoolean(envVar); + } else { + return !Strings.isNullOrEmpty(envVar) && Boolean.parseBoolean(envVar); + } + } + + private InternalFeatureFlags() {} +} diff --git a/core/src/main/java/io/grpc/internal/GrpcUtil.java b/core/src/main/java/io/grpc/internal/GrpcUtil.java index 1b5feeccb4a..ab8d8396dee 100644 --- a/core/src/main/java/io/grpc/internal/GrpcUtil.java +++ b/core/src/main/java/io/grpc/internal/GrpcUtil.java @@ -24,7 +24,6 @@ import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.base.Stopwatch; -import com.google.common.base.Strings; import com.google.common.base.Supplier; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -32,6 +31,7 @@ import io.grpc.ClientStreamTracer; import io.grpc.ClientStreamTracer.StreamInfo; import io.grpc.InternalChannelz.SocketStats; +import io.grpc.InternalFeatureFlags; import io.grpc.InternalLogId; import io.grpc.InternalMetadata; import io.grpc.InternalMetadata.TrustedAsciiMarshaller; @@ -958,18 +958,7 @@ public static String encodeAuthority(String authority) { } public static boolean getFlag(String envVarName, boolean enableByDefault) { - String envVar = System.getenv(envVarName); - if (envVar == null) { - envVar = System.getProperty(envVarName); - } - if (envVar != null) { - envVar = envVar.trim(); - } - if (enableByDefault) { - return Strings.isNullOrEmpty(envVar) || Boolean.parseBoolean(envVar); - } else { - return !Strings.isNullOrEmpty(envVar) && Boolean.parseBoolean(envVar); - } + return InternalFeatureFlags.getFlag(envVarName, enableByDefault); } From 4abd7277c29e5c5cdfb62f69d32dfac90e74cd32 Mon Sep 17 00:00:00 2001 From: John Cormie Date: Wed, 14 Jan 2026 11:18:24 -0800 Subject: [PATCH 3/4] Move RFC 3986 feature flag to 'api' --- .../main/java/io/grpc/InternalFeatureFlags.java | 17 +++++++++++++++++ .../internal/ManagedChannelImplBuilder.java | 13 ++----------- .../internal/ManagedChannelImplBuilderTest.java | 3 ++- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/api/src/main/java/io/grpc/InternalFeatureFlags.java b/api/src/main/java/io/grpc/InternalFeatureFlags.java index 60ef904ca51..6c6bbe1961c 100644 --- a/api/src/main/java/io/grpc/InternalFeatureFlags.java +++ b/api/src/main/java/io/grpc/InternalFeatureFlags.java @@ -16,10 +16,27 @@ package io.grpc; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; +/** Global variables that govern major changes to the behavior of more than one grpc module. */ @Internal public class InternalFeatureFlags { + private static boolean enableRfc3986Uris = getFlag("GRPC_ENABLE_RFC3986_URIS", false); + + /** Whether to parse targets as RFC 3986 URIs (true), or use {@link java.net.URI} (false). */ + @VisibleForTesting + public static boolean setRfc3986UrisEnabled(boolean value) { + boolean prevValue = enableRfc3986Uris; + enableRfc3986Uris = value; + return prevValue; + } + + /** Whether to parse targets as RFC 3986 URIs (true), or use {@link java.net.URI} (false). */ + public static boolean getRfc3986UrisEnabled() { + return enableRfc3986Uris; + } + public static boolean getFlag(String envVarName, boolean enableByDefault) { String envVar = System.getenv(envVarName); if (envVar == null) { diff --git a/core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java b/core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java index 628224b826e..128c929ec0e 100644 --- a/core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java +++ b/core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java @@ -38,6 +38,7 @@ import io.grpc.EquivalentAddressGroup; import io.grpc.InternalChannelz; import io.grpc.InternalConfiguratorRegistry; +import io.grpc.InternalFeatureFlags; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.MethodDescriptor; @@ -106,16 +107,6 @@ public static ManagedChannelBuilder forTarget(String target) { */ static final long IDLE_MODE_MIN_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(1); - private static boolean enableRfc3986Uris = GrpcUtil.getFlag("GRPC_ENABLE_RFC3986_URIS", false); - - /** Whether to parse targets as RFC 3986 URIs (true), or use {@link java.net.URI} (false). */ - @VisibleForTesting - static boolean setRfc3986UrisEnabled(boolean value) { - boolean prevValue = ManagedChannelImplBuilder.enableRfc3986Uris; - ManagedChannelImplBuilder.enableRfc3986Uris = value; - return prevValue; - } - private static final ObjectPool DEFAULT_EXECUTOR_POOL = SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR); @@ -731,7 +722,7 @@ public ManagedChannel build() { ClientTransportFactory clientTransportFactory = clientTransportFactoryBuilder.buildClientTransportFactory(); ResolvedNameResolver resolvedResolver = - enableRfc3986Uris + InternalFeatureFlags.getRfc3986UrisEnabled() ? getNameResolverProviderRfc3986(target, nameResolverRegistry) : getNameResolverProvider(target, nameResolverRegistry); resolvedResolver.checkAddressTypes(clientTransportFactory.getSupportedSocketAddressTypes()); diff --git a/core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java b/core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java index 39224b60220..83a4b8ef9e0 100644 --- a/core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java +++ b/core/src/test/java/io/grpc/internal/ManagedChannelImplBuilderTest.java @@ -40,6 +40,7 @@ import io.grpc.DecompressorRegistry; import io.grpc.InternalConfigurator; import io.grpc.InternalConfiguratorRegistry; +import io.grpc.InternalFeatureFlags; import io.grpc.InternalManagedChannelBuilder.InternalInterceptorFactory; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; @@ -129,7 +130,7 @@ public static Iterable data() { @Before public void setUp() throws Exception { flagResetRule.setFlagForTest( - ManagedChannelImplBuilder::setRfc3986UrisEnabled, enableRfc3986UrisParam); + InternalFeatureFlags::setRfc3986UrisEnabled, enableRfc3986UrisParam); builder = new ManagedChannelImplBuilder( DUMMY_TARGET, From 084a08746411274aed51a7bff7e2ca1f3568acfd Mon Sep 17 00:00:00 2001 From: John Cormie Date: Thu, 15 Jan 2026 15:54:50 -0800 Subject: [PATCH 4/4] api: Use io.grpc.Uri for target parsing in ManagedChannelRegistry --- .../java/io/grpc/ManagedChannelRegistry.java | 7 ++++-- .../io/grpc/ManagedChannelRegistryTest.java | 25 +++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/io/grpc/ManagedChannelRegistry.java b/api/src/main/java/io/grpc/ManagedChannelRegistry.java index aed5eca9abf..e4c52954195 100644 --- a/api/src/main/java/io/grpc/ManagedChannelRegistry.java +++ b/api/src/main/java/io/grpc/ManagedChannelRegistry.java @@ -160,8 +160,11 @@ ManagedChannelBuilder newChannelBuilder(NameResolverRegistry nameResolverRegi String target, ChannelCredentials creds) { NameResolverProvider nameResolverProvider = null; try { - URI uri = new URI(target); - nameResolverProvider = nameResolverRegistry.getProviderForScheme(uri.getScheme()); + String scheme = + InternalFeatureFlags.getRfc3986UrisEnabled() + ? Uri.parse(target).getScheme() + : new URI(target).getScheme(); + nameResolverProvider = nameResolverRegistry.getProviderForScheme(scheme); } catch (URISyntaxException ignore) { // bad URI found, just ignore and continue } diff --git a/api/src/test/java/io/grpc/ManagedChannelRegistryTest.java b/api/src/test/java/io/grpc/ManagedChannelRegistryTest.java index 30de2477d77..5b56a422d2e 100644 --- a/api/src/test/java/io/grpc/ManagedChannelRegistryTest.java +++ b/api/src/test/java/io/grpc/ManagedChannelRegistryTest.java @@ -20,17 +20,23 @@ import static org.junit.Assert.fail; import com.google.common.collect.ImmutableSet; +import io.grpc.internal.testing.FlagResetRule; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.URI; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; /** Unit tests for {@link ManagedChannelRegistry}. */ -@RunWith(JUnit4.class) +@RunWith(Parameterized.class) public class ManagedChannelRegistryTest { private String target = "testing123"; private ChannelCredentials creds = new ChannelCredentials() { @@ -40,6 +46,21 @@ public ChannelCredentials withoutBearerTokens() { } }; + @Rule public final FlagResetRule flagResetRule = new FlagResetRule(); + + @Parameters(name = "enableRfc3986UrisParam={0}") + public static Iterable data() { + return Arrays.asList(new Object[][] {{true}, {false}}); + } + + @Parameter public boolean enableRfc3986UrisParam; + + @Before + public void setUp() { + flagResetRule.setFlagForTest( + InternalFeatureFlags::setRfc3986UrisEnabled, enableRfc3986UrisParam); + } + @Test public void register_unavailableProviderThrows() { ManagedChannelRegistry reg = new ManagedChannelRegistry();