• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc;
18 
19 import com.google.common.base.Preconditions;
20 import java.io.BufferedInputStream;
21 import java.io.InputStream;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.List;
26 
27 /**
28  * Utility methods for working with {@link ServerInterceptor}s.
29  */
30 public final class ServerInterceptors {
31   // Prevent instantiation
ServerInterceptors()32   private ServerInterceptors() {}
33 
34   /**
35    * Create a new {@code ServerServiceDefinition} whose {@link ServerCallHandler}s will call
36    * {@code interceptors} before calling the pre-existing {@code ServerCallHandler}. The first
37    * interceptor will have its {@link ServerInterceptor#interceptCall} called first.
38    *
39    * @param serviceDef   the service definition for which to intercept all its methods.
40    * @param interceptors array of interceptors to apply to the service.
41    * @return a wrapped version of {@code serviceDef} with the interceptors applied.
42    */
interceptForward(ServerServiceDefinition serviceDef, ServerInterceptor... interceptors)43   public static ServerServiceDefinition interceptForward(ServerServiceDefinition serviceDef,
44                                                          ServerInterceptor... interceptors) {
45     return interceptForward(serviceDef, Arrays.asList(interceptors));
46   }
47 
interceptForward(BindableService bindableService, ServerInterceptor... interceptors)48   public static ServerServiceDefinition interceptForward(BindableService bindableService,
49       ServerInterceptor... interceptors) {
50     return interceptForward(bindableService.bindService(), Arrays.asList(interceptors));
51   }
52 
53   /**
54    * Create a new {@code ServerServiceDefinition} whose {@link ServerCallHandler}s will call
55    * {@code interceptors} before calling the pre-existing {@code ServerCallHandler}. The first
56    * interceptor will have its {@link ServerInterceptor#interceptCall} called first.
57    *
58    * @param serviceDef   the service definition for which to intercept all its methods.
59    * @param interceptors list of interceptors to apply to the service.
60    * @return a wrapped version of {@code serviceDef} with the interceptors applied.
61    */
interceptForward( ServerServiceDefinition serviceDef, List<? extends ServerInterceptor> interceptors)62   public static ServerServiceDefinition interceptForward(
63       ServerServiceDefinition serviceDef,
64       List<? extends ServerInterceptor> interceptors) {
65     List<? extends ServerInterceptor> copy = new ArrayList<>(interceptors);
66     Collections.reverse(copy);
67     return intercept(serviceDef, copy);
68   }
69 
interceptForward( BindableService bindableService, List<? extends ServerInterceptor> interceptors)70   public static ServerServiceDefinition interceptForward(
71       BindableService bindableService,
72       List<? extends ServerInterceptor> interceptors) {
73     return interceptForward(bindableService.bindService(), interceptors);
74   }
75 
76   /**
77    * Create a new {@code ServerServiceDefinition} whose {@link ServerCallHandler}s will call
78    * {@code interceptors} before calling the pre-existing {@code ServerCallHandler}. The last
79    * interceptor will have its {@link ServerInterceptor#interceptCall} called first.
80    *
81    * @param serviceDef   the service definition for which to intercept all its methods.
82    * @param interceptors array of interceptors to apply to the service.
83    * @return a wrapped version of {@code serviceDef} with the interceptors applied.
84    */
intercept(ServerServiceDefinition serviceDef, ServerInterceptor... interceptors)85   public static ServerServiceDefinition intercept(ServerServiceDefinition serviceDef,
86                                                   ServerInterceptor... interceptors) {
87     return intercept(serviceDef, Arrays.asList(interceptors));
88   }
89 
intercept(BindableService bindableService, ServerInterceptor... interceptors)90   public static ServerServiceDefinition intercept(BindableService bindableService,
91       ServerInterceptor... interceptors) {
92     Preconditions.checkNotNull(bindableService, "bindableService");
93     return intercept(bindableService.bindService(), Arrays.asList(interceptors));
94   }
95 
96   /**
97    * Create a new {@code ServerServiceDefinition} whose {@link ServerCallHandler}s will call
98    * {@code interceptors} before calling the pre-existing {@code ServerCallHandler}. The last
99    * interceptor will have its {@link ServerInterceptor#interceptCall} called first.
100    *
101    * @param serviceDef   the service definition for which to intercept all its methods.
102    * @param interceptors list of interceptors to apply to the service.
103    * @return a wrapped version of {@code serviceDef} with the interceptors applied.
104    */
intercept(ServerServiceDefinition serviceDef, List<? extends ServerInterceptor> interceptors)105   public static ServerServiceDefinition intercept(ServerServiceDefinition serviceDef,
106                                                   List<? extends ServerInterceptor> interceptors) {
107     Preconditions.checkNotNull(serviceDef, "serviceDef");
108     if (interceptors.isEmpty()) {
109       return serviceDef;
110     }
111     ServerServiceDefinition.Builder serviceDefBuilder
112         = ServerServiceDefinition.builder(serviceDef.getServiceDescriptor());
113     for (ServerMethodDefinition<?, ?> method : serviceDef.getMethods()) {
114       wrapAndAddMethod(serviceDefBuilder, method, interceptors);
115     }
116     return serviceDefBuilder.build();
117   }
118 
intercept(BindableService bindableService, List<? extends ServerInterceptor> interceptors)119   public static ServerServiceDefinition intercept(BindableService bindableService,
120       List<? extends ServerInterceptor> interceptors) {
121     Preconditions.checkNotNull(bindableService, "bindableService");
122     return intercept(bindableService.bindService(), interceptors);
123   }
124 
125   /**
126    * Create a new {@code ServerServiceDefinition} whose {@link MethodDescriptor} serializes to
127    * and from InputStream for all methods.  The InputStream is guaranteed return true for
128    * markSupported().  The {@code ServerCallHandler} created will automatically
129    * convert back to the original types for request and response before calling the existing
130    * {@code ServerCallHandler}.  Calling this method combined with the intercept methods will
131    * allow the developer to choose whether to intercept messages of InputStream, or the modeled
132    * types of their application.
133    *
134    * @param serviceDef the service definition to convert messages to InputStream
135    * @return a wrapped version of {@code serviceDef} with the InputStream conversion applied.
136    */
137   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1712")
useInputStreamMessages( final ServerServiceDefinition serviceDef)138   public static ServerServiceDefinition useInputStreamMessages(
139       final ServerServiceDefinition serviceDef) {
140     final MethodDescriptor.Marshaller<InputStream> marshaller =
141         new MethodDescriptor.Marshaller<InputStream>() {
142           @Override
143           public InputStream stream(final InputStream value) {
144             return value;
145           }
146 
147           @Override
148           public InputStream parse(final InputStream stream) {
149             if (stream.markSupported()) {
150               return stream;
151             } else if (stream instanceof KnownLength) {
152               return new KnownLengthBufferedInputStream(stream);
153             } else {
154               return new BufferedInputStream(stream);
155             }
156           }
157         };
158 
159     return useMarshalledMessages(serviceDef, marshaller);
160   }
161 
162   /** {@link BufferedInputStream} that also implements {@link KnownLength}. */
163   private static final class KnownLengthBufferedInputStream extends BufferedInputStream
164       implements KnownLength {
KnownLengthBufferedInputStream(InputStream in)165     KnownLengthBufferedInputStream(InputStream in) {
166       super(in);
167     }
168   }
169 
170   /**
171    * Create a new {@code ServerServiceDefinition} whose {@link MethodDescriptor} serializes to
172    * and from T for all methods.  The {@code ServerCallHandler} created will automatically
173    * convert back to the original types for request and response before calling the existing
174    * {@code ServerCallHandler}.  Calling this method combined with the intercept methods will
175    * allow the developer to choose whether to intercept messages of T, or the modeled types
176    * of their application.  This can also be chained to allow for interceptors to handle messages
177    * as multiple different T types within the chain if the added cost of serialization is not
178    * a concern.
179    *
180    * @param serviceDef the service definition to convert messages to T
181    * @return a wrapped version of {@code serviceDef} with the T conversion applied.
182    */
183   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1712")
useMarshalledMessages( final ServerServiceDefinition serviceDef, final MethodDescriptor.Marshaller<T> marshaller)184   public static <T> ServerServiceDefinition useMarshalledMessages(
185       final ServerServiceDefinition serviceDef,
186       final MethodDescriptor.Marshaller<T> marshaller) {
187     return useMarshalledMessages(serviceDef, marshaller, marshaller);
188   }
189 
190   /**
191    * Create a new {@code ServerServiceDefinition} with {@link MethodDescriptor} for deserializing
192    * requests and separate {@link MethodDescriptor} for serializing responses. The {@code
193    * ServerCallHandler} created will automatically convert back to the original types for request
194    * and response before calling the existing {@code ServerCallHandler}.  Calling this method
195    * combined with the intercept methods will allow the developer to choose whether to intercept
196    * messages of ReqT/RespT, or the modeled types of their application. This can also be chained
197    * to allow for interceptors to handle messages as multiple different ReqT/RespT types within
198    * the chain if the added cost of serialization is not a concern.
199    *
200    * @param serviceDef         the sevice definition to add request and response marshallers to.
201    * @param requestMarshaller  request marshaller
202    * @param responseMarshaller response marshaller
203    * @param <ReqT>             the request payload type
204    * @param <RespT>            the response payload type.
205    * @return a wrapped version of {@code serviceDef} with the ReqT and RespT conversion applied.
206    */
207   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9870")
useMarshalledMessages( final ServerServiceDefinition serviceDef, final MethodDescriptor.Marshaller<ReqT> requestMarshaller, final MethodDescriptor.Marshaller<RespT> responseMarshaller)208   public static <ReqT, RespT> ServerServiceDefinition useMarshalledMessages(
209       final ServerServiceDefinition serviceDef,
210       final MethodDescriptor.Marshaller<ReqT> requestMarshaller,
211       final MethodDescriptor.Marshaller<RespT> responseMarshaller) {
212     List<ServerMethodDefinition<?, ?>> wrappedMethods =
213         new ArrayList<>();
214     List<MethodDescriptor<?, ?>> wrappedDescriptors =
215         new ArrayList<>();
216     // Wrap the descriptors
217     for (final ServerMethodDefinition<?, ?> definition : serviceDef.getMethods()) {
218       final MethodDescriptor<?, ?> originalMethodDescriptor = definition.getMethodDescriptor();
219       final MethodDescriptor<ReqT, RespT> wrappedMethodDescriptor =
220           originalMethodDescriptor.toBuilder(requestMarshaller, responseMarshaller).build();
221       wrappedDescriptors.add(wrappedMethodDescriptor);
222       wrappedMethods.add(wrapMethod(definition, wrappedMethodDescriptor));
223     }
224     // Build the new service descriptor
225     final ServiceDescriptor.Builder serviceDescriptorBuilder =
226         ServiceDescriptor.newBuilder(serviceDef.getServiceDescriptor().getName())
227             .setSchemaDescriptor(serviceDef.getServiceDescriptor().getSchemaDescriptor());
228     for (MethodDescriptor<?, ?> wrappedDescriptor : wrappedDescriptors) {
229       serviceDescriptorBuilder.addMethod(wrappedDescriptor);
230     }
231     // Create the new service definition.
232     final ServerServiceDefinition.Builder serviceBuilder =
233         ServerServiceDefinition.builder(serviceDescriptorBuilder.build());
234     for (ServerMethodDefinition<?, ?> definition : wrappedMethods) {
235       serviceBuilder.addMethod(definition);
236     }
237     return serviceBuilder.build();
238   }
239 
wrapAndAddMethod( ServerServiceDefinition.Builder serviceDefBuilder, ServerMethodDefinition<ReqT, RespT> method, List<? extends ServerInterceptor> interceptors)240   private static <ReqT, RespT> void wrapAndAddMethod(
241       ServerServiceDefinition.Builder serviceDefBuilder, ServerMethodDefinition<ReqT, RespT> method,
242       List<? extends ServerInterceptor> interceptors) {
243     ServerCallHandler<ReqT, RespT> callHandler = method.getServerCallHandler();
244     for (ServerInterceptor interceptor : interceptors) {
245       callHandler = InterceptCallHandler.create(interceptor, callHandler);
246     }
247     serviceDefBuilder.addMethod(method.withServerCallHandler(callHandler));
248   }
249 
250   static final class InterceptCallHandler<ReqT, RespT> implements ServerCallHandler<ReqT, RespT> {
create( ServerInterceptor interceptor, ServerCallHandler<ReqT, RespT> callHandler)251     public static <ReqT, RespT> InterceptCallHandler<ReqT, RespT> create(
252         ServerInterceptor interceptor, ServerCallHandler<ReqT, RespT> callHandler) {
253       return new InterceptCallHandler<>(interceptor, callHandler);
254     }
255 
256     private final ServerInterceptor interceptor;
257     private final ServerCallHandler<ReqT, RespT> callHandler;
258 
InterceptCallHandler( ServerInterceptor interceptor, ServerCallHandler<ReqT, RespT> callHandler)259     private InterceptCallHandler(
260         ServerInterceptor interceptor, ServerCallHandler<ReqT, RespT> callHandler) {
261       this.interceptor = Preconditions.checkNotNull(interceptor, "interceptor");
262       this.callHandler = callHandler;
263     }
264 
265     @Override
startCall( ServerCall<ReqT, RespT> call, Metadata headers)266     public ServerCall.Listener<ReqT> startCall(
267         ServerCall<ReqT, RespT> call,
268         Metadata headers) {
269       return interceptor.interceptCall(call, headers, callHandler);
270     }
271   }
272 
wrapMethod( final ServerMethodDefinition<OReqT, ORespT> definition, final MethodDescriptor<WReqT, WRespT> wrappedMethod)273   static <OReqT, ORespT, WReqT, WRespT> ServerMethodDefinition<WReqT, WRespT> wrapMethod(
274       final ServerMethodDefinition<OReqT, ORespT> definition,
275       final MethodDescriptor<WReqT, WRespT> wrappedMethod) {
276     final ServerCallHandler<WReqT, WRespT> wrappedHandler = wrapHandler(
277         definition.getServerCallHandler(),
278         definition.getMethodDescriptor(),
279         wrappedMethod);
280     return ServerMethodDefinition.create(wrappedMethod, wrappedHandler);
281   }
282 
wrapHandler( final ServerCallHandler<OReqT, ORespT> originalHandler, final MethodDescriptor<OReqT, ORespT> originalMethod, final MethodDescriptor<WReqT, WRespT> wrappedMethod)283   private static <OReqT, ORespT, WReqT, WRespT> ServerCallHandler<WReqT, WRespT> wrapHandler(
284       final ServerCallHandler<OReqT, ORespT> originalHandler,
285       final MethodDescriptor<OReqT, ORespT> originalMethod,
286       final MethodDescriptor<WReqT, WRespT> wrappedMethod) {
287     return new ServerCallHandler<WReqT, WRespT>() {
288       @Override
289       public ServerCall.Listener<WReqT> startCall(
290           final ServerCall<WReqT, WRespT> call,
291           final Metadata headers) {
292         final ServerCall<OReqT, ORespT> unwrappedCall =
293             new PartialForwardingServerCall<OReqT, ORespT>() {
294           @Override
295           protected ServerCall<WReqT, WRespT> delegate() {
296             return call;
297           }
298 
299           @Override
300           public void sendMessage(ORespT message) {
301             final InputStream is = originalMethod.streamResponse(message);
302             final WRespT wrappedMessage = wrappedMethod.parseResponse(is);
303             delegate().sendMessage(wrappedMessage);
304           }
305 
306           @Override
307           public MethodDescriptor<OReqT, ORespT> getMethodDescriptor() {
308             return originalMethod;
309           }
310         };
311 
312         final ServerCall.Listener<OReqT> originalListener = originalHandler
313             .startCall(unwrappedCall, headers);
314 
315         return new PartialForwardingServerCallListener<WReqT>() {
316           @Override
317           protected ServerCall.Listener<OReqT> delegate() {
318             return originalListener;
319           }
320 
321           @Override
322           public void onMessage(WReqT message) {
323             final InputStream is = wrappedMethod.streamRequest(message);
324             final OReqT originalMessage = originalMethod.parseRequest(is);
325             delegate().onMessage(originalMessage);
326           }
327         };
328       }
329     };
330   }
331 }
332