From 33bbb56413119b11b3ad63cdd3aab5ce602a3b5c Mon Sep 17 00:00:00 2001 From: ioanbsu Date: Thu, 2 Feb 2023 10:50:14 -0800 Subject: [PATCH 1/4] #9870: Enable server e2e encryption by updating ServerInterceptors.java to support different marshallers for Request and Response messages. --- .../main/java/io/grpc/ServerInterceptors.java | 30 ++++++- .../java/io/grpc/ServerInterceptorsTest.java | 87 +++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/io/grpc/ServerInterceptors.java b/api/src/main/java/io/grpc/ServerInterceptors.java index 153c7b4068e..e071e920881 100644 --- a/api/src/main/java/io/grpc/ServerInterceptors.java +++ b/api/src/main/java/io/grpc/ServerInterceptors.java @@ -184,6 +184,32 @@ private static final class KnownLengthBufferedInputStream extends BufferedInputS public static ServerServiceDefinition useMarshalledMessages( final ServerServiceDefinition serviceDef, final MethodDescriptor.Marshaller marshaller) { + return useMarshalledMessages(serviceDef, marshaller, marshaller); + } + + /** + * Create a new {@code ServerServiceDefinition} with {@link MethodDescriptor} for deserializing requests and + * separate {@link MethodDescriptor} for serializing responses. The {@code ServerCallHandler} created will + * automatically convert back to the original types for request and response before calling the existing {@code + * ServerCallHandler}. Calling this method combined with the intercept methods will allow the developer to choose + * whether to intercept messages of ReqT/RespT, or the modeled types of their application. This can also be + * chained to + * allow for interceptors to handle messages as multiple different ReqT/RespT types within the chain if the added + * cost of + * serialization is not a concern. + * + * @param serviceDef the sevice definition to add request and response marshallers to. + * @param requestMarshaller request marshaller + * @param responseMarshaller response marshaller + * @param the request payload type + * @param the response payload type. + * @return a wrapped version of {@code serviceDef} with the ReqT and RespT conversion applied. + */ + @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9870") + public static ServerServiceDefinition useMarshalledMessages( + final ServerServiceDefinition serviceDef, + final MethodDescriptor.Marshaller requestMarshaller, + final MethodDescriptor.Marshaller responseMarshaller) { List> wrappedMethods = new ArrayList<>(); List> wrappedDescriptors = @@ -191,8 +217,8 @@ public static ServerServiceDefinition useMarshalledMessages( // Wrap the descriptors for (final ServerMethodDefinition definition : serviceDef.getMethods()) { final MethodDescriptor originalMethodDescriptor = definition.getMethodDescriptor(); - final MethodDescriptor wrappedMethodDescriptor = - originalMethodDescriptor.toBuilder(marshaller, marshaller).build(); + final MethodDescriptor wrappedMethodDescriptor = + originalMethodDescriptor.toBuilder(requestMarshaller, responseMarshaller).build(); wrappedDescriptors.add(wrappedMethodDescriptor); wrappedMethods.add(wrapMethod(definition, wrappedMethodDescriptor)); } diff --git a/api/src/test/java/io/grpc/ServerInterceptorsTest.java b/api/src/test/java/io/grpc/ServerInterceptorsTest.java index fa4e7e747a2..7b28deadbf1 100644 --- a/api/src/test/java/io/grpc/ServerInterceptorsTest.java +++ b/api/src/test/java/io/grpc/ServerInterceptorsTest.java @@ -33,6 +33,7 @@ import io.grpc.ServerCall.Listener; import io.grpc.internal.NoopServerCall; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; @@ -425,6 +426,92 @@ public void onMessage(ReqT message) { order); } + /** + * Tests the {@link ServerInterceptors#useMarshalledMessages(ServerServiceDefinition, Marshaller, Marshaller)}. + * Makes sure that on incoming request the request marshaller's stream method is called and on response the + * response marshaller's parse method is called + */ + @Test + @SuppressWarnings("unchecked") + public void distinctMarshallerForRequestAndResponse() { + final List requestFlowOrder = new ArrayList<>(); + + final Marshaller requestMarshaller = new Marshaller() { + @Override + public InputStream stream(String value) { + requestFlowOrder.add("RequestStream"); + return new ByteArrayInputStream(value.getBytes()); + } + + @Override + public String parse(InputStream stream) { + requestFlowOrder.add("RequestParse"); + try { + byte[] bytes = new byte[stream.available()]; + stream.read(bytes); + return new String(bytes); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + final Marshaller responseMarshaller = new Marshaller() { + @Override + public InputStream stream(String value) { + requestFlowOrder.add("ResponseStream"); + return new ByteArrayInputStream(value.getBytes()); + } + + @Override + public String parse(InputStream stream) { + requestFlowOrder.add("ResponseParse"); + try { + byte[] bytes = new byte[stream.available()]; + stream.read(bytes); + return new String(bytes); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + final Marshaller dummyMarshaller = new Marshaller() { + @Override + public InputStream stream(Holder value) { + return value.get(); + } + + @Override + public Holder parse(InputStream stream) { + return new Holder(stream); + } + }; + ServerCallHandler handler = (call, headers) -> new Listener() { + @Override + public void onMessage(Holder message) { + requestFlowOrder.add("handler"); + call.sendMessage(message); + } + }; + + MethodDescriptor wrappedMethod = MethodDescriptor.newBuilder() + .setType(MethodType.UNKNOWN) + .setFullMethodName("basic/wrapped") + .setRequestMarshaller(dummyMarshaller) + .setResponseMarshaller(dummyMarshaller) + .build(); + ServerServiceDefinition serviceDef = ServerServiceDefinition.builder( + new ServiceDescriptor("basic", wrappedMethod)) + .addMethod(wrappedMethod, handler).build(); + ServerServiceDefinition intercepted = ServerInterceptors.useMarshalledMessages(serviceDef, requestMarshaller, + responseMarshaller); + ServerMethodDefinition serverMethod = + (ServerMethodDefinition) intercepted.getMethod("basic/wrapped"); + ServerCall serverCall = new NoopServerCall<>(); + serverMethod.getServerCallHandler().startCall(serverCall, headers).onMessage("TestMessage"); + + assertEquals(Arrays.asList("RequestStream", "handler", "ResponseParse"), requestFlowOrder); + } + @SuppressWarnings("unchecked") private static ServerMethodDefinition getSoleMethod( ServerServiceDefinition serviceDef) { From 1153c5f4df01396b55055f875718e5919beb409c Mon Sep 17 00:00:00 2001 From: ioanbsu Date: Thu, 2 Feb 2023 20:15:24 -0800 Subject: [PATCH 2/4] #9870 - Fixed warnings related to bytes-string-conversion. Removed unnecessary code that deals with bytes-string conversion. --- .../java/io/grpc/ServerInterceptorsTest.java | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/api/src/test/java/io/grpc/ServerInterceptorsTest.java b/api/src/test/java/io/grpc/ServerInterceptorsTest.java index 7b28deadbf1..5baadfd1391 100644 --- a/api/src/test/java/io/grpc/ServerInterceptorsTest.java +++ b/api/src/test/java/io/grpc/ServerInterceptorsTest.java @@ -33,7 +33,6 @@ import io.grpc.ServerCall.Listener; import io.grpc.internal.NoopServerCall; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; @@ -440,38 +439,26 @@ public void distinctMarshallerForRequestAndResponse() { @Override public InputStream stream(String value) { requestFlowOrder.add("RequestStream"); - return new ByteArrayInputStream(value.getBytes()); + return null; } @Override public String parse(InputStream stream) { requestFlowOrder.add("RequestParse"); - try { - byte[] bytes = new byte[stream.available()]; - stream.read(bytes); - return new String(bytes); - } catch (IOException e) { - throw new RuntimeException(e); - } + return null; } }; final Marshaller responseMarshaller = new Marshaller() { @Override public InputStream stream(String value) { requestFlowOrder.add("ResponseStream"); - return new ByteArrayInputStream(value.getBytes()); + return null; } @Override public String parse(InputStream stream) { requestFlowOrder.add("ResponseParse"); - try { - byte[] bytes = new byte[stream.available()]; - stream.read(bytes); - return new String(bytes); - } catch (IOException e) { - throw new RuntimeException(e); - } + return null; } }; final Marshaller dummyMarshaller = new Marshaller() { From ffba635c082b7c4319b606197a7e74f437adeb20 Mon Sep 17 00:00:00 2001 From: ioanbsu Date: Thu, 2 Feb 2023 20:23:34 -0800 Subject: [PATCH 3/4] #9870 - Complying with checkstyle rules. --- .../main/java/io/grpc/ServerInterceptors.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/api/src/main/java/io/grpc/ServerInterceptors.java b/api/src/main/java/io/grpc/ServerInterceptors.java index e071e920881..b44a7b6c886 100644 --- a/api/src/main/java/io/grpc/ServerInterceptors.java +++ b/api/src/main/java/io/grpc/ServerInterceptors.java @@ -188,15 +188,14 @@ public static ServerServiceDefinition useMarshalledMessages( } /** - * Create a new {@code ServerServiceDefinition} with {@link MethodDescriptor} for deserializing requests and - * separate {@link MethodDescriptor} for serializing responses. The {@code ServerCallHandler} created will - * automatically convert back to the original types for request and response before calling the existing {@code - * ServerCallHandler}. Calling this method combined with the intercept methods will allow the developer to choose - * whether to intercept messages of ReqT/RespT, or the modeled types of their application. This can also be - * chained to - * allow for interceptors to handle messages as multiple different ReqT/RespT types within the chain if the added - * cost of - * serialization is not a concern. + * Create a new {@code ServerServiceDefinition} with {@link MethodDescriptor} for deserializing + * requests and separate {@link MethodDescriptor} for serializing responses. The {@code + * ServerCallHandler} created will automatically convert back to the original types for request + * and response before calling the existing {@code ServerCallHandler}. Calling this method + * combined with the intercept methods will allow the developer to choose whether to intercept + * messages of ReqT/RespT, or the modeled types of their application. This can also be chained + * to allow for interceptors to handle messages as multiple different ReqT/RespT types within + * the chain if the added cost of serialization is not a concern. * * @param serviceDef the sevice definition to add request and response marshallers to. * @param requestMarshaller request marshaller From 3fc49c2fc55f5600a2a681355e31762a973f573c Mon Sep 17 00:00:00 2001 From: ioanbsu Date: Thu, 2 Feb 2023 20:34:25 -0800 Subject: [PATCH 4/4] #9870 - Complying with checkstyle rules in tests. --- api/src/test/java/io/grpc/ServerInterceptorsTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api/src/test/java/io/grpc/ServerInterceptorsTest.java b/api/src/test/java/io/grpc/ServerInterceptorsTest.java index 5baadfd1391..c9e426a7f93 100644 --- a/api/src/test/java/io/grpc/ServerInterceptorsTest.java +++ b/api/src/test/java/io/grpc/ServerInterceptorsTest.java @@ -426,8 +426,8 @@ public void onMessage(ReqT message) { } /** - * Tests the {@link ServerInterceptors#useMarshalledMessages(ServerServiceDefinition, Marshaller, Marshaller)}. - * Makes sure that on incoming request the request marshaller's stream method is called and on response the + * Tests the ServerInterceptors#useMarshalledMessages()} with two marshallers. Makes sure that + * on incoming request the request marshaller's stream method is called and on response the * response marshaller's parse method is called */ @Test @@ -458,7 +458,7 @@ public InputStream stream(String value) { @Override public String parse(InputStream stream) { requestFlowOrder.add("ResponseParse"); - return null; + return null; } }; final Marshaller dummyMarshaller = new Marshaller() { @@ -489,8 +489,8 @@ public void onMessage(Holder message) { ServerServiceDefinition serviceDef = ServerServiceDefinition.builder( new ServiceDescriptor("basic", wrappedMethod)) .addMethod(wrappedMethod, handler).build(); - ServerServiceDefinition intercepted = ServerInterceptors.useMarshalledMessages(serviceDef, requestMarshaller, - responseMarshaller); + ServerServiceDefinition intercepted = ServerInterceptors.useMarshalledMessages(serviceDef, + requestMarshaller, responseMarshaller); ServerMethodDefinition serverMethod = (ServerMethodDefinition) intercepted.getMethod("basic/wrapped"); ServerCall serverCall = new NoopServerCall<>();