diff --git a/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcClientInterceptors.java b/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcClientInterceptors.java new file mode 100644 index 000000000..10cc79e47 --- /dev/null +++ b/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcClientInterceptors.java @@ -0,0 +1,55 @@ +package io.quarkus.ts.http.advanced; + +import static io.grpc.Metadata.ASCII_STRING_MARSHALLER; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import io.quarkus.grpc.GlobalInterceptor; + +class GrpcClientInterceptors { + public static Metadata.Key CLIENT_METHOD = Metadata.Key.of("client-method-target", ASCII_STRING_MARSHALLER); + public static Metadata.Key CLIENT_CLASS = Metadata.Key.of("client-class-target", ASCII_STRING_MARSHALLER); + + @GlobalInterceptor + @ApplicationScoped + static class ClassTarget extends Base { + } + + static class MethodTarget extends Base { + } + + static class Producer { + @GlobalInterceptor + @Produces + MethodTarget methodTarget() { + return new MethodTarget(); + } + } + + abstract static class Base implements ClientInterceptor { + @Override + public ClientCall interceptCall(MethodDescriptor method, CallOptions options, + Channel next) { + String interceptedTarget = getClass().getName(); + return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, options)) { + @Override + public void start(Listener responseListener, Metadata headers) { + if (interceptedTarget.contains("MethodTarget")) { + headers.put(CLIENT_METHOD, interceptedTarget); + } else if (interceptedTarget.contains("ClassTarget")) { + headers.put(CLIENT_CLASS, interceptedTarget); + } + super.start(responseListener, headers); + } + }; + } + } +} diff --git a/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcInterceptorsService.java b/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcInterceptorsService.java new file mode 100644 index 000000000..a0def8236 --- /dev/null +++ b/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcInterceptorsService.java @@ -0,0 +1,23 @@ +package io.quarkus.ts.http.advanced; + +import io.grpc.stub.StreamObserver; +import io.quarkus.example.HelloReply; +import io.quarkus.example.InterceptedMessageGrpc; +import io.quarkus.example.InterceptedRequest; +import io.quarkus.grpc.GrpcService; + +@GrpcService +public class GrpcInterceptorsService extends InterceptedMessageGrpc.InterceptedMessageImplBase { + + @Override + public void showInterceptedMessage(InterceptedRequest request, StreamObserver responseObserver) { + String serverMethod = GrpcServerInterceptors.SERVER_METHOD.get(); + String serverClass = GrpcServerInterceptors.SERVER_CLASS.get(); + HelloReply reply = HelloReply.newBuilder() + .setMessage("Intercepted client side method passed by server method interceptor is: " + serverMethod + + "\nIntercepted client side class passed by server class interceptor is: " + serverClass) + .build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } +} diff --git a/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcResource.java b/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcResource.java index e1d173f57..864e98983 100644 --- a/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcResource.java +++ b/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcResource.java @@ -6,9 +6,13 @@ import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import io.quarkus.example.GreeterGrpc; +import io.quarkus.example.HelloReply; import io.quarkus.example.HelloRequest; +import io.quarkus.example.InterceptedMessageGrpc; +import io.quarkus.example.InterceptedRequest; import io.quarkus.grpc.GrpcClient; @Path("/grpc") @@ -18,6 +22,10 @@ public class GrpcResource { @GrpcClient("hello") GreeterGrpc.GreeterBlockingStub client; + @Inject + @GrpcClient("hello") + InterceptedMessageGrpc.InterceptedMessageBlockingStub interceptorsClient; + @GET @Path("/{name}") @Produces(MediaType.TEXT_PLAIN) @@ -25,4 +33,11 @@ public String hello(@PathParam("name") String name) { return client.sayHello(HelloRequest.newBuilder().setName(name).build()).getMessage(); } + @GET + @Path("global/interceptors") + public Response globalInterceptors() { + HelloReply helloReply = interceptorsClient.showInterceptedMessage(InterceptedRequest.newBuilder().build()); + return Response.ok(helloReply.getMessage()).build(); + } + } diff --git a/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcServerInterceptors.java b/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcServerInterceptors.java new file mode 100644 index 000000000..d92a03607 --- /dev/null +++ b/http/http-advanced/src/main/java/io/quarkus/ts/http/advanced/GrpcServerInterceptors.java @@ -0,0 +1,57 @@ +package io.quarkus.ts.http.advanced; + +import static io.quarkus.ts.http.advanced.GrpcClientInterceptors.CLIENT_CLASS; +import static io.quarkus.ts.http.advanced.GrpcClientInterceptors.CLIENT_METHOD; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +import io.grpc.Context; +import io.grpc.Contexts; +import io.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import io.quarkus.grpc.GlobalInterceptor; + +class GrpcServerInterceptors { + + public static Context.Key SERVER_METHOD = Context.key("server-method-target"); + public static Context.Key SERVER_CLASS = Context.key("server-class-target"); + + @GlobalInterceptor + @ApplicationScoped + static class ClassTarget extends Base { + } + + static class MethodTarget extends Base { + } + + static class Producer { + @GlobalInterceptor + @Produces + MethodTarget methodTarget() { + return new MethodTarget(); + } + } + + abstract static class Base implements ServerInterceptor { + @Override + public ServerCall.Listener interceptCall(ServerCall call, Metadata metadata, + ServerCallHandler next) { + Context ctx = null; + String interceptedTarget = getClass().getName(); + // Determine where was grpc call intercepted and put client side intercepted data to Context. + if (interceptedTarget.contains("MethodTarget")) { + ctx = Context.current().withValue(SERVER_METHOD, metadata.get(CLIENT_METHOD)); + ctx.attach(); + } else if (interceptedTarget.contains("ClassTarget")) { + ctx = Context.current().withValue(SERVER_CLASS, metadata.get(CLIENT_CLASS)); + ctx.attach(); + } else { + throw new RuntimeException("Unexpected intercepted class or method by GlobalInterceptor."); + } + return Contexts.interceptCall(ctx, call, metadata, next); + } + } +} diff --git a/http/http-advanced/src/main/proto/helloworld.proto b/http/http-advanced/src/main/proto/helloworld.proto index abcd641d8..ada91ee7d 100644 --- a/http/http-advanced/src/main/proto/helloworld.proto +++ b/http/http-advanced/src/main/proto/helloworld.proto @@ -19,6 +19,11 @@ service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } +// Intercepted message service +service InterceptedMessage { + rpc ShowInterceptedMessage (InterceptedRequest) returns (HelloReply) {} +} + // The request message containing the user's name. message HelloRequest { string name = 1; @@ -27,4 +32,7 @@ message HelloRequest { // The response message containing the greetings message HelloReply { string message = 1; -} \ No newline at end of file +} + +message InterceptedRequest { +} diff --git a/http/http-advanced/src/test/java/io/quarkus/ts/http/advanced/BaseHttpAdvancedIT.java b/http/http-advanced/src/test/java/io/quarkus/ts/http/advanced/BaseHttpAdvancedIT.java index d28e33aef..7fb2b950e 100644 --- a/http/http-advanced/src/test/java/io/quarkus/ts/http/advanced/BaseHttpAdvancedIT.java +++ b/http/http-advanced/src/test/java/io/quarkus/ts/http/advanced/BaseHttpAdvancedIT.java @@ -123,6 +123,15 @@ public void testGrpc() { getApp().given().when().get("/api/grpc/trinity").then().statusCode(HttpStatus.SC_OK).body(is("Hello trinity")); } + @Test + @Tag("QUARKUS-3742") + @DisplayName("GRPC client and server global interceptors test") + public void testGrpcGlobalInterceptors() { + getApp().given().when().get("/api/grpc/global/interceptors").then().statusCode(HttpStatus.SC_OK) + .body(containsString("ClientInterceptors$ClassTarget"), + containsString("ClientInterceptors$MethodTarget")); + } + @Test @DisplayName("Http/2 Server test") public void http2Server() throws InterruptedException, URISyntaxException {