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