Skip to content

Commit 5654c64

Browse files
committed
Fixup: Address comments from #12492
1 parent 4baea6b commit 5654c64

28 files changed

+1688
-758
lines changed

xds/src/main/java/io/grpc/xds/GrpcBootstrapperImpl.java

Lines changed: 96 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,19 @@
1919
import com.google.common.annotations.VisibleForTesting;
2020
import com.google.common.collect.ImmutableMap;
2121
import com.google.errorprone.annotations.concurrent.GuardedBy;
22+
import io.grpc.CallCredentials;
2223
import io.grpc.ChannelCredentials;
2324
import io.grpc.internal.JsonUtil;
2425
import io.grpc.xds.client.BootstrapperImpl;
2526
import io.grpc.xds.client.XdsInitializationException;
2627
import io.grpc.xds.client.XdsLogger;
28+
import io.grpc.xds.internal.grpcservice.ChannelCredsConfig;
29+
import io.grpc.xds.internal.grpcservice.ConfiguredChannelCredentials;
30+
import io.grpc.xds.internal.grpcservice.GrpcServiceXdsContext;
2731
import java.io.IOException;
2832
import java.util.List;
2933
import java.util.Map;
34+
import java.util.Optional;
3035
import javax.annotation.Nullable;
3136

3237
class GrpcBootstrapperImpl extends BootstrapperImpl {
@@ -97,7 +102,8 @@ protected String getJsonContent() throws XdsInitializationException, IOException
97102
@Override
98103
protected Object getImplSpecificConfig(Map<String, ?> serverConfig, String serverUri)
99104
throws XdsInitializationException {
100-
return getChannelCredentials(serverConfig, serverUri);
105+
ConfiguredChannelCredentials configuredChannel = getChannelCredentials(serverConfig, serverUri);
106+
return configuredChannel != null ? configuredChannel.channelCredentials() : null;
101107
}
102108

103109
@GuardedBy("GrpcBootstrapperImpl.class")
@@ -120,26 +126,26 @@ static synchronized BootstrapInfo defaultBootstrap() throws XdsInitializationExc
120126
return defaultBootstrap;
121127
}
122128

