• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 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.istio;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertNotEquals;
22 import static org.junit.Assert.fail;
23 
24 import com.google.common.base.Splitter;
25 import com.google.common.collect.ImmutableList;
26 import com.google.common.collect.ImmutableSet;
27 import io.grpc.Grpc;
28 import io.grpc.InsecureChannelCredentials;
29 import io.grpc.InsecureServerCredentials;
30 import io.grpc.ManagedChannel;
31 import io.grpc.ManagedChannelBuilder;
32 import io.grpc.Metadata;
33 import io.grpc.Server;
34 import io.grpc.Status;
35 import io.grpc.StatusRuntimeException;
36 import io.grpc.stub.MetadataUtils;
37 import io.grpc.stub.StreamObserver;
38 import io.istio.test.Echo.EchoRequest;
39 import io.istio.test.Echo.EchoResponse;
40 import io.istio.test.Echo.ForwardEchoRequest;
41 import io.istio.test.Echo.ForwardEchoResponse;
42 import io.istio.test.Echo.Header;
43 import io.istio.test.EchoTestServiceGrpc;
44 import io.istio.test.EchoTestServiceGrpc.EchoTestServiceImplBase;
45 import java.io.IOException;
46 import java.time.Duration;
47 import java.time.Instant;
48 import java.util.ArrayList;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Set;
52 import java.util.concurrent.TimeUnit;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 import org.junit.runners.JUnit4;
56 
57 /**
58  * Unit tests for {@link EchoTestServer}.
59  */
60 @RunWith(JUnit4.class)
61 public class EchoTestServerTest {
62 
63   private static final String[] EXPECTED_KEY_SET = {
64       "--server_first", "--forwarding-address",
65       "--bind_ip", "--istio-version", "--bind_localhost", "--grpc", "--tls",
66       "--cluster", "--key", "--tcp", "--crt", "--metrics", "--port", "--version"
67   };
68 
69   private static final String TEST_ARGS =
70       "--metrics=15014 --cluster=\"cluster-0\" --port=\"18080\" --grpc=\"17070\" --port=\"18085\""
71           + " --tcp=\"19090\" --port=\"18443\" --tls=18443 --tcp=\"16060\" --server_first=16060"
72           + " --tcp=\"19091\" --tcp=\"16061\" --server_first=16061 --port=\"18081\""
73           + " --grpc=\"17071\" --port=\"19443\" --tls=19443 --port=\"18082\" --bind_ip=18082"
74           + " --port=\"18084\" --bind_localhost=18084 --tcp=\"19092\" --port=\"18083\""
75           + " --port=\"8080\" --port=\"3333\" --version=\"v1\" --istio-version=3 --crt=/cert.crt"
76           + " --key=/cert.key --forwarding-address=192.168.1.10:7072";
77 
78   private static final String TEST_ARGS_PORTS =
79       "--metrics=15014 --cluster=\"cluster-0\" --port=\"18080\" --grpc=17070 --port=18085"
80           + " --tcp=\"19090\" --port=\"18443\" --tls=18443 --tcp=16060 --server_first=16060"
81           + " --tcp=\"19091\" --tcp=\"16061\" --server_first=16061 --port=\"18081\""
82           + " --grpc=\"17071\" --port=\"19443\" --tls=\"19443\" --port=\"18082\" --bind_ip=18082"
83           + " --port=\"18084\" --bind_localhost=18084 --tcp=\"19092\" --port=\"18083\""
84           + " --port=\"8080\" --port=3333 --version=\"v1\" --istio-version=3 --crt=/cert.crt"
85           + " --key=/cert.key --xds-grpc-server=12034 --xds-grpc-server=\"34012\"";
86 
87   @Test
preprocessArgsTest()88   public void preprocessArgsTest() {
89     String[] splitArgs = TEST_ARGS.split(" ");
90     Map<String, List<String>> processedArgs = EchoTestServer.preprocessArgs(splitArgs);
91 
92     assertEquals(processedArgs.keySet(), ImmutableSet.copyOf(EXPECTED_KEY_SET));
93     assertEquals(processedArgs.get("--server_first"), ImmutableList.of("16060", "16061"));
94     assertEquals(processedArgs.get("--bind_ip"), ImmutableList.of("18082"));
95     assertEquals(processedArgs.get("--bind_localhost"), ImmutableList.of("18084"));
96     assertEquals(processedArgs.get("--grpc"), ImmutableList.of("\"17070\"", "\"17071\""));
97     assertEquals(processedArgs.get("--tls"), ImmutableList.of("18443", "19443"));
98     assertEquals(processedArgs.get("--cluster"), ImmutableList.of("\"cluster-0\""));
99     assertEquals(processedArgs.get("--key"), ImmutableList.of("/cert.key"));
100     assertEquals(processedArgs.get("--tcp"), ImmutableList.of("\"19090\"", "\"16060\"",
101         "\"19091\"","\"16061\"","\"19092\""));
102     assertEquals(processedArgs.get("--istio-version"), ImmutableList.of("3"));
103     assertEquals(processedArgs.get("--crt"), ImmutableList.of("/cert.crt"));
104     assertEquals(processedArgs.get("--metrics"), ImmutableList.of("15014"));
105     assertEquals(ImmutableList.of("192.168.1.10:7072"), processedArgs.get("--forwarding-address"));
106     assertEquals(
107         processedArgs.get("--port"),
108         ImmutableList.of(
109             "\"18080\"",
110             "\"18085\"",
111             "\"18443\"",
112             "\"18081\"",
113             "\"19443\"",
114             "\"18082\"",
115             "\"18084\"",
116             "\"18083\"",
117             "\"8080\"",
118             "\"3333\""));
119   }
120 
121   @Test
preprocessArgsPortsTest()122   public void preprocessArgsPortsTest() {
123     String[] splitArgs = TEST_ARGS_PORTS.split(" ");
124     Map<String, List<String>> processedArgs = EchoTestServer.preprocessArgs(splitArgs);
125 
126     Set<Integer> ports = EchoTestServer.getPorts(processedArgs, "--port");
127     assertThat(ports).containsExactly(18080, 8080, 18081, 18082, 19443, 18083, 18084, 18085,
128         3333, 18443);
129     ports = EchoTestServer.getPorts(processedArgs, "--grpc");
130     assertThat(ports).containsExactly(17070, 17071);
131     ports = EchoTestServer.getPorts(processedArgs, "--tls");
132     assertThat(ports).containsExactly(18443, 19443);
133     ports = EchoTestServer.getPorts(processedArgs, "--xds-grpc-server");
134     assertThat(ports).containsExactly(34012, 12034);
135   }
136 
137 
138   @Test
echoTest()139   public void echoTest() throws IOException, InterruptedException {
140     EchoTestServer echoTestServer = new EchoTestServer();
141 
142     echoTestServer.runServers(
143         "test-host",
144         ImmutableList.of(0, 0),
145         ImmutableList.of(),
146         ImmutableList.of(),
147         "0.0.0.0:7072",
148         null);
149     assertEquals(2, echoTestServer.servers.size());
150     int port = echoTestServer.servers.get(0).getPort();
151     assertNotEquals(0, port);
152     assertNotEquals(0, echoTestServer.servers.get(1).getPort());
153 
154     ManagedChannelBuilder<?> channelBuilder =
155         Grpc.newChannelBuilderForAddress("localhost", port, InsecureChannelCredentials.create());
156     ManagedChannel channel = channelBuilder.build();
157 
158     Metadata metadata = new Metadata();
159     metadata.put(Metadata.Key.of("header1", Metadata.ASCII_STRING_MARSHALLER), "value1");
160     metadata.put(Metadata.Key.of("header2", Metadata.ASCII_STRING_MARSHALLER), "value2");
161 
162     EchoTestServiceGrpc.EchoTestServiceBlockingStub stub =
163         EchoTestServiceGrpc.newBlockingStub(channel)
164             .withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata));
165 
166     EchoRequest echoRequest = EchoRequest.newBuilder()
167         .setMessage("test-message1")
168         .build();
169     EchoResponse echoResponse = stub.echo(echoRequest);
170     String echoMessage = echoResponse.getMessage();
171     Set<String> lines = ImmutableSet.copyOf(echoMessage.split("\n"));
172 
173     assertThat(lines).contains("RequestHeader=header1:value1");
174     assertThat(lines).contains("RequestHeader=header2:value2");
175     assertThat(lines).contains("Echo=test-message1");
176     assertThat(lines).contains("Hostname=test-host");
177     assertThat(lines).contains("Host=localhost:" + port);
178     assertThat(lines).contains("StatusCode=200");
179 
180     echoTestServer.stopServers();
181     echoTestServer.blockUntilShutdown();
182   }
183 
184   static final int COUNT_OF_REQUESTS_TO_FORWARD = 60;
185 
186   @Test
forwardEchoTest()187   public void forwardEchoTest() throws IOException, InterruptedException {
188     EchoTestServer echoTestServer = new EchoTestServer();
189 
190     echoTestServer.runServers(
191         "test-host",
192         ImmutableList.of(0, 0),
193         ImmutableList.of(),
194         ImmutableList.of(),
195         "0.0.0.0:7072",
196         null);
197     assertEquals(2, echoTestServer.servers.size());
198     int port1 = echoTestServer.servers.get(0).getPort();
199     int port2 = echoTestServer.servers.get(1).getPort();
200 
201     ManagedChannelBuilder<?> channelBuilder =
202         Grpc.newChannelBuilderForAddress("localhost", port1, InsecureChannelCredentials.create());
203     ManagedChannel channel = channelBuilder.build();
204 
205     ForwardEchoRequest forwardEchoRequest =
206         ForwardEchoRequest.newBuilder()
207             .setCount(COUNT_OF_REQUESTS_TO_FORWARD)
208             .setQps(100)
209             .setTimeoutMicros(5000_000L) // 5000 millis
210             .setUrl("grpc://localhost:" + port2)
211             .addHeaders(
212                 Header.newBuilder().setKey("test-key1").setValue("test-value1").build())
213             .addHeaders(
214                 Header.newBuilder().setKey("test-key2").setValue("test-value2").build())
215             .setMessage("forward-echo-test-message")
216             .build();
217 
218     EchoTestServiceGrpc.EchoTestServiceBlockingStub stub =
219         EchoTestServiceGrpc.newBlockingStub(channel);
220 
221     Instant start = Instant.now();
222     ForwardEchoResponse forwardEchoResponse = stub.forwardEcho(forwardEchoRequest);
223     Instant end = Instant.now();
224     List<String> outputs = forwardEchoResponse.getOutputList();
225     assertEquals(COUNT_OF_REQUESTS_TO_FORWARD, outputs.size());
226     for (int i = 0; i < COUNT_OF_REQUESTS_TO_FORWARD; i++) {
227       validateOutput(outputs.get(i), i);
228     }
229     long duration = Duration.between(start, end).toMillis();
230     assertThat(duration).isAtLeast(COUNT_OF_REQUESTS_TO_FORWARD * 10L);
231     echoTestServer.stopServers();
232     echoTestServer.blockUntilShutdown();
233   }
234 
validateOutput(String output, int i)235   private static void validateOutput(String output, int i) {
236     List<String> content = Splitter.on('\n').splitToList(output);
237     assertThat(content.size()).isAtLeast(7); // see echo implementation
238     assertThat(content.get(0))
239         .isEqualTo(String.format("[%d] grpcecho.Echo(forward-echo-test-message)", i));
240     String prefix = "[" + i + " body] ";
241     assertThat(content).contains(prefix + "RequestHeader=x-request-id:" + i);
242     assertThat(content).contains(prefix + "RequestHeader=test-key1:test-value1");
243     assertThat(content).contains(prefix + "RequestHeader=test-key2:test-value2");
244     assertThat(content).contains(prefix + "Hostname=test-host");
245     assertThat(content).contains(prefix + "StatusCode=200");
246   }
247 
248   @Test
nonGrpcForwardEchoTest()249   public void nonGrpcForwardEchoTest() throws IOException, InterruptedException {
250     ForwardServiceForNonGrpcImpl forwardServiceForNonGrpc = new ForwardServiceForNonGrpcImpl();
251     forwardServiceForNonGrpc.receivedRequests = new ArrayList<>();
252     forwardServiceForNonGrpc.responsesToReturn = new ArrayList<>();
253     Server nonGrpcEchoServer =
254         EchoTestServer.runServer(
255             0, forwardServiceForNonGrpc.bindService(), InsecureServerCredentials.create(),
256                 "", false);
257     int nonGrpcEchoServerPort = nonGrpcEchoServer.getPort();
258 
259     EchoTestServer echoTestServer = new EchoTestServer();
260 
261     echoTestServer.runServers(
262         "test-host",
263         ImmutableList.of(0),
264         ImmutableList.of(),
265         ImmutableList.of(),
266         "0.0.0.0:" + nonGrpcEchoServerPort,
267         null);
268     assertEquals(1, echoTestServer.servers.size());
269     int port1 = echoTestServer.servers.get(0).getPort();
270 
271     ManagedChannelBuilder<?> channelBuilder =
272         Grpc.newChannelBuilderForAddress("localhost", port1, InsecureChannelCredentials.create());
273     ManagedChannel channel = channelBuilder.build();
274 
275     EchoTestServiceGrpc.EchoTestServiceBlockingStub stub =
276         EchoTestServiceGrpc.newBlockingStub(channel);
277 
278     forwardServiceForNonGrpc.responsesToReturn.add(
279         ForwardEchoResponse.newBuilder().addOutput("line 1").addOutput("line 2").build());
280 
281     ForwardEchoRequest forwardEchoRequest =
282         ForwardEchoRequest.newBuilder()
283             .setCount(COUNT_OF_REQUESTS_TO_FORWARD)
284             .setQps(100)
285             .setTimeoutMicros(2000_000L) // 2000 millis
286             .setUrl("http://www.example.com") // non grpc protocol
287             .addHeaders(
288                 Header.newBuilder().setKey("test-key1").setValue("test-value1").build())
289             .addHeaders(
290                 Header.newBuilder().setKey("test-key2").setValue("test-value2").build())
291             .setMessage("non-grpc-forward-echo-test-message1")
292             .build();
293 
294     ForwardEchoResponse forwardEchoResponse = stub.forwardEcho(forwardEchoRequest);
295     List<String> outputs = forwardEchoResponse.getOutputList();
296     assertEquals(2, outputs.size());
297     assertThat(outputs.get(0)).isEqualTo("line 1");
298     assertThat(outputs.get(1)).isEqualTo("line 2");
299 
300     assertThat(forwardServiceForNonGrpc.receivedRequests).hasSize(1);
301     ForwardEchoRequest receivedRequest = forwardServiceForNonGrpc.receivedRequests.remove(0);
302     assertThat(receivedRequest.getUrl()).isEqualTo("http://www.example.com");
303     assertThat(receivedRequest.getMessage()).isEqualTo("non-grpc-forward-echo-test-message1");
304     assertThat(receivedRequest.getCount()).isEqualTo(COUNT_OF_REQUESTS_TO_FORWARD);
305     assertThat(receivedRequest.getQps()).isEqualTo(100);
306 
307     forwardServiceForNonGrpc.responsesToReturn.add(
308         Status.UNIMPLEMENTED.asRuntimeException());
309     forwardEchoRequest =
310         ForwardEchoRequest.newBuilder()
311             .setCount(1)
312             .setQps(100)
313             .setTimeoutMicros(2000_000L) // 2000 millis
314             .setUrl("redis://192.168.1.1") // non grpc protocol
315             .addHeaders(
316                 Header.newBuilder().setKey("test-key1").setValue("test-value1").build())
317             .setMessage("non-grpc-forward-echo-test-message2")
318             .build();
319 
320     try {
321       ForwardEchoResponse unused = stub.forwardEcho(forwardEchoRequest);
322       fail("exception expected");
323     } catch (StatusRuntimeException e) {
324       assertThat(e.getStatus()).isEqualTo(Status.UNIMPLEMENTED);
325     }
326 
327     assertThat(forwardServiceForNonGrpc.receivedRequests).hasSize(1);
328     receivedRequest = forwardServiceForNonGrpc.receivedRequests.remove(0);
329     assertThat(receivedRequest.getUrl()).isEqualTo("redis://192.168.1.1");
330     assertThat(receivedRequest.getMessage()).isEqualTo("non-grpc-forward-echo-test-message2");
331     assertThat(receivedRequest.getCount()).isEqualTo(1);
332 
333     forwardServiceForNonGrpc.responsesToReturn.add(
334         ForwardEchoResponse.newBuilder().addOutput("line 3").build());
335 
336     forwardEchoRequest =
337         ForwardEchoRequest.newBuilder()
338             .setCount(1)
339             .setQps(100)
340             .setTimeoutMicros(2000_000L) // 2000 millis
341             .setUrl("http2://192.168.1.1") // non grpc protocol
342             .addHeaders(
343                 Header.newBuilder().setKey("test-key3").setValue("test-value3").build())
344             .setMessage("non-grpc-forward-echo-test-message3")
345             .build();
346     forwardEchoResponse = stub.forwardEcho(forwardEchoRequest);
347     outputs = forwardEchoResponse.getOutputList();
348     assertEquals(1, outputs.size());
349     assertThat(outputs.get(0)).isEqualTo("line 3");
350 
351     assertThat(forwardServiceForNonGrpc.receivedRequests).hasSize(1);
352     receivedRequest = forwardServiceForNonGrpc.receivedRequests.remove(0);
353     assertThat(receivedRequest.getUrl()).isEqualTo("http2://192.168.1.1");
354     assertThat(receivedRequest.getMessage()).isEqualTo("non-grpc-forward-echo-test-message3");
355     List<Header> headers = receivedRequest.getHeadersList();
356     assertThat(headers).hasSize(1);
357     assertThat(headers.get(0).getKey()).isEqualTo("test-key3");
358     assertThat(headers.get(0).getValue()).isEqualTo("test-value3");
359 
360     echoTestServer.stopServers();
361     echoTestServer.blockUntilShutdown();
362     nonGrpcEchoServer.shutdown();
363     nonGrpcEchoServer.awaitTermination(5, TimeUnit.SECONDS);
364   }
365 
366   /**
367    * Emulate the Go Echo server that receives the non-grpc protocol requests.
368    */
369   private static class ForwardServiceForNonGrpcImpl extends EchoTestServiceImplBase {
370 
371     List<ForwardEchoRequest> receivedRequests;
372     List<Object> responsesToReturn;
373 
374     @Override
forwardEcho(ForwardEchoRequest request, StreamObserver<ForwardEchoResponse> responseObserver)375     public void forwardEcho(ForwardEchoRequest request,
376         StreamObserver<ForwardEchoResponse> responseObserver) {
377       receivedRequests.add(request);
378       Object response = responsesToReturn.remove(0);
379       if (response instanceof Throwable) {
380         responseObserver.onError((Throwable) response);
381       } else if (response instanceof ForwardEchoResponse) {
382         responseObserver.onNext((ForwardEchoResponse) response);
383         responseObserver.onCompleted();
384       }
385       responseObserver.onError(new IllegalArgumentException("Unknown type in responsesToReturn"));
386     }
387   }
388 }
389