From 9f5e0443569ff47b9a77cc22c18fa71de8a5be6b Mon Sep 17 00:00:00 2001 From: eottabom Date: Tue, 4 Jun 2024 14:34:49 +0900 Subject: [PATCH 01/22] Add gRPC reverse proxy server example --- examples/grpc/build.gradle | 1 + examples/grpc/envoy/envoy.yaml | 45 +++++++++++++ .../grpc/GrpcReverseProxyServerTest.java | 65 +++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 examples/grpc/envoy/envoy.yaml create mode 100644 examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java diff --git a/examples/grpc/build.gradle b/examples/grpc/build.gradle index 615a44c1516..818e3586ea5 100644 --- a/examples/grpc/build.gradle +++ b/examples/grpc/build.gradle @@ -16,6 +16,7 @@ dependencies { testImplementation libs.awaitility testImplementation libs.junit5.jupiter.api testImplementation libs.protobuf.java.util + testImplementation libs.testcontainers.junit.jupiter } application { diff --git a/examples/grpc/envoy/envoy.yaml b/examples/grpc/envoy/envoy.yaml new file mode 100644 index 00000000000..70a5a76d581 --- /dev/null +++ b/examples/grpc/envoy/envoy.yaml @@ -0,0 +1,45 @@ +static_resources: + listeners: + - name: listener_0 + address: + socket_address: + address: 0.0.0.0 + port_value: 9090 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + codec_type: AUTO + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: ["*"] + routes: + - match: + prefix: "/" + route: + cluster: grpc_service + timeout: 0s + idle_timeout: 0s + http_filters: + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: grpc_service + connect_timeout: 1s + type: STRICT_DNS + lb_policy: round_robin + http2_protocol_options: {} + load_assignment: + cluster_name: grpc_service + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: host.docker.internal + port_value: 8080 \ No newline at end of file diff --git a/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java b/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java new file mode 100644 index 00000000000..9df459251e4 --- /dev/null +++ b/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java @@ -0,0 +1,65 @@ +package example.armeria.grpc; + +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.client.grpc.GrpcClients; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.server.grpc.GrpcService; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; + +import example.armeria.grpc.Hello.HelloReply; +import example.armeria.grpc.Hello.HelloRequest; +import org.testcontainers.junit.jupiter.Container; + +import static org.assertj.core.api.Assertions.assertThat; + +class GrpcReverseProxyServerTest { + + private static Server server; + + @Container + private static GenericContainer envoy = new GenericContainer<>("envoyproxy/envoy:v1.22.0") + .withExposedPorts(9090) + .withFileSystemBind("./envoy/envoy.yaml", "/etc/envoy/envoy.yaml", BindMode.READ_WRITE); + + + @BeforeAll + static void startServer() { + server = Server.builder() + .http(8080) + .service(GrpcService.builder() + .addService(new HelloServiceImpl()) + .build()) + .build(); + server.start().join(); + envoy.start(); + + } + + @AfterAll + static void stopServer() { + server.stop().join(); + envoy.stop(); + } + + @Test + void reverseProxy() { + + // given + String envoyAddress = "http://" + envoy.getHost() + ":" + envoy.getMappedPort(9090); + + WebClient client = WebClient.of(envoyAddress); + HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(client.uri()) + .build(HelloServiceGrpc.HelloServiceBlockingStub.class); + + // when + HelloReply reply = helloService.hello(HelloRequest.newBuilder().setName("Armeria").build()); + + // then + assertThat(reply.getMessage()).isEqualTo("Hello, Armeria!"); + } + +} From 4cacfc1edc3a4f95f3b750a41e0cc217de46d4eb Mon Sep 17 00:00:00 2001 From: eottabom Date: Tue, 4 Jun 2024 14:57:59 +0900 Subject: [PATCH 02/22] Add gRPC reverse proxy server example - style: Apply checkStyle --- .../grpc/GrpcReverseProxyServerTest.java | 100 +++++++++--------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java b/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java index 9df459251e4..8dc939950a5 100644 --- a/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java +++ b/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java @@ -1,65 +1,63 @@ package example.armeria.grpc; -import com.linecorp.armeria.client.WebClient; -import com.linecorp.armeria.client.grpc.GrpcClients; -import com.linecorp.armeria.server.Server; -import com.linecorp.armeria.server.grpc.GrpcService; +import static org.assertj.core.api.Assertions.assertThat; + import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; + +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.client.grpc.GrpcClients; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.server.grpc.GrpcService; import example.armeria.grpc.Hello.HelloReply; import example.armeria.grpc.Hello.HelloRequest; -import org.testcontainers.junit.jupiter.Container; - -import static org.assertj.core.api.Assertions.assertThat; class GrpcReverseProxyServerTest { - private static Server server; - - @Container - private static GenericContainer envoy = new GenericContainer<>("envoyproxy/envoy:v1.22.0") - .withExposedPorts(9090) - .withFileSystemBind("./envoy/envoy.yaml", "/etc/envoy/envoy.yaml", BindMode.READ_WRITE); - - - @BeforeAll - static void startServer() { - server = Server.builder() - .http(8080) - .service(GrpcService.builder() - .addService(new HelloServiceImpl()) - .build()) - .build(); - server.start().join(); - envoy.start(); - - } - - @AfterAll - static void stopServer() { - server.stop().join(); - envoy.stop(); - } - - @Test - void reverseProxy() { - - // given - String envoyAddress = "http://" + envoy.getHost() + ":" + envoy.getMappedPort(9090); - - WebClient client = WebClient.of(envoyAddress); - HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(client.uri()) - .build(HelloServiceGrpc.HelloServiceBlockingStub.class); - - // when - HelloReply reply = helloService.hello(HelloRequest.newBuilder().setName("Armeria").build()); - - // then - assertThat(reply.getMessage()).isEqualTo("Hello, Armeria!"); - } - + private static Server server; + + @Container + private static GenericContainer envoy = new GenericContainer<>("envoyproxy/envoy:v1.22.0") + .withExposedPorts(9090) + .withFileSystemBind("./envoy/envoy.yaml", "/etc/envoy/envoy.yaml", BindMode.READ_WRITE); + + @BeforeAll + static void startServer() { + server = Server.builder() + .http(8080) + .service(GrpcService.builder() + .addService(new HelloServiceImpl()) + .build()) + .build(); + server.start().join(); + envoy.start(); + } + + @AfterAll + static void stopServer() { + server.stop().join(); + envoy.stop(); + } + + @Test + void reverseProxy() { + + // given + final String envoyAddress = "http://" + envoy.getHost() + ":" + envoy.getMappedPort(9090); + + final WebClient client = WebClient.of(envoyAddress); + final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(client.uri()) + .build(HelloServiceGrpc.HelloServiceBlockingStub.class); + + // when + final HelloReply reply = helloService.hello(HelloRequest.newBuilder().setName("Armeria").build()); + + // then + assertThat(reply.getMessage()).isEqualTo("Hello, Armeria!"); + } } From 774b34ffd6cf1b2b75732ccede83a67efca81f8c Mon Sep 17 00:00:00 2001 From: eottabom Date: Tue, 4 Jun 2024 15:25:16 +0900 Subject: [PATCH 03/22] Add gRPC reverse proxy server example - fix typo --- examples/grpc/envoy/envoy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/grpc/envoy/envoy.yaml b/examples/grpc/envoy/envoy.yaml index 70a5a76d581..dbfd5e522bd 100644 --- a/examples/grpc/envoy/envoy.yaml +++ b/examples/grpc/envoy/envoy.yaml @@ -42,4 +42,4 @@ static_resources: address: socket_address: address: host.docker.internal - port_value: 8080 \ No newline at end of file + port_value: 8080 From 8f22629f4fdf627ade2f7f66ad82558009272d48 Mon Sep 17 00:00:00 2001 From: yukeun Date: Thu, 6 Jun 2024 01:12:35 +0900 Subject: [PATCH 04/22] Add gRPC reverse proxy server example - fix: Modify armeria server port --- examples/grpc/envoy/envoy.yaml | 2 +- .../java/example/armeria/grpc/GrpcReverseProxyServerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/grpc/envoy/envoy.yaml b/examples/grpc/envoy/envoy.yaml index dbfd5e522bd..c29af1c7995 100644 --- a/examples/grpc/envoy/envoy.yaml +++ b/examples/grpc/envoy/envoy.yaml @@ -42,4 +42,4 @@ static_resources: address: socket_address: address: host.docker.internal - port_value: 8080 + port_value: 18080 diff --git a/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java b/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java index 8dc939950a5..145eee9bbff 100644 --- a/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java +++ b/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java @@ -29,7 +29,7 @@ class GrpcReverseProxyServerTest { @BeforeAll static void startServer() { server = Server.builder() - .http(8080) + .http(18080) .service(GrpcService.builder() .addService(new HelloServiceImpl()) .build()) From 61c3429031f068f61cc646488166cc3d240f5d2c Mon Sep 17 00:00:00 2001 From: eottabom Date: Mon, 10 Jun 2024 12:24:38 +0900 Subject: [PATCH 05/22] ISSUE-2353: Add reverse proxy example - Remove reverse proxy in grpc project - Add reverse proxy project - Add reverse proxy example --- examples/grpc/build.gradle | 1 - .../grpc/GrpcReverseProxyServerTest.java | 63 ------------ examples/reverse-proxy/build.gradle | 25 +++++ .../{grpc => reverse-proxy}/envoy/envoy.yaml | 4 +- .../armeria/reverseproxy/HelloService.java | 29 ++++++ .../example/armeria/reverseproxy/Main.java | 85 ++++++++++++++++ .../reverse-proxy/src/main/proto/hello.proto | 19 ++++ .../reverse-proxy/src/main/resources/.gitkeep | 0 .../GrpcReverseProxyServerTest.java | 98 +++++++++++++++++++ settings.gradle | 2 + 10 files changed, 260 insertions(+), 66 deletions(-) delete mode 100644 examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java create mode 100644 examples/reverse-proxy/build.gradle rename examples/{grpc => reverse-proxy}/envoy/envoy.yaml (94%) create mode 100644 examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/HelloService.java create mode 100644 examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java create mode 100644 examples/reverse-proxy/src/main/proto/hello.proto create mode 100644 examples/reverse-proxy/src/main/resources/.gitkeep create mode 100644 examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java diff --git a/examples/grpc/build.gradle b/examples/grpc/build.gradle index 818e3586ea5..615a44c1516 100644 --- a/examples/grpc/build.gradle +++ b/examples/grpc/build.gradle @@ -16,7 +16,6 @@ dependencies { testImplementation libs.awaitility testImplementation libs.junit5.jupiter.api testImplementation libs.protobuf.java.util - testImplementation libs.testcontainers.junit.jupiter } application { diff --git a/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java b/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java deleted file mode 100644 index 145eee9bbff..00000000000 --- a/examples/grpc/src/test/java/example/armeria/grpc/GrpcReverseProxyServerTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package example.armeria.grpc; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.testcontainers.containers.BindMode; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.junit.jupiter.Container; - -import com.linecorp.armeria.client.WebClient; -import com.linecorp.armeria.client.grpc.GrpcClients; -import com.linecorp.armeria.server.Server; -import com.linecorp.armeria.server.grpc.GrpcService; - -import example.armeria.grpc.Hello.HelloReply; -import example.armeria.grpc.Hello.HelloRequest; - -class GrpcReverseProxyServerTest { - - private static Server server; - - @Container - private static GenericContainer envoy = new GenericContainer<>("envoyproxy/envoy:v1.22.0") - .withExposedPorts(9090) - .withFileSystemBind("./envoy/envoy.yaml", "/etc/envoy/envoy.yaml", BindMode.READ_WRITE); - - @BeforeAll - static void startServer() { - server = Server.builder() - .http(18080) - .service(GrpcService.builder() - .addService(new HelloServiceImpl()) - .build()) - .build(); - server.start().join(); - envoy.start(); - } - - @AfterAll - static void stopServer() { - server.stop().join(); - envoy.stop(); - } - - @Test - void reverseProxy() { - - // given - final String envoyAddress = "http://" + envoy.getHost() + ":" + envoy.getMappedPort(9090); - - final WebClient client = WebClient.of(envoyAddress); - final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(client.uri()) - .build(HelloServiceGrpc.HelloServiceBlockingStub.class); - - // when - final HelloReply reply = helloService.hello(HelloRequest.newBuilder().setName("Armeria").build()); - - // then - assertThat(reply.getMessage()).isEqualTo("Hello, Armeria!"); - } -} diff --git a/examples/reverse-proxy/build.gradle b/examples/reverse-proxy/build.gradle new file mode 100644 index 00000000000..0ac3c46a3d6 --- /dev/null +++ b/examples/reverse-proxy/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'application' +} + +dependencies { + implementation project(':core') + implementation project(':grpc') + implementation libs.reactor.core + implementation libs.testcontainers.junit.jupiter + compileOnly libs.javax.annotation + runtimeOnly libs.slf4j.simple + + testImplementation project(':junit5') + testImplementation libs.javax.annotation + testImplementation libs.json.unit.fluent + testImplementation libs.assertj + testImplementation libs.awaitility + testImplementation libs.junit5.jupiter.api + testImplementation libs.protobuf.java.util + testImplementation libs.testcontainers.junit.jupiter +} + +application { + mainClass.set('example.armeria.reverseproxy.Main') +} diff --git a/examples/grpc/envoy/envoy.yaml b/examples/reverse-proxy/envoy/envoy.yaml similarity index 94% rename from examples/grpc/envoy/envoy.yaml rename to examples/reverse-proxy/envoy/envoy.yaml index c29af1c7995..74b92e20d85 100644 --- a/examples/grpc/envoy/envoy.yaml +++ b/examples/reverse-proxy/envoy/envoy.yaml @@ -4,7 +4,7 @@ static_resources: address: socket_address: address: 0.0.0.0 - port_value: 9090 + port_value: ${ENVOY_PORT} filter_chains: - filters: - name: envoy.filters.network.http_connection_manager @@ -42,4 +42,4 @@ static_resources: address: socket_address: address: host.docker.internal - port_value: 18080 + port_value: ${SERVER_PORT} diff --git a/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/HelloService.java b/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/HelloService.java new file mode 100644 index 00000000000..7fc3b1fdf74 --- /dev/null +++ b/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/HelloService.java @@ -0,0 +1,29 @@ +package example.armeria.reverseproxy; + +import example.armeria.reverseproxy.Hello.HelloReply; +import example.armeria.reverseproxy.Hello.HelloRequest; +import example.armeria.reverseproxy.HelloServiceGrpc.HelloServiceImplBase; +import io.grpc.Status; +import io.grpc.stub.StreamObserver; + +public class HelloService extends HelloServiceImplBase { + + @Override + public void hello(HelloRequest request, StreamObserver responseObserver) { + if (request.getName().isEmpty()) { + responseObserver.onError( + Status.FAILED_PRECONDITION.withDescription("Name cannot be empty").asRuntimeException()); + } else { + responseObserver.onNext(buildReply(toMessage(request.getName()))); + responseObserver.onCompleted(); + } + } + + static String toMessage(String name) { + return "Hello, " + name + '!'; + } + + private static HelloReply buildReply(Object message) { + return HelloReply.newBuilder().setMessage(String.valueOf(message)).build(); + } +} diff --git a/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java b/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java new file mode 100644 index 00000000000..04d34d487c7 --- /dev/null +++ b/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java @@ -0,0 +1,85 @@ +package example.armeria.reverseproxy; + +import java.util.concurrent.CompletableFuture; + + +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.client.grpc.GrpcClients; +import com.linecorp.armeria.common.AggregatedHttpResponse; +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.internal.common.util.PortUtil; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.server.annotation.Get; +import com.linecorp.armeria.server.annotation.Param; +import com.linecorp.armeria.server.annotation.ProducesJson; +import com.linecorp.armeria.server.annotation.decorator.LoggingDecorator; +import com.linecorp.armeria.server.grpc.GrpcService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class Main { + + private static final Logger logger = LoggerFactory.getLogger(Main.class); + + private static final int serverPort = PortUtil.unusedTcpPort(); + private static final int backendServerPort = PortUtil.unusedTcpPort(); + + public static void main(String[] args) { + final Server backendServer = startBackendServer(); + + final Server reverseProxyServer = startReverseProxyServer(); + + backendServer.start().join(); + reverseProxyServer.start().join(); + + final WebClient client = WebClient.of("http://localhost:" + serverPort); + + final CompletableFuture responseFuture = client.get("/hello/John") + .aggregate(); + + responseFuture.thenAccept(response -> { + final String responseBody = response.contentUtf8(); + logger.info("Response from proxy server: " + responseBody); + }).exceptionally(throwable -> { + logger.error("Failed to receive response from proxy server: " + throwable.getMessage()); + return null; + }).thenRun(() -> { + backendServer.stop(); + reverseProxyServer.stop(); + }); + } + + private static Server startBackendServer() { + return Server.builder() + .http(backendServerPort) + .service(GrpcService.builder() + .addService(new HelloService()) + .build()) + .build(); + } + + private static Server startReverseProxyServer() { + return Server.builder() + .http(serverPort) + .annotatedService(new ReverseProxyService()) + .build(); + } + + public static class ReverseProxyService { + private final WebClient backendClient; + + public ReverseProxyService() { + backendClient = WebClient.builder("http://localhost:" + backendServerPort).build(); + } + + @Get("/hello/{name}") + @ProducesJson + @LoggingDecorator + public HttpResponse proxyHello(@Param("name") String name) { + final HelloServiceGrpc.HelloServiceBlockingStub stub = GrpcClients.builder(backendClient.uri()) + .build(HelloServiceGrpc.HelloServiceBlockingStub.class); + final Hello.HelloReply reply = stub.hello(Hello.HelloRequest.newBuilder().setName(name).build()); + return HttpResponse.ofJson(reply.getMessage()); + } + } +} diff --git a/examples/reverse-proxy/src/main/proto/hello.proto b/examples/reverse-proxy/src/main/proto/hello.proto new file mode 100644 index 00000000000..1010d788444 --- /dev/null +++ b/examples/reverse-proxy/src/main/proto/hello.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package example.grpc.hello; +option java_package = "example.armeria.reverseproxy"; +option java_multiple_files = false; + +import "google/api/annotations.proto"; + +service HelloService { + rpc Hello (HelloRequest) returns (HelloReply); +} + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string message = 1; +} diff --git a/examples/reverse-proxy/src/main/resources/.gitkeep b/examples/reverse-proxy/src/main/resources/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java new file mode 100644 index 00000000000..5d5e25af212 --- /dev/null +++ b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java @@ -0,0 +1,98 @@ +package example.armeria.reverseproxy; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.time.Duration; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; + +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.client.grpc.GrpcClients; +import com.linecorp.armeria.internal.common.util.PortUtil; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.server.grpc.GrpcService; + +import example.armeria.reverseproxy.Hello.HelloReply; +import example.armeria.reverseproxy.Hello.HelloRequest; + +class GrpcReverseProxyServerTest { + + private static Server server; + + private static final int serverPort = PortUtil.unusedTcpPort(); + + private static final int envoyPort = PortUtil.unusedTcpPort(); + + @Container + private static GenericContainer envoy; + + static { + try { + final Path source = Paths.get("./envoy/envoy.yaml"); + final Path destination = Paths.get("./envoy/envoy_backup.yaml"); + Files.copy(source, destination); + String envoyConfig = new String(Files.readAllBytes(source)); + envoyConfig = envoyConfig.replace("${ENVOY_PORT}", String.valueOf(envoyPort)) + .replace("${SERVER_PORT}", String.valueOf(serverPort)); + Files.write(source, envoyConfig.getBytes()); + envoy = new GenericContainer<>("envoyproxy/envoy:v1.22.0") + .withExposedPorts(envoyPort) + .withFileSystemBind(source.toString(), "/etc/envoy/envoy.yaml", BindMode.READ_WRITE) + .withStartupTimeout(Duration.ofSeconds(60)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @BeforeAll + static void startServer() { + server = Server.builder() + .http(serverPort) + .service(GrpcService.builder() + .addService(new HelloService()) + .build()) + .build(); + server.start().join(); + envoy.start(); + } + + @AfterAll + static void stopServer() { + try { + final Path source = Paths.get("./envoy/envoy_backup.yaml"); + final Path destination = Paths.get("./envoy/envoy.yaml"); + Files.move(source, destination, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + e.printStackTrace(); + } + server.stop().join(); + envoy.stop(); + } + + @Test + void reverseProxy() { + + // given + final String envoyAddress = "http://" + envoy.getHost() + ":" + envoy.getMappedPort(envoyPort); + + final WebClient client = WebClient.of(envoyAddress); + final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(client.uri()) + .build(HelloServiceGrpc.HelloServiceBlockingStub.class); + + // when + final HelloReply reply = helloService.hello(HelloRequest.newBuilder().setName("Armeria").build()); + + // then + assertThat(reply.getMessage()).isEqualTo("Hello, Armeria!"); + } +} diff --git a/settings.gradle b/settings.gradle index 41d6a2ecc73..1927698e747 100644 --- a/settings.gradle +++ b/settings.gradle @@ -259,6 +259,8 @@ includeWithFlags ':examples:grpc-krotodc', 'java11', 'kr includeWithFlags ':examples:grpc-scala', 'java11', 'scala-grpc_2.13', 'scala_2.13' includeWithFlags ':examples:grpc-reactor', 'java11', 'reactor-grpc' includeWithFlags ':examples:proxy-server', 'java11' +includeWithFlags ':examples:reverse-proxy-example', 'java11' +project(':examples:reverse-proxy-example').projectDir = file('examples/reverse-proxy') includeWithFlags ':examples:resilience4j-spring', 'java17' includeWithFlags ':examples:saml-service-provider', 'java11' includeWithFlags ':examples:server-sent-events', 'java11' From b4abca8f86ca425fef2fd20377d6f16723bc9347 Mon Sep 17 00:00:00 2001 From: eottabom Date: Mon, 10 Jun 2024 12:32:43 +0900 Subject: [PATCH 06/22] ISSUE-2353: Add reverse proxy example - Apply checkstyle --- .../src/main/java/example/armeria/reverseproxy/Main.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java b/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java index 04d34d487c7..6ecee900c2e 100644 --- a/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java +++ b/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java @@ -2,6 +2,8 @@ import java.util.concurrent.CompletableFuture; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.linecorp.armeria.client.WebClient; import com.linecorp.armeria.client.grpc.GrpcClients; @@ -14,8 +16,6 @@ import com.linecorp.armeria.server.annotation.ProducesJson; import com.linecorp.armeria.server.annotation.decorator.LoggingDecorator; import com.linecorp.armeria.server.grpc.GrpcService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public final class Main { From 00fa9624cc070bfb1d232d082a8b78eef3547b41 Mon Sep 17 00:00:00 2001 From: eottabom Date: Mon, 10 Jun 2024 15:19:36 +0900 Subject: [PATCH 07/22] ISSUE-2353: Add reverse proxy example - Modify envoy start, stop async --- .../GrpcReverseProxyServerTest.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java index 5d5e25af212..dbc5d8065b8 100644 --- a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java +++ b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java @@ -8,10 +8,13 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.time.Duration; +import java.util.concurrent.CompletableFuture; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; @@ -27,6 +30,8 @@ class GrpcReverseProxyServerTest { + private static final Logger logger = LoggerFactory.getLogger(GrpcReverseProxyServerTest.class); + private static Server server; private static final int serverPort = PortUtil.unusedTcpPort(); @@ -50,7 +55,7 @@ class GrpcReverseProxyServerTest { .withFileSystemBind(source.toString(), "/etc/envoy/envoy.yaml", BindMode.READ_WRITE) .withStartupTimeout(Duration.ofSeconds(60)); } catch (IOException e) { - e.printStackTrace(); + logger.error("Failed to configure Envoy container", e); } } @@ -63,7 +68,7 @@ static void startServer() { .build()) .build(); server.start().join(); - envoy.start(); + CompletableFuture.runAsync(() -> envoy.start()).join(); } @AfterAll @@ -73,10 +78,12 @@ static void stopServer() { final Path destination = Paths.get("./envoy/envoy.yaml"); Files.move(source, destination, StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { - e.printStackTrace(); + logger.error("Failed to restore Envoy configuration", e); } - server.stop().join(); - envoy.stop(); + CompletableFuture.runAsync(() -> { + server.stop().join(); + envoy.stop(); + }).join(); } @Test From 5759a6ca81c28010acb96b60a4dc144908a70919 Mon Sep 17 00:00:00 2001 From: eottabom Date: Mon, 10 Jun 2024 15:40:51 +0900 Subject: [PATCH 08/22] ISSUE-2353: Add reverse proxy example - Modify timeout --- examples/reverse-proxy/envoy/envoy.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/reverse-proxy/envoy/envoy.yaml b/examples/reverse-proxy/envoy/envoy.yaml index 74b92e20d85..07909889181 100644 --- a/examples/reverse-proxy/envoy/envoy.yaml +++ b/examples/reverse-proxy/envoy/envoy.yaml @@ -22,15 +22,15 @@ static_resources: prefix: "/" route: cluster: grpc_service - timeout: 0s - idle_timeout: 0s + timeout: 60s + idle_timeout: 60s http_filters: - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: grpc_service - connect_timeout: 1s + connect_timeout: 60s type: STRICT_DNS lb_policy: round_robin http2_protocol_options: {} From 7016aaae79c57b7500397257be573f29bf332989 Mon Sep 17 00:00:00 2001 From: eottabom Date: Mon, 10 Jun 2024 16:04:49 +0900 Subject: [PATCH 09/22] ISSUE-2353: Add reverse proxy example - Modify envoy start, stop --- .../GrpcReverseProxyServerTest.java | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java index dbc5d8065b8..c3ef3966cc3 100644 --- a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java +++ b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java @@ -68,7 +68,6 @@ static void startServer() { .build()) .build(); server.start().join(); - CompletableFuture.runAsync(() -> envoy.start()).join(); } @AfterAll @@ -80,26 +79,29 @@ static void stopServer() { } catch (IOException e) { logger.error("Failed to restore Envoy configuration", e); } - CompletableFuture.runAsync(() -> { - server.stop().join(); - envoy.stop(); - }).join(); + server.stop().join(); } @Test void reverseProxy() { // given - final String envoyAddress = "http://" + envoy.getHost() + ":" + envoy.getMappedPort(envoyPort); + final CompletableFuture envoyStartFuture = CompletableFuture.runAsync(() -> envoy.start()); + + envoyStartFuture.thenRun(() -> { + final String envoyAddress = "http://" + envoy.getHost() + ":" + envoy.getMappedPort(envoyPort); - final WebClient client = WebClient.of(envoyAddress); - final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(client.uri()) - .build(HelloServiceGrpc.HelloServiceBlockingStub.class); + final WebClient client = WebClient.of(envoyAddress); + final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(client.uri()) + .build(HelloServiceGrpc.HelloServiceBlockingStub.class); - // when - final HelloReply reply = helloService.hello(HelloRequest.newBuilder().setName("Armeria").build()); + // when + final HelloReply reply = helloService.hello(HelloRequest.newBuilder().setName("Armeria").build()); - // then - assertThat(reply.getMessage()).isEqualTo("Hello, Armeria!"); + // then + assertThat(reply.getMessage()).isEqualTo("Hello, Armeria!"); + envoy.stop(); + }); + envoyStartFuture.join(); } } From cd3d8e0d259f9a929addcb840d029f8cfc460578 Mon Sep 17 00:00:00 2001 From: eottabom Date: Mon, 10 Jun 2024 17:05:25 +0900 Subject: [PATCH 10/22] ISSUE-2353: Add reverse proxy example - Add try/catch CompletionException --- .../GrpcReverseProxyServerTest.java | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java index c3ef3966cc3..575910dea85 100644 --- a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java +++ b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java @@ -9,6 +9,7 @@ import java.nio.file.StandardCopyOption; import java.time.Duration; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -54,8 +55,8 @@ class GrpcReverseProxyServerTest { .withExposedPorts(envoyPort) .withFileSystemBind(source.toString(), "/etc/envoy/envoy.yaml", BindMode.READ_WRITE) .withStartupTimeout(Duration.ofSeconds(60)); - } catch (IOException e) { - logger.error("Failed to configure Envoy container", e); + } catch (IOException ex) { + logger.error("Failed to configure Envoy container", ex); } } @@ -76,8 +77,8 @@ static void stopServer() { final Path source = Paths.get("./envoy/envoy_backup.yaml"); final Path destination = Paths.get("./envoy/envoy.yaml"); Files.move(source, destination, StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - logger.error("Failed to restore Envoy configuration", e); + } catch (IOException ex) { + logger.error("Failed to restore Envoy configuration", ex); } server.stop().join(); } @@ -85,23 +86,27 @@ static void stopServer() { @Test void reverseProxy() { - // given - final CompletableFuture envoyStartFuture = CompletableFuture.runAsync(() -> envoy.start()); - - envoyStartFuture.thenRun(() -> { - final String envoyAddress = "http://" + envoy.getHost() + ":" + envoy.getMappedPort(envoyPort); - - final WebClient client = WebClient.of(envoyAddress); - final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(client.uri()) - .build(HelloServiceGrpc.HelloServiceBlockingStub.class); - - // when - final HelloReply reply = helloService.hello(HelloRequest.newBuilder().setName("Armeria").build()); - - // then - assertThat(reply.getMessage()).isEqualTo("Hello, Armeria!"); - envoy.stop(); - }); - envoyStartFuture.join(); + try { + // given + final CompletableFuture envoyStartFuture = CompletableFuture.runAsync(() -> envoy.start()); + + envoyStartFuture.thenRun(() -> { + final String envoyAddress = "http://" + envoy.getHost() + ":" + envoy.getMappedPort(envoyPort); + + final WebClient client = WebClient.of(envoyAddress); + final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(client.uri()) + .build(HelloServiceGrpc.HelloServiceBlockingStub.class); + + // when + final HelloReply reply = helloService.hello(HelloRequest.newBuilder().setName("Armeria").build()); + + // then + assertThat(reply.getMessage()).isEqualTo("Hello, Armeria!"); + envoy.stop(); + }); + envoyStartFuture.join(); + } catch (CompletionException ex) { + logger.error("Could not find a valid Docker environment", ex); + } } } From b1efcca5d5dda6b33e40853f780948f676da1627 Mon Sep 17 00:00:00 2001 From: eottabom Date: Mon, 10 Jun 2024 17:14:56 +0900 Subject: [PATCH 11/22] ISSUE-2353: Add reverse proxy example - Apply checkstyle --- .../armeria/reverseproxy/GrpcReverseProxyServerTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java index 575910dea85..01adc87b48d 100644 --- a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java +++ b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java @@ -98,7 +98,8 @@ void reverseProxy() { .build(HelloServiceGrpc.HelloServiceBlockingStub.class); // when - final HelloReply reply = helloService.hello(HelloRequest.newBuilder().setName("Armeria").build()); + final var helloRequest = HelloRequest.newBuilder().setName("Armeria").build(); + final HelloReply reply = helloService.hello(helloRequest); // then assertThat(reply.getMessage()).isEqualTo("Hello, Armeria!"); From ce6ab185e1024dc782f7431454c5b96a9df17c05 Mon Sep 17 00:00:00 2001 From: eottabom Date: Tue, 11 Jun 2024 10:08:38 +0900 Subject: [PATCH 12/22] ISSUE-2353: Add reverse proxy example - test: Using server port 0 --- .../GrpcReverseProxyServerTest.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java index 01adc87b48d..16b0bdba5e2 100644 --- a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java +++ b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java @@ -11,6 +11,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import com.linecorp.armeria.common.SessionProtocol; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -35,14 +36,23 @@ class GrpcReverseProxyServerTest { private static Server server; - private static final int serverPort = PortUtil.unusedTcpPort(); - private static final int envoyPort = PortUtil.unusedTcpPort(); @Container private static GenericContainer envoy; - static { + @BeforeAll + static void startServer() { + server = Server.builder() + .http(0) + .service(GrpcService.builder() + .addService(new HelloService()) + .build()) + .build(); + + server.start().join(); + final int serverPort = server.activeLocalPort(SessionProtocol.HTTP); + try { final Path source = Paths.get("./envoy/envoy.yaml"); final Path destination = Paths.get("./envoy/envoy_backup.yaml"); @@ -60,17 +70,6 @@ class GrpcReverseProxyServerTest { } } - @BeforeAll - static void startServer() { - server = Server.builder() - .http(serverPort) - .service(GrpcService.builder() - .addService(new HelloService()) - .build()) - .build(); - server.start().join(); - } - @AfterAll static void stopServer() { try { From 9424ddbc89eddaa53cc7193a6771d9bac5955284 Mon Sep 17 00:00:00 2001 From: eottabom Date: Tue, 11 Jun 2024 10:08:56 +0900 Subject: [PATCH 13/22] ISSUE-2353: Add reverse proxy example - fix: Modify default port --- .../main/java/example/armeria/reverseproxy/Main.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java b/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java index 6ecee900c2e..18b7b3c3dcc 100644 --- a/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java +++ b/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java @@ -9,7 +9,6 @@ import com.linecorp.armeria.client.grpc.GrpcClients; import com.linecorp.armeria.common.AggregatedHttpResponse; import com.linecorp.armeria.common.HttpResponse; -import com.linecorp.armeria.internal.common.util.PortUtil; import com.linecorp.armeria.server.Server; import com.linecorp.armeria.server.annotation.Get; import com.linecorp.armeria.server.annotation.Param; @@ -21,9 +20,6 @@ public final class Main { private static final Logger logger = LoggerFactory.getLogger(Main.class); - private static final int serverPort = PortUtil.unusedTcpPort(); - private static final int backendServerPort = PortUtil.unusedTcpPort(); - public static void main(String[] args) { final Server backendServer = startBackendServer(); @@ -32,7 +28,7 @@ public static void main(String[] args) { backendServer.start().join(); reverseProxyServer.start().join(); - final WebClient client = WebClient.of("http://localhost:" + serverPort); + final WebClient client = WebClient.of("http://localhost:9090"); final CompletableFuture responseFuture = client.get("/hello/John") .aggregate(); @@ -51,7 +47,7 @@ public static void main(String[] args) { private static Server startBackendServer() { return Server.builder() - .http(backendServerPort) + .http(8080) .service(GrpcService.builder() .addService(new HelloService()) .build()) @@ -60,7 +56,7 @@ private static Server startBackendServer() { private static Server startReverseProxyServer() { return Server.builder() - .http(serverPort) + .http(9090) .annotatedService(new ReverseProxyService()) .build(); } @@ -69,7 +65,7 @@ public static class ReverseProxyService { private final WebClient backendClient; public ReverseProxyService() { - backendClient = WebClient.builder("http://localhost:" + backendServerPort).build(); + backendClient = WebClient.builder("http://localhost:8080").build(); } @Get("/hello/{name}") From 4946a11aa09411de6e28c8ab7e8090d102d825c5 Mon Sep 17 00:00:00 2001 From: eottabom Date: Tue, 11 Jun 2024 10:48:42 +0900 Subject: [PATCH 14/22] ISSUE-2353: Add reverse proxy example - test: Apply checkstyle --- .../armeria/reverseproxy/GrpcReverseProxyServerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java index 16b0bdba5e2..79e9ee52d76 100644 --- a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java +++ b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java @@ -11,7 +11,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; -import com.linecorp.armeria.common.SessionProtocol; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -23,6 +22,7 @@ import com.linecorp.armeria.client.WebClient; import com.linecorp.armeria.client.grpc.GrpcClients; +import com.linecorp.armeria.common.SessionProtocol; import com.linecorp.armeria.internal.common.util.PortUtil; import com.linecorp.armeria.server.Server; import com.linecorp.armeria.server.grpc.GrpcService; From 0dd24d8225ab95b9adb2a77774cd5173a8cdb92c Mon Sep 17 00:00:00 2001 From: jrhee17 Date: Fri, 14 Jun 2024 14:51:49 +0900 Subject: [PATCH 15/22] minor updates --- .../build.gradle | 3 +- .../armeria/grpc/envoy/EnvoyContainer.java | 62 ++++++++++ .../armeria/grpc/envoy}/HelloService.java | 8 +- .../java/example/armeria/grpc/envoy/Main.java | 51 ++++++++ .../src/main/proto/hello.proto | 2 +- .../src/main/resources/.gitkeep | 0 .../src/main/resources}/envoy/envoy.yaml | 7 +- .../src/main/resources/envoy/launch_envoy.sh | 16 +++ .../grpc/envoy/GrpcEnvoyProxyTest.java | 50 ++++++++ .../example/armeria/reverseproxy/Main.java | 81 ------------- .../GrpcReverseProxyServerTest.java | 112 ------------------ settings.gradle | 3 +- 12 files changed, 191 insertions(+), 204 deletions(-) rename examples/{reverse-proxy => grpc-envoy}/build.gradle (84%) create mode 100644 examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/EnvoyContainer.java rename examples/{reverse-proxy/src/main/java/example/armeria/reverseproxy => grpc-envoy/src/main/java/example/armeria/grpc/envoy}/HelloService.java (78%) create mode 100644 examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/Main.java rename examples/{reverse-proxy => grpc-envoy}/src/main/proto/hello.proto (84%) rename examples/{reverse-proxy => grpc-envoy}/src/main/resources/.gitkeep (100%) rename examples/{reverse-proxy => grpc-envoy/src/main/resources}/envoy/envoy.yaml (90%) create mode 100755 examples/grpc-envoy/src/main/resources/envoy/launch_envoy.sh create mode 100644 examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java delete mode 100644 examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java delete mode 100644 examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java diff --git a/examples/reverse-proxy/build.gradle b/examples/grpc-envoy/build.gradle similarity index 84% rename from examples/reverse-proxy/build.gradle rename to examples/grpc-envoy/build.gradle index 0ac3c46a3d6..4d3c4133516 100644 --- a/examples/reverse-proxy/build.gradle +++ b/examples/grpc-envoy/build.gradle @@ -17,9 +17,8 @@ dependencies { testImplementation libs.awaitility testImplementation libs.junit5.jupiter.api testImplementation libs.protobuf.java.util - testImplementation libs.testcontainers.junit.jupiter } application { - mainClass.set('example.armeria.reverseproxy.Main') + mainClass.set('example.armeria.grpc.envoy.Main') } diff --git a/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/EnvoyContainer.java b/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/EnvoyContainer.java new file mode 100644 index 00000000000..d62d55767fb --- /dev/null +++ b/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/EnvoyContainer.java @@ -0,0 +1,62 @@ +package example.armeria.grpc.envoy; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; + +import com.github.dockerjava.api.command.InspectContainerResponse; + +import com.linecorp.armeria.common.annotation.Nullable; + +// https://github.com/envoyproxy/java-control-plane/blob/eaca1a4380e53b4b6339db4e9ffe0ada5e0b7f8f/server/src/test/java/io/envoyproxy/controlplane/server/EnvoyContainer.java +class EnvoyContainer extends GenericContainer { + + private static final Logger LOGGER = LoggerFactory.getLogger(EnvoyContainer.class); + + private static final String CONFIG_DEST = "/etc/envoy/envoy.yaml"; + private static final String LAUNCH_ENVOY_SCRIPT = "envoy/launch_envoy.sh"; + private static final String LAUNCH_ENVOY_SCRIPT_DEST = "/usr/local/bin/launch_envoy.sh"; + + static final int ADMIN_PORT = 9901; + + private final String config; + @Nullable + private final String sedCommand; + + /** + * A {@link GenericContainer} implementation for envoy containers. + * + * @param sedCommand optional sed command which may be used to postprocess the provided {@param config}. + * This parameter will be fed into the command {@code sed -e }. + * An example command may be {@code "s/foo/bar/g;s/abc/def/g"}. + */ + EnvoyContainer(String config, @Nullable String sedCommand) { + super("envoyproxy/envoy:v1.30.1"); + this.config = config; + this.sedCommand = sedCommand; + } + + @Override + protected void configure() { + super.configure(); + + withClasspathResourceMapping(LAUNCH_ENVOY_SCRIPT, LAUNCH_ENVOY_SCRIPT_DEST, BindMode.READ_ONLY); + withClasspathResourceMapping(config, CONFIG_DEST, BindMode.READ_ONLY); + + if (sedCommand != null) { + withCommand("/bin/bash", "/usr/local/bin/launch_envoy.sh", + sedCommand, CONFIG_DEST, "-l", "debug"); + } + + addExposedPort(ADMIN_PORT); + } + + @Override + protected void containerIsStarting(InspectContainerResponse containerInfo) { + followOutput(new Slf4jLogConsumer(LOGGER).withPrefix("ENVOY")); + + super.containerIsStarting(containerInfo); + } +} diff --git a/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/HelloService.java b/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/HelloService.java similarity index 78% rename from examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/HelloService.java rename to examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/HelloService.java index 7fc3b1fdf74..9622c5e6d90 100644 --- a/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/HelloService.java +++ b/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/HelloService.java @@ -1,8 +1,8 @@ -package example.armeria.reverseproxy; +package example.armeria.grpc.envoy; -import example.armeria.reverseproxy.Hello.HelloReply; -import example.armeria.reverseproxy.Hello.HelloRequest; -import example.armeria.reverseproxy.HelloServiceGrpc.HelloServiceImplBase; +import example.armeria.grpc.envoy.Hello.HelloReply; +import example.armeria.grpc.envoy.Hello.HelloRequest; +import example.armeria.grpc.envoy.HelloServiceGrpc.HelloServiceImplBase; import io.grpc.Status; import io.grpc.stub.StreamObserver; diff --git a/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/Main.java b/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/Main.java new file mode 100644 index 00000000000..e38f9170d47 --- /dev/null +++ b/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/Main.java @@ -0,0 +1,51 @@ +package example.armeria.grpc.envoy; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.DockerClientFactory; + +import com.linecorp.armeria.common.util.ShutdownHooks; +import com.linecorp.armeria.server.Server; +import com.linecorp.armeria.server.grpc.GrpcService; + +public final class Main { + + private static final Logger logger = LoggerFactory.getLogger(Main.class); + + private static final int serverPort = 8080; + private static final int envoyPort = 10000; + + public static void main(String[] args) { + if (!DockerClientFactory.instance().isDockerAvailable()) { + throw new IllegalStateException("Docker is not available"); + } + + final Server backendServer = startBackendServer(serverPort); + backendServer.closeOnJvmShutdown(); + backendServer.start().join(); + logger.info("Serving backend at http://127.0.0.1:{}/", backendServer.activePort()); + + final EnvoyContainer envoyProxy = configureEnvoy(serverPort, envoyPort); + ShutdownHooks.addClosingTask(envoyProxy::stop); + envoyProxy.start(); + final Integer mappedEnvoyPort = envoyProxy.getMappedPort(envoyPort); + logger.info("Serving envoy at http://127.0.0.1:{}/", mappedEnvoyPort); + } + + private static Server startBackendServer(int serverPort) { + return Server.builder() + .http(serverPort) + .service(GrpcService.builder() + .addService(new HelloService()) + .build()) + .build(); + } + + static EnvoyContainer configureEnvoy(int serverPort, int envoyPort) { + final String sedPattern = String.format("s/SERVER_PORT/%s/g;s/ENVOY_PORT/%s/g", serverPort, envoyPort); + return new EnvoyContainer("envoy/envoy.yaml", sedPattern) + .withExposedPorts(envoyPort); + } + + private Main() {} +} diff --git a/examples/reverse-proxy/src/main/proto/hello.proto b/examples/grpc-envoy/src/main/proto/hello.proto similarity index 84% rename from examples/reverse-proxy/src/main/proto/hello.proto rename to examples/grpc-envoy/src/main/proto/hello.proto index 1010d788444..f5c4ac2c9a7 100644 --- a/examples/reverse-proxy/src/main/proto/hello.proto +++ b/examples/grpc-envoy/src/main/proto/hello.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package example.grpc.hello; -option java_package = "example.armeria.reverseproxy"; +option java_package = "example.armeria.grpc.envoy"; option java_multiple_files = false; import "google/api/annotations.proto"; diff --git a/examples/reverse-proxy/src/main/resources/.gitkeep b/examples/grpc-envoy/src/main/resources/.gitkeep similarity index 100% rename from examples/reverse-proxy/src/main/resources/.gitkeep rename to examples/grpc-envoy/src/main/resources/.gitkeep diff --git a/examples/reverse-proxy/envoy/envoy.yaml b/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml similarity index 90% rename from examples/reverse-proxy/envoy/envoy.yaml rename to examples/grpc-envoy/src/main/resources/envoy/envoy.yaml index 07909889181..20d4f615e67 100644 --- a/examples/reverse-proxy/envoy/envoy.yaml +++ b/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml @@ -1,10 +1,13 @@ +admin: + address: + socket_address: { address: 0.0.0.0, port_value: 9901 } static_resources: listeners: - name: listener_0 address: socket_address: address: 0.0.0.0 - port_value: ${ENVOY_PORT} + port_value: ENVOY_PORT filter_chains: - filters: - name: envoy.filters.network.http_connection_manager @@ -42,4 +45,4 @@ static_resources: address: socket_address: address: host.docker.internal - port_value: ${SERVER_PORT} + port_value: SERVER_PORT diff --git a/examples/grpc-envoy/src/main/resources/envoy/launch_envoy.sh b/examples/grpc-envoy/src/main/resources/envoy/launch_envoy.sh new file mode 100755 index 00000000000..34581990ee6 --- /dev/null +++ b/examples/grpc-envoy/src/main/resources/envoy/launch_envoy.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +SED_COMMAND=$1 + +CONFIG=$(cat $2) +CONFIG_DIR=$(mktemp -d) +CONFIG_FILE="$CONFIG_DIR/envoy.yaml" + +echo "${CONFIG}" | sed -e "${SED_COMMAND}" > "${CONFIG_FILE}" + + +shift 2 +/usr/local/bin/envoy --drain-time-s 1 -c "${CONFIG_FILE}" "$@" + +rm -rf "${CONFIG_DIR}" diff --git a/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java b/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java new file mode 100644 index 00000000000..db109f261f9 --- /dev/null +++ b/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java @@ -0,0 +1,50 @@ +package example.armeria.grpc.envoy; + +import static example.armeria.grpc.envoy.Main.configureEnvoy; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.testcontainers.junit.jupiter.Testcontainers; + +import com.linecorp.armeria.client.grpc.GrpcClients; +import com.linecorp.armeria.server.ServerBuilder; +import com.linecorp.armeria.server.grpc.GrpcService; +import com.linecorp.armeria.testing.junit5.server.ServerExtension; + +import example.armeria.grpc.envoy.Hello.HelloReply; +import example.armeria.grpc.envoy.Hello.HelloRequest; + +@Testcontainers(disabledWithoutDocker = true) +class GrpcEnvoyProxyTest { + + // the port envoy binds to within the container + private static final int envoyPort = 10000; + + @RegisterExtension + static ServerExtension server = new ServerExtension() { + @Override + protected void configure(ServerBuilder sb) throws Exception { + sb.service(GrpcService.builder() + .addService(new HelloService()) + .build()); + } + }; + + @Test + void reverseProxy() { + org.testcontainers.Testcontainers.exposeHostPorts(server.httpPort()); + try (EnvoyContainer envoy = configureEnvoy(server.httpPort(), envoyPort)) { + envoy.start(); + final String uri = "http://" + envoy.getHost() + ':' + envoy.getMappedPort(envoyPort); + final HelloServiceGrpc.HelloServiceBlockingStub helloService = + GrpcClients.builder(uri) + .build(HelloServiceGrpc.HelloServiceBlockingStub.class); + final HelloReply reply = + helloService.hello(HelloRequest.newBuilder() + .setName("Armeria") + .build()); + assertThat(reply.getMessage()).isEqualTo("Hello, Armeria!"); + } + } +} diff --git a/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java b/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java deleted file mode 100644 index 18b7b3c3dcc..00000000000 --- a/examples/reverse-proxy/src/main/java/example/armeria/reverseproxy/Main.java +++ /dev/null @@ -1,81 +0,0 @@ -package example.armeria.reverseproxy; - -import java.util.concurrent.CompletableFuture; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.linecorp.armeria.client.WebClient; -import com.linecorp.armeria.client.grpc.GrpcClients; -import com.linecorp.armeria.common.AggregatedHttpResponse; -import com.linecorp.armeria.common.HttpResponse; -import com.linecorp.armeria.server.Server; -import com.linecorp.armeria.server.annotation.Get; -import com.linecorp.armeria.server.annotation.Param; -import com.linecorp.armeria.server.annotation.ProducesJson; -import com.linecorp.armeria.server.annotation.decorator.LoggingDecorator; -import com.linecorp.armeria.server.grpc.GrpcService; - -public final class Main { - - private static final Logger logger = LoggerFactory.getLogger(Main.class); - - public static void main(String[] args) { - final Server backendServer = startBackendServer(); - - final Server reverseProxyServer = startReverseProxyServer(); - - backendServer.start().join(); - reverseProxyServer.start().join(); - - final WebClient client = WebClient.of("http://localhost:9090"); - - final CompletableFuture responseFuture = client.get("/hello/John") - .aggregate(); - - responseFuture.thenAccept(response -> { - final String responseBody = response.contentUtf8(); - logger.info("Response from proxy server: " + responseBody); - }).exceptionally(throwable -> { - logger.error("Failed to receive response from proxy server: " + throwable.getMessage()); - return null; - }).thenRun(() -> { - backendServer.stop(); - reverseProxyServer.stop(); - }); - } - - private static Server startBackendServer() { - return Server.builder() - .http(8080) - .service(GrpcService.builder() - .addService(new HelloService()) - .build()) - .build(); - } - - private static Server startReverseProxyServer() { - return Server.builder() - .http(9090) - .annotatedService(new ReverseProxyService()) - .build(); - } - - public static class ReverseProxyService { - private final WebClient backendClient; - - public ReverseProxyService() { - backendClient = WebClient.builder("http://localhost:8080").build(); - } - - @Get("/hello/{name}") - @ProducesJson - @LoggingDecorator - public HttpResponse proxyHello(@Param("name") String name) { - final HelloServiceGrpc.HelloServiceBlockingStub stub = GrpcClients.builder(backendClient.uri()) - .build(HelloServiceGrpc.HelloServiceBlockingStub.class); - final Hello.HelloReply reply = stub.hello(Hello.HelloRequest.newBuilder().setName(name).build()); - return HttpResponse.ofJson(reply.getMessage()); - } - } -} diff --git a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java b/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java deleted file mode 100644 index 79e9ee52d76..00000000000 --- a/examples/reverse-proxy/src/test/java/example/armeria/reverseproxy/GrpcReverseProxyServerTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package example.armeria.reverseproxy; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.time.Duration; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testcontainers.containers.BindMode; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.junit.jupiter.Container; - -import com.linecorp.armeria.client.WebClient; -import com.linecorp.armeria.client.grpc.GrpcClients; -import com.linecorp.armeria.common.SessionProtocol; -import com.linecorp.armeria.internal.common.util.PortUtil; -import com.linecorp.armeria.server.Server; -import com.linecorp.armeria.server.grpc.GrpcService; - -import example.armeria.reverseproxy.Hello.HelloReply; -import example.armeria.reverseproxy.Hello.HelloRequest; - -class GrpcReverseProxyServerTest { - - private static final Logger logger = LoggerFactory.getLogger(GrpcReverseProxyServerTest.class); - - private static Server server; - - private static final int envoyPort = PortUtil.unusedTcpPort(); - - @Container - private static GenericContainer envoy; - - @BeforeAll - static void startServer() { - server = Server.builder() - .http(0) - .service(GrpcService.builder() - .addService(new HelloService()) - .build()) - .build(); - - server.start().join(); - final int serverPort = server.activeLocalPort(SessionProtocol.HTTP); - - try { - final Path source = Paths.get("./envoy/envoy.yaml"); - final Path destination = Paths.get("./envoy/envoy_backup.yaml"); - Files.copy(source, destination); - String envoyConfig = new String(Files.readAllBytes(source)); - envoyConfig = envoyConfig.replace("${ENVOY_PORT}", String.valueOf(envoyPort)) - .replace("${SERVER_PORT}", String.valueOf(serverPort)); - Files.write(source, envoyConfig.getBytes()); - envoy = new GenericContainer<>("envoyproxy/envoy:v1.22.0") - .withExposedPorts(envoyPort) - .withFileSystemBind(source.toString(), "/etc/envoy/envoy.yaml", BindMode.READ_WRITE) - .withStartupTimeout(Duration.ofSeconds(60)); - } catch (IOException ex) { - logger.error("Failed to configure Envoy container", ex); - } - } - - @AfterAll - static void stopServer() { - try { - final Path source = Paths.get("./envoy/envoy_backup.yaml"); - final Path destination = Paths.get("./envoy/envoy.yaml"); - Files.move(source, destination, StandardCopyOption.REPLACE_EXISTING); - } catch (IOException ex) { - logger.error("Failed to restore Envoy configuration", ex); - } - server.stop().join(); - } - - @Test - void reverseProxy() { - - try { - // given - final CompletableFuture envoyStartFuture = CompletableFuture.runAsync(() -> envoy.start()); - - envoyStartFuture.thenRun(() -> { - final String envoyAddress = "http://" + envoy.getHost() + ":" + envoy.getMappedPort(envoyPort); - - final WebClient client = WebClient.of(envoyAddress); - final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(client.uri()) - .build(HelloServiceGrpc.HelloServiceBlockingStub.class); - - // when - final var helloRequest = HelloRequest.newBuilder().setName("Armeria").build(); - final HelloReply reply = helloService.hello(helloRequest); - - // then - assertThat(reply.getMessage()).isEqualTo("Hello, Armeria!"); - envoy.stop(); - }); - envoyStartFuture.join(); - } catch (CompletionException ex) { - logger.error("Could not find a valid Docker environment", ex); - } - } -} diff --git a/settings.gradle b/settings.gradle index 1927698e747..e335e009490 100644 --- a/settings.gradle +++ b/settings.gradle @@ -252,6 +252,7 @@ includeWithFlags ':examples:graphql-kotlin-example', 'java17', 'ko project(':examples:graphql-kotlin-example').projectDir = file('examples/graphql-kotlin') includeWithFlags ':examples:graphql-sangria-example', 'java11', 'scala_2.13' project(':examples:graphql-sangria-example').projectDir = file('examples/graphql-sangria') +includeWithFlags ':examples:grpc-envoy', 'java11' includeWithFlags ':examples:grpc-example', 'java11' project(':examples:grpc-example').projectDir = file('examples/grpc') includeWithFlags ':examples:grpc-kotlin', 'java11', 'kotlin-grpc', 'kotlin' @@ -259,8 +260,6 @@ includeWithFlags ':examples:grpc-krotodc', 'java11', 'kr includeWithFlags ':examples:grpc-scala', 'java11', 'scala-grpc_2.13', 'scala_2.13' includeWithFlags ':examples:grpc-reactor', 'java11', 'reactor-grpc' includeWithFlags ':examples:proxy-server', 'java11' -includeWithFlags ':examples:reverse-proxy-example', 'java11' -project(':examples:reverse-proxy-example').projectDir = file('examples/reverse-proxy') includeWithFlags ':examples:resilience4j-spring', 'java17' includeWithFlags ':examples:saml-service-provider', 'java11' includeWithFlags ':examples:server-sent-events', 'java11' From 958c0f63cd04e6c2a39610b7c3155141a84de00d Mon Sep 17 00:00:00 2001 From: jrhee17 Date: Fri, 14 Jun 2024 14:57:37 +0900 Subject: [PATCH 16/22] test for multiple protocols --- .../grpc-envoy/src/main/resources/envoy/envoy.yaml | 14 +++++++++----- .../armeria/grpc/envoy/GrpcEnvoyProxyTest.java | 13 ++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml b/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml index 20d4f615e67..40182cf7670 100644 --- a/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml +++ b/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml @@ -14,6 +14,8 @@ static_resources: typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http + http_protocol_options: + enable_trailers: true codec_type: AUTO route_config: name: local_route @@ -25,18 +27,20 @@ static_resources: prefix: "/" route: cluster: grpc_service - timeout: 60s - idle_timeout: 60s http_filters: - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: grpc_service - connect_timeout: 60s type: STRICT_DNS - lb_policy: round_robin - http2_protocol_options: {} + lb_policy: ROUND_ROBIN + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http_protocol_options: + enable_trailers: true load_assignment: cluster_name: grpc_service endpoints: diff --git a/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java b/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java index db109f261f9..6b37ab24a97 100644 --- a/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java +++ b/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java @@ -3,11 +3,13 @@ import static example.armeria.grpc.envoy.Main.configureEnvoy; import static org.assertj.core.api.Assertions.assertThat; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.testcontainers.junit.jupiter.Testcontainers; import com.linecorp.armeria.client.grpc.GrpcClients; +import com.linecorp.armeria.common.SessionProtocol; import com.linecorp.armeria.server.ServerBuilder; import com.linecorp.armeria.server.grpc.GrpcService; import com.linecorp.armeria.testing.junit5.server.ServerExtension; @@ -31,12 +33,13 @@ protected void configure(ServerBuilder sb) throws Exception { } }; - @Test - void reverseProxy() { - org.testcontainers.Testcontainers.exposeHostPorts(server.httpPort()); + @ParameterizedTest + @EnumSource(value = SessionProtocol.class, names = {"H1C", "H2C"}) + void reverseProxy(SessionProtocol sessionProtocol) { try (EnvoyContainer envoy = configureEnvoy(server.httpPort(), envoyPort)) { envoy.start(); - final String uri = "http://" + envoy.getHost() + ':' + envoy.getMappedPort(envoyPort); + final String uri = sessionProtocol.uriText() + "://" + envoy.getHost() + + ':' + envoy.getMappedPort(envoyPort); final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(uri) .build(HelloServiceGrpc.HelloServiceBlockingStub.class); From 4ff7116331b73921fb54078cacd9d2a1159d7f70 Mon Sep 17 00:00:00 2001 From: jrhee17 Date: Fri, 14 Jun 2024 15:07:37 +0900 Subject: [PATCH 17/22] remove unnecessary dependencies --- examples/grpc-envoy/build.gradle | 7 ------- .../src/main/java/example/armeria/grpc/envoy/Main.java | 1 + 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/examples/grpc-envoy/build.gradle b/examples/grpc-envoy/build.gradle index 4d3c4133516..33b3cb0e82b 100644 --- a/examples/grpc-envoy/build.gradle +++ b/examples/grpc-envoy/build.gradle @@ -5,18 +5,11 @@ plugins { dependencies { implementation project(':core') implementation project(':grpc') - implementation libs.reactor.core implementation libs.testcontainers.junit.jupiter compileOnly libs.javax.annotation runtimeOnly libs.slf4j.simple testImplementation project(':junit5') - testImplementation libs.javax.annotation - testImplementation libs.json.unit.fluent - testImplementation libs.assertj - testImplementation libs.awaitility - testImplementation libs.junit5.jupiter.api - testImplementation libs.protobuf.java.util } application { diff --git a/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/Main.java b/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/Main.java index e38f9170d47..d0c3e12807a 100644 --- a/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/Main.java +++ b/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/Main.java @@ -13,6 +13,7 @@ public final class Main { private static final Logger logger = LoggerFactory.getLogger(Main.class); private static final int serverPort = 8080; + // the port envoy binds to within the container private static final int envoyPort = 10000; public static void main(String[] args) { From b3fd20073655156a6dc1eef06a9e38031eaac667 Mon Sep 17 00:00:00 2001 From: jrhee17 Date: Fri, 14 Jun 2024 16:57:59 +0900 Subject: [PATCH 18/22] handle flakiness --- .../example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java b/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java index 6b37ab24a97..f4f8d0e5404 100644 --- a/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java +++ b/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java @@ -9,6 +9,8 @@ import org.testcontainers.junit.jupiter.Testcontainers; import com.linecorp.armeria.client.grpc.GrpcClients; +import com.linecorp.armeria.client.retry.RetryRule; +import com.linecorp.armeria.client.retry.RetryingClient; import com.linecorp.armeria.common.SessionProtocol; import com.linecorp.armeria.server.ServerBuilder; import com.linecorp.armeria.server.grpc.GrpcService; @@ -40,8 +42,14 @@ void reverseProxy(SessionProtocol sessionProtocol) { envoy.start(); final String uri = sessionProtocol.uriText() + "://" + envoy.getHost() + ':' + envoy.getMappedPort(envoyPort); + // if envoy isn't ready for requests, a `UNAVAILABLE: no healthy upstream` is returned + final RetryRule retryRule = + RetryRule.builder() + .onGrpcTrailers((ctx, trailers) -> trailers.containsInt("grpc-status", 14)) + .thenBackoff(); final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(uri) + .decorator(RetryingClient.newDecorator(retryRule)) .build(HelloServiceGrpc.HelloServiceBlockingStub.class); final HelloReply reply = helloService.hello(HelloRequest.newBuilder() From 39322a3e53fe23bd9c0d3b7ddf2dcde5e04d2d1d Mon Sep 17 00:00:00 2001 From: jrhee17 Date: Fri, 14 Jun 2024 17:48:16 +0900 Subject: [PATCH 19/22] retry for http status unavailable --- .../java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java b/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java index f4f8d0e5404..36d77c58807 100644 --- a/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java +++ b/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java @@ -43,10 +43,7 @@ void reverseProxy(SessionProtocol sessionProtocol) { final String uri = sessionProtocol.uriText() + "://" + envoy.getHost() + ':' + envoy.getMappedPort(envoyPort); // if envoy isn't ready for requests, a `UNAVAILABLE: no healthy upstream` is returned - final RetryRule retryRule = - RetryRule.builder() - .onGrpcTrailers((ctx, trailers) -> trailers.containsInt("grpc-status", 14)) - .thenBackoff(); + final RetryRule retryRule = RetryRule.builder().onServerErrorStatus().thenBackoff(); final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(uri) .decorator(RetryingClient.newDecorator(retryRule)) From 70a7c66148c9d09e5cdd69c24bfe0994c58e0ac8 Mon Sep 17 00:00:00 2001 From: jrhee17 Date: Fri, 14 Jun 2024 18:41:33 +0900 Subject: [PATCH 20/22] use testcontainers host, open the host port preemptively --- examples/grpc-envoy/src/main/resources/envoy/envoy.yaml | 2 +- .../java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml b/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml index 40182cf7670..983a7c0f775 100644 --- a/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml +++ b/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml @@ -48,5 +48,5 @@ static_resources: - endpoint: address: socket_address: - address: host.docker.internal + address: host.testcontainers.internal port_value: SERVER_PORT diff --git a/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java b/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java index 36d77c58807..01b9f3b622a 100644 --- a/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java +++ b/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java @@ -9,8 +9,6 @@ import org.testcontainers.junit.jupiter.Testcontainers; import com.linecorp.armeria.client.grpc.GrpcClients; -import com.linecorp.armeria.client.retry.RetryRule; -import com.linecorp.armeria.client.retry.RetryingClient; import com.linecorp.armeria.common.SessionProtocol; import com.linecorp.armeria.server.ServerBuilder; import com.linecorp.armeria.server.grpc.GrpcService; @@ -38,15 +36,13 @@ protected void configure(ServerBuilder sb) throws Exception { @ParameterizedTest @EnumSource(value = SessionProtocol.class, names = {"H1C", "H2C"}) void reverseProxy(SessionProtocol sessionProtocol) { + org.testcontainers.Testcontainers.exposeHostPorts(server.httpPort()); try (EnvoyContainer envoy = configureEnvoy(server.httpPort(), envoyPort)) { envoy.start(); final String uri = sessionProtocol.uriText() + "://" + envoy.getHost() + ':' + envoy.getMappedPort(envoyPort); - // if envoy isn't ready for requests, a `UNAVAILABLE: no healthy upstream` is returned - final RetryRule retryRule = RetryRule.builder().onServerErrorStatus().thenBackoff(); final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(uri) - .decorator(RetryingClient.newDecorator(retryRule)) .build(HelloServiceGrpc.HelloServiceBlockingStub.class); final HelloReply reply = helloService.hello(HelloRequest.newBuilder() From f2569dcfe364aedc78ff63139d90eda6be5e5e75 Mon Sep 17 00:00:00 2001 From: eottabom Date: Wed, 19 Jun 2024 16:15:56 +0900 Subject: [PATCH 21/22] ISSUE-2353: Add reverse proxy example - Apply code style - Add dependencies - Modify variable name --- examples/grpc-envoy/build.gradle | 2 ++ .../src/main/java/example/armeria/grpc/envoy/Main.java | 10 +++++----- .../example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/grpc-envoy/build.gradle b/examples/grpc-envoy/build.gradle index 33b3cb0e82b..1203c0986af 100644 --- a/examples/grpc-envoy/build.gradle +++ b/examples/grpc-envoy/build.gradle @@ -10,6 +10,8 @@ dependencies { runtimeOnly libs.slf4j.simple testImplementation project(':junit5') + testImplementation libs.assertj + testImplementation libs.junit5.jupiter.api } application { diff --git a/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/Main.java b/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/Main.java index d0c3e12807a..c8d1f142475 100644 --- a/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/Main.java +++ b/examples/grpc-envoy/src/main/java/example/armeria/grpc/envoy/Main.java @@ -35,11 +35,11 @@ public static void main(String[] args) { private static Server startBackendServer(int serverPort) { return Server.builder() - .http(serverPort) - .service(GrpcService.builder() - .addService(new HelloService()) - .build()) - .build(); + .http(serverPort) + .service(GrpcService.builder() + .addService(new HelloService()) + .build()) + .build(); } static EnvoyContainer configureEnvoy(int serverPort, int envoyPort) { diff --git a/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java b/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java index 6b37ab24a97..d0bba59535b 100644 --- a/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java +++ b/examples/grpc-envoy/src/test/java/example/armeria/grpc/envoy/GrpcEnvoyProxyTest.java @@ -21,7 +21,7 @@ class GrpcEnvoyProxyTest { // the port envoy binds to within the container - private static final int envoyPort = 10000; + private static final int ENVOY_PORT = 10000; @RegisterExtension static ServerExtension server = new ServerExtension() { @@ -36,10 +36,10 @@ protected void configure(ServerBuilder sb) throws Exception { @ParameterizedTest @EnumSource(value = SessionProtocol.class, names = {"H1C", "H2C"}) void reverseProxy(SessionProtocol sessionProtocol) { - try (EnvoyContainer envoy = configureEnvoy(server.httpPort(), envoyPort)) { + try (EnvoyContainer envoy = configureEnvoy(server.httpPort(), ENVOY_PORT)) { envoy.start(); final String uri = sessionProtocol.uriText() + "://" + envoy.getHost() + - ':' + envoy.getMappedPort(envoyPort); + ':' + envoy.getMappedPort(ENVOY_PORT); final HelloServiceGrpc.HelloServiceBlockingStub helloService = GrpcClients.builder(uri) .build(HelloServiceGrpc.HelloServiceBlockingStub.class); From 0c8ecb410a9b4548ec4924af83d7763ac7383d8c Mon Sep 17 00:00:00 2001 From: eottabom Date: Wed, 19 Jun 2024 17:06:10 +0900 Subject: [PATCH 22/22] ISSUE-2353: Add reverse proxy example - Change host --- examples/grpc-envoy/src/main/resources/envoy/envoy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml b/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml index 40182cf7670..983a7c0f775 100644 --- a/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml +++ b/examples/grpc-envoy/src/main/resources/envoy/envoy.yaml @@ -48,5 +48,5 @@ static_resources: - endpoint: address: socket_address: - address: host.docker.internal + address: host.testcontainers.internal port_value: SERVER_PORT