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..6c6bbe1961c --- /dev/null +++ b/api/src/main/java/io/grpc/InternalFeatureFlags.java @@ -0,0 +1,56 @@ +/* + * 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.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) { + 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/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(); 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); } 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/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..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; @@ -52,6 +53,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; @@ -128,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, 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;