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.netty; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static io.grpc.internal.GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE; 21 import static io.grpc.internal.GrpcUtil.DEFAULT_SERVER_KEEPALIVE_TIMEOUT_NANOS; 22 import static io.grpc.internal.GrpcUtil.DEFAULT_SERVER_KEEPALIVE_TIME_NANOS; 23 import static io.grpc.internal.GrpcUtil.SERVER_KEEPALIVE_TIME_NANOS_DISABLED; 24 25 import com.google.common.annotations.VisibleForTesting; 26 import com.google.common.base.Preconditions; 27 import com.google.errorprone.annotations.CanIgnoreReturnValue; 28 import io.grpc.ExperimentalApi; 29 import io.grpc.Internal; 30 import io.grpc.ServerStreamTracer; 31 import io.grpc.internal.AbstractServerImplBuilder; 32 import io.grpc.internal.GrpcUtil; 33 import io.grpc.internal.KeepAliveManager; 34 import io.grpc.internal.TransportTracer; 35 import io.netty.channel.ChannelOption; 36 import io.netty.channel.EventLoopGroup; 37 import io.netty.channel.ServerChannel; 38 import io.netty.channel.socket.nio.NioServerSocketChannel; 39 import io.netty.handler.ssl.SslContext; 40 import java.io.File; 41 import java.io.InputStream; 42 import java.net.InetSocketAddress; 43 import java.net.SocketAddress; 44 import java.util.HashMap; 45 import java.util.List; 46 import java.util.Map; 47 import java.util.concurrent.TimeUnit; 48 import javax.annotation.CheckReturnValue; 49 import javax.annotation.Nullable; 50 import javax.net.ssl.SSLException; 51 52 /** 53 * A builder to help simplify the construction of a Netty-based GRPC server. 54 */ 55 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1784") 56 @CanIgnoreReturnValue 57 public final class NettyServerBuilder extends AbstractServerImplBuilder<NettyServerBuilder> { 58 public static final int DEFAULT_FLOW_CONTROL_WINDOW = 1048576; // 1MiB 59 60 static final long MAX_CONNECTION_IDLE_NANOS_DISABLED = Long.MAX_VALUE; 61 static final long MAX_CONNECTION_AGE_NANOS_DISABLED = Long.MAX_VALUE; 62 static final long MAX_CONNECTION_AGE_GRACE_NANOS_INFINITE = Long.MAX_VALUE; 63 64 private static final long MIN_KEEPALIVE_TIME_NANO = TimeUnit.MILLISECONDS.toNanos(1L); 65 private static final long MIN_KEEPALIVE_TIMEOUT_NANO = TimeUnit.MICROSECONDS.toNanos(499L); 66 private static final long MIN_MAX_CONNECTION_IDLE_NANO = TimeUnit.SECONDS.toNanos(1L); 67 private static final long MIN_MAX_CONNECTION_AGE_NANO = TimeUnit.SECONDS.toNanos(1L); 68 private static final long AS_LARGE_AS_INFINITE = TimeUnit.DAYS.toNanos(1000L); 69 70 private final SocketAddress address; 71 private Class<? extends ServerChannel> channelType = NioServerSocketChannel.class; 72 private final Map<ChannelOption<?>, Object> channelOptions = 73 new HashMap<ChannelOption<?>, Object>(); 74 @Nullable 75 private EventLoopGroup bossEventLoopGroup; 76 @Nullable 77 private EventLoopGroup workerEventLoopGroup; 78 private SslContext sslContext; 79 private ProtocolNegotiator protocolNegotiator; 80 private int maxConcurrentCallsPerConnection = Integer.MAX_VALUE; 81 private int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW; 82 private int maxMessageSize = DEFAULT_MAX_MESSAGE_SIZE; 83 private int maxHeaderListSize = GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE; 84 private long keepAliveTimeInNanos = DEFAULT_SERVER_KEEPALIVE_TIME_NANOS; 85 private long keepAliveTimeoutInNanos = DEFAULT_SERVER_KEEPALIVE_TIMEOUT_NANOS; 86 private long maxConnectionIdleInNanos = MAX_CONNECTION_IDLE_NANOS_DISABLED; 87 private long maxConnectionAgeInNanos = MAX_CONNECTION_AGE_NANOS_DISABLED; 88 private long maxConnectionAgeGraceInNanos = MAX_CONNECTION_AGE_GRACE_NANOS_INFINITE; 89 private boolean permitKeepAliveWithoutCalls; 90 private long permitKeepAliveTimeInNanos = TimeUnit.MINUTES.toNanos(5); 91 92 /** 93 * Creates a server builder that will bind to the given port. 94 * 95 * @param port the port on which the server is to be bound. 96 * @return the server builder. 97 */ 98 @CheckReturnValue forPort(int port)99 public static NettyServerBuilder forPort(int port) { 100 return new NettyServerBuilder(port); 101 } 102 103 /** 104 * Creates a server builder configured with the given {@link SocketAddress}. 105 * 106 * @param address the socket address on which the server is to be bound. 107 * @return the server builder 108 */ 109 @CheckReturnValue forAddress(SocketAddress address)110 public static NettyServerBuilder forAddress(SocketAddress address) { 111 return new NettyServerBuilder(address); 112 } 113 114 @CheckReturnValue NettyServerBuilder(int port)115 private NettyServerBuilder(int port) { 116 this.address = new InetSocketAddress(port); 117 } 118 119 @CheckReturnValue NettyServerBuilder(SocketAddress address)120 private NettyServerBuilder(SocketAddress address) { 121 this.address = address; 122 } 123 124 /** 125 * Specify the channel type to use, by default we use {@link NioServerSocketChannel}. 126 */ channelType(Class<? extends ServerChannel> channelType)127 public NettyServerBuilder channelType(Class<? extends ServerChannel> channelType) { 128 this.channelType = Preconditions.checkNotNull(channelType, "channelType"); 129 return this; 130 } 131 132 /** 133 * Specifies a channel option. As the underlying channel as well as network implementation may 134 * ignore this value applications should consider it a hint. 135 * 136 * @since 1.9.0 137 */ withChildOption(ChannelOption<T> option, T value)138 public <T> NettyServerBuilder withChildOption(ChannelOption<T> option, T value) { 139 this.channelOptions.put(option, value); 140 return this; 141 } 142 143 /** 144 * Provides the boss EventGroupLoop to the server. 145 * 146 * <p>It's an optional parameter. If the user has not provided one when the server is built, the 147 * builder will use the default one which is static. 148 * 149 * <p>The server won't take ownership of the given EventLoopGroup. It's caller's responsibility 150 * to shut it down when it's desired. 151 * 152 * <p>Grpc uses non-daemon {@link Thread}s by default and thus a {@link io.grpc.Server} will 153 * continue to run even after the main thread has terminated. However, users have to be cautious 154 * when providing their own {@link EventLoopGroup}s. 155 * For example, Netty's {@link EventLoopGroup}s use daemon threads by default 156 * and thus an application with only daemon threads running besides the main thread will exit as 157 * soon as the main thread completes. 158 * A simple solution to this problem is to call {@link io.grpc.Server#awaitTermination()} to 159 * keep the main thread alive until the server has terminated. 160 */ bossEventLoopGroup(EventLoopGroup group)161 public NettyServerBuilder bossEventLoopGroup(EventLoopGroup group) { 162 this.bossEventLoopGroup = group; 163 return this; 164 } 165 166 /** 167 * Provides the worker EventGroupLoop to the server. 168 * 169 * <p>It's an optional parameter. If the user has not provided one when the server is built, the 170 * builder will create one. 171 * 172 * <p>The server won't take ownership of the given EventLoopGroup. It's caller's responsibility 173 * to shut it down when it's desired. 174 * 175 * <p>Grpc uses non-daemon {@link Thread}s by default and thus a {@link io.grpc.Server} will 176 * continue to run even after the main thread has terminated. However, users have to be cautious 177 * when providing their own {@link EventLoopGroup}s. 178 * For example, Netty's {@link EventLoopGroup}s use daemon threads by default 179 * and thus an application with only daemon threads running besides the main thread will exit as 180 * soon as the main thread completes. 181 * A simple solution to this problem is to call {@link io.grpc.Server#awaitTermination()} to 182 * keep the main thread alive until the server has terminated. 183 */ workerEventLoopGroup(EventLoopGroup group)184 public NettyServerBuilder workerEventLoopGroup(EventLoopGroup group) { 185 this.workerEventLoopGroup = group; 186 return this; 187 } 188 189 /** 190 * Sets the TLS context to use for encryption. Providing a context enables encryption. It must 191 * have been configured with {@link GrpcSslContexts}, but options could have been overridden. 192 */ sslContext(SslContext sslContext)193 public NettyServerBuilder sslContext(SslContext sslContext) { 194 if (sslContext != null) { 195 checkArgument(sslContext.isServer(), 196 "Client SSL context can not be used for server"); 197 GrpcSslContexts.ensureAlpnAndH2Enabled(sslContext.applicationProtocolNegotiator()); 198 } 199 this.sslContext = sslContext; 200 return this; 201 } 202 203 /** 204 * Sets the {@link ProtocolNegotiator} to be used. If non-{@code null}, overrides the value 205 * specified in {@link #sslContext(SslContext)}. 206 * 207 * <p>Default: {@code null}. 208 */ 209 @Internal protocolNegotiator( @ullable ProtocolNegotiator protocolNegotiator)210 public final NettyServerBuilder protocolNegotiator( 211 @Nullable ProtocolNegotiator protocolNegotiator) { 212 this.protocolNegotiator = protocolNegotiator; 213 return this; 214 } 215 216 @Override setTracingEnabled(boolean value)217 protected void setTracingEnabled(boolean value) { 218 super.setTracingEnabled(value); 219 } 220 221 @Override setStatsEnabled(boolean value)222 protected void setStatsEnabled(boolean value) { 223 super.setStatsEnabled(value); 224 } 225 226 @Override setStatsRecordStartedRpcs(boolean value)227 protected void setStatsRecordStartedRpcs(boolean value) { 228 super.setStatsRecordStartedRpcs(value); 229 } 230 231 @VisibleForTesting setTransportTracerFactory(TransportTracer.Factory transportTracerFactory)232 NettyServerBuilder setTransportTracerFactory(TransportTracer.Factory transportTracerFactory) { 233 this.transportTracerFactory = transportTracerFactory; 234 return this; 235 } 236 237 /** 238 * The maximum number of concurrent calls permitted for each incoming connection. Defaults to no 239 * limit. 240 */ maxConcurrentCallsPerConnection(int maxCalls)241 public NettyServerBuilder maxConcurrentCallsPerConnection(int maxCalls) { 242 checkArgument(maxCalls > 0, "max must be positive: %s", maxCalls); 243 this.maxConcurrentCallsPerConnection = maxCalls; 244 return this; 245 } 246 247 /** 248 * Sets the HTTP/2 flow control window. If not called, the default value 249 * is {@link #DEFAULT_FLOW_CONTROL_WINDOW}). 250 */ flowControlWindow(int flowControlWindow)251 public NettyServerBuilder flowControlWindow(int flowControlWindow) { 252 checkArgument(flowControlWindow > 0, "flowControlWindow must be positive"); 253 this.flowControlWindow = flowControlWindow; 254 return this; 255 } 256 257 /** 258 * Sets the maximum message size allowed to be received on the server. If not called, 259 * defaults to 4 MiB. The default provides protection to services who haven't considered the 260 * possibility of receiving large messages while trying to be large enough to not be hit in normal 261 * usage. 262 * 263 * @deprecated Call {@link #maxInboundMessageSize} instead. This method will be removed in a 264 * future release. 265 */ 266 @Deprecated maxMessageSize(int maxMessageSize)267 public NettyServerBuilder maxMessageSize(int maxMessageSize) { 268 return maxInboundMessageSize(maxMessageSize); 269 } 270 271 /** {@inheritDoc} */ 272 @Override maxInboundMessageSize(int bytes)273 public NettyServerBuilder maxInboundMessageSize(int bytes) { 274 checkArgument(bytes >= 0, "bytes must be >= 0"); 275 this.maxMessageSize = bytes; 276 return this; 277 } 278 279 /** 280 * Sets the maximum size of header list allowed to be received. This is cumulative size of the 281 * headers with some overhead, as defined for 282 * <a href="http://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2"> 283 * HTTP/2's SETTINGS_MAX_HEADER_LIST_SIZE</a>. The default is 8 KiB. 284 */ maxHeaderListSize(int maxHeaderListSize)285 public NettyServerBuilder maxHeaderListSize(int maxHeaderListSize) { 286 checkArgument(maxHeaderListSize > 0, "maxHeaderListSize must be > 0"); 287 this.maxHeaderListSize = maxHeaderListSize; 288 return this; 289 } 290 291 /** 292 * Sets a custom keepalive time, the delay time for sending next keepalive ping. An unreasonably 293 * small value might be increased, and {@code Long.MAX_VALUE} nano seconds or an unreasonably 294 * large value will disable keepalive. 295 * 296 * @since 1.3.0 297 */ keepAliveTime(long keepAliveTime, TimeUnit timeUnit)298 public NettyServerBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) { 299 checkArgument(keepAliveTime > 0L, "keepalive time must be positive"); 300 keepAliveTimeInNanos = timeUnit.toNanos(keepAliveTime); 301 keepAliveTimeInNanos = KeepAliveManager.clampKeepAliveTimeInNanos(keepAliveTimeInNanos); 302 if (keepAliveTimeInNanos >= AS_LARGE_AS_INFINITE) { 303 // Bump keepalive time to infinite. This disables keep alive. 304 keepAliveTimeInNanos = SERVER_KEEPALIVE_TIME_NANOS_DISABLED; 305 } 306 if (keepAliveTimeInNanos < MIN_KEEPALIVE_TIME_NANO) { 307 // Bump keepalive time. 308 keepAliveTimeInNanos = MIN_KEEPALIVE_TIME_NANO; 309 } 310 return this; 311 } 312 313 /** 314 * Sets a custom keepalive timeout, the timeout for keepalive ping requests. An unreasonably small 315 * value might be increased. 316 * 317 * @since 1.3.0 318 */ keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit)319 public NettyServerBuilder keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit) { 320 checkArgument(keepAliveTimeout > 0L, "keepalive timeout must be positive"); 321 keepAliveTimeoutInNanos = timeUnit.toNanos(keepAliveTimeout); 322 keepAliveTimeoutInNanos = 323 KeepAliveManager.clampKeepAliveTimeoutInNanos(keepAliveTimeoutInNanos); 324 if (keepAliveTimeoutInNanos < MIN_KEEPALIVE_TIMEOUT_NANO) { 325 // Bump keepalive timeout. 326 keepAliveTimeoutInNanos = MIN_KEEPALIVE_TIMEOUT_NANO; 327 } 328 return this; 329 } 330 331 /** 332 * Sets a custom max connection idle time, connection being idle for longer than which will be 333 * gracefully terminated. Idleness duration is defined since the most recent time the number of 334 * outstanding RPCs became zero or the connection establishment. An unreasonably small value might 335 * be increased. {@code Long.MAX_VALUE} nano seconds or an unreasonably large value will disable 336 * max connection idle. 337 * 338 * @since 1.4.0 339 */ maxConnectionIdle(long maxConnectionIdle, TimeUnit timeUnit)340 public NettyServerBuilder maxConnectionIdle(long maxConnectionIdle, TimeUnit timeUnit) { 341 checkArgument(maxConnectionIdle > 0L, "max connection idle must be positive"); 342 maxConnectionIdleInNanos = timeUnit.toNanos(maxConnectionIdle); 343 if (maxConnectionIdleInNanos >= AS_LARGE_AS_INFINITE) { 344 maxConnectionIdleInNanos = MAX_CONNECTION_IDLE_NANOS_DISABLED; 345 } 346 if (maxConnectionIdleInNanos < MIN_MAX_CONNECTION_IDLE_NANO) { 347 maxConnectionIdleInNanos = MIN_MAX_CONNECTION_IDLE_NANO; 348 } 349 return this; 350 } 351 352 /** 353 * Sets a custom max connection age, connection lasting longer than which will be gracefully 354 * terminated. An unreasonably small value might be increased. A random jitter of +/-10% will be 355 * added to it. {@code Long.MAX_VALUE} nano seconds or an unreasonably large value will disable 356 * max connection age. 357 * 358 * @since 1.3.0 359 */ maxConnectionAge(long maxConnectionAge, TimeUnit timeUnit)360 public NettyServerBuilder maxConnectionAge(long maxConnectionAge, TimeUnit timeUnit) { 361 checkArgument(maxConnectionAge > 0L, "max connection age must be positive"); 362 maxConnectionAgeInNanos = timeUnit.toNanos(maxConnectionAge); 363 if (maxConnectionAgeInNanos >= AS_LARGE_AS_INFINITE) { 364 maxConnectionAgeInNanos = MAX_CONNECTION_AGE_NANOS_DISABLED; 365 } 366 if (maxConnectionAgeInNanos < MIN_MAX_CONNECTION_AGE_NANO) { 367 maxConnectionAgeInNanos = MIN_MAX_CONNECTION_AGE_NANO; 368 } 369 return this; 370 } 371 372 /** 373 * Sets a custom grace time for the graceful connection termination. Once the max connection age 374 * is reached, RPCs have the grace time to complete. RPCs that do not complete in time will be 375 * cancelled, allowing the connection to terminate. {@code Long.MAX_VALUE} nano seconds or an 376 * unreasonably large value are considered infinite. 377 * 378 * @see #maxConnectionAge(long, TimeUnit) 379 * @since 1.3.0 380 */ maxConnectionAgeGrace(long maxConnectionAgeGrace, TimeUnit timeUnit)381 public NettyServerBuilder maxConnectionAgeGrace(long maxConnectionAgeGrace, TimeUnit timeUnit) { 382 checkArgument(maxConnectionAgeGrace >= 0L, "max connection age grace must be non-negative"); 383 maxConnectionAgeGraceInNanos = timeUnit.toNanos(maxConnectionAgeGrace); 384 if (maxConnectionAgeGraceInNanos >= AS_LARGE_AS_INFINITE) { 385 maxConnectionAgeGraceInNanos = MAX_CONNECTION_AGE_GRACE_NANOS_INFINITE; 386 } 387 return this; 388 } 389 390 /** 391 * Specify the most aggressive keep-alive time clients are permitted to configure. The server will 392 * try to detect clients exceeding this rate and when detected will forcefully close the 393 * connection. The default is 5 minutes. 394 * 395 * <p>Even though a default is defined that allows some keep-alives, clients must not use 396 * keep-alive without approval from the service owner. Otherwise, they may experience failures in 397 * the future if the service becomes more restrictive. When unthrottled, keep-alives can cause a 398 * significant amount of traffic and CPU usage, so clients and servers should be conservative in 399 * what they use and accept. 400 * 401 * @see #permitKeepAliveWithoutCalls(boolean) 402 * @since 1.3.0 403 */ permitKeepAliveTime(long keepAliveTime, TimeUnit timeUnit)404 public NettyServerBuilder permitKeepAliveTime(long keepAliveTime, TimeUnit timeUnit) { 405 checkArgument(keepAliveTime >= 0, "permit keepalive time must be non-negative"); 406 permitKeepAliveTimeInNanos = timeUnit.toNanos(keepAliveTime); 407 return this; 408 } 409 410 /** 411 * Sets whether to allow clients to send keep-alive HTTP/2 PINGs even if there are no outstanding 412 * RPCs on the connection. Defaults to {@code false}. 413 * 414 * @see #permitKeepAliveTime(long, TimeUnit) 415 * @since 1.3.0 416 */ permitKeepAliveWithoutCalls(boolean permit)417 public NettyServerBuilder permitKeepAliveWithoutCalls(boolean permit) { 418 permitKeepAliveWithoutCalls = permit; 419 return this; 420 } 421 422 @Override 423 @CheckReturnValue buildTransportServer( List<ServerStreamTracer.Factory> streamTracerFactories)424 protected NettyServer buildTransportServer( 425 List<ServerStreamTracer.Factory> streamTracerFactories) { 426 ProtocolNegotiator negotiator = protocolNegotiator; 427 if (negotiator == null) { 428 negotiator = sslContext != null ? ProtocolNegotiators.serverTls(sslContext) : 429 ProtocolNegotiators.serverPlaintext(); 430 } 431 432 return new NettyServer( 433 address, channelType, channelOptions, bossEventLoopGroup, workerEventLoopGroup, 434 negotiator, streamTracerFactories, transportTracerFactory, 435 maxConcurrentCallsPerConnection, flowControlWindow, 436 maxMessageSize, maxHeaderListSize, keepAliveTimeInNanos, keepAliveTimeoutInNanos, 437 maxConnectionIdleInNanos, 438 maxConnectionAgeInNanos, maxConnectionAgeGraceInNanos, 439 permitKeepAliveWithoutCalls, permitKeepAliveTimeInNanos, channelz); 440 } 441 442 @Override useTransportSecurity(File certChain, File privateKey)443 public NettyServerBuilder useTransportSecurity(File certChain, File privateKey) { 444 try { 445 sslContext = GrpcSslContexts.forServer(certChain, privateKey).build(); 446 } catch (SSLException e) { 447 // This should likely be some other, easier to catch exception. 448 throw new RuntimeException(e); 449 } 450 return this; 451 } 452 453 @Override useTransportSecurity(InputStream certChain, InputStream privateKey)454 public NettyServerBuilder useTransportSecurity(InputStream certChain, InputStream privateKey) { 455 try { 456 sslContext = GrpcSslContexts.forServer(certChain, privateKey).build(); 457 } catch (SSLException e) { 458 // This should likely be some other, easier to catch exception. 459 throw new RuntimeException(e); 460 } 461 return this; 462 } 463 } 464