Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions api/src/main/java/io/grpc/Grpc.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,35 @@ public static ManagedChannelBuilder<?> newChannelBuilder(
return ManagedChannelRegistry.getDefaultRegistry().newChannelBuilder(target, creds);
}

/**
* Creates a channel builder with a target string, credentials, and a specific
* name resolver registry.
*
* <p>This method uses the {@link ManagedChannelRegistry#getDefaultRegistry()} to
* find an appropriate underlying transport provider based on the target and credentials.
* The provided {@code nameResolverRegistry} is used to resolve the target address
* into physical addresses (e.g., DNS or custom schemes).
*
* @param target the target URI for the channel, such as {@code "localhost:8080"}
* or {@code "dns:///example.com"}
* @param creds the channel credentials to use for secure communication
* @param nameResolverRegistry the registry used to look up {@link NameResolver}
* providers for the target
* @return a {@link ManagedChannelBuilder} instance configured with the given parameters
* @throws IllegalArgumentException if no provider is available for the given target
* or credentials
* @since 1.79.0
*/
public static ManagedChannelBuilder<?> newChannelBuilder(
String target,
ChannelCredentials creds,
NameResolverRegistry nameResolverRegistry) {
return ManagedChannelRegistry.getDefaultRegistry().newChannelBuilder(
nameResolverRegistry,
target,
creds);
}

/**
* Creates a channel builder from a host, port, and credentials. The host and port are combined to
* form an authority string and then passed to {@link #newChannelBuilder(String,
Expand Down
25 changes: 25 additions & 0 deletions api/src/main/java/io/grpc/ManagedChannelProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,31 @@ protected NewChannelBuilderResult newChannelBuilder(String target, ChannelCreden
return NewChannelBuilderResult.error("ChannelCredentials are unsupported");
}

/**
* Creates a channel builder using the provided target, credentials, and resolution
* components.
*
* <p>This method allows for fine-grained control over name resolution by providing
* both a {@link NameResolverRegistry} and a specific {@link NameResolverProvider}.
* Unlike the public factory methods, this returns a {@link NewChannelBuilderResult},
* which may contain an error string if the provided credentials or target are
* not supported by this provider.
*
* @param target the target URI for the channel
* @param creds the channel credentials to use
* @param nameResolverRegistry the registry used for looking up name resolvers
* @param nameResolverProvider a specific provider to use, or {@code null} to
* search the registry
* @return a {@link NewChannelBuilderResult} containing either the builder or an
* error description
* @since 1.79.0
*/
protected NewChannelBuilderResult newChannelBuilder(String target, ChannelCredentials creds,
NameResolverRegistry nameResolverRegistry,
NameResolverProvider nameResolverProvider) {
return newChannelBuilder(target, creds);
}

/**
* Returns the {@link SocketAddress} types this ManagedChannelProvider supports.
*/
Expand Down
3 changes: 1 addition & 2 deletions api/src/main/java/io/grpc/ManagedChannelRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,6 @@ ManagedChannelBuilder<?> newChannelBuilder(String target, ChannelCredentials cre
return newChannelBuilder(NameResolverRegistry.getDefaultRegistry(), target, creds);
}

