diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientReactiveRequestFilter.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientReactiveRequestFilter.java deleted file mode 100644 index 3e9a7d0ba5d0e..0000000000000 --- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientReactiveRequestFilter.java +++ /dev/null @@ -1,91 +0,0 @@ -package io.quarkus.rest.client.reactive.runtime; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import javax.annotation.Priority; -import javax.enterprise.context.RequestScoped; -import javax.ws.rs.client.ClientRequestContext; -import javax.ws.rs.client.ClientRequestFilter; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; - -import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory; -import org.eclipse.microprofile.rest.client.ext.DefaultClientHeadersFactoryImpl; - -import io.quarkus.arc.Arc; -import io.quarkus.rest.client.reactive.HeaderFiller; - -@Priority(Integer.MIN_VALUE) -public class RestClientReactiveRequestFilter implements ClientRequestFilter { - - private static final MultivaluedMap EMPTY_MAP = new MultivaluedHashMap<>(); - - private final ClientHeadersFactory clientHeadersFactory; - - public RestClientReactiveRequestFilter(ClientHeadersFactory clientHeadersFactory) { - this.clientHeadersFactory = clientHeadersFactory; - } - - @Override - public void filter(ClientRequestContext requestContext) { - HeaderFiller headerFiller = (HeaderFiller) requestContext.getProperty(HeaderFiller.class.getName()); - - // mutable collection of headers - MultivaluedMap headers = new MultivaluedHashMap<>(); - - // gather original headers - for (Map.Entry> headerEntry : requestContext.getHeaders().entrySet()) { - headers.put(headerEntry.getKey(), castToListOfStrings(headerEntry.getValue())); - } - - // add headers from MP annotations - if (headerFiller != null) { - // add headers to a mutable headers collection - headerFiller.addHeaders(headers); - } - - MultivaluedMap incomingHeaders = RestClientReactiveRequestFilter.EMPTY_MAP; - if (Arc.container().getActiveContext(RequestScoped.class) != null) { - HeaderContainer headerContainer = Arc.container().instance(HeaderContainer.class).get(); - if (headerContainer != null) { - incomingHeaders = headerContainer.getHeaders(); - } - } - - if (clientHeadersFactory instanceof DefaultClientHeadersFactoryImpl) { - // When using the default factory, pass the proposed outgoing headers onto the request context. - // Propagation with the default factory will then overwrite any values if required. - for (Map.Entry> headerEntry : headers.entrySet()) { - requestContext.getHeaders().put(headerEntry.getKey(), castToListOfObjects(headerEntry.getValue())); - } - } - - if (clientHeadersFactory != null) { - incomingHeaders = clientHeadersFactory.update(incomingHeaders, headers); - } - - for (Map.Entry> headerEntry : incomingHeaders.entrySet()) { - requestContext.getHeaders().put(headerEntry.getKey(), castToListOfObjects(headerEntry.getValue())); - } - } - - private static List castToListOfStrings(List values) { - List result = new ArrayList<>(); - for (Object value : values) { - if (value instanceof String) { - result.add((String) value); - } else { - result.add(String.valueOf(value)); - } - } - return result; - } - - @SuppressWarnings("unchecked") - private static List castToListOfObjects(List values) { - return (List) (List) values; - } - -} diff --git a/integration-tests/grpc-plain-text-mutiny/pom.xml b/integration-tests/grpc-plain-text-mutiny/pom.xml index 9f09512c3ef40..4ea3011af9411 100644 --- a/integration-tests/grpc-plain-text-mutiny/pom.xml +++ b/integration-tests/grpc-plain-text-mutiny/pom.xml @@ -16,7 +16,7 @@ io.quarkus - quarkus-resteasy + quarkus-resteasy-jackson io.quarkus @@ -63,7 +63,7 @@ io.quarkus - quarkus-resteasy-deployment + quarkus-resteasy-jackson-deployment ${project.version} pom test diff --git a/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldEndpoint.java b/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldEndpoint.java index 1e077c7dcf71e..625c55b5bee63 100644 --- a/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldEndpoint.java +++ b/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/HelloWorldEndpoint.java @@ -1,13 +1,28 @@ package io.quarkus.grpc.examples.hello; +import static io.quarkus.grpc.examples.hello.IncomingInterceptor.EXTRA_BLOCKING_HEADER; +import static io.quarkus.grpc.examples.hello.IncomingInterceptor.EXTRA_HEADER; +import static io.quarkus.grpc.examples.hello.IncomingInterceptor.INTERFACE_HEADER; + +import java.util.Map; + +import javax.inject.Inject; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import examples.Greeter; +import examples.GreeterClient; import examples.GreeterGrpc; import examples.HelloReply; import examples.HelloRequest; import examples.MutinyGreeterGrpc; +import io.grpc.Metadata; +import io.grpc.stub.MetadataUtils; import io.quarkus.grpc.GrpcClient; import io.smallrye.mutiny.Uni; @@ -15,24 +30,68 @@ public class HelloWorldEndpoint { @GrpcClient("hello") - GreeterGrpc.GreeterBlockingStub blockingHelloService; + GreeterGrpc.GreeterBlockingStub blockingHelloClient; + + @GrpcClient("hello") + MutinyGreeterGrpc.MutinyGreeterStub mutinyHelloClient; @GrpcClient("hello") - MutinyGreeterGrpc.MutinyGreeterStub mutinyHelloService; + Greeter interfaceHelloClient; + + @Inject + IncomingInterceptor interceptor; @GET @Path("/blocking/{name}") - public String helloBlocking(@PathParam("name") String name) { - HelloReply reply = blockingHelloService.sayHello(HelloRequest.newBuilder().setName(name).build()); + public String helloBlocking(@PathParam("name") String name, @QueryParam("headers") boolean headers) { + Metadata extraHeaders = new Metadata(); + if (headers) { + extraHeaders.put(EXTRA_BLOCKING_HEADER, "my-blocking-value"); + } + HelloReply reply = MetadataUtils.attachHeaders(blockingHelloClient, extraHeaders) + .sayHello(HelloRequest.newBuilder().setName(name).build()); return generateResponse(reply); } @GET @Path("/mutiny/{name}") - public Uni helloMutiny(@PathParam("name") String name) { - return mutinyHelloService.sayHello(HelloRequest.newBuilder().setName(name).build()) - .onItem().transform((reply) -> generateResponse(reply)); + public Uni helloMutiny(@PathParam("name") String name, @QueryParam("headers") boolean headers) { + Metadata extraHeaders = new Metadata(); + if (headers) { + extraHeaders.put(EXTRA_HEADER, "my-extra-value"); + } + MutinyGreeterGrpc.MutinyGreeterStub alteredClient = MetadataUtils.attachHeaders(mutinyHelloClient, extraHeaders); + return alteredClient.sayHello(HelloRequest.newBuilder().setName(name).build()) + .onItem().transform(this::generateResponse); + } + + @GET + @Path("/interface/{name}") + public Uni helloInterface(@PathParam("name") String name, @QueryParam("headers") boolean headers) { + Metadata extraHeaders = new Metadata(); + if (headers) { + extraHeaders.put(INTERFACE_HEADER, "my-interface-value"); + } + + MutinyGreeterGrpc.MutinyGreeterStub stub = ((GreeterClient) interfaceHelloClient).getStub(); + MutinyGreeterGrpc.MutinyGreeterStub alteredClient = MetadataUtils.attachHeaders(stub, extraHeaders); + + return alteredClient.sayHello(HelloRequest.newBuilder().setName(name).build()) + .onItem().transform(this::generateResponse); + + } + + @DELETE + public void clear() { + interceptor.clear(); + } + + @GET + @Path("/headers") + @Produces(MediaType.APPLICATION_JSON) + public Map getCollectedHeaders() { + return interceptor.getCollectedHeaders(); } public String generateResponse(HelloReply reply) { diff --git a/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/IncomingInterceptor.java b/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/IncomingInterceptor.java new file mode 100644 index 0000000000000..6b7f5a626250e --- /dev/null +++ b/integration-tests/grpc-plain-text-mutiny/src/main/java/io/quarkus/grpc/examples/hello/IncomingInterceptor.java @@ -0,0 +1,48 @@ +package io.quarkus.grpc.examples.hello; + +import static java.util.Arrays.asList; + +import java.util.HashMap; +import java.util.Map; + +import javax.enterprise.context.ApplicationScoped; + +import io.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; + +@ApplicationScoped +public class IncomingInterceptor implements ServerInterceptor { + + public static final Metadata.Key EXTRA_HEADER = Metadata.Key.of("my-extra-header", + Metadata.ASCII_STRING_MARSHALLER); + public static final Metadata.Key INTERFACE_HEADER = Metadata.Key.of("my-interface-header", + Metadata.ASCII_STRING_MARSHALLER); + public static final Metadata.Key EXTRA_BLOCKING_HEADER = Metadata.Key.of("my-blocking-header", + Metadata.ASCII_STRING_MARSHALLER); + + private final Map headerValues = new HashMap<>(); + + @Override + public ServerCall.Listener interceptCall(ServerCall serverCall, Metadata metadata, + ServerCallHandler serverCallHandler) { + + for (Metadata.Key key : asList(EXTRA_HEADER, INTERFACE_HEADER, EXTRA_BLOCKING_HEADER)) { + String header = metadata.get(key); + if (header != null) { + headerValues.put(key.name(), header); + } + } + + return serverCallHandler.startCall(serverCall, metadata); + } + + public void clear() { + headerValues.clear(); + } + + public Map getCollectedHeaders() { + return headerValues; + } +} diff --git a/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldEndpointTest.java b/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldEndpointTest.java index 2c6c673852346..681f98fbc7d77 100644 --- a/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldEndpointTest.java +++ b/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldEndpointTest.java @@ -1,8 +1,13 @@ package io.quarkus.grpc.examples.hello; +import static io.restassured.RestAssured.delete; import static io.restassured.RestAssured.get; +import static io.restassured.RestAssured.given; import static org.assertj.core.api.Assertions.assertThat; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import io.quarkus.test.junit.QuarkusTest; @@ -14,12 +19,55 @@ class HelloWorldEndpointTest { public void testHelloWorldServiceUsingBlockingStub() { String response = get("/hello/blocking/neo").asString(); assertThat(response).startsWith("Hello neo"); + + assertNoHeaders(); } @Test public void testHelloWorldServiceUsingMutinyStub() { String response = get("/hello/mutiny/neo-mutiny").asString(); assertThat(response).startsWith("Hello neo-mutiny"); + assertNoHeaders(); + } + + @Test + void shouldSetHeaderWithMutiny() { + String response = given().queryParam("headers", "true") + .when().get("/hello/mutiny/neo-mutiny-w-headers").asString(); + assertThat(response).startsWith("Hello neo-mutiny-w-headers"); + assertHasHeader("my-extra-header", "my-extra-value"); + } + + @Test + void shouldSetHeader() { + String response = given().queryParam("headers", "true") + .when().get("/hello/blocking/neo-w-headers").asString(); + assertThat(response).startsWith("Hello neo-w-headers"); + assertHasHeader("my-blocking-header", "my-blocking-value"); + } + + @Test + void shouldSetHeaderWithInterface() { + String response = given().queryParam("headers", "true") + .when().get("/hello/interface/i-neo-w-headers").asString(); + assertThat(response).startsWith("Hello i-neo-w-headers"); + assertHasHeader("my-interface-header", "my-interface-value"); + } + + @BeforeEach + public void setUp() { + delete("/hello").then().statusCode(204); + } + + private void assertHasHeader(String key, String value) { + Map result = get("/hello/headers").as(Map.class); + assertThat(result).hasSize(1); + assertThat(result.get(key)).isEqualTo(value); + } + + private void assertNoHeaders() { + Map result = get("/hello/headers").as(Map.class); + assertThat(result).hasSize(0); } } diff --git a/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldServiceTest.java b/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldServiceTest.java index 534638ddae232..94689891b0928 100644 --- a/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldServiceTest.java +++ b/integration-tests/grpc-plain-text-mutiny/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldServiceTest.java @@ -44,9 +44,9 @@ public void testHelloWorldServiceUsingBlockingStub() { @Test public void testHelloWorldServiceUsingMutinyStub() { HelloReply reply = MutinyGreeterGrpc.newMutinyStub(channel) - .sayHello(HelloRequest.newBuilder().setName("neo-blocking").build()) + .sayHello(HelloRequest.newBuilder().setName("neo-mutiny").build()) .await().atMost(Duration.ofSeconds(5)); - assertThat(reply.getMessage()).isEqualTo("Hello neo-blocking"); + assertThat(reply.getMessage()).isEqualTo("Hello neo-mutiny"); } }