1 /* 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 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 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 16 package software.amazon.awssdk.http.nio.netty; 17 18 import static software.amazon.awssdk.http.HttpMetric.HTTP_CLIENT_NAME; 19 import static software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration.EVENTLOOP_SHUTDOWN_FUTURE_TIMEOUT_SECONDS; 20 import static software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration.EVENTLOOP_SHUTDOWN_QUIET_PERIOD_SECONDS; 21 import static software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration.EVENTLOOP_SHUTDOWN_TIMEOUT_SECONDS; 22 import static software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils.runAndLogError; 23 import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely; 24 25 import io.netty.channel.ChannelOption; 26 import io.netty.channel.EventLoopGroup; 27 import io.netty.handler.ssl.SslContext; 28 import io.netty.handler.ssl.SslProvider; 29 import java.net.SocketOptions; 30 import java.net.URI; 31 import java.time.Duration; 32 import java.util.concurrent.CompletableFuture; 33 import java.util.concurrent.ExecutionException; 34 import java.util.concurrent.TimeUnit; 35 import java.util.concurrent.TimeoutException; 36 import java.util.function.Consumer; 37 import software.amazon.awssdk.annotations.SdkPublicApi; 38 import software.amazon.awssdk.annotations.SdkTestInternalApi; 39 import software.amazon.awssdk.http.Protocol; 40 import software.amazon.awssdk.http.SdkHttpConfigurationOption; 41 import software.amazon.awssdk.http.SdkHttpRequest; 42 import software.amazon.awssdk.http.SystemPropertyTlsKeyManagersProvider; 43 import software.amazon.awssdk.http.TlsKeyManagersProvider; 44 import software.amazon.awssdk.http.TlsTrustManagersProvider; 45 import software.amazon.awssdk.http.async.AsyncExecuteRequest; 46 import software.amazon.awssdk.http.async.SdkAsyncHttpClient; 47 import software.amazon.awssdk.http.nio.netty.internal.AwaitCloseChannelPoolMap; 48 import software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration; 49 import software.amazon.awssdk.http.nio.netty.internal.NettyRequestExecutor; 50 import software.amazon.awssdk.http.nio.netty.internal.NonManagedEventLoopGroup; 51 import software.amazon.awssdk.http.nio.netty.internal.RequestContext; 52 import software.amazon.awssdk.http.nio.netty.internal.SdkChannelOptions; 53 import software.amazon.awssdk.http.nio.netty.internal.SdkChannelPool; 54 import software.amazon.awssdk.http.nio.netty.internal.SdkChannelPoolMap; 55 import software.amazon.awssdk.http.nio.netty.internal.SharedSdkEventLoopGroup; 56 import software.amazon.awssdk.http.nio.netty.internal.utils.NettyClientLogger; 57 import software.amazon.awssdk.utils.AttributeMap; 58 import software.amazon.awssdk.utils.Either; 59 import software.amazon.awssdk.utils.Validate; 60 61 /** 62 * An implementation of {@link SdkAsyncHttpClient} that uses a Netty non-blocking HTTP client to communicate with the service. 63 * 64 * <p>This can be created via {@link #builder()}</p> 65 */ 66 @SdkPublicApi 67 public final class NettyNioAsyncHttpClient implements SdkAsyncHttpClient { 68 69 private static final String CLIENT_NAME = "NettyNio"; 70 71 private static final NettyClientLogger log = NettyClientLogger.getLogger(NettyNioAsyncHttpClient.class); 72 private static final long MAX_STREAMS_ALLOWED = 4294967295L; // unsigned 32-bit, 2^32 -1 73 private static final int DEFAULT_INITIAL_WINDOW_SIZE = 1_048_576; // 1MiB 74 75 // Override connection idle timeout for Netty http client to reduce the frequency of "server failed to complete the 76 // response error". see https://github.com/aws/aws-sdk-java-v2/issues/1122 77 private static final AttributeMap NETTY_HTTP_DEFAULTS = 78 AttributeMap.builder() 79 .put(SdkHttpConfigurationOption.CONNECTION_MAX_IDLE_TIMEOUT, Duration.ofSeconds(5)) 80 .build(); 81 82 private final SdkEventLoopGroup sdkEventLoopGroup; 83 private final SdkChannelPoolMap<URI, ? extends SdkChannelPool> pools; 84 private final NettyConfiguration configuration; 85 NettyNioAsyncHttpClient(DefaultBuilder builder, AttributeMap serviceDefaultsMap)86 private NettyNioAsyncHttpClient(DefaultBuilder builder, AttributeMap serviceDefaultsMap) { 87 this.configuration = new NettyConfiguration(serviceDefaultsMap); 88 Protocol protocol = serviceDefaultsMap.get(SdkHttpConfigurationOption.PROTOCOL); 89 this.sdkEventLoopGroup = eventLoopGroup(builder); 90 91 Http2Configuration http2Configuration = builder.http2Configuration; 92 93 long maxStreams = resolveMaxHttp2Streams(builder.maxHttp2Streams, http2Configuration); 94 int initialWindowSize = resolveInitialWindowSize(http2Configuration); 95 96 this.pools = AwaitCloseChannelPoolMap.builder() 97 .sdkChannelOptions(builder.sdkChannelOptions) 98 .configuration(configuration) 99 .protocol(protocol) 100 .maxStreams(maxStreams) 101 .initialWindowSize(initialWindowSize) 102 .healthCheckPingPeriod(resolveHealthCheckPingPeriod(http2Configuration)) 103 .sdkEventLoopGroup(sdkEventLoopGroup) 104 .sslProvider(resolveSslProvider(builder)) 105 .proxyConfiguration(builder.proxyConfiguration) 106 .useNonBlockingDnsResolver(builder.useNonBlockingDnsResolver) 107 .build(); 108 } 109 110 @SdkTestInternalApi NettyNioAsyncHttpClient(SdkEventLoopGroup sdkEventLoopGroup, SdkChannelPoolMap<URI, ? extends SdkChannelPool> pools, NettyConfiguration configuration)111 NettyNioAsyncHttpClient(SdkEventLoopGroup sdkEventLoopGroup, 112 SdkChannelPoolMap<URI, ? extends SdkChannelPool> pools, 113 NettyConfiguration configuration) { 114 this.sdkEventLoopGroup = sdkEventLoopGroup; 115 this.pools = pools; 116 this.configuration = configuration; 117 } 118 119 @Override execute(AsyncExecuteRequest request)120 public CompletableFuture<Void> execute(AsyncExecuteRequest request) { 121 RequestContext ctx = createRequestContext(request); 122 ctx.metricCollector().reportMetric(HTTP_CLIENT_NAME, clientName()); // TODO: Can't this be done in core? 123 return new NettyRequestExecutor(ctx).execute(); 124 } 125 builder()126 public static Builder builder() { 127 return new DefaultBuilder(); 128 } 129 130 /** 131 * Create a {@link NettyNioAsyncHttpClient} with the default properties 132 * 133 * @return an {@link NettyNioAsyncHttpClient} 134 */ create()135 public static SdkAsyncHttpClient create() { 136 return new DefaultBuilder().build(); 137 } 138 createRequestContext(AsyncExecuteRequest request)139 private RequestContext createRequestContext(AsyncExecuteRequest request) { 140 SdkChannelPool pool = pools.get(poolKey(request.request())); 141 return new RequestContext(pool, sdkEventLoopGroup.eventLoopGroup(), request, configuration); 142 } 143 eventLoopGroup(DefaultBuilder builder)144 private SdkEventLoopGroup eventLoopGroup(DefaultBuilder builder) { 145 Validate.isTrue(builder.eventLoopGroup == null || builder.eventLoopGroupBuilder == null, 146 "The eventLoopGroup and the eventLoopGroupFactory can't both be configured."); 147 return Either.fromNullable(builder.eventLoopGroup, builder.eventLoopGroupBuilder) 148 .map(e -> e.map(this::nonManagedEventLoopGroup, SdkEventLoopGroup.Builder::build)) 149 .orElseGet(SharedSdkEventLoopGroup::get); 150 } 151 poolKey(SdkHttpRequest sdkRequest)152 private static URI poolKey(SdkHttpRequest sdkRequest) { 153 return invokeSafely(() -> new URI(sdkRequest.protocol(), null, sdkRequest.host(), 154 sdkRequest.port(), null, null, null)); 155 } 156 resolveSslProvider(DefaultBuilder builder)157 private SslProvider resolveSslProvider(DefaultBuilder builder) { 158 if (builder.sslProvider != null) { 159 return builder.sslProvider; 160 } 161 162 return SslContext.defaultClientProvider(); 163 } 164 resolveMaxHttp2Streams(Integer topLevelValue, Http2Configuration http2Configuration)165 private long resolveMaxHttp2Streams(Integer topLevelValue, Http2Configuration http2Configuration) { 166 if (topLevelValue != null) { 167 return topLevelValue; 168 } 169 170 if (http2Configuration == null || http2Configuration.maxStreams() == null) { 171 return MAX_STREAMS_ALLOWED; 172 } 173 174 return Math.min(http2Configuration.maxStreams(), MAX_STREAMS_ALLOWED); 175 } 176 resolveInitialWindowSize(Http2Configuration http2Configuration)177 private int resolveInitialWindowSize(Http2Configuration http2Configuration) { 178 if (http2Configuration == null || http2Configuration.initialWindowSize() == null) { 179 return DEFAULT_INITIAL_WINDOW_SIZE; 180 } 181 return http2Configuration.initialWindowSize(); 182 } 183 resolveHealthCheckPingPeriod(Http2Configuration http2Configuration)184 private Duration resolveHealthCheckPingPeriod(Http2Configuration http2Configuration) { 185 if (http2Configuration != null) { 186 return http2Configuration.healthCheckPingPeriod(); 187 } 188 return null; 189 } 190 nonManagedEventLoopGroup(SdkEventLoopGroup eventLoopGroup)191 private SdkEventLoopGroup nonManagedEventLoopGroup(SdkEventLoopGroup eventLoopGroup) { 192 return SdkEventLoopGroup.create(new NonManagedEventLoopGroup(eventLoopGroup.eventLoopGroup()), 193 eventLoopGroup.channelFactory()); 194 } 195 196 @Override close()197 public void close() { 198 runAndLogError(log, "Unable to close channel pools", pools::close); 199 runAndLogError(log, "Unable to shutdown event loop", () -> 200 closeEventLoopUninterruptibly(sdkEventLoopGroup.eventLoopGroup())); 201 } 202 closeEventLoopUninterruptibly(EventLoopGroup eventLoopGroup)203 private void closeEventLoopUninterruptibly(EventLoopGroup eventLoopGroup) throws ExecutionException { 204 try { 205 eventLoopGroup.shutdownGracefully(EVENTLOOP_SHUTDOWN_QUIET_PERIOD_SECONDS, 206 EVENTLOOP_SHUTDOWN_TIMEOUT_SECONDS, 207 TimeUnit.SECONDS) 208 .get(EVENTLOOP_SHUTDOWN_FUTURE_TIMEOUT_SECONDS, TimeUnit.SECONDS); 209 } catch (InterruptedException e) { 210 Thread.currentThread().interrupt(); 211 throw new RuntimeException(e); 212 } catch (TimeoutException e) { 213 log.error(null, () -> String.format("Shutting down Netty EventLoopGroup did not complete within %s seconds", 214 EVENTLOOP_SHUTDOWN_FUTURE_TIMEOUT_SECONDS)); 215 } 216 } 217 218 @Override clientName()219 public String clientName() { 220 return CLIENT_NAME; 221 } 222 223 @SdkTestInternalApi configuration()224 NettyConfiguration configuration() { 225 return configuration; 226 } 227 228 /** 229 * Builder that allows configuration of the Netty NIO HTTP implementation. Use {@link #builder()} to configure and construct 230 * a Netty HTTP client. 231 */ 232 public interface Builder extends SdkAsyncHttpClient.Builder<NettyNioAsyncHttpClient.Builder> { 233 234 /** 235 * Maximum number of allowed concurrent requests. For HTTP/1.1 this is the same as max connections. For HTTP/2 236 * the number of connections that will be used depends on the max streams allowed per connection. 237 * 238 * <p> 239 * If the maximum number of concurrent requests is exceeded they may be queued in the HTTP client (see 240 * {@link #maxPendingConnectionAcquires(Integer)}</p>) and can cause increased latencies. If the client is overloaded 241 * enough such that the pending connection queue fills up, subsequent requests may be rejected or time out 242 * (see {@link #connectionAcquisitionTimeout(Duration)}). 243 * 244 * @param maxConcurrency New value for max concurrency. 245 * @return This builder for method chaining. 246 */ maxConcurrency(Integer maxConcurrency)247 Builder maxConcurrency(Integer maxConcurrency); 248 249 /** 250 * The maximum number of pending acquires allowed. Once this exceeds, acquire tries will be failed. 251 * 252 * @param maxPendingAcquires Max number of pending acquires 253 * @return This builder for method chaining. 254 */ maxPendingConnectionAcquires(Integer maxPendingAcquires)255 Builder maxPendingConnectionAcquires(Integer maxPendingAcquires); 256 257 /** 258 * The amount of time to wait for a read on a socket before an exception is thrown. 259 * Specify {@code Duration.ZERO} to disable. 260 * 261 * @param readTimeout timeout duration 262 * @return this builder for method chaining. 263 */ readTimeout(Duration readTimeout)264 Builder readTimeout(Duration readTimeout); 265 266 /** 267 * The amount of time to wait for a write on a socket before an exception is thrown. 268 * Specify {@code Duration.ZERO} to disable. 269 * 270 * @param writeTimeout timeout duration 271 * @return this builder for method chaining. 272 */ writeTimeout(Duration writeTimeout)273 Builder writeTimeout(Duration writeTimeout); 274 275 /** 276 * The amount of time to wait when initially establishing a connection before giving up and timing out. 277 * 278 * @param timeout the timeout duration 279 * @return this builder for method chaining. 280 */ connectionTimeout(Duration timeout)281 Builder connectionTimeout(Duration timeout); 282 283 /** 284 * The amount of time to wait when acquiring a connection from the pool before giving up and timing out. 285 * @param connectionAcquisitionTimeout the timeout duration 286 * @return this builder for method chaining. 287 */ connectionAcquisitionTimeout(Duration connectionAcquisitionTimeout)288 Builder connectionAcquisitionTimeout(Duration connectionAcquisitionTimeout); 289 290 /** 291 * The maximum amount of time that a connection should be allowed to remain open, regardless of usage frequency. 292 * 293 * Unlike {@link #readTimeout(Duration)} and {@link #writeTimeout(Duration)}, this will never close a connection that 294 * is currently in use, so long-lived connections may remain open longer than this time. In particular, an HTTP/2 295 * connection won't be closed as long as there is at least one stream active on the connection. 296 */ connectionTimeToLive(Duration connectionTimeToLive)297 Builder connectionTimeToLive(Duration connectionTimeToLive); 298 299 /** 300 * Configure the maximum amount of time that a connection should be allowed to remain open while idle. Currently has no 301 * effect if {@link #useIdleConnectionReaper(Boolean)} is false. 302 * 303 * Unlike {@link #readTimeout(Duration)} and {@link #writeTimeout(Duration)}, this will never close a connection that 304 * is currently in use, so long-lived connections may remain open longer than this time. 305 */ connectionMaxIdleTime(Duration maxIdleConnectionTimeout)306 Builder connectionMaxIdleTime(Duration maxIdleConnectionTimeout); 307 308 /** 309 * Configure the maximum amount of time that a TLS handshake is allowed to take from the time the CLIENT HELLO 310 * message is sent to the time the client and server have fully negotiated ciphers and exchanged keys. 311 * @param tlsNegotiationTimeout the timeout duration 312 * 313 * <p> 314 * By default, it's 10 seconds. 315 * 316 * @return this builder for method chaining. 317 */ tlsNegotiationTimeout(Duration tlsNegotiationTimeout)318 Builder tlsNegotiationTimeout(Duration tlsNegotiationTimeout); 319 320 /** 321 * Configure whether the idle connections in the connection pool should be closed. 322 * <p> 323 * When enabled, connections left idling for longer than {@link #connectionMaxIdleTime(Duration)} will be 324 * closed. This will not close connections currently in use. By default, this is enabled. 325 */ useIdleConnectionReaper(Boolean useConnectionReaper)326 Builder useIdleConnectionReaper(Boolean useConnectionReaper); 327 328 /** 329 * Sets the {@link SdkEventLoopGroup} to use for the Netty HTTP client. This event loop group may be shared 330 * across multiple HTTP clients for better resource and thread utilization. The preferred way to create 331 * an {@link EventLoopGroup} is by using the {@link SdkEventLoopGroup#builder()} method which will choose the 332 * optimal implementation per the platform. 333 * 334 * <p>The {@link EventLoopGroup} <b>MUST</b> be closed by the caller when it is ready to 335 * be disposed. The SDK will not close the {@link EventLoopGroup} when the HTTP client is closed. See 336 * {@link EventLoopGroup#shutdownGracefully()} to properly close the event loop group.</p> 337 * 338 * <p>This configuration method is only recommended when you wish to share an {@link EventLoopGroup} 339 * with multiple clients. If you do not need to share the group it is recommended to use 340 * {@link #eventLoopGroupBuilder(SdkEventLoopGroup.Builder)} as the SDK will handle its cleanup when 341 * the HTTP client is closed.</p> 342 * 343 * @param eventLoopGroup Netty {@link SdkEventLoopGroup} to use. 344 * @return This builder for method chaining. 345 * @see SdkEventLoopGroup 346 */ eventLoopGroup(SdkEventLoopGroup eventLoopGroup)347 Builder eventLoopGroup(SdkEventLoopGroup eventLoopGroup); 348 349 /** 350 * Sets the {@link SdkEventLoopGroup.Builder} which will be used to create the {@link SdkEventLoopGroup} for the Netty 351 * HTTP client. This allows for custom configuration of the Netty {@link EventLoopGroup}. 352 * 353 * <p>The {@link EventLoopGroup} created by the builder is managed by the SDK and will be shutdown 354 * when the HTTP client is closed.</p> 355 * 356 * <p>This is the preferred configuration method when you just want to customize the {@link EventLoopGroup} 357 * but not share it across multiple HTTP clients. If you do wish to share an {@link EventLoopGroup}, see 358 * {@link #eventLoopGroup(SdkEventLoopGroup)}</p> 359 * 360 * @param eventLoopGroupBuilder {@link SdkEventLoopGroup.Builder} to use. 361 * @return This builder for method chaining. 362 * @see SdkEventLoopGroup.Builder 363 */ eventLoopGroupBuilder(SdkEventLoopGroup.Builder eventLoopGroupBuilder)364 Builder eventLoopGroupBuilder(SdkEventLoopGroup.Builder eventLoopGroupBuilder); 365 366 /** 367 * Sets the HTTP protocol to use (i.e. HTTP/1.1 or HTTP/2). Not all services support HTTP/2. 368 * 369 * @param protocol Protocol to use. 370 * @return This builder for method chaining. 371 */ protocol(Protocol protocol)372 Builder protocol(Protocol protocol); 373 374 /** 375 * Configure whether to enable or disable TCP KeepAlive. 376 * The configuration will be passed to the socket option {@link SocketOptions#SO_KEEPALIVE}. 377 * <p> 378 * By default, this is disabled. 379 * <p> 380 * When enabled, the actual KeepAlive mechanism is dependent on the Operating System and therefore additional TCP 381 * KeepAlive values (like timeout, number of packets, etc) must be configured via the Operating System (sysctl on 382 * Linux/Mac, and Registry values on Windows). 383 */ tcpKeepAlive(Boolean keepConnectionAlive)384 Builder tcpKeepAlive(Boolean keepConnectionAlive); 385 386 /** 387 * Configures additional {@link ChannelOption} which will be used to create Netty Http client. This allows custom 388 * configuration for Netty. 389 * 390 * <p> 391 * If a {@link ChannelOption} was previously configured, the old value is replaced. 392 * 393 * @param channelOption {@link ChannelOption} to set 394 * @param value See {@link ChannelOption} to find the type of value for each option 395 * @return This builder for method chaining. 396 */ putChannelOption(ChannelOption channelOption, Object value)397 Builder putChannelOption(ChannelOption channelOption, Object value); 398 399 /** 400 * Sets the max number of concurrent streams for an HTTP/2 connection. This setting is only respected when the HTTP/2 401 * protocol is used. 402 * 403 * <p>Note that this cannot exceed the value of the MAX_CONCURRENT_STREAMS setting returned by the service. If it 404 * does the service setting is used instead.</p> 405 * 406 * @param maxHttp2Streams Max concurrent HTTP/2 streams per connection. 407 * @return This builder for method chaining. 408 * 409 * @deprecated Use {@link #http2Configuration(Http2Configuration)} along with 410 * {@link Http2Configuration.Builder#maxStreams(Long)} instead. 411 */ maxHttp2Streams(Integer maxHttp2Streams)412 Builder maxHttp2Streams(Integer maxHttp2Streams); 413 414 /** 415 * Sets the {@link SslProvider} to be used in the Netty client. 416 * 417 * <p>If not configured, {@link SslContext#defaultClientProvider()} will be used to determine the SslProvider. 418 * 419 * <p>Note that you might need to add other dependencies if not using JDK's default Ssl Provider. 420 * See https://netty.io/wiki/requirements-for-4.x.html#transport-security-tls 421 * 422 * @param sslProvider the SslProvider 423 * @return the builder of the method chaining. 424 */ sslProvider(SslProvider sslProvider)425 Builder sslProvider(SslProvider sslProvider); 426 427 /** 428 * Set the proxy configuration for this client. The configured proxy will be used to proxy any HTTP request 429 * destined for any host that does not match any of the hosts in configured non proxy hosts. 430 * 431 * @param proxyConfiguration The proxy configuration. 432 * @return The builder for method chaining. 433 * @see ProxyConfiguration#nonProxyHosts() 434 */ proxyConfiguration(ProxyConfiguration proxyConfiguration)435 Builder proxyConfiguration(ProxyConfiguration proxyConfiguration); 436 437 /** 438 * Set the {@link TlsKeyManagersProvider} for this client. The {@code KeyManager}s will be used by the client to 439 * authenticate itself with the remote server if necessary when establishing the TLS connection. 440 * <p> 441 * If no provider is configured, the client will default to {@link SystemPropertyTlsKeyManagersProvider}. To 442 * disable any automatic resolution via the system properties, use {@link TlsKeyManagersProvider#noneProvider()}. 443 * 444 * @param keyManagersProvider The {@code TlsKeyManagersProvider}. 445 * @return The builder for method chaining. 446 */ tlsKeyManagersProvider(TlsKeyManagersProvider keyManagersProvider)447 Builder tlsKeyManagersProvider(TlsKeyManagersProvider keyManagersProvider); 448 449 /** 450 * Configure the {@link TlsTrustManagersProvider} that will provide the {@link javax.net.ssl.TrustManager}s to use 451 * when constructing the SSL context. 452 * 453 * @param trustManagersProvider The {@code TlsKeyManagersProvider}. 454 * @return The builder for method chaining. 455 */ tlsTrustManagersProvider(TlsTrustManagersProvider trustManagersProvider)456 Builder tlsTrustManagersProvider(TlsTrustManagersProvider trustManagersProvider); 457 458 /** 459 * Set the HTTP/2 specific configuration for this client. 460 * <p> 461 * <b>Note:</b>If {@link #maxHttp2Streams(Integer)} and {@link Http2Configuration#maxStreams()} are both set, 462 * the value set using {@link #maxHttp2Streams(Integer)} takes precedence. 463 * 464 * @param http2Configuration The HTTP/2 configuration object. 465 * @return the builder for method chaining. 466 */ http2Configuration(Http2Configuration http2Configuration)467 Builder http2Configuration(Http2Configuration http2Configuration); 468 469 /** 470 * Set the HTTP/2 specific configuration for this client. 471 * <p> 472 * <b>Note:</b>If {@link #maxHttp2Streams(Integer)} and {@link Http2Configuration#maxStreams()} are both set, 473 * the value set using {@link #maxHttp2Streams(Integer)} takes precedence. 474 * 475 * @param http2ConfigurationBuilderConsumer The consumer of the HTTP/2 configuration builder object. 476 * @return the builder for method chaining. 477 */ http2Configuration(Consumer<Http2Configuration.Builder> http2ConfigurationBuilderConsumer)478 Builder http2Configuration(Consumer<Http2Configuration.Builder> http2ConfigurationBuilderConsumer); 479 480 /** 481 * Configure whether to use a non-blocking dns resolver or not. False by default, as netty's default dns resolver is 482 * blocking; it namely calls java.net.InetAddress.getByName. 483 * <p> 484 * When enabled, a non-blocking dns resolver will be used instead, by modifying netty's bootstrap configuration. 485 * See https://netty.io/news/2016/05/26/4-1-0-Final.html 486 */ useNonBlockingDnsResolver(Boolean useNonBlockingDnsResolver)487 Builder useNonBlockingDnsResolver(Boolean useNonBlockingDnsResolver); 488 } 489 490 /** 491 * Factory that allows more advanced configuration of the Netty NIO HTTP implementation. Use {@link #builder()} to 492 * configure and construct an immutable instance of the factory. 493 */ 494 private static final class DefaultBuilder implements Builder { 495 private final AttributeMap.Builder standardOptions = AttributeMap.builder(); 496 497 private SdkChannelOptions sdkChannelOptions = new SdkChannelOptions(); 498 499 private SdkEventLoopGroup eventLoopGroup; 500 private SdkEventLoopGroup.Builder eventLoopGroupBuilder; 501 private Integer maxHttp2Streams; 502 private Http2Configuration http2Configuration; 503 private SslProvider sslProvider; 504 private ProxyConfiguration proxyConfiguration; 505 private Boolean useNonBlockingDnsResolver; 506 DefaultBuilder()507 private DefaultBuilder() { 508 } 509 510 @Override maxConcurrency(Integer maxConcurrency)511 public Builder maxConcurrency(Integer maxConcurrency) { 512 standardOptions.put(SdkHttpConfigurationOption.MAX_CONNECTIONS, maxConcurrency); 513 return this; 514 } 515 setMaxConcurrency(Integer maxConnectionsPerEndpoint)516 public void setMaxConcurrency(Integer maxConnectionsPerEndpoint) { 517 maxConcurrency(maxConnectionsPerEndpoint); 518 } 519 520 @Override maxPendingConnectionAcquires(Integer maxPendingAcquires)521 public Builder maxPendingConnectionAcquires(Integer maxPendingAcquires) { 522 standardOptions.put(SdkHttpConfigurationOption.MAX_PENDING_CONNECTION_ACQUIRES, maxPendingAcquires); 523 return this; 524 } 525 setMaxPendingConnectionAcquires(Integer maxPendingAcquires)526 public void setMaxPendingConnectionAcquires(Integer maxPendingAcquires) { 527 maxPendingConnectionAcquires(maxPendingAcquires); 528 } 529 530 @Override readTimeout(Duration readTimeout)531 public Builder readTimeout(Duration readTimeout) { 532 Validate.isNotNegative(readTimeout, "readTimeout"); 533 standardOptions.put(SdkHttpConfigurationOption.READ_TIMEOUT, readTimeout); 534 return this; 535 } 536 setReadTimeout(Duration readTimeout)537 public void setReadTimeout(Duration readTimeout) { 538 readTimeout(readTimeout); 539 } 540 541 @Override writeTimeout(Duration writeTimeout)542 public Builder writeTimeout(Duration writeTimeout) { 543 Validate.isNotNegative(writeTimeout, "writeTimeout"); 544 standardOptions.put(SdkHttpConfigurationOption.WRITE_TIMEOUT, writeTimeout); 545 return this; 546 } 547 setWriteTimeout(Duration writeTimeout)548 public void setWriteTimeout(Duration writeTimeout) { 549 writeTimeout(writeTimeout); 550 } 551 552 @Override connectionTimeout(Duration timeout)553 public Builder connectionTimeout(Duration timeout) { 554 Validate.isPositive(timeout, "connectionTimeout"); 555 standardOptions.put(SdkHttpConfigurationOption.CONNECTION_TIMEOUT, timeout); 556 return this; 557 } 558 setConnectionTimeout(Duration connectionTimeout)559 public void setConnectionTimeout(Duration connectionTimeout) { 560 connectionTimeout(connectionTimeout); 561 } 562 563 @Override connectionAcquisitionTimeout(Duration connectionAcquisitionTimeout)564 public Builder connectionAcquisitionTimeout(Duration connectionAcquisitionTimeout) { 565 Validate.isPositive(connectionAcquisitionTimeout, "connectionAcquisitionTimeout"); 566 standardOptions.put(SdkHttpConfigurationOption.CONNECTION_ACQUIRE_TIMEOUT, connectionAcquisitionTimeout); 567 return this; 568 } 569 setConnectionAcquisitionTimeout(Duration connectionAcquisitionTimeout)570 public void setConnectionAcquisitionTimeout(Duration connectionAcquisitionTimeout) { 571 connectionAcquisitionTimeout(connectionAcquisitionTimeout); 572 } 573 574 @Override connectionTimeToLive(Duration connectionTimeToLive)575 public Builder connectionTimeToLive(Duration connectionTimeToLive) { 576 Validate.isNotNegative(connectionTimeToLive, "connectionTimeToLive"); 577 standardOptions.put(SdkHttpConfigurationOption.CONNECTION_TIME_TO_LIVE, connectionTimeToLive); 578 return this; 579 } 580 setConnectionTimeToLive(Duration connectionTimeToLive)581 public void setConnectionTimeToLive(Duration connectionTimeToLive) { 582 connectionTimeToLive(connectionTimeToLive); 583 } 584 585 @Override connectionMaxIdleTime(Duration connectionMaxIdleTime)586 public Builder connectionMaxIdleTime(Duration connectionMaxIdleTime) { 587 Validate.isPositive(connectionMaxIdleTime, "connectionMaxIdleTime"); 588 standardOptions.put(SdkHttpConfigurationOption.CONNECTION_MAX_IDLE_TIMEOUT, connectionMaxIdleTime); 589 return this; 590 } 591 setConnectionMaxIdleTime(Duration connectionMaxIdleTime)592 public void setConnectionMaxIdleTime(Duration connectionMaxIdleTime) { 593 connectionMaxIdleTime(connectionMaxIdleTime); 594 } 595 596 @Override useIdleConnectionReaper(Boolean useIdleConnectionReaper)597 public Builder useIdleConnectionReaper(Boolean useIdleConnectionReaper) { 598 standardOptions.put(SdkHttpConfigurationOption.REAP_IDLE_CONNECTIONS, useIdleConnectionReaper); 599 return this; 600 } 601 setUseIdleConnectionReaper(Boolean useIdleConnectionReaper)602 public void setUseIdleConnectionReaper(Boolean useIdleConnectionReaper) { 603 useIdleConnectionReaper(useIdleConnectionReaper); 604 } 605 606 @Override tlsNegotiationTimeout(Duration tlsNegotiationTimeout)607 public Builder tlsNegotiationTimeout(Duration tlsNegotiationTimeout) { 608 Validate.isPositive(tlsNegotiationTimeout, "tlsNegotiationTimeout"); 609 standardOptions.put(SdkHttpConfigurationOption.TLS_NEGOTIATION_TIMEOUT, tlsNegotiationTimeout); 610 return this; 611 } 612 setTlsNegotiationTimeout(Duration tlsNegotiationTimeout)613 public void setTlsNegotiationTimeout(Duration tlsNegotiationTimeout) { 614 tlsNegotiationTimeout(tlsNegotiationTimeout); 615 } 616 617 @Override eventLoopGroup(SdkEventLoopGroup eventLoopGroup)618 public Builder eventLoopGroup(SdkEventLoopGroup eventLoopGroup) { 619 this.eventLoopGroup = eventLoopGroup; 620 return this; 621 } 622 setEventLoopGroup(SdkEventLoopGroup eventLoopGroup)623 public void setEventLoopGroup(SdkEventLoopGroup eventLoopGroup) { 624 eventLoopGroup(eventLoopGroup); 625 } 626 627 @Override eventLoopGroupBuilder(SdkEventLoopGroup.Builder eventLoopGroupBuilder)628 public Builder eventLoopGroupBuilder(SdkEventLoopGroup.Builder eventLoopGroupBuilder) { 629 this.eventLoopGroupBuilder = eventLoopGroupBuilder; 630 return this; 631 } 632 setEventLoopGroupBuilder(SdkEventLoopGroup.Builder eventLoopGroupBuilder)633 public void setEventLoopGroupBuilder(SdkEventLoopGroup.Builder eventLoopGroupBuilder) { 634 eventLoopGroupBuilder(eventLoopGroupBuilder); 635 } 636 637 @Override protocol(Protocol protocol)638 public Builder protocol(Protocol protocol) { 639 standardOptions.put(SdkHttpConfigurationOption.PROTOCOL, protocol); 640 return this; 641 } 642 setProtocol(Protocol protocol)643 public void setProtocol(Protocol protocol) { 644 protocol(protocol); 645 } 646 647 @Override tcpKeepAlive(Boolean keepConnectionAlive)648 public Builder tcpKeepAlive(Boolean keepConnectionAlive) { 649 standardOptions.put(SdkHttpConfigurationOption.TCP_KEEPALIVE, keepConnectionAlive); 650 return this; 651 } 652 setTcpKeepAlive(Boolean keepConnectionAlive)653 public void setTcpKeepAlive(Boolean keepConnectionAlive) { 654 tcpKeepAlive(keepConnectionAlive); 655 } 656 657 @Override putChannelOption(ChannelOption channelOption, Object value)658 public Builder putChannelOption(ChannelOption channelOption, Object value) { 659 this.sdkChannelOptions.putOption(channelOption, value); 660 return this; 661 } 662 663 @Override maxHttp2Streams(Integer maxHttp2Streams)664 public Builder maxHttp2Streams(Integer maxHttp2Streams) { 665 this.maxHttp2Streams = maxHttp2Streams; 666 return this; 667 } 668 setMaxHttp2Streams(Integer maxHttp2Streams)669 public void setMaxHttp2Streams(Integer maxHttp2Streams) { 670 maxHttp2Streams(maxHttp2Streams); 671 } 672 673 @Override sslProvider(SslProvider sslProvider)674 public Builder sslProvider(SslProvider sslProvider) { 675 this.sslProvider = sslProvider; 676 return this; 677 } 678 setSslProvider(SslProvider sslProvider)679 public void setSslProvider(SslProvider sslProvider) { 680 sslProvider(sslProvider); 681 } 682 683 @Override proxyConfiguration(ProxyConfiguration proxyConfiguration)684 public Builder proxyConfiguration(ProxyConfiguration proxyConfiguration) { 685 this.proxyConfiguration = proxyConfiguration; 686 return this; 687 } 688 setProxyConfiguration(ProxyConfiguration proxyConfiguration)689 public void setProxyConfiguration(ProxyConfiguration proxyConfiguration) { 690 proxyConfiguration(proxyConfiguration); 691 } 692 693 @Override tlsKeyManagersProvider(TlsKeyManagersProvider tlsKeyManagersProvider)694 public Builder tlsKeyManagersProvider(TlsKeyManagersProvider tlsKeyManagersProvider) { 695 this.standardOptions.put(SdkHttpConfigurationOption.TLS_KEY_MANAGERS_PROVIDER, tlsKeyManagersProvider); 696 return this; 697 } 698 setTlsKeyManagersProvider(TlsKeyManagersProvider tlsKeyManagersProvider)699 public void setTlsKeyManagersProvider(TlsKeyManagersProvider tlsKeyManagersProvider) { 700 tlsKeyManagersProvider(tlsKeyManagersProvider); 701 } 702 703 @Override tlsTrustManagersProvider(TlsTrustManagersProvider tlsTrustManagersProvider)704 public Builder tlsTrustManagersProvider(TlsTrustManagersProvider tlsTrustManagersProvider) { 705 standardOptions.put(SdkHttpConfigurationOption.TLS_TRUST_MANAGERS_PROVIDER, tlsTrustManagersProvider); 706 return this; 707 } 708 setTlsTrustManagersProvider(TlsTrustManagersProvider tlsTrustManagersProvider)709 public void setTlsTrustManagersProvider(TlsTrustManagersProvider tlsTrustManagersProvider) { 710 tlsTrustManagersProvider(tlsTrustManagersProvider); 711 } 712 713 @Override http2Configuration(Http2Configuration http2Configuration)714 public Builder http2Configuration(Http2Configuration http2Configuration) { 715 this.http2Configuration = http2Configuration; 716 return this; 717 } 718 719 @Override http2Configuration(Consumer<Http2Configuration.Builder> http2ConfigurationBuilderConsumer)720 public Builder http2Configuration(Consumer<Http2Configuration.Builder> http2ConfigurationBuilderConsumer) { 721 Http2Configuration.Builder builder = Http2Configuration.builder(); 722 http2ConfigurationBuilderConsumer.accept(builder); 723 return http2Configuration(builder.build()); 724 } 725 setHttp2Configuration(Http2Configuration http2Configuration)726 public void setHttp2Configuration(Http2Configuration http2Configuration) { 727 http2Configuration(http2Configuration); 728 } 729 730 @Override useNonBlockingDnsResolver(Boolean useNonBlockingDnsResolver)731 public Builder useNonBlockingDnsResolver(Boolean useNonBlockingDnsResolver) { 732 this.useNonBlockingDnsResolver = useNonBlockingDnsResolver; 733 return this; 734 } 735 setUseNonBlockingDnsResolver(Boolean useNonBlockingDnsResolver)736 public void setUseNonBlockingDnsResolver(Boolean useNonBlockingDnsResolver) { 737 useNonBlockingDnsResolver(useNonBlockingDnsResolver); 738 } 739 740 @Override buildWithDefaults(AttributeMap serviceDefaults)741 public SdkAsyncHttpClient buildWithDefaults(AttributeMap serviceDefaults) { 742 if (standardOptions.get(SdkHttpConfigurationOption.TLS_NEGOTIATION_TIMEOUT) == null) { 743 standardOptions.put(SdkHttpConfigurationOption.TLS_NEGOTIATION_TIMEOUT, 744 standardOptions.get(SdkHttpConfigurationOption.CONNECTION_TIMEOUT)); 745 } 746 747 return new NettyNioAsyncHttpClient(this, standardOptions.build() 748 .merge(serviceDefaults) 749 .merge(NETTY_HTTP_DEFAULTS) 750 .merge(SdkHttpConfigurationOption.GLOBAL_HTTP_DEFAULTS)); 751 752 } 753 } 754 } 755