Skip to content

Commit 72b24f6

Browse files
committed
direct-path-interconnect
1 parent 6737eb5 commit 72b24f6

3 files changed

Lines changed: 324 additions & 35 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,7 @@ public Builder setQuery(@Nullable String query) {
793793
}
794794

795795
@CanIgnoreReturnValue
796-
Builder setRawQuery(String query) {
796+
public Builder setRawQuery(String query) {
797797
checkPercentEncodedArg(query, "query", queryChars);
798798
this.query = query;
799799
return this;

googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java

Lines changed: 104 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import com.google.common.annotations.VisibleForTesting;
2222
import com.google.common.base.Preconditions;
23+
import com.google.common.base.Splitter;
2324
import com.google.common.collect.ImmutableList;
2425
import com.google.common.collect.ImmutableMap;
2526
import com.google.common.io.CharStreams;
@@ -76,23 +77,43 @@ final class GoogleCloudToProdNameResolver extends NameResolver {
7677
private static final String serverUriOverride =
7778
System.getenv("GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI");
7879

79-
@GuardedBy("GoogleCloudToProdNameResolver.class")
80+
private static final Object BOOTSTRAP_LOCK = new Object();
81+
private static final Object FORCE_XDS_BOOTSTRAP_LOCK = new Object();
82+
83+
@GuardedBy("BOOTSTRAP_LOCK")
8084
private static BootstrapInfo bootstrapInfo;
85+
@GuardedBy("FORCE_XDS_BOOTSTRAP_LOCK")
86+
private static BootstrapInfo forceXdsBootstrapInfo;
8187
private static HttpConnectionProvider httpConnectionProvider = HttpConnectionFactory.INSTANCE;
8288
private static int c2pId = new Random().nextInt();
8389

84-
private static synchronized BootstrapInfo getBootstrapInfo()
90+
private static BootstrapInfo getBootstrapInfo(boolean isForcedXds)
8591
throws XdsInitializationException, IOException {
86-
if (bootstrapInfo != null) {
87-
return bootstrapInfo;
88-
}
89-
BootstrapInfo bootstrapInfoTmp =
90-
InternalGrpcBootstrapperImpl.parseBootstrap(generateBootstrap());
91-
// Avoid setting global when testing
92-
if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) {
93-
bootstrapInfo = bootstrapInfoTmp;
92+
if (isForcedXds) {
93+
synchronized (FORCE_XDS_BOOTSTRAP_LOCK) {
94+
if (forceXdsBootstrapInfo != null) {
95+
return forceXdsBootstrapInfo;
96+
}
97+
BootstrapInfo newInfo = InternalGrpcBootstrapperImpl.parseBootstrap(
98+
generateBootstrap("", true, true));
99+
if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) {
100+
forceXdsBootstrapInfo = newInfo;
101+
}
102+
return newInfo;
103+
}
104+
} else {
105+
synchronized (BOOTSTRAP_LOCK) {
106+
if (bootstrapInfo != null) {
107+
return bootstrapInfo;
108+
}
109+
BootstrapInfo newInfo = InternalGrpcBootstrapperImpl.parseBootstrap(
110+
generateBootstrap());
111+
if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) {
112+
bootstrapInfo = newInfo;
113+
}
114+
return newInfo;
115+
}
94116
}
95-
return bootstrapInfoTmp;
96117
}
97118

98119
private final String authority;
@@ -102,7 +123,8 @@ private static synchronized BootstrapInfo getBootstrapInfo()
102123
private final MetricRecorder metricRecorder;
103124
private final NameResolver delegate;
104125
private final boolean usingExecutorResource;
105-
private final String schemeOverride = !isOnGcp ? "dns" : "xds";
126+
private final boolean forceXds;
127+
private final String schemeOverride;
106128
private XdsClientResult xdsClientPool;
107129
private XdsClient xdsClient;
108130
private Executor executor;
@@ -121,6 +143,11 @@ private static synchronized BootstrapInfo getBootstrapInfo()
121143
GoogleCloudToProdNameResolver(URI targetUri, Args args, Resource<Executor> executorResource,
122144
NameResolver.Factory nameResolverFactory) {
123145
this.executorResource = checkNotNull(executorResource, "executorResource");
146+
String query = targetUri.getRawQuery();
147+
this.forceXds = checkForceXds(query);
148+
this.schemeOverride = (forceXds || isOnGcp) ? "xds" : "dns";
149+
String newQuery = stripForceXds(query);
150+
124151
String targetPath = checkNotNull(checkNotNull(targetUri, "targetUri").getPath(), "targetPath");
125152
Preconditions.checkArgument(
126153
targetPath.startsWith("/"),
@@ -129,9 +156,31 @@ private static synchronized BootstrapInfo getBootstrapInfo()
129156
targetUri);
130157
authority = GrpcUtil.checkAuthority(targetPath.substring(1));
131158
syncContext = checkNotNull(args, "args").getSynchronizationContext();
132-
targetUri = overrideUriScheme(targetUri, schemeOverride);
159+
160+
String rawAuthority = schemeOverride.equals("xds")
161+
? C2P_AUTHORITY
162+
: targetUri.getRawAuthority();
163+
String rawPath = targetUri.getRawPath();
164+
String rawFragment = targetUri.getRawFragment();
165+
try {
166+
StringBuilder uriStr = new StringBuilder();
167+
uriStr.append(schemeOverride).append(":");
168+
if (rawAuthority != null) {
169+
uriStr.append("//").append(rawAuthority);
170+
}
171+
uriStr.append(rawPath);
172+
if (newQuery != null) {
173+
uriStr.append("?").append(newQuery);
174+
}
175+
if (rawFragment != null) {
176+
uriStr.append("#").append(rawFragment);
177+
}
178+
targetUri = new URI(uriStr.toString());
179+
} catch (URISyntaxException e) {
180+
throw new IllegalArgumentException("Invalid URI", e);
181+
}
182+
133183
if (schemeOverride.equals("xds")) {
134-
targetUri = overrideUriAuthority(targetUri, C2P_AUTHORITY);
135184
args = args.toBuilder()
136185
.setArg(XdsNameResolverProvider.XDS_CLIENT_SUPPLIER, () -> xdsClient)
137186
.build();
@@ -155,6 +204,11 @@ private static synchronized BootstrapInfo getBootstrapInfo()
155204
Resource<Executor> executorResource,
156205
NameResolver.Factory nameResolverFactory) {
157206
this.executorResource = checkNotNull(executorResource, "executorResource");
207+
String query = targetUri.getRawQuery();
208+
this.forceXds = checkForceXds(query);
209+
this.schemeOverride = (forceXds || isOnGcp) ? "xds" : "dns";
210+
String newQuery = stripForceXds(query);
211+
158212
Preconditions.checkArgument(
159213
targetUri.isPathAbsolute(),
160214
"the path component of the target (%s) must start with '/'",
@@ -167,6 +221,12 @@ private static synchronized BootstrapInfo getBootstrapInfo()
167221
authority = GrpcUtil.checkAuthority(pathSegments.get(0));
168222
syncContext = checkNotNull(args, "args").getSynchronizationContext();
169223
Uri.Builder modifiedTargetBuilder = targetUri.toBuilder().setScheme(schemeOverride);
224+
if (newQuery != null) {
225+
modifiedTargetBuilder.setRawQuery(newQuery);
226+
} else {
227+
modifiedTargetBuilder.setQuery(null);
228+
}
229+
170230
if (schemeOverride.equals("xds")) {
171231
modifiedTargetBuilder.setRawAuthority(C2P_AUTHORITY);
172232
args =
@@ -226,7 +286,7 @@ class Resolve implements Runnable {
226286
public void run() {
227287
BootstrapInfo bootstrapInfo = null;
228288
try {
229-
bootstrapInfo = getBootstrapInfo();
289+
bootstrapInfo = getBootstrapInfo(forceXds);
230290
} catch (IOException e) {
231291
listener.onError(
232292
Status.INTERNAL.withDescription("Unable to get metadata").withCause(e));
@@ -263,16 +323,18 @@ public void run() {
263323
static ImmutableMap<String, ?> generateBootstrap() throws IOException {
264324
return generateBootstrap(
265325
queryZoneMetadata(METADATA_URL_ZONE),
266-
queryIpv6SupportMetadata(METADATA_URL_SUPPORT_IPV6));
326+
queryIpv6SupportMetadata(METADATA_URL_SUPPORT_IPV6), false);
267327
}
268328

269-
private static ImmutableMap<String, ?> generateBootstrap(String zone, boolean supportIpv6) {
329+
static ImmutableMap<String, ?> generateBootstrap(
330+
String zone, boolean supportIpv6, boolean isForcedXds) {
270331
ImmutableMap.Builder<String, Object> nodeBuilder = ImmutableMap.builder();
271-
nodeBuilder.put("id", "C2P-" + (c2pId & Integer.MAX_VALUE));
272-
if (!zone.isEmpty()) {
332+
String nodeIdPrefix = isOnGcp ? "C2P-" : "C2P-non-gcp-";
333+
nodeBuilder.put("id", nodeIdPrefix + (c2pId & Integer.MAX_VALUE));
334+
if (!isForcedXds && !zone.isEmpty()) {
273335
nodeBuilder.put("locality", ImmutableMap.of("zone", zone));
274336
}
275-
if (supportIpv6) {
337+
if (isForcedXds || supportIpv6) {
276338
nodeBuilder.put("metadata",
277339
ImmutableMap.of("TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", true));
278340
}
@@ -373,24 +435,32 @@ static void setC2pId(int c2pId) {
373435
GoogleCloudToProdNameResolver.c2pId = c2pId;
374436
}
375437

376-
private static URI overrideUriScheme(URI uri, String scheme) {
377-
URI res;
378-
try {
379-
res = new URI(scheme, uri.getAuthority(), uri.getPath(), uri.getQuery(), uri.getFragment());
380-
} catch (URISyntaxException ex) {
381-
throw new IllegalArgumentException("Invalid scheme: " + scheme, ex);
438+
private static boolean checkForceXds(String query) {
439+
if (query == null) {
440+
return false;
441+
}
442+
for (String part : Splitter.on('&').split(query)) {
443+
if (part.equals("force-xds") || part.startsWith("force-xds=")) {
444+
return true;
445+
}
382446
}
383-
return res;
447+
return false;
384448
}
385449

386-
private static URI overrideUriAuthority(URI uri, String authority) {
387-
URI res;
388-
try {
389-
res = new URI(uri.getScheme(), authority, uri.getPath(), uri.getQuery(), uri.getFragment());
390-
} catch (URISyntaxException ex) {
391-
throw new IllegalArgumentException("Invalid authority: " + authority, ex);
450+
private static String stripForceXds(String query) {
451+
if (query == null) {
452+
return null;
453+
}
454+
StringBuilder sb = new StringBuilder();
455+
for (String part : Splitter.on('&').split(query)) {
456+
if (!part.equals("force-xds") && !part.startsWith("force-xds=")) {
457+
if (sb.length() > 0) {
458+
sb.append("&");
459+
}
460+
sb.append(part);
461+
}
392462
}
393-
return res;
463+
return sb.length() == 0 ? null : sb.toString();
394464
}
395465

396466
private enum HttpConnectionFactory implements HttpConnectionProvider {

0 commit comments

Comments
 (0)