| /* |
| * Copyright 2014, Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| package io.grpc.testing.integration; |
| |
| import com.google.common.io.Files; |
| |
| import io.grpc.ManagedChannel; |
| import io.grpc.internal.GrpcUtil; |
| import io.grpc.netty.GrpcSslContexts; |
| import io.grpc.netty.NegotiationType; |
| import io.grpc.netty.NettyChannelBuilder; |
| import io.grpc.okhttp.OkHttpChannelBuilder; |
| import io.grpc.okhttp.internal.Platform; |
| import io.grpc.testing.TestUtils; |
| import io.netty.handler.ssl.SslContext; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.net.InetAddress; |
| import java.net.InetSocketAddress; |
| import java.net.UnknownHostException; |
| import java.nio.charset.Charset; |
| |
| import javax.net.ssl.SSLSocketFactory; |
| |
| /** |
| * Application that starts a client for the {@link TestServiceGrpc.TestServiceImplBase} and runs |
| * through a series of tests. |
| */ |
| public class TestServiceClient { |
| |
| private static final Charset UTF_8 = Charset.forName("UTF-8"); |
| |
| /** |
| * The main application allowing this client to be launched from the command line. |
| */ |
| public static void main(String[] args) throws Exception { |
| final TestServiceClient client = new TestServiceClient(); |
| client.parseArgs(args); |
| client.setUp(); |
| |
| Runtime.getRuntime().addShutdownHook(new Thread() { |
| @Override |
| public void run() { |
| System.out.println("Shutting down"); |
| try { |
| client.tearDown(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| }); |
| |
| try { |
| client.run(); |
| } finally { |
| client.tearDown(); |
| } |
| System.exit(0); |
| } |
| |
| private String serverHost = "localhost"; |
| private String serverHostOverride; |
| private int serverPort = 8080; |
| private String testCase = "empty_unary"; |
| private boolean useTls = true; |
| private boolean useTestCa; |
| private boolean useOkHttp; |
| private String defaultServiceAccount; |
| private String serviceAccountKeyFile; |
| private String oauthScope; |
| |
| private Tester tester = new Tester(); |
| |
| private void parseArgs(String[] args) { |
| boolean usage = false; |
| for (String arg : args) { |
| if (!arg.startsWith("--")) { |
| System.err.println("All arguments must start with '--': " + arg); |
| usage = true; |
| break; |
| } |
| String[] parts = arg.substring(2).split("=", 2); |
| String key = parts[0]; |
| if ("help".equals(key)) { |
| usage = true; |
| break; |
| } |
| if (parts.length != 2) { |
| System.err.println("All arguments must be of the form --arg=value"); |
| usage = true; |
| break; |
| } |
| String value = parts[1]; |
| if ("server_host".equals(key)) { |
| serverHost = value; |
| } else if ("server_host_override".equals(key)) { |
| serverHostOverride = value; |
| } else if ("server_port".equals(key)) { |
| serverPort = Integer.parseInt(value); |
| } else if ("test_case".equals(key)) { |
| testCase = value; |
| } else if ("use_tls".equals(key)) { |
| useTls = Boolean.parseBoolean(value); |
| } else if ("use_test_ca".equals(key)) { |
| useTestCa = Boolean.parseBoolean(value); |
| } else if ("use_okhttp".equals(key)) { |
| useOkHttp = Boolean.parseBoolean(value); |
| } else if ("grpc_version".equals(key)) { |
| if (!"2".equals(value)) { |
| System.err.println("Only grpc version 2 is supported"); |
| usage = true; |
| break; |
| } |
| } else if ("default_service_account".equals(key)) { |
| defaultServiceAccount = value; |
| } else if ("service_account_key_file".equals(key)) { |
| serviceAccountKeyFile = value; |
| } else if ("oauth_scope".equals(key)) { |
| oauthScope = value; |
| } else { |
| System.err.println("Unknown argument: " + key); |
| usage = true; |
| break; |
| } |
| } |
| if (usage) { |
| TestServiceClient c = new TestServiceClient(); |
| System.out.println( |
| "Usage: [ARGS...]" |
| + "\n" |
| + "\n --server_host=HOST Server to connect to. Default " + c.serverHost |
| + "\n --server_host_override=HOST Claimed identification expected of server." |
| + "\n Defaults to server host" |
| + "\n --server_port=PORT Port to connect to. Default " + c.serverPort |
| + "\n --test_case=TESTCASE Test case to run. Default " + c.testCase |
| + "\n Valid options:" |
| + validTestCasesHelpText() |
| + "\n --use_tls=true|false Whether to use TLS. Default " + c.useTls |
| + "\n --use_test_ca=true|false Whether to trust our fake CA. Requires --use_tls=true " |
| + "\n to have effect. Default " + c.useTestCa |
| + "\n --use_okhttp=true|false Whether to use OkHttp instead of Netty. Default " |
| + c.useOkHttp |
| + "\n --default_service_account Email of GCE default service account. Default " |
| + c.defaultServiceAccount |
| + "\n --service_account_key_file Path to service account json key file." |
| + c.serviceAccountKeyFile |
| + "\n --oauth_scope Scope for OAuth tokens. Default " + c.oauthScope |
| ); |
| System.exit(1); |
| } |
| } |
| |
| private void setUp() { |
| tester.setUp(); |
| } |
| |
| private synchronized void tearDown() { |
| try { |
| tester.tearDown(); |
| } catch (RuntimeException ex) { |
| throw ex; |
| } catch (Exception ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| private void run() { |
| System.out.println("Running test " + testCase); |
| try { |
| runTest(TestCases.fromString(testCase)); |
| } catch (RuntimeException ex) { |
| throw ex; |
| } catch (Exception ex) { |
| throw new RuntimeException(ex); |
| } |
| System.out.println("Test completed."); |
| } |
| |
| private void runTest(TestCases testCase) throws Exception { |
| switch (testCase) { |
| case EMPTY_UNARY: |
| tester.emptyUnary(); |
| break; |
| |
| case LARGE_UNARY: |
| tester.largeUnary(); |
| break; |
| |
| case CLIENT_STREAMING: |
| tester.clientStreaming(); |
| break; |
| |
| case SERVER_STREAMING: |
| tester.serverStreaming(); |
| break; |
| |
| case PING_PONG: |
| tester.pingPong(); |
| break; |
| |
| case EMPTY_STREAM: |
| tester.emptyStream(); |
| break; |
| |
| case COMPUTE_ENGINE_CREDS: |
| tester.computeEngineCreds(defaultServiceAccount, oauthScope); |
| break; |
| |
| case SERVICE_ACCOUNT_CREDS: { |
| String jsonKey = Files.toString(new File(serviceAccountKeyFile), UTF_8); |
| FileInputStream credentialsStream = new FileInputStream(new File(serviceAccountKeyFile)); |
| tester.serviceAccountCreds(jsonKey, credentialsStream, oauthScope); |
| break; |
| } |
| |
| case JWT_TOKEN_CREDS: { |
| FileInputStream credentialsStream = new FileInputStream(new File(serviceAccountKeyFile)); |
| tester.jwtTokenCreds(credentialsStream); |
| break; |
| } |
| |
| case OAUTH2_AUTH_TOKEN: { |
| String jsonKey = Files.toString(new File(serviceAccountKeyFile), UTF_8); |
| FileInputStream credentialsStream = new FileInputStream(new File(serviceAccountKeyFile)); |
| tester.oauth2AuthToken(jsonKey, credentialsStream, oauthScope); |
| break; |
| } |
| |
| case PER_RPC_CREDS: { |
| String jsonKey = Files.toString(new File(serviceAccountKeyFile), UTF_8); |
| FileInputStream credentialsStream = new FileInputStream(new File(serviceAccountKeyFile)); |
| tester.perRpcCreds(jsonKey, credentialsStream, oauthScope); |
| break; |
| } |
| |
| case CUSTOM_METADATA: { |
| tester.customMetadata(); |
| break; |
| } |
| |
| case STATUS_CODE_AND_MESSAGE: { |
| tester.statusCodeAndMessage(); |
| break; |
| } |
| |
| case UNIMPLEMENTED_METHOD: { |
| tester.unimplementedMethod(); |
| break; |
| } |
| |
| case UNIMPLEMENTED_SERVICE: { |
| tester.unimplementedService(); |
| break; |
| } |
| |
| case CANCEL_AFTER_BEGIN: { |
| tester.cancelAfterBegin(); |
| break; |
| } |
| |
| case CANCEL_AFTER_FIRST_RESPONSE: { |
| tester.cancelAfterFirstResponse(); |
| break; |
| } |
| |
| case TIMEOUT_ON_SLEEPING_SERVER: { |
| tester.timeoutOnSleepingServer(); |
| break; |
| } |
| |
| default: |
| throw new IllegalArgumentException("Unknown test case: " + testCase); |
| } |
| } |
| |
| private class Tester extends AbstractInteropTest { |
| @Override |
| protected ManagedChannel createChannel() { |
| if (!useOkHttp) { |
| InetAddress address; |
| try { |
| address = InetAddress.getByName(serverHost); |
| if (serverHostOverride != null) { |
| // Force the hostname to match the cert the server uses. |
| address = InetAddress.getByAddress(serverHostOverride, address.getAddress()); |
| } |
| } catch (UnknownHostException ex) { |
| throw new RuntimeException(ex); |
| } |
| SslContext sslContext = null; |
| if (useTestCa) { |
| try { |
| sslContext = GrpcSslContexts.forClient().trustManager( |
| TestUtils.loadCert("ca.pem")).build(); |
| } catch (Exception ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| return NettyChannelBuilder.forAddress(new InetSocketAddress(address, serverPort)) |
| .flowControlWindow(65 * 1024) |
| .negotiationType(useTls ? NegotiationType.TLS : NegotiationType.PLAINTEXT) |
| .sslContext(sslContext) |
| .build(); |
| } else { |
| OkHttpChannelBuilder builder = OkHttpChannelBuilder.forAddress(serverHost, serverPort); |
| if (serverHostOverride != null) { |
| // Force the hostname to match the cert the server uses. |
| builder.overrideAuthority( |
| GrpcUtil.authorityFromHostAndPort(serverHostOverride, serverPort)); |
| } |
| if (useTls) { |
| try { |
| SSLSocketFactory factory = useTestCa |
| ? TestUtils.newSslSocketFactoryForCa(Platform.get().getProvider(), |
| TestUtils.loadCert("ca.pem")) |
| : (SSLSocketFactory) SSLSocketFactory.getDefault(); |
| builder.sslSocketFactory(factory); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } else { |
| builder.usePlaintext(true); |
| } |
| return builder.build(); |
| } |
| } |
| |
| @Override |
| protected boolean metricsExpected() { |
| // Server-side metrics won't be found, because server is a separate process. |
| return false; |
| } |
| } |
| |
| private static String validTestCasesHelpText() { |
| StringBuilder builder = new StringBuilder(); |
| for (TestCases testCase : TestCases.values()) { |
| String strTestcase = testCase.name().toLowerCase(); |
| builder.append("\n ") |
| .append(strTestcase) |
| .append(": ") |
| .append(testCase.description()); |
| } |
| return builder.toString(); |
| } |
| } |