123-
private static ChannelCredentials getChannelCredentials(Map<String, ?> serverConfig,
124-
String serverUri)
129+
private static ConfiguredChannelCredentials getChannelCredentials(Map<String, ?> serverConfig,
130+
String serverUri)
125131
throws XdsInitializationException {
126132
List<?> rawChannelCredsList = JsonUtil.getList(serverConfig, "channel_creds");
127133
if (rawChannelCredsList == null || rawChannelCredsList.isEmpty()) {
128134
throw new XdsInitializationException(
129135
"Invalid bootstrap: server " + serverUri + " 'channel_creds' required");
130136
}
131-
ChannelCredentials channelCredentials =
137+
ConfiguredChannelCredentials credentials =
132138
parseChannelCredentials(JsonUtil.checkObjectList(rawChannelCredsList), serverUri);
133-
if (channelCredentials == null) {
139+
if (credentials == null) {
134140
throw new XdsInitializationException(
135141
"Server " + serverUri + ": no supported channel credentials found");
136142
}
137-
return channelCredentials;
143+
return credentials;
138144
}
139145

140146
@Nullable
141-
private static ChannelCredentials parseChannelCredentials(List<Map<String, ?>> jsonList,
142-
String serverUri)
147+
private static ConfiguredChannelCredentials parseChannelCredentials(List<Map<String, ?>> jsonList,
148+
String serverUri)
143149
throws XdsInitializationException {
144150
for (Map<String, ?> channelCreds : jsonList) {
145151
String type = JsonUtil.getString(channelCreds, "type");
@@ -155,9 +161,90 @@ private static ChannelCredentials parseChannelCredentials(List<Map<String, ?>> j
155161
config = ImmutableMap.of();
156162
}
157163

158-
return provider.newChannelCredentials(config);
164+
ChannelCredentials creds = provider.newChannelCredentials(config);
165+
if (creds == null) {
166+
continue;
167+
}
168+
return ConfiguredChannelCredentials.create(creds, new JsonChannelCredsConfig(type, config));
159169
}
160170
}
161171
return null;
162172
}
173+
174+
@Override
175+
protected Optional<Object> parseAllowedGrpcServices(
176+
Map<String, ?> rawAllowedGrpcServices)
177+
throws XdsInitializationException {
178+
ImmutableMap.Builder<String, GrpcServiceXdsContext.AllowedGrpcService> builder =
179+
ImmutableMap.builder();
180+
for (String targetUri : rawAllowedGrpcServices.keySet()) {
181+
Map<String, ?> serviceConfig = JsonUtil.getObject(rawAllowedGrpcServices, targetUri);
182+
if (serviceConfig == null) {
183+
throw new XdsInitializationException(
184+
"Invalid allowed_grpc_services config for " + targetUri);
185+
}
186+
ConfiguredChannelCredentials configuredChannel =
187+
getChannelCredentials(serviceConfig, targetUri);
188+
189+
Optional<CallCredentials> callCredentials = Optional.empty();
190+
List<?> rawCallCredsList = JsonUtil.getList(serviceConfig, "call_creds");
191+
if (rawCallCredsList != null && !rawCallCredsList.isEmpty()) {
192+
callCredentials =
193+
parseCallCredentials(JsonUtil.checkObjectList(rawCallCredsList), targetUri);
194+
}
195+
196+
GrpcServiceXdsContext.AllowedGrpcService.Builder b = GrpcServiceXdsContext.AllowedGrpcService
197+
.builder().configuredChannelCredentials(configuredChannel);
198+
callCredentials.ifPresent(b::callCredentials);
199+
builder.put(targetUri, b.build());
200+
}
201+
ImmutableMap<String, GrpcServiceXdsContext.AllowedGrpcService> parsed = builder.buildOrThrow();
202+
return parsed.isEmpty() ? Optional.empty() : Optional.of(parsed);
203+
}
204+
205+
@SuppressWarnings("unused")
206+
private static Optional<CallCredentials> parseCallCredentials(List<Map<String, ?>> jsonList,
207+
String targetUri)
208+
throws XdsInitializationException {
209+
// TODO(sauravzg): Currently no xDS call credentials providers are implemented (no
210+
// XdsCallCredentialsRegistry).
211+
// As per A102/A97, we should just ignore unsupported call credentials types
212+
// without throwing an exception.
213+
return Optional.empty();
214+
}
215+
216+
private static final class JsonChannelCredsConfig implements ChannelCredsConfig {
217+
private final String type;
218+
private final Map<String, ?> config;
219+
220+
JsonChannelCredsConfig(String type, Map<String, ?> config) {
221+
this.type = type;
222+
this.config = config;
223+
}
224+
225+
@Override
226+
public String type() {
227+
return type;
228+
}
229+
230+
@Override
231+
public boolean equals(Object o) {
232+
if (this == o) {
233+
return true;
234+
}
235+
if (o == null || getClass() != o.getClass()) {
236+
return false;
237+
}
238+
JsonChannelCredsConfig that = (JsonChannelCredsConfig) o;
239+
return java.util.Objects.equals(type, that.type)
240+
&& java.util.Objects.equals(config, that.config);
241+
}
242+
243+
@Override
244+
public int hashCode() {
245+
return java.util.Objects.hash(type, config);
246+
}
247+
}
248+
163249
}
250+

xds/src/main/java/io/grpc/xds/client/Bootstrapper.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import io.grpc.xds.client.EnvoyProtoData.Node;
2727
import java.util.List;
2828
import java.util.Map;
29+
import java.util.Optional;
2930
import javax.annotation.Nullable;
3031

