Skip to content

Commit c8a94d1

Browse files
committed
api: Add ChannelCredentials
1 parent 9b73e23 commit c8a94d1

19 files changed

+998
-100
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2020 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc;
18+
19+
/**
20+
* Represents a security configuration to be used for channels. There is no generic mechanism for
21+
* processing arbitrary {@code ChannelCredentials}; the consumer of the credential (the channel)
22+
* must support each implementation explicitly and separately. Consumers are not required to support
23+
* all types or even all possible configurations for types that are partially supported, but they
24+
* <em>must</em> at least fully support {@link ChoiceChannelCredentials}.
25+
*
26+
* <p>A {@code ChannelCredential} provides client identity and authenticates the server. This is
27+
* different from {@link CallCredentials}, which only provides client identity. They can also
28+
* influence types of encryption used and similar security configuration.
29+
*
30+
* <p>The concrete credential type should not be relevant to most users of the API and may be an
31+
* implementation decision. Users should generally use the {@code ChannelCredentials} type for
32+
* variables instead of the concrete type. Freshly-constructed credentials should be returned as
33+
* {@code ChannelCredentials} instead of a concrete type to encourage this pattern. Concrete types
34+
* would only be used after {@code instanceof} checks (which must consider
35+
* {@code ChoiceChannelCredentials}!).
36+
*/
37+
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
38+
public abstract class ChannelCredentials {}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2020 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc;
18+
19+
import java.util.ArrayList;
20+
import java.util.Arrays;
21+
import java.util.Collections;
22+
import java.util.List;
23+
24+
/**
25+
* Provides a list of {@link ChannelCredentials}, where any one may be used. The credentials are in
26+
* preference order.
27+
*/
28+
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
29+
public final class ChoiceChannelCredentials extends ChannelCredentials {
30+
/**
31+
* Constructs with the provided {@code creds} as options, with preferred credentials first.
32+
*
33+
* @throws IllegalArgumentException if no creds are provided
34+
*/
35+
public static ChannelCredentials create(ChannelCredentials... creds) {
36+
if (creds.length == 0) {
37+
throw new IllegalArgumentException("At least one credential is required");
38+
}
39+
return new ChoiceChannelCredentials(creds);
40+
}
41+
42+
private final List<ChannelCredentials> creds;
43+
44+
private ChoiceChannelCredentials(ChannelCredentials... creds) {
45+
for (ChannelCredentials cred : creds) {
46+
if (cred == null) {
47+
throw new NullPointerException();
48+
}
49+
}
50+
this.creds = Collections.unmodifiableList(new ArrayList<>(Arrays.asList(creds)));
51+
}
52+
53+
/** Non-empty list of credentials, in preference order. */
54+
public List<ChannelCredentials> getCredentialsList() {
55+
return creds;
56+
}
57+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2020 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc;
18+
19+
import com.google.common.base.Preconditions;
20+
import java.util.concurrent.Executor;
21+
22+
/**
23+
* Uses multiple {@code CallCredentials} as if they were one. If the first credential fails, the
24+
* second will not be used. Both must succeed to allow the RPC.
25+
*/
26+
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
27+
public final class CompositeCallCredentials extends CallCredentials {
28+
private final CallCredentials credentials1;
29+
private final CallCredentials credentials2;
30+
31+
public CompositeCallCredentials(CallCredentials creds1, CallCredentials creds2) {
32+
this.credentials1 = Preconditions.checkNotNull(creds1, "creds1");
33+
this.credentials2 = Preconditions.checkNotNull(creds2, "creds2");
34+
}
35+
36+
@Override
37+
public void applyRequestMetadata(
38+
RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier) {
39+
credentials1.applyRequestMetadata(requestInfo, appExecutor,
40+
new WrappingMetadataApplier(requestInfo, appExecutor, applier, Context.current()));
41+
}
42+
43+
@Override
44+
public void thisUsesUnstableApi() {}
45+
46+
private final class WrappingMetadataApplier extends MetadataApplier {
47+
private final RequestInfo requestInfo;
48+
private final Executor appExecutor;
49+
private final MetadataApplier delegate;
50+
private final Context context;
51+
52+
public WrappingMetadataApplier(
53+
RequestInfo requestInfo, Executor appExecutor, MetadataApplier delegate, Context context) {
54+
this.requestInfo = requestInfo;
55+
this.appExecutor = appExecutor;
56+
this.delegate = Preconditions.checkNotNull(delegate, "delegate");
57+
this.context = Preconditions.checkNotNull(context, "context");
58+
}
59+
60+
@Override
61+
public void apply(Metadata headers) {
62+
Preconditions.checkNotNull(headers, "headers");
63+
Context previous = context.attach();
64+
try {
65+
credentials2.applyRequestMetadata(
66+
requestInfo, appExecutor, new CombiningMetadataApplier(delegate, headers));
67+
} finally {
68+
context.detach(previous);
69+
}
70+
}
71+
72+
@Override
73+
public void fail(Status status) {
74+
delegate.fail(status);
75+
}
76+
}
77+
78+
private static final class CombiningMetadataApplier extends MetadataApplier {
79+
private final MetadataApplier delegate;
80+
private final Metadata firstHeaders;
81+
82+
public CombiningMetadataApplier(MetadataApplier delegate, Metadata firstHeaders) {
83+
this.delegate = delegate;
84+
this.firstHeaders = firstHeaders;
85+
}
86+
87+
@Override
88+
public void apply(Metadata headers) {
89+
Preconditions.checkNotNull(headers, "headers");
90+
Metadata combined = new Metadata();
91+
combined.merge(firstHeaders);
92+
combined.merge(headers);
93+
delegate.apply(combined);
94+
}
95+
96+
@Override
97+
public void fail(Status status) {
98+
delegate.fail(status);
99+
}
100+
}
101+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2020 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc;
18+
19+
import com.google.common.base.Preconditions;
20+
21+
/**
22+
* {@code ChannelCredentials} which use per-RPC {@link CallCredentials}. If the {@code
23+
* ChannelCredentials} has multiple {@code CallCredentials} (e.g., a composite credential inside a
24+
* composite credential), then all of the {@code CallCredentials} should be used; one {@code
25+
* CallCredentials} does not override another.
26+
*/
27+
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
28+
public final class CompositeChannelCredentials extends ChannelCredentials {
29+
public static ChannelCredentials create(
30+
ChannelCredentials channelCreds, CallCredentials callCreds) {
31+
return new CompositeChannelCredentials(channelCreds, callCreds);
32+
}
33+
34+
private final ChannelCredentials channelCredentials;
35+
private final CallCredentials callCredentials;
36+
37+
private CompositeChannelCredentials(ChannelCredentials channelCreds, CallCredentials callCreds) {
38+
this.channelCredentials = Preconditions.checkNotNull(channelCreds, "channelCreds");
39+
this.callCredentials = Preconditions.checkNotNull(callCreds, "callCreds");
40+
}
41+
42+
public ChannelCredentials getChannelCredentials() {
43+
return channelCredentials;
44+
}
45+
46+
public CallCredentials getCallCredentials() {
47+
return callCredentials;
48+
}
49+
}

api/src/main/java/io/grpc/Grpc.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import java.lang.annotation.Retention;
2121
import java.lang.annotation.RetentionPolicy;
2222
import java.net.SocketAddress;
23+
import java.net.URI;
24+
import java.net.URISyntaxException;
2325
import javax.net.ssl.SSLSession;
2426

2527
/**
@@ -62,4 +64,64 @@ private Grpc() {
6264
@Retention(RetentionPolicy.SOURCE)
6365
@Documented
6466
public @interface TransportAttr {}
67+
68+
/**
69+
* Creates a channel builder with a target string and credentials. The target can be either a
70+
* valid {@link NameResolver}-compliant URI, or an authority string.
71+
*
72+
* <p>A {@code NameResolver}-compliant URI is an absolute hierarchical URI as defined by {@link
73+
* java.net.URI}. Example URIs:
74+
* <ul>
75+
* <li>{@code "dns:///foo.googleapis.com:8080"}</li>
76+
* <li>{@code "dns:///foo.googleapis.com"}</li>
77+
* <li>{@code "dns:///%5B2001:db8:85a3:8d3:1319:8a2e:370:7348%5D:443"}</li>
78+
* <li>{@code "dns://8.8.8.8/foo.googleapis.com:8080"}</li>
79+
* <li>{@code "dns://8.8.8.8/foo.googleapis.com"}</li>
80+
* <li>{@code "zookeeper://zk.example.com:9900/example_service"}</li>
81+
* </ul>
82+
*
83+
* <p>An authority string will be converted to a {@code NameResolver}-compliant URI, which has
84+
* the scheme from the name resolver with the highest priority (e.g. {@code "dns"}),
85+
* no authority, and the original authority string as its path after properly escaped.
86+
* We recommend libraries to specify the schema explicitly if it is known, since libraries cannot
87+
* know which NameResolver will be default during runtime.
88+
* Example authority strings:
89+
* <ul>
90+
* <li>{@code "localhost"}</li>
91+
* <li>{@code "127.0.0.1"}</li>
92+
* <li>{@code "localhost:8080"}</li>
93+
* <li>{@code "foo.googleapis.com:8080"}</li>
94+
* <li>{@code "127.0.0.1:8080"}</li>
95+
* <li>{@code "[2001:db8:85a3:8d3:1319:8a2e:370:7348]"}</li>
96+
* <li>{@code "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443"}</li>
97+
* </ul>
98+
*/
99+
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
100+
public static ManagedChannelBuilder<?> newChannelBuilder(
101+
String target, ChannelCredentials creds) {
102+
return ManagedChannelRegistry.getDefaultRegistry().newChannelBuilder(target, creds);
103+
}
104+
105+
/**
106+
* Creates a channel builder from a host, port, and credentials. The host and port are combined to
107+
* form an authority string and then passed to {@link #newChannelBuilder(String,
108+
* ChannelCredentials)}. IPv6 addresses are properly surrounded by square brackets ("[]").
109+
*/
110+
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
111+
public static ManagedChannelBuilder<?> newChannelBuilderForAddress(
112+
String host, int port, ChannelCredentials creds) {
113+
return newChannelBuilder(authorityFromHostAndPort(host, port), creds);
114+
}
115+
116+
/**
117+
* Combine a host and port into an authority string.
118+
*/
119+
// A copy of GrpcUtil.authorityFromHostAndPort
120+
private static String authorityFromHostAndPort(String host, int port) {
121+
try {
122+
return new URI(null, null, host, port, null, null, null).getAuthority();
123+
} catch (URISyntaxException ex) {
124+
throw new IllegalArgumentException("Invalid host or port: " + host + " " + port, ex);
125+
}
126+
}
65127
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2020 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc;
18+
19+
/** No client identity, authentication, or encryption is to be used. */
20+
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/7479")
21+
public final class InsecureChannelCredentials extends ChannelCredentials {
22+
public static ChannelCredentials create() {
23+
return new InsecureChannelCredentials();
24+
}
25+
26+
private InsecureChannelCredentials() {}
27+
}

api/src/main/java/io/grpc/ManagedChannelBuilder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ public T offloadExecutor(Executor executor) {
182182
* not perform HTTP/1.1 upgrades.
183183
*
184184
* @return this
185+
* @throws IllegalStateException if ChannelCredentials were provided when constructing the builder
185186
* @throws UnsupportedOperationException if plaintext mode is not supported.
186187
* @since 1.11.0
187188
*/
@@ -193,6 +194,7 @@ public T usePlaintext() {
193194
* Makes the client use TLS.
194195
*
195196
* @return this
197+
* @throws IllegalStateException if ChannelCredentials were provided when constructing the builder
196198
* @throws UnsupportedOperationException if transport security is not supported.
197199
* @since 1.9.0
198200
*/

0 commit comments

Comments
 (0)