mirror of https://github.com/grpc/grpc-java.git
api: Add ChannelCredentials
This commit is contained in:
parent
9b73e2365d
commit
c8a94d1059
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a security configuration to be used for channels. There is no generic mechanism for
|
||||||
|
* processing arbitrary {@code ChannelCredentials}; the consumer of the credential (the channel)
|
||||||
|
* must support each implementation explicitly and separately. Consumers are not required to support
|
||||||
|
* all types or even all possible configurations for types that are partially supported, but they
|
||||||
|
* <em>must</em> at least fully support {@link ChoiceChannelCredentials}.
|
||||||
|
*
|
||||||
|
* <p>A {@code ChannelCredential} provides client identity and authenticates the server. This is
|
||||||
|
* different from {@link CallCredentials}, which only provides client identity. They can also
|
||||||
|
* influence types of encryption used and similar security configuration.
|
||||||
|
*
|
||||||
|
* <p>The concrete credential type should not be relevant to most users of the API and may be an
|
||||||
|
* implementation decision. Users should generally use the {@code ChannelCredentials} type for
|
||||||
|
* variables instead of the concrete type. Freshly-constructed credentials should be returned as
|
||||||
|
* {@code ChannelCredentials} instead of a concrete type to encourage this pattern. Concrete types
|
||||||
|
* would only be used after {@code instanceof} checks (which must consider
|
||||||
|
* {@code ChoiceChannelCredentials}!).
|
||||||
|
*/
|
||||||
|
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
|
||||||
|
public abstract class ChannelCredentials {}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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 java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a list of {@link ChannelCredentials}, where any one may be used. The credentials are in
|
||||||
|
* preference order.
|
||||||
|
*/
|
||||||
|
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
|
||||||
|
public final class ChoiceChannelCredentials extends ChannelCredentials {
|
||||||
|
/**
|
||||||
|
* Constructs with the provided {@code creds} as options, with preferred credentials first.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if no creds are provided
|
||||||
|
*/
|
||||||
|
public static ChannelCredentials create(ChannelCredentials... creds) {
|
||||||
|
if (creds.length == 0) {
|
||||||
|
throw new IllegalArgumentException("At least one credential is required");
|
||||||
|
}
|
||||||
|
return new ChoiceChannelCredentials(creds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<ChannelCredentials> creds;
|
||||||
|
|
||||||
|
private ChoiceChannelCredentials(ChannelCredentials... creds) {
|
||||||
|
for (ChannelCredentials cred : creds) {
|
||||||
|
if (cred == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.creds = Collections.unmodifiableList(new ArrayList<>(Arrays.asList(creds)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Non-empty list of credentials, in preference order. */
|
||||||
|
public List<ChannelCredentials> getCredentialsList() {
|
||||||
|
return creds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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.Preconditions;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses multiple {@code CallCredentials} as if they were one. If the first credential fails, the
|
||||||
|
* second will not be used. Both must succeed to allow the RPC.
|
||||||
|
*/
|
||||||
|
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
|
||||||
|
public final class CompositeCallCredentials extends CallCredentials {
|
||||||
|
private final CallCredentials credentials1;
|
||||||
|
private final CallCredentials credentials2;
|
||||||
|
|
||||||
|
public CompositeCallCredentials(CallCredentials creds1, CallCredentials creds2) {
|
||||||
|
this.credentials1 = Preconditions.checkNotNull(creds1, "creds1");
|
||||||
|
this.credentials2 = Preconditions.checkNotNull(creds2, "creds2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyRequestMetadata(
|
||||||
|
RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier) {
|
||||||
|
credentials1.applyRequestMetadata(requestInfo, appExecutor,
|
||||||
|
new WrappingMetadataApplier(requestInfo, appExecutor, applier, Context.current()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void thisUsesUnstableApi() {}
|
||||||
|
|
||||||
|
private final class WrappingMetadataApplier extends MetadataApplier {
|
||||||
|
private final RequestInfo requestInfo;
|
||||||
|
private final Executor appExecutor;
|
||||||
|
private final MetadataApplier delegate;
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
public WrappingMetadataApplier(
|
||||||
|
RequestInfo requestInfo, Executor appExecutor, MetadataApplier delegate, Context context) {
|
||||||
|
this.requestInfo = requestInfo;
|
||||||
|
this.appExecutor = appExecutor;
|
||||||
|
this.delegate = Preconditions.checkNotNull(delegate, "delegate");
|
||||||
|
this.context = Preconditions.checkNotNull(context, "context");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Metadata headers) {
|
||||||
|
Preconditions.checkNotNull(headers, "headers");
|
||||||
|
Context previous = context.attach();
|
||||||
|
try {
|
||||||
|
credentials2.applyRequestMetadata(
|
||||||
|
requestInfo, appExecutor, new CombiningMetadataApplier(delegate, headers));
|
||||||
|
} finally {
|
||||||
|
context.detach(previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fail(Status status) {
|
||||||
|
delegate.fail(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CombiningMetadataApplier extends MetadataApplier {
|
||||||
|
private final MetadataApplier delegate;
|
||||||
|
private final Metadata firstHeaders;
|
||||||
|
|
||||||
|
public CombiningMetadataApplier(MetadataApplier delegate, Metadata firstHeaders) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.firstHeaders = firstHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Metadata headers) {
|
||||||
|
Preconditions.checkNotNull(headers, "headers");
|
||||||
|
Metadata combined = new Metadata();
|
||||||
|
combined.merge(firstHeaders);
|
||||||
|
combined.merge(headers);
|
||||||
|
delegate.apply(combined);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fail(Status status) {
|
||||||
|
delegate.fail(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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.Preconditions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code ChannelCredentials} which use per-RPC {@link CallCredentials}. If the {@code
|
||||||
|
* ChannelCredentials} has multiple {@code CallCredentials} (e.g., a composite credential inside a
|
||||||
|
* composite credential), then all of the {@code CallCredentials} should be used; one {@code
|
||||||
|
* CallCredentials} does not override another.
|
||||||
|
*/
|
||||||
|
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
|
||||||
|
public final class CompositeChannelCredentials extends ChannelCredentials {
|
||||||
|
public static ChannelCredentials create(
|
||||||
|
ChannelCredentials channelCreds, CallCredentials callCreds) {
|
||||||
|
return new CompositeChannelCredentials(channelCreds, callCreds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ChannelCredentials channelCredentials;
|
||||||
|
private final CallCredentials callCredentials;
|
||||||
|
|
||||||
|
private CompositeChannelCredentials(ChannelCredentials channelCreds, CallCredentials callCreds) {
|
||||||
|
this.channelCredentials = Preconditions.checkNotNull(channelCreds, "channelCreds");
|
||||||
|
this.callCredentials = Preconditions.checkNotNull(callCreds, "callCreds");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelCredentials getChannelCredentials() {
|
||||||
|
return channelCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CallCredentials getCallCredentials() {
|
||||||
|
return callCredentials;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,8 @@ import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import javax.net.ssl.SSLSession;
|
import javax.net.ssl.SSLSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -62,4 +64,64 @@ public final class Grpc {
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface TransportAttr {}
|
public @interface TransportAttr {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a channel builder with a target string and credentials. The target can be either a
|
||||||
|
* valid {@link NameResolver}-compliant URI, or an authority string.
|
||||||
|
*
|
||||||
|
* <p>A {@code NameResolver}-compliant URI is an absolute hierarchical URI as defined by {@link
|
||||||
|
* java.net.URI}. Example URIs:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code "dns:///foo.googleapis.com:8080"}</li>
|
||||||
|
* <li>{@code "dns:///foo.googleapis.com"}</li>
|
||||||
|
* <li>{@code "dns:///%5B2001:db8:85a3:8d3:1319:8a2e:370:7348%5D:443"}</li>
|
||||||
|
* <li>{@code "dns://8.8.8.8/foo.googleapis.com:8080"}</li>
|
||||||
|
* <li>{@code "dns://8.8.8.8/foo.googleapis.com"}</li>
|
||||||
|
* <li>{@code "zookeeper://zk.example.com:9900/example_service"}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>An authority string will be converted to a {@code NameResolver}-compliant URI, which has
|
||||||
|
* the scheme from the name resolver with the highest priority (e.g. {@code "dns"}),
|
||||||
|
* no authority, and the original authority string as its path after properly escaped.
|
||||||
|
* We recommend libraries to specify the schema explicitly if it is known, since libraries cannot
|
||||||
|
* know which NameResolver will be default during runtime.
|
||||||
|
* Example authority strings:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code "localhost"}</li>
|
||||||
|
* <li>{@code "127.0.0.1"}</li>
|
||||||
|
* <li>{@code "localhost:8080"}</li>
|
||||||
|
* <li>{@code "foo.googleapis.com:8080"}</li>
|
||||||
|
* <li>{@code "127.0.0.1:8080"}</li>
|
||||||
|
* <li>{@code "[2001:db8:85a3:8d3:1319:8a2e:370:7348]"}</li>
|
||||||
|
* <li>{@code "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443"}</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
|
||||||
|
public static ManagedChannelBuilder<?> newChannelBuilder(
|
||||||
|
String target, ChannelCredentials creds) {
|
||||||
|
return ManagedChannelRegistry.getDefaultRegistry().newChannelBuilder(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,
|
||||||
|
* ChannelCredentials)}. IPv6 addresses are properly surrounded by square brackets ("[]").
|
||||||
|
*/
|
||||||
|
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
|
||||||
|
public static ManagedChannelBuilder<?> newChannelBuilderForAddress(
|
||||||
|
String host, int port, ChannelCredentials creds) {
|
||||||
|
return newChannelBuilder(authorityFromHostAndPort(host, port), creds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combine a host and port into an authority string.
|
||||||
|
*/
|
||||||
|
// A copy of GrpcUtil.authorityFromHostAndPort
|
||||||
|
private static String authorityFromHostAndPort(String host, int port) {
|
||||||
|
try {
|
||||||
|
return new URI(null, null, host, port, null, null, null).getAuthority();
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
throw new IllegalArgumentException("Invalid host or port: " + host + " " + port, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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;
|
||||||
|
|
||||||
|
/** No client identity, authentication, or encryption is to be used. */
|
||||||
|
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
|
||||||
|
public final class InsecureChannelCredentials extends ChannelCredentials {
|
||||||
|
public static ChannelCredentials create() {
|
||||||
|
return new InsecureChannelCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
|
private InsecureChannelCredentials() {}
|
||||||
|
}
|
||||||
|
|
@ -182,6 +182,7 @@ public abstract class ManagedChannelBuilder<T extends ManagedChannelBuilder<T>>
|
||||||
* not perform HTTP/1.1 upgrades.
|
* not perform HTTP/1.1 upgrades.
|
||||||
*
|
*
|
||||||
* @return this
|
* @return this
|
||||||
|
* @throws IllegalStateException if ChannelCredentials were provided when constructing the builder
|
||||||
* @throws UnsupportedOperationException if plaintext mode is not supported.
|
* @throws UnsupportedOperationException if plaintext mode is not supported.
|
||||||
* @since 1.11.0
|
* @since 1.11.0
|
||||||
*/
|
*/
|
||||||
|
|
@ -193,6 +194,7 @@ public abstract class ManagedChannelBuilder<T extends ManagedChannelBuilder<T>>
|
||||||
* Makes the client use TLS.
|
* Makes the client use TLS.
|
||||||
*
|
*
|
||||||
* @return this
|
* @return this
|
||||||
|
* @throws IllegalStateException if ChannelCredentials were provided when constructing the builder
|
||||||
* @throws UnsupportedOperationException if transport security is not supported.
|
* @throws UnsupportedOperationException if transport security is not supported.
|
||||||
* @since 1.9.0
|
* @since 1.9.0
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,7 @@
|
||||||
|
|
||||||
package io.grpc;
|
package io.grpc;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.base.Preconditions;
|
||||||
import io.grpc.ServiceProviders.PriorityAccessor;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provider of managed channels for transport agnostic consumption.
|
* Provider of managed channels for transport agnostic consumption.
|
||||||
|
|
@ -36,31 +32,13 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
@Internal
|
@Internal
|
||||||
public abstract class ManagedChannelProvider {
|
public abstract class ManagedChannelProvider {
|
||||||
@VisibleForTesting
|
|
||||||
static final Iterable<Class<?>> HARDCODED_CLASSES = new HardcodedClasses();
|
|
||||||
|
|
||||||
private static final ManagedChannelProvider provider = ServiceProviders.load(
|
|
||||||
ManagedChannelProvider.class,
|
|
||||||
HARDCODED_CLASSES,
|
|
||||||
ManagedChannelProvider.class.getClassLoader(),
|
|
||||||
new PriorityAccessor<ManagedChannelProvider>() {
|
|
||||||
@Override
|
|
||||||
public boolean isAvailable(ManagedChannelProvider provider) {
|
|
||||||
return provider.isAvailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPriority(ManagedChannelProvider provider) {
|
|
||||||
return provider.priority();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ClassLoader-wide default channel.
|
* Returns the ClassLoader-wide default channel.
|
||||||
*
|
*
|
||||||
* @throws ProviderNotFoundException if no provider is available
|
* @throws ProviderNotFoundException if no provider is available
|
||||||
*/
|
*/
|
||||||
public static ManagedChannelProvider provider() {
|
public static ManagedChannelProvider provider() {
|
||||||
|
ManagedChannelProvider provider = ManagedChannelRegistry.getDefaultRegistry().provider();
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
throw new ProviderNotFoundException("No functional channel service provider found. "
|
throw new ProviderNotFoundException("No functional channel service provider found. "
|
||||||
+ "Try adding a dependency on the grpc-okhttp, grpc-netty, or grpc-netty-shaded "
|
+ "Try adding a dependency on the grpc-okhttp, grpc-netty, or grpc-netty-shaded "
|
||||||
|
|
@ -93,6 +71,40 @@ public abstract class ManagedChannelProvider {
|
||||||
*/
|
*/
|
||||||
protected abstract ManagedChannelBuilder<?> builderForTarget(String target);
|
protected abstract ManagedChannelBuilder<?> builderForTarget(String target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new builder with the given target URI and credentials. Returns an error-string result
|
||||||
|
* if unable to understand the credentials.
|
||||||
|
*/
|
||||||
|
protected NewChannelBuilderResult newChannelBuilder(String target, ChannelCredentials creds) {
|
||||||
|
return NewChannelBuilderResult.error("ChannelCredentials are unsupported");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class NewChannelBuilderResult {
|
||||||
|
private final ManagedChannelBuilder<?> channelBuilder;
|
||||||
|
private final String error;
|
||||||
|
|
||||||
|
private NewChannelBuilderResult(ManagedChannelBuilder<?> channelBuilder, String error) {
|
||||||
|
this.channelBuilder = channelBuilder;
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NewChannelBuilderResult channelBuilder(ManagedChannelBuilder<?> builder) {
|
||||||
|
return new NewChannelBuilderResult(Preconditions.checkNotNull(builder), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NewChannelBuilderResult error(String error) {
|
||||||
|
return new NewChannelBuilderResult(null, Preconditions.checkNotNull(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManagedChannelBuilder<?> getChannelBuilder() {
|
||||||
|
return channelBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when no suitable {@link ManagedChannelProvider} objects can be found.
|
* Thrown when no suitable {@link ManagedChannelProvider} objects can be found.
|
||||||
*/
|
*/
|
||||||
|
|
@ -103,22 +115,4 @@ public abstract class ManagedChannelProvider {
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class HardcodedClasses implements Iterable<Class<?>> {
|
|
||||||
@Override
|
|
||||||
public Iterator<Class<?>> iterator() {
|
|
||||||
List<Class<?>> list = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
list.add(Class.forName("io.grpc.okhttp.OkHttpChannelProvider"));
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
list.add(Class.forName("io.grpc.netty.NettyChannelProvider"));
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return list.iterator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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.Preconditions;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry of {@link ManagedChannelProvider}s. The {@link #getDefaultRegistry default instance}
|
||||||
|
* loads providers at runtime through the Java service provider mechanism.
|
||||||
|
*
|
||||||
|
* @since 1.32.0
|
||||||
|
*/
|
||||||
|
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
|
||||||
|
@ThreadSafe
|
||||||
|
public final class ManagedChannelRegistry {
|
||||||
|
private static final Logger logger = Logger.getLogger(ManagedChannelRegistry.class.getName());
|
||||||
|
private static ManagedChannelRegistry instance;
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
private final LinkedHashSet<ManagedChannelProvider> allProviders = new LinkedHashSet<>();
|
||||||
|
/** Immutable, sorted version of {@code allProviders}. Is replaced instead of mutating. */
|
||||||
|
@GuardedBy("this")
|
||||||
|
private List<ManagedChannelProvider> effectiveProviders = Collections.emptyList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a provider.
|
||||||
|
*
|
||||||
|
* <p>If the provider's {@link ManagedChannelProvider#isAvailable isAvailable()} returns
|
||||||
|
* {@code false}, this method will throw {@link IllegalArgumentException}.
|
||||||
|
*
|
||||||
|
* <p>Providers will be used in priority order. In case of ties, providers are used in
|
||||||
|
* registration order.
|
||||||
|
*/
|
||||||
|
public synchronized void register(ManagedChannelProvider provider) {
|
||||||
|
addProvider(provider);
|
||||||
|
refreshProviders();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void addProvider(ManagedChannelProvider provider) {
|
||||||
|
Preconditions.checkArgument(provider.isAvailable(), "isAvailable() returned false");
|
||||||
|
allProviders.add(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deregisters a provider. No-op if the provider is not in the registry.
|
||||||
|
*
|
||||||
|
* @param provider the provider that was added to the register via {@link #register}.
|
||||||
|
*/
|
||||||
|
public synchronized void deregister(ManagedChannelProvider provider) {
|
||||||
|
allProviders.remove(provider);
|
||||||
|
refreshProviders();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void refreshProviders() {
|
||||||
|
List<ManagedChannelProvider> providers = new ArrayList<>(allProviders);
|
||||||
|
// Sort descending based on priority.
|
||||||
|
// sort() must be stable, as we prefer first-registered providers
|
||||||
|
Collections.sort(providers, Collections.reverseOrder(new Comparator<ManagedChannelProvider>() {
|
||||||
|
@Override
|
||||||
|
public int compare(ManagedChannelProvider o1, ManagedChannelProvider o2) {
|
||||||
|
return o1.priority() - o2.priority();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
effectiveProviders = Collections.unmodifiableList(providers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default registry that loads providers via the Java service loader mechanism.
|
||||||
|
*/
|
||||||
|
public static synchronized ManagedChannelRegistry getDefaultRegistry() {
|
||||||
|
if (instance == null) {
|
||||||
|
List<ManagedChannelProvider> providerList = ServiceProviders.loadAll(
|
||||||
|
ManagedChannelProvider.class,
|
||||||
|
getHardCodedClasses(),
|
||||||
|
ManagedChannelProvider.class.getClassLoader(),
|
||||||
|
new ManagedChannelPriorityAccessor());
|
||||||
|
instance = new ManagedChannelRegistry();
|
||||||
|
for (ManagedChannelProvider provider : providerList) {
|
||||||
|
logger.fine("Service loader found " + provider);
|
||||||
|
if (provider.isAvailable()) {
|
||||||
|
instance.addProvider(provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instance.refreshProviders();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns effective providers, in priority order.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
synchronized List<ManagedChannelProvider> providers() {
|
||||||
|
return effectiveProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For emulating ManagedChannelProvider.provider()
|
||||||
|
ManagedChannelProvider provider() {
|
||||||
|
List<ManagedChannelProvider> providers = providers();
|
||||||
|
return providers.isEmpty() ? null : providers.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static List<Class<?>> getHardCodedClasses() {
|
||||||
|
// Class.forName(String) is used to remove the need for ProGuard configuration. Note that
|
||||||
|
// ProGuard does not detect usages of Class.forName(String, boolean, ClassLoader):
|
||||||
|
// https://sourceforge.net/p/proguard/bugs/418/
|
||||||
|
List<Class<?>> list = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
list.add(Class.forName("io.grpc.okhttp.OkHttpChannelProvider"));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
logger.log(Level.FINE, "Unable to find OkHttpChannelProvider", e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
list.add(Class.forName("io.grpc.netty.NettyChannelProvider"));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
logger.log(Level.FINE, "Unable to find NettyChannelProvider", e);
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
ManagedChannelBuilder<?> newChannelBuilder(String target, ChannelCredentials creds) {
|
||||||
|
List<ManagedChannelProvider> providers = providers();
|
||||||
|
if (providers.isEmpty()) {
|
||||||
|
throw new ProviderNotFoundException("No functional channel service provider found. "
|
||||||
|
+ "Try adding a dependency on the grpc-okhttp, grpc-netty, or grpc-netty-shaded "
|
||||||
|
+ "artifact");
|
||||||
|
}
|
||||||
|
StringBuilder error = new StringBuilder();
|
||||||
|
for (ManagedChannelProvider provider : providers()) {
|
||||||
|
ManagedChannelProvider.NewChannelBuilderResult result
|
||||||
|
= provider.newChannelBuilder(target, creds);
|
||||||
|
if (result.getChannelBuilder() != null) {
|
||||||
|
return result.getChannelBuilder();
|
||||||
|
}
|
||||||
|
error.append("; ");
|
||||||
|
error.append(provider.getClass().getName());
|
||||||
|
error.append(": ");
|
||||||
|
error.append(result.getError());
|
||||||
|
}
|
||||||
|
throw new ProviderNotFoundException(error.substring(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ManagedChannelPriorityAccessor
|
||||||
|
implements ServiceProviders.PriorityAccessor<ManagedChannelProvider> {
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable(ManagedChannelProvider provider) {
|
||||||
|
return provider.isAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority(ManagedChannelProvider provider) {
|
||||||
|
return provider.priority();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Thrown when no suitable {@link ManagedChannelProvider} objects can be found. */
|
||||||
|
public static final class ProviderNotFoundException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 1;
|
||||||
|
|
||||||
|
public ProviderNotFoundException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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 java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TLS credentials, providing server authentication and encryption. Consumers of this credential
|
||||||
|
* must verify they understand the configuration via the {@link #incomprehensible
|
||||||
|
* incomprehensible()} method. Unless overridden by a {@code Feature}, server verification should
|
||||||
|
* use customary default root certificates.
|
||||||
|
*/
|
||||||
|
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
|
||||||
|
public final class TlsChannelCredentials extends ChannelCredentials {
|
||||||
|
/** Use TLS with its defaults. */
|
||||||
|
public static ChannelCredentials create() {
|
||||||
|
return newBuilder().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final boolean fakeFeature;
|
||||||
|
|
||||||
|
TlsChannelCredentials(Builder builder) {
|
||||||
|
fakeFeature = builder.fakeFeature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an empty set if this credential can be adequately understood via
|
||||||
|
* the features listed, otherwise returns a hint of features that are lacking
|
||||||
|
* to understand the configuration to be used for manual debugging.
|
||||||
|
*
|
||||||
|
* <p>An "understood" feature does not imply the caller is able to fully
|
||||||
|
* handle the feature. It simply means the caller understands the feature
|
||||||
|
* enough to use the appropriate APIs to read the configuration. The caller
|
||||||
|
* may support just a subset of a feature, in which case the caller would
|
||||||
|
* need to look at the configuration to determine if only the supported
|
||||||
|
* subset is used.
|
||||||
|
*
|
||||||
|
* <p>This method may not be as simple as a set difference. There may be
|
||||||
|
* multiple features that can independently satisfy a piece of configuration.
|
||||||
|
* If the configuration is incomprehensible, all such features would be
|
||||||
|
* returned, even though only one may be necessary.
|
||||||
|
*
|
||||||
|
* <p>An empty set does not imply that the credentials are fully understood.
|
||||||
|
* There may be optional configuration that can be ignored if not understood.
|
||||||
|
*
|
||||||
|
* <p>Since {@code Feature} is an {@code enum}, {@code understoodFeatures}
|
||||||
|
* should generally be an {@link java.util.EnumSet}. {@code
|
||||||
|
* understoodFeatures} will not be modified.
|
||||||
|
*
|
||||||
|
* @param understoodFeatures the features understood by the caller
|
||||||
|
* @return empty set if the caller can adequately understand the configuration
|
||||||
|
*/
|
||||||
|
public Set<Feature> incomprehensible(Set<Feature> understoodFeatures) {
|
||||||
|
Set<Feature> incomprehensible = EnumSet.noneOf(Feature.class);
|
||||||
|
if (fakeFeature) {
|
||||||
|
requiredFeature(understoodFeatures, incomprehensible, Feature.FAKE);
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableSet(incomprehensible);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void requiredFeature(
|
||||||
|
Set<Feature> understoodFeatures, Set<Feature> incomprehensible, Feature feature) {
|
||||||
|
if (!understoodFeatures.contains(feature)) {
|
||||||
|
incomprehensible.add(feature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Features to understand TLS configuration. Additional enum values may be added in the future.
|
||||||
|
*/
|
||||||
|
public enum Feature {
|
||||||
|
/**
|
||||||
|
* A feature that no consumer should understand. It should be used for unit testing to confirm
|
||||||
|
* a call to {@link #incomprehensible incomprehensible()} is implemented properly.
|
||||||
|
*/
|
||||||
|
FAKE,
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a builder for changing default configuration. */
|
||||||
|
public static Builder newBuilder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Builder for {@link TlsChannelCredentials}. */
|
||||||
|
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
|
||||||
|
public static final class Builder {
|
||||||
|
private boolean fakeFeature;
|
||||||
|
|
||||||
|
private Builder() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires {@link Feature#FAKE} to be understood. For use in testing consumers of this
|
||||||
|
* credential.
|
||||||
|
*/
|
||||||
|
public Builder requireFakeFeature() {
|
||||||
|
fakeFeature = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Construct the credentials. */
|
||||||
|
public ChannelCredentials build() {
|
||||||
|
return new TlsChannelCredentials(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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 static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public final class CompositeCallCredentialsTest {
|
||||||
|
private static final Metadata.Key<String> METADATA_KEY =
|
||||||
|
Metadata.Key.of("key", Metadata.ASCII_STRING_MARSHALLER);
|
||||||
|
|
||||||
|
private CompositeCallCredentials callCredentials;
|
||||||
|
private CallCredentials.RequestInfo requestInfo;
|
||||||
|
private Executor appExecutor;
|
||||||
|
private FakeMetadataApplier applier = new FakeMetadataApplier();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void applyRequestMetadata_firstFails() {
|
||||||
|
Status failure = Status.UNAVAILABLE.withDescription("expected");
|
||||||
|
callCredentials = new CompositeCallCredentials(
|
||||||
|
new FakeCallCredentials(failure),
|
||||||
|
new FakeCallCredentials(createMetadata(METADATA_KEY, "value2")));
|
||||||
|
callCredentials.applyRequestMetadata(requestInfo, appExecutor, applier);
|
||||||
|
assertThat(applier.status).isSameInstanceAs(failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void applyRequestMetadata_secondFails() {
|
||||||
|
Status failure = Status.UNAVAILABLE.withDescription("expected");
|
||||||
|
callCredentials = new CompositeCallCredentials(
|
||||||
|
new FakeCallCredentials(createMetadata(METADATA_KEY, "value1")),
|
||||||
|
new FakeCallCredentials(failure));
|
||||||
|
callCredentials.applyRequestMetadata(requestInfo, appExecutor, applier);
|
||||||
|
assertThat(applier.status).isSameInstanceAs(failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void applyRequestMetadata_bothSucceed() {
|
||||||
|
callCredentials = new CompositeCallCredentials(
|
||||||
|
new FakeCallCredentials(createMetadata(METADATA_KEY, "value1")),
|
||||||
|
new FakeCallCredentials(createMetadata(METADATA_KEY, "value2")));
|
||||||
|
callCredentials.applyRequestMetadata(requestInfo, appExecutor, applier);
|
||||||
|
assertThat(applier.headers).isNotNull();
|
||||||
|
assertThat(applier.headers.getAll(METADATA_KEY)).containsExactly("value1", "value2");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Metadata createMetadata(Metadata.Key<String> key, String value) {
|
||||||
|
Metadata metadata = new Metadata();
|
||||||
|
metadata.put(key, value);
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FakeMetadataApplier extends CallCredentials.MetadataApplier {
|
||||||
|
private Metadata headers;
|
||||||
|
private Status status;
|
||||||
|
|
||||||
|
@Override public void apply(Metadata headers) {
|
||||||
|
assertThat(this.headers).isNull();
|
||||||
|
assertThat(this.status).isNull();
|
||||||
|
this.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void fail(Status status) {
|
||||||
|
assertThat(this.headers).isNull();
|
||||||
|
assertThat(this.status).isNull();
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FakeCallCredentials extends CallCredentials {
|
||||||
|
private final Metadata headers;
|
||||||
|
private final Status status;
|
||||||
|
|
||||||
|
public FakeCallCredentials(Metadata headers) {
|
||||||
|
this.headers = headers;
|
||||||
|
this.status = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FakeCallCredentials(Status status) {
|
||||||
|
this.headers = null;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void applyRequestMetadata(
|
||||||
|
CallCredentials.RequestInfo requestInfo, Executor appExecutor,
|
||||||
|
CallCredentials.MetadataApplier applier) {
|
||||||
|
if (headers != null) {
|
||||||
|
applier.apply(headers);
|
||||||
|
} else {
|
||||||
|
applier.fail(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void thisUsesUnstableApi() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2015 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.collect.ImmutableSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
/** Unit tests for {@link ManagedChannelProvider}. */
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class ManagedChannelProviderTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getCandidatesViaHardCoded_triesToLoadClasses() throws Exception {
|
|
||||||
ServiceProvidersTestUtil.testHardcodedClasses(
|
|
||||||
HardcodedClassesCallable.class.getName(),
|
|
||||||
getClass().getClassLoader(),
|
|
||||||
ImmutableSet.of(
|
|
||||||
"io.grpc.okhttp.OkHttpChannelProvider",
|
|
||||||
"io.grpc.netty.NettyChannelProvider"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class HardcodedClassesCallable implements Callable<Iterator<Class<?>>> {
|
|
||||||
@Override
|
|
||||||
public Iterator<Class<?>> call() {
|
|
||||||
return ManagedChannelProvider.HARDCODED_CLASSES.iterator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2018 The gRPC Authors
|
* Copyright 2020 The gRPC Authors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,7 +16,11 @@
|
||||||
|
|
||||||
package io.grpc;
|
package io.grpc;
|
||||||
|
|
||||||
public final class InternalManagedChannelProvider {
|
/** Accesses test-only methods of {@link ManagedChannelRegistry}. */
|
||||||
public static final Iterable<Class<?>> HARDCODED_CLASSES =
|
public final class ManagedChannelRegistryAccessor {
|
||||||
ManagedChannelProvider.HARDCODED_CLASSES;
|
private ManagedChannelRegistryAccessor() {}
|
||||||
|
|
||||||
|
public static Iterable<Class<?>> getHardCodedClasses() {
|
||||||
|
return ManagedChannelRegistry.getHardCodedClasses();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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 static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
/** Unit tests for {@link ManagedChannelRegistry}. */
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class ManagedChannelRegistryTest {
|
||||||
|
private String target = "testing123";
|
||||||
|
private ChannelCredentials creds = new ChannelCredentials() {};
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void register_unavilableProviderThrows() {
|
||||||
|
ManagedChannelRegistry reg = new ManagedChannelRegistry();
|
||||||
|
try {
|
||||||
|
reg.register(new BaseProvider(false, 5));
|
||||||
|
fail("Should throw");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
assertThat(e).hasMessageThat().contains("isAvailable() returned false");
|
||||||
|
}
|
||||||
|
assertThat(reg.providers()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deregister() {
|
||||||
|
ManagedChannelRegistry reg = new ManagedChannelRegistry();
|
||||||
|
ManagedChannelProvider p1 = new BaseProvider(true, 5);
|
||||||
|
ManagedChannelProvider p2 = new BaseProvider(true, 5);
|
||||||
|
ManagedChannelProvider p3 = new BaseProvider(true, 5);
|
||||||
|
reg.register(p1);
|
||||||
|
reg.register(p2);
|
||||||
|
reg.register(p3);
|
||||||
|
assertThat(reg.providers()).containsExactly(p1, p2, p3).inOrder();
|
||||||
|
reg.deregister(p2);
|
||||||
|
assertThat(reg.providers()).containsExactly(p1, p3).inOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void provider_sorted() {
|
||||||
|
ManagedChannelRegistry reg = new ManagedChannelRegistry();
|
||||||
|
ManagedChannelProvider p1 = new BaseProvider(true, 5);
|
||||||
|
ManagedChannelProvider p2 = new BaseProvider(true, 3);
|
||||||
|
ManagedChannelProvider p3 = new BaseProvider(true, 8);
|
||||||
|
ManagedChannelProvider p4 = new BaseProvider(true, 3);
|
||||||
|
ManagedChannelProvider p5 = new BaseProvider(true, 8);
|
||||||
|
reg.register(p1);
|
||||||
|
reg.register(p2);
|
||||||
|
reg.register(p3);
|
||||||
|
reg.register(p4);
|
||||||
|
reg.register(p5);
|
||||||
|
assertThat(reg.providers()).containsExactly(p3, p5, p1, p2, p4).inOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getProvider_noProvider() {
|
||||||
|
assertThat(new ManagedChannelRegistry().provider()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void newChannelBuilder_providerReturnsError() {
|
||||||
|
final String errorString = "brisking";
|
||||||
|
class ErrorProvider extends BaseProvider {
|
||||||
|
ErrorProvider() {
|
||||||
|
super(true, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NewChannelBuilderResult newChannelBuilder(
|
||||||
|
String passedTarget, ChannelCredentials passedCreds) {
|
||||||
|
assertThat(passedTarget).isSameInstanceAs(target);
|
||||||
|
assertThat(passedCreds).isSameInstanceAs(creds);
|
||||||
|
return NewChannelBuilderResult.error(errorString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ManagedChannelRegistry registry = new ManagedChannelRegistry();
|
||||||
|
registry.register(new ErrorProvider());
|
||||||
|
try {
|
||||||
|
registry.newChannelBuilder(target, creds);
|
||||||
|
fail("expected exception");
|
||||||
|
} catch (ManagedChannelRegistry.ProviderNotFoundException ex) {
|
||||||
|
assertThat(ex).hasMessageThat().contains(errorString);
|
||||||
|
assertThat(ex).hasMessageThat().contains(ErrorProvider.class.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void newChannelBuilder_providerReturnsNonNull() {
|
||||||
|
ManagedChannelRegistry registry = new ManagedChannelRegistry();
|
||||||
|
registry.register(new BaseProvider(true, 5) {
|
||||||
|
@Override
|
||||||
|
public NewChannelBuilderResult newChannelBuilder(
|
||||||
|
String passedTarget, ChannelCredentials passedCreds) {
|
||||||
|
return NewChannelBuilderResult.error("dodging");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
class MockChannelBuilder extends ForwardingChannelBuilder<MockChannelBuilder> {
|
||||||
|
@Override public ManagedChannelBuilder<?> delegate() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final ManagedChannelBuilder<?> mcb = new MockChannelBuilder();
|
||||||
|
registry.register(new BaseProvider(true, 4) {
|
||||||
|
@Override
|
||||||
|
public NewChannelBuilderResult newChannelBuilder(
|
||||||
|
String passedTarget, ChannelCredentials passedCreds) {
|
||||||
|
return NewChannelBuilderResult.channelBuilder(mcb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
registry.register(new BaseProvider(true, 3) {
|
||||||
|
@Override
|
||||||
|
public NewChannelBuilderResult newChannelBuilder(
|
||||||
|
String passedTarget, ChannelCredentials passedCreds) {
|
||||||
|
fail("Should not be called");
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertThat(registry.newChannelBuilder(target, creds)).isSameInstanceAs(mcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void newChannelBuilder_noProvider() {
|
||||||
|
ManagedChannelRegistry registry = new ManagedChannelRegistry();
|
||||||
|
try {
|
||||||
|
registry.newChannelBuilder(target, creds);
|
||||||
|
fail("expected exception");
|
||||||
|
} catch (ManagedChannelRegistry.ProviderNotFoundException ex) {
|
||||||
|
assertThat(ex).hasMessageThat().contains("No functional channel service provider found");
|
||||||
|
assertThat(ex).hasMessageThat().contains("grpc-netty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BaseProvider extends ManagedChannelProvider {
|
||||||
|
private final boolean isAvailable;
|
||||||
|
private final int priority;
|
||||||
|
|
||||||
|
public BaseProvider(boolean isAvailable, int priority) {
|
||||||
|
this.isAvailable = isAvailable;
|
||||||
|
this.priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isAvailable() {
|
||||||
|
return isAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int priority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ManagedChannelBuilder<?> builderForAddress(String name, int port) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ManagedChannelBuilder<?> builderForTarget(String target) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -508,6 +508,7 @@ public final class GrpcUtil {
|
||||||
/**
|
/**
|
||||||
* Combine a host and port into an authority string.
|
* Combine a host and port into an authority string.
|
||||||
*/
|
*/
|
||||||
|
// There is a copy of this method in io.grpc.Grpc
|
||||||
public static String authorityFromHostAndPort(String host, int port) {
|
public static String authorityFromHostAndPort(String host, int port) {
|
||||||
try {
|
try {
|
||||||
return new URI(null, null, host, port, null, null, null).getAuthority();
|
return new URI(null, null, host, port, null, null, null).getAuthority();
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ dependencies {
|
||||||
|
|
||||||
// Tests depend on base class defined by core module.
|
// Tests depend on base class defined by core module.
|
||||||
testImplementation project(':grpc-core').sourceSets.test.output,
|
testImplementation project(':grpc-core').sourceSets.test.output,
|
||||||
|
project(':grpc-api').sourceSets.test.output,
|
||||||
project(':grpc-testing'),
|
project(':grpc-testing'),
|
||||||
project(':grpc-testing-proto')
|
project(':grpc-testing-proto')
|
||||||
testRuntimeOnly libraries.netty_tcnative,
|
testRuntimeOnly libraries.netty_tcnative,
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import io.grpc.InternalManagedChannelProvider;
|
|
||||||
import io.grpc.InternalServiceProviders;
|
import io.grpc.InternalServiceProviders;
|
||||||
import io.grpc.ManagedChannelProvider;
|
import io.grpc.ManagedChannelProvider;
|
||||||
|
import io.grpc.ManagedChannelRegistryAccessor;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
@ -47,9 +47,8 @@ public class NettyChannelProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void providedHardCoded() {
|
public void providedHardCoded() {
|
||||||
for (ManagedChannelProvider current : InternalServiceProviders.getCandidatesViaHardCoded(
|
for (Class<?> current : ManagedChannelRegistryAccessor.getHardCodedClasses()) {
|
||||||
ManagedChannelProvider.class, InternalManagedChannelProvider.HARDCODED_CLASSES)) {
|
if (current == NettyChannelProvider.class) {
|
||||||
if (current instanceof NettyChannelProvider) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ dependencies {
|
||||||
perfmarkDependency 'implementation'
|
perfmarkDependency 'implementation'
|
||||||
// Tests depend on base class defined by core module.
|
// Tests depend on base class defined by core module.
|
||||||
testImplementation project(':grpc-core').sourceSets.test.output,
|
testImplementation project(':grpc-core').sourceSets.test.output,
|
||||||
|
project(':grpc-api').sourceSets.test.output,
|
||||||
project(':grpc-testing'),
|
project(':grpc-testing'),
|
||||||
project(':grpc-netty')
|
project(':grpc-netty')
|
||||||
signature "org.codehaus.mojo.signature:java17:1.0@signature"
|
signature "org.codehaus.mojo.signature:java17:1.0@signature"
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import io.grpc.InternalManagedChannelProvider;
|
|
||||||
import io.grpc.InternalServiceProviders;
|
import io.grpc.InternalServiceProviders;
|
||||||
import io.grpc.ManagedChannelProvider;
|
import io.grpc.ManagedChannelProvider;
|
||||||
|
import io.grpc.ManagedChannelRegistryAccessor;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
@ -46,9 +46,8 @@ public class OkHttpChannelProviderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void providedHardCoded() {
|
public void providedHardCoded() {
|
||||||
for (ManagedChannelProvider current : InternalServiceProviders.getCandidatesViaHardCoded(
|
for (Class<?> current : ManagedChannelRegistryAccessor.getHardCodedClasses()) {
|
||||||
ManagedChannelProvider.class, InternalManagedChannelProvider.HARDCODED_CLASSES)) {
|
if (current == OkHttpChannelProvider.class) {
|
||||||
if (current instanceof OkHttpChannelProvider) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue