• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 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.testing.integration;
18 
19 import com.google.common.annotations.VisibleForTesting;
20 import com.google.common.io.Files;
21 import io.grpc.ChannelCredentials;
22 import io.grpc.ClientInterceptor;
23 import io.grpc.Grpc;
24 import io.grpc.InsecureChannelCredentials;
25 import io.grpc.InsecureServerCredentials;
26 import io.grpc.LoadBalancerProvider;
27 import io.grpc.LoadBalancerRegistry;
28 import io.grpc.ManagedChannel;
29 import io.grpc.ManagedChannelBuilder;
30 import io.grpc.Metadata;
31 import io.grpc.ServerBuilder;
32 import io.grpc.TlsChannelCredentials;
33 import io.grpc.alts.AltsChannelCredentials;
34 import io.grpc.alts.ComputeEngineChannelCredentials;
35 import io.grpc.alts.GoogleDefaultChannelCredentials;
36 import io.grpc.internal.GrpcUtil;
37 import io.grpc.internal.JsonParser;
38 import io.grpc.netty.InsecureFromHttp1ChannelCredentials;
39 import io.grpc.netty.InternalNettyChannelBuilder;
40 import io.grpc.netty.NettyChannelBuilder;
41 import io.grpc.okhttp.InternalOkHttpChannelBuilder;
42 import io.grpc.okhttp.OkHttpChannelBuilder;
43 import io.grpc.stub.MetadataUtils;
44 import io.grpc.testing.TlsTesting;
45 import java.io.File;
46 import java.io.FileInputStream;
47 import java.nio.charset.Charset;
48 import java.util.Map;
49 import java.util.concurrent.TimeUnit;
50 import javax.annotation.Nullable;
51 
52 /**
53  * Application that starts a client for the {@link TestServiceGrpc.TestServiceImplBase} and runs
54  * through a series of tests.
55  */
56 public class TestServiceClient {
57 
58   private static final Charset UTF_8 = Charset.forName("UTF-8");
59 
60   /**
61    * The main application allowing this client to be launched from the command line.
62    */
main(String[] args)63   public static void main(String[] args) throws Exception {
64     final TestServiceClient client = new TestServiceClient();
65     client.parseArgs(args);
66     customBackendMetricsLoadBalancerProvider = new CustomBackendMetricsLoadBalancerProvider();
67     LoadBalancerRegistry.getDefaultRegistry().register(customBackendMetricsLoadBalancerProvider);
68     client.setUp();
69 
70     try {
71       client.run();
72     } finally {
73       client.tearDown();
74     }
75   }
76 
77   private String serverHost = "localhost";
78   private String serverHostOverride;
79   private int serverPort = 8080;
80   private String testCase = "empty_unary";
81   private int numTimes = 1;
82   private boolean useTls = true;
83   private boolean useAlts = false;
84   private boolean useH2cUpgrade = false;
85   private String customCredentialsType;
86   private boolean useTestCa;
87   private boolean useOkHttp;
88   private String defaultServiceAccount;
89   private String serviceAccountKeyFile;
90   private String oauthScope;
91   private boolean fullStreamDecompression;
92   private int localHandshakerPort = -1;
93   private Map<String, ?> serviceConfig = null;
94   private int soakIterations = 10;
95   private int soakMaxFailures = 0;
96   private int soakPerIterationMaxAcceptableLatencyMs = 1000;
97   private int soakMinTimeMsBetweenRpcs = 0;
98   private int soakOverallTimeoutSeconds =
99       soakIterations * soakPerIterationMaxAcceptableLatencyMs / 1000;
100   private String additionalMetadata = "";
101   private static LoadBalancerProvider customBackendMetricsLoadBalancerProvider;
102 
103   private Tester tester = new Tester();
104 
105   @VisibleForTesting
parseArgs(String[] args)106   void parseArgs(String[] args) throws Exception {
107     boolean usage = false;
108     for (String arg : args) {
109       if (!arg.startsWith("--")) {
110         System.err.println("All arguments must start with '--': " + arg);
111         usage = true;
112         break;
113       }
114       String[] parts = arg.substring(2).split("=", 2);
115       String key = parts[0];
116       if ("help".equals(key)) {
117         usage = true;
118         break;
119       }
120       if (parts.length != 2) {
121         System.err.println("All arguments must be of the form --arg=value");
122         usage = true;
123         break;
124       }
125       String value = parts[1];
126       if ("server_host".equals(key)) {
127         serverHost = value;
128       } else if ("server_host_override".equals(key)) {
129         serverHostOverride = value;
130       } else if ("server_port".equals(key)) {
131         serverPort = Integer.parseInt(value);
132       } else if ("test_case".equals(key)) {
133         testCase = value;
134       } else if ("num_times".equals(key)) {
135         numTimes = Integer.parseInt(value);
136       } else if ("use_tls".equals(key)) {
137         useTls = Boolean.parseBoolean(value);
138       } else if ("use_upgrade".equals(key)) {
139         useH2cUpgrade = Boolean.parseBoolean(value);
140       } else if ("use_alts".equals(key)) {
141         useAlts = Boolean.parseBoolean(value);
142       } else if ("custom_credentials_type".equals(key)) {
143         customCredentialsType = value;
144       } else if ("use_test_ca".equals(key)) {
145         useTestCa = Boolean.parseBoolean(value);
146       } else if ("use_okhttp".equals(key)) {
147         useOkHttp = Boolean.parseBoolean(value);
148       } else if ("grpc_version".equals(key)) {
149         if (!"2".equals(value)) {
150           System.err.println("Only grpc version 2 is supported");
151           usage = true;
152           break;
153         }
154       } else if ("default_service_account".equals(key)) {
155         defaultServiceAccount = value;
156       } else if ("service_account_key_file".equals(key)) {
157         serviceAccountKeyFile = value;
158       } else if ("oauth_scope".equals(key)) {
159         oauthScope = value;
160       } else if ("full_stream_decompression".equals(key)) {
161         fullStreamDecompression = Boolean.parseBoolean(value);
162       } else if ("local_handshaker_port".equals(key)) {
163         localHandshakerPort = Integer.parseInt(value);
164       } else if ("service_config_json".equals(key)) {
165         @SuppressWarnings("unchecked")
166         Map<String, ?> map = (Map<String, ?>) JsonParser.parse(value);
167         serviceConfig = map;
168       } else if ("soak_iterations".equals(key)) {
169         soakIterations = Integer.parseInt(value);
170       } else if ("soak_max_failures".equals(key)) {
171         soakMaxFailures = Integer.parseInt(value);
172       } else if ("soak_per_iteration_max_acceptable_latency_ms".equals(key)) {
173         soakPerIterationMaxAcceptableLatencyMs = Integer.parseInt(value);
174       } else if ("soak_min_time_ms_between_rpcs".equals(key)) {
175         soakMinTimeMsBetweenRpcs = Integer.parseInt(value);
176       } else if ("soak_overall_timeout_seconds".equals(key)) {
177         soakOverallTimeoutSeconds = Integer.parseInt(value);
178       } else if ("additional_metadata".equals(key)) {
179         additionalMetadata = value;
180       } else {
181         System.err.println("Unknown argument: " + key);
182         usage = true;
183         break;
184       }
185     }
186     if (useAlts || useH2cUpgrade) {
187       useTls = false;
188     }
189     if (usage) {
190       TestServiceClient c = new TestServiceClient();
191       System.out.println(
192           "Usage: [ARGS...]"
193           + "\n"
194           + "\n  --server_host=HOST          Server to connect to. Default " + c.serverHost
195           + "\n  --server_host_override=HOST Claimed identification expected of server."
196           + "\n                              Defaults to server host"
197           + "\n  --server_port=PORT          Port to connect to. Default " + c.serverPort
198           + "\n  --test_case=TESTCASE        Test case to run. Default " + c.testCase
199           + "\n    Valid options:"
200           + validTestCasesHelpText()
201           + "\n  --num_times=INT             Number of times to run the test case. Default: "
202           + c.numTimes
203           + "\n  --use_tls=true|false        Whether to use TLS. Default " + c.useTls
204           + "\n  --use_alts=true|false       Whether to use ALTS. Enable ALTS will disable TLS."
205           + "\n                              Default " + c.useAlts
206           + "\n  --local_handshaker_port=PORT"
207           + "\n                              Use local ALTS handshaker service on the specified "
208           + "\n                              port for testing. Only effective when --use_alts=true."
209           + "\n  --use_upgrade=true|false    Whether to use the h2c Upgrade mechanism."
210           + "\n                              Enabling h2c Upgrade will disable TLS."
211           + "\n                              Default " + c.useH2cUpgrade
212           + "\n  --custom_credentials_type   Custom credentials type to use. Default "
213             + c.customCredentialsType
214           + "\n  --use_test_ca=true|false    Whether to trust our fake CA. Requires --use_tls=true "
215           + "\n                              to have effect. Default " + c.useTestCa
216           + "\n  --use_okhttp=true|false     Whether to use OkHttp instead of Netty. Default "
217             + c.useOkHttp
218           + "\n  --default_service_account   Email of GCE default service account. Default "
219             + c.defaultServiceAccount
220           + "\n  --service_account_key_file  Path to service account json key file."
221             + c.serviceAccountKeyFile
222           + "\n  --oauth_scope               Scope for OAuth tokens. Default " + c.oauthScope
223           + "\n  --full_stream_decompression Enable full-stream decompression. Default "
224             + c.fullStreamDecompression
225           + "\n --service_config_json=SERVICE_CONFIG_JSON"
226           + "\n                              Disables service config lookups and sets the provided "
227           + "\n                              string as the default service config."
228           + "\n --soak_iterations            The number of iterations to use for the two soak "
229           + "\n                              tests: rpc_soak and channel_soak. Default "
230             + c.soakIterations
231           + "\n --soak_max_failures          The number of iterations in soak tests that are "
232           + "\n                              allowed to fail (either due to non-OK status code or "
233           + "\n                              exceeding the per-iteration max acceptable latency). "
234           + "\n                              Default " + c.soakMaxFailures
235           + "\n --soak_per_iteration_max_acceptable_latency_ms "
236           + "\n                              The number of milliseconds a single iteration in the "
237           + "\n                              two soak tests (rpc_soak and channel_soak) should "
238           + "\n                              take. Default "
239             + c.soakPerIterationMaxAcceptableLatencyMs
240           + "\n --soak_min_time_ms_between_rpcs "
241           + "\n                              The minimum time in milliseconds between consecutive "
242           + "\n                              RPCs in a soak test (rpc_soak or channel_soak), "
243           + "\n                              useful for limiting QPS. Default: "
244           + c.soakMinTimeMsBetweenRpcs
245           + "\n --soak_overall_timeout_seconds "
246           + "\n                              The overall number of seconds after which a soak test "
247           + "\n                              should stop and fail, if the desired number of "
248           + "\n                              iterations have not yet completed. Default "
249             + c.soakOverallTimeoutSeconds
250           + "\n --additional_metadata "
251           + "\n                              Additional metadata to send in each request, as a "
252           + "\n                              semicolon-separated list of key:value pairs. Default "
253             + c.additionalMetadata
254       );
255       System.exit(1);
256     }
257   }
258 
259   @VisibleForTesting
setUp()260   void setUp() {
261     tester.setUp();
262   }
263 
tearDown()264   private synchronized void tearDown() {
265     try {
266       tester.tearDown();
267       if (customBackendMetricsLoadBalancerProvider != null) {
268         LoadBalancerRegistry.getDefaultRegistry()
269             .deregister(customBackendMetricsLoadBalancerProvider);
270       }
271     } catch (RuntimeException ex) {
272       throw ex;
273     } catch (Exception ex) {
274       throw new RuntimeException(ex);
275     }
276   }
277 
run()278   private void run() {
279     System.out.println("Running test " + testCase);
280     try {
281       for (int i = 0; i < numTimes; i++) {
282         runTest(TestCases.fromString(testCase));
283       }
284     } catch (RuntimeException ex) {
285       throw ex;
286     } catch (Exception ex) {
287       throw new RuntimeException(ex);
288     }
289     System.out.println("Test completed.");
290   }
291 
runTest(TestCases testCase)292   private void runTest(TestCases testCase) throws Exception {
293     switch (testCase) {
294       case EMPTY_UNARY:
295         tester.emptyUnary();
296         break;
297 
298       case CACHEABLE_UNARY: {
299         tester.cacheableUnary();
300         break;
301       }
302 
303       case LARGE_UNARY:
304         tester.largeUnary();
305         break;
306 
307       case CLIENT_COMPRESSED_UNARY:
308         tester.clientCompressedUnary(true);
309         break;
310 
311       case CLIENT_COMPRESSED_UNARY_NOPROBE:
312         tester.clientCompressedUnary(false);
313         break;
314 
315       case SERVER_COMPRESSED_UNARY:
316         tester.serverCompressedUnary();
317         break;
318 
319       case CLIENT_STREAMING:
320         tester.clientStreaming();
321         break;
322 
323       case CLIENT_COMPRESSED_STREAMING:
324         tester.clientCompressedStreaming(true);
325         break;
326 
327       case CLIENT_COMPRESSED_STREAMING_NOPROBE:
328         tester.clientCompressedStreaming(false);
329         break;
330 
331       case SERVER_STREAMING:
332         tester.serverStreaming();
333         break;
334 
335       case SERVER_COMPRESSED_STREAMING:
336         tester.serverCompressedStreaming();
337         break;
338 
339       case PING_PONG:
340         tester.pingPong();
341         break;
342 
343       case EMPTY_STREAM:
344         tester.emptyStream();
345         break;
346 
347       case COMPUTE_ENGINE_CREDS:
348         tester.computeEngineCreds(defaultServiceAccount, oauthScope);
349         break;
350 
351       case COMPUTE_ENGINE_CHANNEL_CREDENTIALS: {
352         ManagedChannelBuilder<?> builder;
353         if (serverPort == 0) {
354           builder = Grpc.newChannelBuilder(serverHost, ComputeEngineChannelCredentials.create());
355         } else {
356           builder =
357               Grpc.newChannelBuilderForAddress(
358                   serverHost, serverPort, ComputeEngineChannelCredentials.create());
359         }
360         if (serviceConfig != null) {
361           builder.disableServiceConfigLookUp();
362           builder.defaultServiceConfig(serviceConfig);
363         }
364         ManagedChannel channel = builder.build();
365         try {
366           TestServiceGrpc.TestServiceBlockingStub computeEngineStub =
367               TestServiceGrpc.newBlockingStub(channel);
368           tester.computeEngineChannelCredentials(defaultServiceAccount, computeEngineStub);
369         } finally {
370           channel.shutdownNow();
371           channel.awaitTermination(5, TimeUnit.SECONDS);
372         }
373         break;
374       }
375 
376       case SERVICE_ACCOUNT_CREDS: {
377         String jsonKey = Files.asCharSource(new File(serviceAccountKeyFile), UTF_8).read();
378         FileInputStream credentialsStream = new FileInputStream(new File(serviceAccountKeyFile));
379         tester.serviceAccountCreds(jsonKey, credentialsStream, oauthScope);
380         break;
381       }
382 
383       case JWT_TOKEN_CREDS: {
384         FileInputStream credentialsStream = new FileInputStream(new File(serviceAccountKeyFile));
385         tester.jwtTokenCreds(credentialsStream);
386         break;
387       }
388 
389       case OAUTH2_AUTH_TOKEN: {
390         String jsonKey = Files.asCharSource(new File(serviceAccountKeyFile), UTF_8).read();
391         FileInputStream credentialsStream = new FileInputStream(new File(serviceAccountKeyFile));
392         tester.oauth2AuthToken(jsonKey, credentialsStream, oauthScope);
393         break;
394       }
395 
396       case PER_RPC_CREDS: {
397         String jsonKey = Files.asCharSource(new File(serviceAccountKeyFile), UTF_8).read();
398         FileInputStream credentialsStream = new FileInputStream(new File(serviceAccountKeyFile));
399         tester.perRpcCreds(jsonKey, credentialsStream, oauthScope);
400         break;
401       }
402 
403       case GOOGLE_DEFAULT_CREDENTIALS: {
404         ManagedChannelBuilder<?> builder;
405         if (serverPort == 0) {
406           builder = Grpc.newChannelBuilder(serverHost, GoogleDefaultChannelCredentials.create());
407         } else {
408           builder =
409               Grpc.newChannelBuilderForAddress(
410                   serverHost, serverPort, GoogleDefaultChannelCredentials.create());
411         }
412         if (serviceConfig != null) {
413           builder.disableServiceConfigLookUp();
414           builder.defaultServiceConfig(serviceConfig);
415         }
416         ManagedChannel channel = builder.build();
417         try {
418           TestServiceGrpc.TestServiceBlockingStub googleDefaultStub =
419               TestServiceGrpc.newBlockingStub(channel);
420           tester.googleDefaultCredentials(defaultServiceAccount, googleDefaultStub);
421         } finally {
422           channel.shutdownNow();
423         }
424         break;
425       }
426 
427       case CUSTOM_METADATA: {
428         tester.customMetadata();
429         break;
430       }
431 
432       case STATUS_CODE_AND_MESSAGE: {
433         tester.statusCodeAndMessage();
434         break;
435       }
436 
437       case SPECIAL_STATUS_MESSAGE:
438         tester.specialStatusMessage();
439         break;
440 
441       case UNIMPLEMENTED_METHOD: {
442         tester.unimplementedMethod();
443         break;
444       }
445 
446       case UNIMPLEMENTED_SERVICE: {
447         tester.unimplementedService();
448         break;
449       }
450 
451       case CANCEL_AFTER_BEGIN: {
452         tester.cancelAfterBegin();
453         break;
454       }
455 
456       case CANCEL_AFTER_FIRST_RESPONSE: {
457         tester.cancelAfterFirstResponse();
458         break;
459       }
460 
461       case TIMEOUT_ON_SLEEPING_SERVER: {
462         tester.timeoutOnSleepingServer();
463         break;
464       }
465 
466       case VERY_LARGE_REQUEST: {
467         tester.veryLargeRequest();
468         break;
469       }
470 
471       case PICK_FIRST_UNARY: {
472         tester.pickFirstUnary();
473         break;
474       }
475 
476       case RPC_SOAK: {
477         tester.performSoakTest(
478             serverHost,
479             false /* resetChannelPerIteration */,
480             soakIterations,
481             soakMaxFailures,
482             soakPerIterationMaxAcceptableLatencyMs,
483             soakMinTimeMsBetweenRpcs,
484             soakOverallTimeoutSeconds);
485         break;
486       }
487 
488       case CHANNEL_SOAK: {
489         tester.performSoakTest(
490             serverHost,
491             true /* resetChannelPerIteration */,
492             soakIterations,
493             soakMaxFailures,
494             soakPerIterationMaxAcceptableLatencyMs,
495             soakMinTimeMsBetweenRpcs,
496             soakOverallTimeoutSeconds);
497         break;
498 
499       }
500 
501       case ORCA_PER_RPC: {
502         tester.testOrcaPerRpc();
503         break;
504       }
505 
506       case ORCA_OOB: {
507         tester.testOrcaOob();
508         break;
509       }
510 
511       default:
512         throw new IllegalArgumentException("Unknown test case: " + testCase);
513     }
514   }
515 
516   /* Parses input string as a semi-colon-separated list of colon-separated key/value pairs.
517    * Allow any character but semicolons in values.
518    * If the string is emtpy, return null.
519    * Otherwise, return a client interceptor which inserts the provided metadata.
520    */
521   @Nullable
maybeCreateAdditionalMetadataInterceptor( String additionalMd)522   private ClientInterceptor maybeCreateAdditionalMetadataInterceptor(
523       String additionalMd)
524       throws IllegalArgumentException {
525     if (additionalMd.length() == 0) {
526       return null;
527     }
528     Metadata metadata = new Metadata();
529     String[] pairs = additionalMd.split(";", -1);
530     for (String pair : pairs) {
531       String[] parts = pair.split(":", 2);
532       if (parts.length != 2) {
533         throw new IllegalArgumentException(
534             "error parsing --additional_metadata string, expected k:v pairs separated by ;");
535       }
536       Metadata.Key<String> key = Metadata.Key.of(parts[0], Metadata.ASCII_STRING_MARSHALLER);
537       metadata.put(key, parts[1]);
538     }
539     return MetadataUtils.newAttachHeadersInterceptor(metadata);
540   }
541 
542   private class Tester extends AbstractInteropTest {
543     @Override
createChannelBuilder()544     protected ManagedChannelBuilder<?> createChannelBuilder() {
545       boolean useGeneric = false;
546       ChannelCredentials channelCredentials;
547       if (customCredentialsType != null) {
548         useGeneric = true; // Retain old behavior; avoids erroring if incompatible
549         if (customCredentialsType.equals("google_default_credentials")) {
550           channelCredentials = GoogleDefaultChannelCredentials.create();
551         } else if (customCredentialsType.equals("compute_engine_channel_creds")) {
552           channelCredentials = ComputeEngineChannelCredentials.create();
553         } else {
554           throw new IllegalArgumentException(
555               "Unknown custom credentials: " + customCredentialsType);
556         }
557 
558       } else if (useAlts) {
559         useGeneric = true; // Retain old behavior; avoids erroring if incompatible
560         if (localHandshakerPort > -1) {
561           channelCredentials = AltsChannelCredentials.newBuilder()
562               .enableUntrustedAltsForTesting()
563               .setHandshakerAddressForTesting("localhost:" + localHandshakerPort).build();
564         } else {
565           channelCredentials = AltsChannelCredentials.create();
566         }
567 
568       } else if (useTls) {
569         if (!useTestCa) {
570           channelCredentials = TlsChannelCredentials.create();
571         } else {
572           try {
573             channelCredentials = TlsChannelCredentials.newBuilder()
574                 .trustManager(TlsTesting.loadCert("ca.pem"))
575                 .build();
576           } catch (Exception ex) {
577             throw new RuntimeException(ex);
578           }
579         }
580 
581       } else {
582         if (useH2cUpgrade) {
583           if (useOkHttp) {
584             throw new IllegalArgumentException("OkHttp does not support HTTP/1 upgrade");
585           } else {
586             channelCredentials = InsecureFromHttp1ChannelCredentials.create();
587           }
588         } else {
589           channelCredentials = InsecureChannelCredentials.create();
590         }
591       }
592       ClientInterceptor addMdInterceptor = maybeCreateAdditionalMetadataInterceptor(
593           additionalMetadata);
594       if (useGeneric) {
595         ManagedChannelBuilder<?> channelBuilder;
596         if (serverPort == 0) {
597           channelBuilder = Grpc.newChannelBuilder(serverHost, channelCredentials);
598         } else {
599           channelBuilder =
600               Grpc.newChannelBuilderForAddress(serverHost, serverPort, channelCredentials);
601         }
602         if (serverHostOverride != null) {
603           channelBuilder.overrideAuthority(serverHostOverride);
604         }
605         if (serviceConfig != null) {
606           channelBuilder.disableServiceConfigLookUp();
607           channelBuilder.defaultServiceConfig(serviceConfig);
608         }
609         if (addMdInterceptor != null) {
610           channelBuilder.intercept(addMdInterceptor);
611         }
612         return channelBuilder;
613       }
614       if (!useOkHttp) {
615         NettyChannelBuilder nettyBuilder;
616         if (serverPort == 0) {
617           nettyBuilder = NettyChannelBuilder.forTarget(serverHost, channelCredentials);
618         } else {
619           nettyBuilder = NettyChannelBuilder.forAddress(serverHost, serverPort, channelCredentials);
620         }
621         nettyBuilder.flowControlWindow(AbstractInteropTest.TEST_FLOW_CONTROL_WINDOW);
622         if (serverHostOverride != null) {
623           nettyBuilder.overrideAuthority(serverHostOverride);
624         }
625         if (fullStreamDecompression) {
626           nettyBuilder.enableFullStreamDecompression();
627         }
628         // Disable the default census stats interceptor, use testing interceptor instead.
629         InternalNettyChannelBuilder.setStatsEnabled(nettyBuilder, false);
630         if (serviceConfig != null) {
631           nettyBuilder.disableServiceConfigLookUp();
632           nettyBuilder.defaultServiceConfig(serviceConfig);
633         }
634         if (addMdInterceptor != null) {
635           nettyBuilder.intercept(addMdInterceptor);
636         }
637         return nettyBuilder.intercept(createCensusStatsClientInterceptor());
638       }
639 
640       OkHttpChannelBuilder okBuilder;
641       if (serverPort == 0) {
642         okBuilder = OkHttpChannelBuilder.forTarget(serverHost, channelCredentials);
643       } else {
644         okBuilder = OkHttpChannelBuilder.forAddress(serverHost, serverPort, channelCredentials);
645       }
646       if (serverHostOverride != null) {
647         // Force the hostname to match the cert the server uses.
648         okBuilder.overrideAuthority(
649             GrpcUtil.authorityFromHostAndPort(serverHostOverride, serverPort));
650       }
651       if (fullStreamDecompression) {
652         okBuilder.enableFullStreamDecompression();
653       }
654       // Disable the default census stats interceptor, use testing interceptor instead.
655       InternalOkHttpChannelBuilder.setStatsEnabled(okBuilder, false);
656       if (serviceConfig != null) {
657         okBuilder.disableServiceConfigLookUp();
658         okBuilder.defaultServiceConfig(serviceConfig);
659       }
660       if (addMdInterceptor != null) {
661         okBuilder.intercept(addMdInterceptor);
662       }
663       return okBuilder.intercept(createCensusStatsClientInterceptor());
664     }
665 
666     @Override
metricsExpected()667     protected boolean metricsExpected() {
668       // Exact message size doesn't match when testing with Go servers:
669       // https://github.com/grpc/grpc-go/issues/1572
670       // TODO(zhangkun83): remove this override once the said issue is fixed.
671       return false;
672     }
673 
674     @Override
675     @Nullable
getHandshakerServerBuilder()676     protected ServerBuilder<?> getHandshakerServerBuilder() {
677       if (localHandshakerPort > -1) {
678         return Grpc.newServerBuilderForPort(localHandshakerPort,
679             InsecureServerCredentials.create())
680             .addService(new AltsHandshakerTestService());
681       } else {
682         return null;
683       }
684     }
685 
686     @Override
operationTimeoutMillis()687     protected int operationTimeoutMillis() {
688       return 15000;
689     }
690   }
691 
validTestCasesHelpText()692   private static String validTestCasesHelpText() {
693     StringBuilder builder = new StringBuilder();
694     for (TestCases testCase : TestCases.values()) {
695       String strTestcase = testCase.name().toLowerCase();
696       builder.append("\n      ")
697           .append(strTestcase)
698           .append(": ")
699           .append(testCase.description());
700     }
701     return builder.toString();
702   }
703 }
704