1 /* 2 * Copyright 2015 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.inprocess; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 22 import com.google.errorprone.annotations.DoNotCall; 23 import io.grpc.ChannelCredentials; 24 import io.grpc.ChannelLogger; 25 import io.grpc.ExperimentalApi; 26 import io.grpc.Internal; 27 import io.grpc.ManagedChannelBuilder; 28 import io.grpc.internal.AbstractManagedChannelImplBuilder; 29 import io.grpc.internal.ClientTransportFactory; 30 import io.grpc.internal.ConnectionClientTransport; 31 import io.grpc.internal.GrpcUtil; 32 import io.grpc.internal.ManagedChannelImplBuilder; 33 import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder; 34 import io.grpc.internal.SharedResourceHolder; 35 import java.net.SocketAddress; 36 import java.util.concurrent.ScheduledExecutorService; 37 import java.util.concurrent.TimeUnit; 38 import javax.annotation.Nullable; 39 40 /** 41 * Builder for a channel that issues in-process requests. Clients identify the in-process server by 42 * its name. 43 * 44 * <p>The channel is intended to be fully-featured, high performance, and useful in testing. 45 * 46 * <p>For usage examples, see {@link InProcessServerBuilder}. 47 */ 48 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1783") 49 public final class InProcessChannelBuilder extends 50 AbstractManagedChannelImplBuilder<InProcessChannelBuilder> { 51 /** 52 * Create a channel builder that will connect to the server with the given name. 53 * 54 * @param name the identity of the server to connect to 55 * @return a new builder 56 */ forName(String name)57 public static InProcessChannelBuilder forName(String name) { 58 return forAddress(new InProcessSocketAddress(checkNotNull(name, "name"))); 59 } 60 61 /** 62 * Create a channel builder that will connect to the server referenced by the given target URI. 63 * Only intended for use with a custom name resolver. 64 * 65 * @param target the identity of the server to connect to 66 * @return a new builder 67 */ forTarget(String target)68 public static InProcessChannelBuilder forTarget(String target) { 69 return new InProcessChannelBuilder(null, checkNotNull(target, "target")); 70 } 71 72 /** 73 * Create a channel builder that will connect to the server referenced by the given address. 74 * 75 * @param address the address of the server to connect to 76 * @return a new builder 77 */ forAddress(SocketAddress address)78 public static InProcessChannelBuilder forAddress(SocketAddress address) { 79 return new InProcessChannelBuilder(checkNotNull(address, "address"), null); 80 } 81 82 /** 83 * Always fails. Call {@link #forName} instead. 84 */ 85 @DoNotCall("Unsupported. Use forName() instead") forAddress(String name, int port)86 public static InProcessChannelBuilder forAddress(String name, int port) { 87 throw new UnsupportedOperationException("call forName() instead"); 88 } 89 90 private final ManagedChannelImplBuilder managedChannelImplBuilder; 91 private ScheduledExecutorService scheduledExecutorService; 92 private int maxInboundMetadataSize = Integer.MAX_VALUE; 93 private boolean transportIncludeStatusCause = false; 94 InProcessChannelBuilder(@ullable SocketAddress directAddress, @Nullable String target)95 private InProcessChannelBuilder(@Nullable SocketAddress directAddress, @Nullable String target) { 96 97 final class InProcessChannelTransportFactoryBuilder implements ClientTransportFactoryBuilder { 98 @Override 99 public ClientTransportFactory buildClientTransportFactory() { 100 return buildTransportFactory(); 101 } 102 } 103 104 if (directAddress != null) { 105 managedChannelImplBuilder = new ManagedChannelImplBuilder(directAddress, "localhost", 106 new InProcessChannelTransportFactoryBuilder(), null); 107 } else { 108 managedChannelImplBuilder = new ManagedChannelImplBuilder(target, 109 new InProcessChannelTransportFactoryBuilder(), null); 110 } 111 112 // In-process transport should not record its traffic to the stats module. 113 // https://github.com/grpc/grpc-java/issues/2284 114 managedChannelImplBuilder.setStatsRecordStartedRpcs(false); 115 managedChannelImplBuilder.setStatsRecordFinishedRpcs(false); 116 managedChannelImplBuilder.setStatsRecordRetryMetrics(false); 117 118 // By default, In-process transport should not be retriable as that leaks memory. Since 119 // there is no wire, bytes aren't calculated so buffer limit isn't respected 120 managedChannelImplBuilder.disableRetry(); 121 } 122 123 @Internal 124 @Override delegate()125 protected ManagedChannelBuilder<?> delegate() { 126 return managedChannelImplBuilder; 127 } 128 129 @Override maxInboundMessageSize(int max)130 public InProcessChannelBuilder maxInboundMessageSize(int max) { 131 // TODO(carl-mastrangelo): maybe throw an exception since this not enforced? 132 return super.maxInboundMessageSize(max); 133 } 134 135 /** 136 * Does nothing. 137 */ 138 @Override useTransportSecurity()139 public InProcessChannelBuilder useTransportSecurity() { 140 return this; 141 } 142 143 /** 144 * Does nothing. 145 */ 146 @Override usePlaintext()147 public InProcessChannelBuilder usePlaintext() { 148 return this; 149 } 150 151 /** Does nothing. */ 152 @Override keepAliveTime(long keepAliveTime, TimeUnit timeUnit)153 public InProcessChannelBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) { 154 return this; 155 } 156 157 /** Does nothing. */ 158 @Override keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit)159 public InProcessChannelBuilder keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit) { 160 return this; 161 } 162 163 /** Does nothing. */ 164 @Override keepAliveWithoutCalls(boolean enable)165 public InProcessChannelBuilder keepAliveWithoutCalls(boolean enable) { 166 return this; 167 } 168 169 /** 170 * Provides a custom scheduled executor service. 171 * 172 * <p>It's an optional parameter. If the user has not provided a scheduled executor service when 173 * the channel is built, the builder will use a static cached thread pool. 174 * 175 * @return this 176 * 177 * @since 1.11.0 178 */ scheduledExecutorService( ScheduledExecutorService scheduledExecutorService)179 public InProcessChannelBuilder scheduledExecutorService( 180 ScheduledExecutorService scheduledExecutorService) { 181 this.scheduledExecutorService = 182 checkNotNull(scheduledExecutorService, "scheduledExecutorService"); 183 return this; 184 } 185 186 /** 187 * Sets the maximum size of metadata allowed to be received. {@code Integer.MAX_VALUE} disables 188 * the enforcement. Defaults to no limit ({@code Integer.MAX_VALUE}). 189 * 190 * <p>There is potential for performance penalty when this setting is enabled, as the Metadata 191 * must actually be serialized. Since the current implementation of Metadata pre-serializes, it's 192 * currently negligible. But Metadata is free to change its implementation. 193 * 194 * @param bytes the maximum size of received metadata 195 * @return this 196 * @throws IllegalArgumentException if bytes is non-positive 197 * @since 1.17.0 198 */ 199 @Override maxInboundMetadataSize(int bytes)200 public InProcessChannelBuilder maxInboundMetadataSize(int bytes) { 201 checkArgument(bytes > 0, "maxInboundMetadataSize must be > 0"); 202 this.maxInboundMetadataSize = bytes; 203 return this; 204 } 205 206 /** 207 * Sets whether to include the cause with the status that is propagated 208 * forward from the InProcessTransport. This was added to make debugging failing 209 * tests easier by showing the cause of the status. 210 * 211 * <p>By default, this is set to false. 212 * A default value of false maintains consistency with other transports which strip causal 213 * information from the status to avoid leaking information to untrusted clients, and 214 * to avoid sharing language-specific information with the client. 215 * For the in-process implementation, this is not a concern. 216 * 217 * @param enable whether to include cause in status 218 * @return this 219 */ propagateCauseWithStatus(boolean enable)220 public InProcessChannelBuilder propagateCauseWithStatus(boolean enable) { 221 this.transportIncludeStatusCause = enable; 222 return this; 223 } 224 buildTransportFactory()225 ClientTransportFactory buildTransportFactory() { 226 return new InProcessClientTransportFactory( 227 scheduledExecutorService, maxInboundMetadataSize, transportIncludeStatusCause); 228 } 229 setStatsEnabled(boolean value)230 void setStatsEnabled(boolean value) { 231 this.managedChannelImplBuilder.setStatsEnabled(value); 232 } 233 234 /** 235 * Creates InProcess transports. Exposed for internal use, as it should be private. 236 */ 237 static final class InProcessClientTransportFactory implements ClientTransportFactory { 238 private final ScheduledExecutorService timerService; 239 private final boolean useSharedTimer; 240 private final int maxInboundMetadataSize; 241 private boolean closed; 242 private final boolean includeCauseWithStatus; 243 InProcessClientTransportFactory( @ullable ScheduledExecutorService scheduledExecutorService, int maxInboundMetadataSize, boolean includeCauseWithStatus)244 private InProcessClientTransportFactory( 245 @Nullable ScheduledExecutorService scheduledExecutorService, 246 int maxInboundMetadataSize, boolean includeCauseWithStatus) { 247 useSharedTimer = scheduledExecutorService == null; 248 timerService = useSharedTimer 249 ? SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE) : scheduledExecutorService; 250 this.maxInboundMetadataSize = maxInboundMetadataSize; 251 this.includeCauseWithStatus = includeCauseWithStatus; 252 } 253 254 @Override newClientTransport( SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger)255 public ConnectionClientTransport newClientTransport( 256 SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger) { 257 if (closed) { 258 throw new IllegalStateException("The transport factory is closed."); 259 } 260 // TODO(carl-mastrangelo): Pass channelLogger in. 261 return new InProcessTransport( 262 addr, maxInboundMetadataSize, options.getAuthority(), options.getUserAgent(), 263 options.getEagAttributes(), includeCauseWithStatus); 264 } 265 266 @Override getScheduledExecutorService()267 public ScheduledExecutorService getScheduledExecutorService() { 268 return timerService; 269 } 270 271 @Override swapChannelCredentials(ChannelCredentials channelCreds)272 public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) { 273 return null; 274 } 275 276 @Override close()277 public void close() { 278 if (closed) { 279 return; 280 } 281 closed = true; 282 if (useSharedTimer) { 283 SharedResourceHolder.release(GrpcUtil.TIMER_SERVICE, timerService); 284 } 285 } 286 } 287 } 288