3132
/**
@@ -205,6 +206,12 @@ public abstract static class BootstrapInfo {
205206
*/
206207
public abstract ImmutableMap<String, AuthorityInfo> authorities();
207208

209+
/**
210+
* Parsed allowed_grpc_services configuration.
211+
* Returns an opaque object containing the parsed configuration.
212+
*/
213+
public abstract Optional<Object> allowedGrpcServices();
214+
208215
@VisibleForTesting
209216
public static Builder builder() {
210217
return new AutoValue_Bootstrapper_BootstrapInfo.Builder()
@@ -231,7 +238,10 @@ public abstract Builder clientDefaultListenerResourceNameTemplate(
231238

232239
public abstract Builder authorities(Map<String, AuthorityInfo> authorities);
233240

241+
public abstract Builder allowedGrpcServices(Optional<Object> allowedGrpcServices);
242+
234243
public abstract BootstrapInfo build();
235244
}
236245
}
246+
237247
}

xds/src/main/java/io/grpc/xds/client/BootstrapperImpl.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,20 @@ protected BootstrapInfo.Builder bootstrapBuilder(Map<String, ?> rawData)
239239
builder.authorities(authorityInfoMapBuilder.buildOrThrow());
240240
}
241241

242+
Map<String, ?> rawAllowedGrpcServices = JsonUtil.getObject(rawData, "allowed_grpc_services");
243+
if (rawAllowedGrpcServices != null) {
244+
builder.allowedGrpcServices(parseAllowedGrpcServices(rawAllowedGrpcServices));
245+
}
246+
242247
return builder;
243248
}
244249

250+
protected java.util.Optional<Object> parseAllowedGrpcServices(
251+
Map<String, ?> rawAllowedGrpcServices)
252+
throws XdsInitializationException {
253+
return java.util.Optional.empty();
254+
}
255+
245256
private List<ServerInfo> parseServerInfos(List<?> rawServerConfigs, XdsLogger logger)
246257
throws XdsInitializationException {
247258
logger.log(XdsLogLevel.INFO, "Configured with {0} xDS servers", rawServerConfigs.size());

xds/src/main/java/io/grpc/xds/internal/MatcherParser.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,25 @@ public static Matchers.StringMatcher parseStringMatcher(
9797
"Unknown StringMatcher match pattern: " + proto.getMatchPatternCase());
9898
}
9999
}
100+
101+
/** Translates envoy proto FractionalPercent to internal FractionMatcher. */
102+
public static Matchers.FractionMatcher parseFractionMatcher(
103+
io.envoyproxy.envoy.type.v3.FractionalPercent proto) {
104+
int denominator;
105+
switch (proto.getDenominator()) {
106+
case HUNDRED:
107+
denominator = 100;
108+
break;
109+
case TEN_THOUSAND:
110+
denominator = 10_000;
111+
break;
112+
case MILLION:
113+
denominator = 1_000_000;
114+
break;
115+
case UNRECOGNIZED:
116+
default:
117+
throw new IllegalArgumentException("Unknown denominator type: " + proto.getDenominator());
118+
}
119+
return Matchers.FractionMatcher.create(proto.getNumerator(), denominator);
120+
}
100121
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2025 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.xds.internal;
18+
19+
/**
20+
* Utility for validating header keys and values against xDS and Envoy specifications.
21+
*/
22+
public final class XdsHeaderValidator {
23+
24+
private XdsHeaderValidator() {}
25+
26+
/**
27+
* Returns whether the header parameter is valid. The length to check is either the
28+
* length of the string value or the size of the binary raw value.
29+
*/
30+
public static boolean isValid(String key, int valueLength) {
31+
if (key.isEmpty() || !key.equals(key.toLowerCase(java.util.Locale.ROOT)) || key.length() > 16384
32+
|| key.equals("host") || key.startsWith(":")) {
33+
return false;
34+
}
35+
if (valueLength > 16384) {
36+
return false;
37+
}
38+
return true;
39+
}
40+
}

0 commit comments

Comments
 (0)