@VisibleForTesting
ManagedChannelBuilder<?> newChannelBuilder(NameResolverRegistry nameResolverRegistry,
String target, ChannelCredentials creds) {
NameResolverProvider nameResolverProvider = null;
Expand Down Expand Up @@ -192,7 +191,7 @@ ManagedChannelBuilder<?> newChannelBuilder(NameResolverRegistry nameResolverRegi
continue;
}
ManagedChannelProvider.NewChannelBuilderResult result
= provider.newChannelBuilder(target, creds);
= provider.newChannelBuilder(target, creds, nameResolverRegistry, nameResolverProvider);
if (result.getChannelBuilder() != null) {
return result.getChannelBuilder();
}
Expand Down
37 changes: 27 additions & 10 deletions core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ static boolean setRfc3986UrisEnabled(boolean value) {

private final List<ClientInterceptor> interceptors = new ArrayList<>();
NameResolverRegistry nameResolverRegistry = NameResolverRegistry.getDefaultRegistry();
@Nullable
NameResolverProvider nameResolverProvider;

final List<ClientTransportFilter> transportFilters = new ArrayList<>();

Expand Down Expand Up @@ -445,11 +447,16 @@ public ManagedChannelImplBuilder nameResolverFactory(NameResolver.Factory resolv
return this;
}

ManagedChannelImplBuilder nameResolverRegistry(NameResolverRegistry resolverRegistry) {
public ManagedChannelImplBuilder nameResolverRegistry(NameResolverRegistry resolverRegistry) {
this.nameResolverRegistry = resolverRegistry;
return this;
}

public ManagedChannelImplBuilder nameResolverProvider(NameResolverProvider provider) {
this.nameResolverProvider = provider;
return this;
}

@Override
public ManagedChannelImplBuilder defaultLoadBalancingPolicy(String policy) {
Preconditions.checkState(directServerAddress == null,
Expand Down Expand Up @@ -733,7 +740,7 @@ public ManagedChannel build() {
ResolvedNameResolver resolvedResolver =
enableRfc3986Uris
? getNameResolverProviderRfc3986(target, nameResolverRegistry)
: getNameResolverProvider(target, nameResolverRegistry);
: getNameResolverProvider(target, nameResolverRegistry, nameResolverProvider);
resolvedResolver.checkAddressTypes(clientTransportFactory.getSupportedSocketAddressTypes());
return new ManagedChannelOrphanWrapper(new ManagedChannelImpl(
this,
Expand Down Expand Up @@ -854,7 +861,8 @@ void checkAddressTypes(

@VisibleForTesting
static ResolvedNameResolver getNameResolverProvider(
String target, NameResolverRegistry nameResolverRegistry) {
String target, NameResolverRegistry nameResolverRegistry,
NameResolverProvider nameResolverProvider) {
// Finding a NameResolver. Try using the target string as the URI. If that fails, try prepending
// "dns:///".
NameResolverProvider provider = null;
Expand All @@ -869,19 +877,28 @@ static ResolvedNameResolver getNameResolverProvider(
if (targetUri != null) {
// For "localhost:8080" this would likely cause provider to be null, because "localhost" is
// parsed as the scheme. Will hit the next case and try "dns:///localhost:8080".
provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
provider = nameResolverProvider;
if (provider == null) {
provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
}
}

if (provider == null && !URI_PATTERN.matcher(target).matches()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why go through this code block if you will ignore the result?

// It doesn't look like a URI target. Maybe it's an authority string. Try with the default
// scheme from the registry.
if (!URI_PATTERN.matcher(target).matches()) {
// It doesn't look like a URI target. Maybe it's an authority string. Try with
// the default scheme from the registry (if provider is not specified) or
// the provider's default scheme (if provider is specified).
String scheme = (provider != null)
? provider.getDefaultScheme()
: nameResolverRegistry.getDefaultScheme();
try {
targetUri = new URI(nameResolverRegistry.getDefaultScheme(), "", "/" + target, null);
targetUri = new URI(scheme, "", "/" + target, null);
} catch (URISyntaxException e) {
// Should not be possible.
// Should not happen because we just validated the URI.
throw new IllegalArgumentException(e);
}
provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
if (provider == null) {
provider = nameResolverRegistry.getProviderForScheme(targetUri.getScheme());
}
}

if (provider == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ public void validTargetNoProvider() {
NameResolverRegistry nameResolverRegistry = new NameResolverRegistry();
try {
ManagedChannelImplBuilder.getNameResolverProvider(
"foo.googleapis.com:8080", nameResolverRegistry);
"foo.googleapis.com:8080", nameResolverRegistry,
null);
fail("Should fail");
} catch (IllegalArgumentException e) {
// expected
Expand All @@ -130,7 +131,7 @@ public void validTargetProviderAddrTypesNotSupported() {
NameResolverRegistry nameResolverRegistry = getTestRegistry("testscheme");
try {
ManagedChannelImplBuilder.getNameResolverProvider(
"testscheme:///foo.googleapis.com:8080", nameResolverRegistry)
"testscheme:///foo.googleapis.com:8080", nameResolverRegistry, null)
.checkAddressTypes(Collections.singleton(CustomSocketAddress.class));
fail("Should fail");
} catch (IllegalArgumentException e) {
Expand All @@ -143,7 +144,7 @@ public void validTargetProviderAddrTypesNotSupported() {
private void testValidTarget(String target, String expectedUriString, URI expectedUri) {
NameResolverRegistry nameResolverRegistry = getTestRegistry(expectedUri.getScheme());
ManagedChannelImplBuilder.ResolvedNameResolver resolved =
ManagedChannelImplBuilder.getNameResolverProvider(target, nameResolverRegistry);
ManagedChannelImplBuilder.getNameResolverProvider(target, nameResolverRegistry, null);
assertThat(resolved.provider).isInstanceOf(FakeNameResolverProvider.class);
assertThat(resolved.targetUri).isEqualTo(wrap(expectedUri));
assertThat(resolved.targetUri.toString()).isEqualTo(expectedUriString);
Expand All @@ -154,7 +155,7 @@ private void testInvalidTarget(String target) {

try {
ManagedChannelImplBuilder.ResolvedNameResolver resolved =
ManagedChannelImplBuilder.getNameResolverProvider(target, nameResolverRegistry);
ManagedChannelImplBuilder.getNameResolverProvider(target, nameResolverRegistry, null);
FakeNameResolverProvider nameResolverProvider = (FakeNameResolverProvider) resolved.provider;
fail("Should have failed, but got resolver provider " + nameResolverProvider);
} catch (IllegalArgumentException e) {
Expand Down
20 changes: 20 additions & 0 deletions netty/src/main/java/io/grpc/netty/NettyChannelBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import io.grpc.HttpConnectProxiedSocketAddress;
import io.grpc.Internal;
import io.grpc.ManagedChannelBuilder;
import io.grpc.NameResolverProvider;
import io.grpc.NameResolverRegistry;
import io.grpc.internal.AtomicBackoff;
import io.grpc.internal.ClientTransportFactory;
import io.grpc.internal.ConnectionClientTransport;
Expand Down Expand Up @@ -708,6 +710,24 @@ NettyChannelBuilder setTransportTracerFactory(TransportTracer.Factory transportT
return this;
}

/**
* Sets the registry used for looking up name resolvers.
*/
@CanIgnoreReturnValue
public NettyChannelBuilder nameResolverRegistry(NameResolverRegistry registry) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of us adding this new API is so that people can't change the name resolver after creating the builder. So it defeats the purpose to add new public methods.

We should use a separate constructor, because we'll want to disallow changing the nameResolverFactory when using the API to pass the registry.

managedChannelImplBuilder.nameResolverRegistry(registry);
return this;
}

/**
* Sets the {@link io.grpc.NameResolverProvider} to use.
*/
@CanIgnoreReturnValue
public NettyChannelBuilder nameResolverProvider(NameResolverProvider provider) {
managedChannelImplBuilder.nameResolverProvider(provider);
return this;
}

static Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
return Collections.singleton(InetSocketAddress.class);
}
Expand Down
21 changes: 21 additions & 0 deletions netty/src/main/java/io/grpc/netty/NettyChannelProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import io.grpc.ChannelCredentials;
import io.grpc.Internal;
import io.grpc.ManagedChannelProvider;
import io.grpc.NameResolverProvider;
import io.grpc.NameResolverRegistry;
import java.net.SocketAddress;
import java.util.Collection;

Expand Down Expand Up @@ -55,6 +57,25 @@ public NewChannelBuilderResult newChannelBuilder(String target, ChannelCredentia
new NettyChannelBuilder(target, creds, result.callCredentials, result.negotiator));
}

@Override
public NewChannelBuilderResult newChannelBuilder(String target, ChannelCredentials creds,
NameResolverRegistry nameResolverRegistry,
NameResolverProvider nameResolverProvider) {
ProtocolNegotiators.FromChannelCredentialsResult result = ProtocolNegotiators.from(creds);
if (result.error != null) {
return NewChannelBuilderResult.error(result.error);
}
NettyChannelBuilder builder = new NettyChannelBuilder(target, creds,
result.callCredentials, result.negotiator);
if (nameResolverRegistry != null) {
builder.nameResolverRegistry(nameResolverRegistry);
}
if (nameResolverProvider != null) {
builder.nameResolverProvider(nameResolverProvider);
}
return NewChannelBuilderResult.channelBuilder(builder);
}

@Override
protected Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
return NettyChannelBuilder.getSupportedSocketAddressTypes();
Expand Down
17 changes: 17 additions & 0 deletions netty/src/main/java/io/grpc/netty/UdsNettyChannelProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import io.grpc.ChannelCredentials;
import io.grpc.Internal;
import io.grpc.ManagedChannelProvider;
import io.grpc.NameResolverProvider;
import io.grpc.NameResolverRegistry;
import io.grpc.internal.SharedResourcePool;
import io.netty.channel.unix.DomainSocketAddress;
import java.net.SocketAddress;
Expand Down Expand Up @@ -62,6 +64,21 @@ public NewChannelBuilderResult newChannelBuilder(String target, ChannelCredentia
return result;
}

@Override
public NewChannelBuilderResult newChannelBuilder(String target, ChannelCredentials creds,
NameResolverRegistry nameResolverRegistry,
NameResolverProvider nameResolverProvider) {
Preconditions.checkState(isAvailable());
NewChannelBuilderResult result = new NettyChannelProvider().newChannelBuilder(
target, creds, nameResolverRegistry, nameResolverProvider);
if (result.getChannelBuilder() != null) {
((NettyChannelBuilder) result.getChannelBuilder())
.eventLoopGroupPool(SharedResourcePool.forResource(Utils.DEFAULT_WORKER_EVENT_LOOP_GROUP))
.channelType(Utils.EPOLL_DOMAIN_CLIENT_CHANNEL_TYPE, DomainSocketAddress.class);
}
return result;
}

@Override
protected Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
return Collections.singleton(DomainSocketAddress.class);
Expand Down
Loading