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 { 152 return new BufferedInputStream(stream); 153 } 154 } 155 }; 156 157 return useMarshalledMessages(serviceDef, marshaller); 158 } 159 160 /** 161 * Create a new {@code ServerServiceDefinition} whose {@link MethodDescriptor} serializes to 162 * and from T for all methods. The {@code ServerCallHandler} created will automatically 163 * convert back to the original types for request and response before calling the existing 164 * {@code ServerCallHandler}. Calling this method combined with the intercept methods will 165 * allow the developer to choose whether to intercept messages of T, or the modeled types 166 * of their application. This can also be chained to allow for interceptors to handle messages 167 * as multiple different T types within the chain if the added cost of serialization is not 168 * a concern. 169 * 170 * @param serviceDef the service definition to convert messages to T 171 * @return a wrapped version of {@code serviceDef} with the T conversion applied. 172 */ 173 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1712") useMarshalledMessages( final ServerServiceDefinition serviceDef, final MethodDescriptor.Marshaller<T> marshaller)174 public static <T> ServerServiceDefinition useMarshalledMessages( 175 final ServerServiceDefinition serviceDef, 176 final MethodDescriptor.Marshaller<T> marshaller) { 177 List<ServerMethodDefinition<?, ?>> wrappedMethods = 178 new ArrayList<ServerMethodDefinition<?, ?>>(); 179 List<MethodDescriptor<?, ?>> wrappedDescriptors = 180 new ArrayList<MethodDescriptor<?, ?>>(); 181 // Wrap the descriptors 182 for (final ServerMethodDefinition<?, ?> definition : serviceDef.getMethods()) { 183 final MethodDescriptor<?, ?> originalMethodDescriptor = definition.getMethodDescriptor(); 184 final MethodDescriptor<T, T> wrappedMethodDescriptor = 185 originalMethodDescriptor.toBuilder(marshaller, marshaller).build(); 186 wrappedDescriptors.add(wrappedMethodDescriptor); 187 wrappedMethods.add(wrapMethod(definition, wrappedMethodDescriptor)); 188 } 189 // Build the new service descriptor 190 final ServerServiceDefinition.Builder serviceBuilder = ServerServiceDefinition 191 .builder(new ServiceDescriptor(serviceDef.getServiceDescriptor().getName(), 192 wrappedDescriptors)); 193 // Create the new service definiton. 194 for (ServerMethodDefinition<?, ?> definition : wrappedMethods) { 195 serviceBuilder.addMethod(definition); 196 } 197 return serviceBuilder.build(); 198 } 199 wrapAndAddMethod( ServerServiceDefinition.Builder serviceDefBuilder, ServerMethodDefinition<ReqT, RespT> method, List<? extends ServerInterceptor> interceptors)200 private static <ReqT, RespT> void wrapAndAddMethod( 201 ServerServiceDefinition.Builder serviceDefBuilder, ServerMethodDefinition<ReqT, RespT> method, 202 List<? extends ServerInterceptor> interceptors) { 203 ServerCallHandler<ReqT, RespT> callHandler = method.getServerCallHandler(); 204 for (ServerInterceptor interceptor : interceptors) { 205 callHandler = InterceptCallHandler.create(interceptor, callHandler); 206 } 207 serviceDefBuilder.addMethod(method.withServerCallHandler(callHandler)); 208 } 209 210 static final class InterceptCallHandler<ReqT, RespT> implements ServerCallHandler<ReqT, RespT> { create( ServerInterceptor interceptor, ServerCallHandler<ReqT, RespT> callHandler)211 public static <ReqT, RespT> InterceptCallHandler<ReqT, RespT> create( 212 ServerInterceptor interceptor, ServerCallHandler<ReqT, RespT> callHandler) { 213 return new InterceptCallHandler<ReqT, RespT>(interceptor, callHandler); 214 } 215 216 private final ServerInterceptor interceptor; 217 private final ServerCallHandler<ReqT, RespT> callHandler; 218 InterceptCallHandler( ServerInterceptor interceptor, ServerCallHandler<ReqT, RespT> callHandler)219 private InterceptCallHandler( 220 ServerInterceptor interceptor, ServerCallHandler<ReqT, RespT> callHandler) { 221 this.interceptor = Preconditions.checkNotNull(interceptor, "interceptor"); 222 this.callHandler = callHandler; 223 } 224 225 @Override startCall( ServerCall<ReqT, RespT> call, Metadata headers)226 public ServerCall.Listener<ReqT> startCall( 227 ServerCall<ReqT, RespT> call, 228 Metadata headers) { 229 return interceptor.interceptCall(call, headers, callHandler); 230 } 231 } 232 wrapMethod( final ServerMethodDefinition<OReqT, ORespT> definition, final MethodDescriptor<WReqT, WRespT> wrappedMethod)233 static <OReqT, ORespT, WReqT, WRespT> ServerMethodDefinition<WReqT, WRespT> wrapMethod( 234 final ServerMethodDefinition<OReqT, ORespT> definition, 235 final MethodDescriptor<WReqT, WRespT> wrappedMethod) { 236 final ServerCallHandler<WReqT, WRespT> wrappedHandler = wrapHandler( 237 definition.getServerCallHandler(), 238 definition.getMethodDescriptor(), 239 wrappedMethod); 240 return ServerMethodDefinition.create(wrappedMethod, wrappedHandler); 241 } 242 wrapHandler( final ServerCallHandler<OReqT, ORespT> originalHandler, final MethodDescriptor<OReqT, ORespT> originalMethod, final MethodDescriptor<WReqT, WRespT> wrappedMethod)243 private static <OReqT, ORespT, WReqT, WRespT> ServerCallHandler<WReqT, WRespT> wrapHandler( 244 final ServerCallHandler<OReqT, ORespT> originalHandler, 245 final MethodDescriptor<OReqT, ORespT> originalMethod, 246 final MethodDescriptor<WReqT, WRespT> wrappedMethod) { 247 return new ServerCallHandler<WReqT, WRespT>() { 248 @Override 249 public ServerCall.Listener<WReqT> startCall( 250 final ServerCall<WReqT, WRespT> call, 251 final Metadata headers) { 252 final ServerCall<OReqT, ORespT> unwrappedCall = 253 new PartialForwardingServerCall<OReqT, ORespT>() { 254 @Override 255 protected ServerCall<WReqT, WRespT> delegate() { 256 return call; 257 } 258 259 @Override 260 public void sendMessage(ORespT message) { 261 final InputStream is = originalMethod.streamResponse(message); 262 final WRespT wrappedMessage = wrappedMethod.parseResponse(is); 263 delegate().sendMessage(wrappedMessage); 264 } 265 266 @Override 267 public MethodDescriptor<OReqT, ORespT> getMethodDescriptor() { 268 return originalMethod; 269 } 270 }; 271 272 final ServerCall.Listener<OReqT> originalListener = originalHandler 273 .startCall(unwrappedCall, headers); 274 275 return new PartialForwardingServerCallListener<WReqT>() { 276 @Override 277 protected ServerCall.Listener<OReqT> delegate() { 278 return originalListener; 279 } 280 281 @Override 282 public void onMessage(WReqT message) { 283 final InputStream is = wrappedMethod.streamRequest(message); 284 final OReqT originalMessage = originalMethod.parseRequest(is); 285 delegate().onMessage(originalMessage); 286 } 287 }; 288 } 289 }; 290 } 291 } 292