All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient Maven / Gradle / Ivy

Go to download

A single bundled dependency that includes all service and dependent JARs with third-party libraries relocated to different namespaces.

There is a newer version: 2.5.20
Show newest version
/*
 * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package software.amazon.awssdk.http.nio.netty;

import static software.amazon.awssdk.http.SdkHttpConfigurationOption.CONNECTION_ACQUIRE_TIMEOUT;
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.CONNECTION_TIMEOUT;
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.MAX_CONNECTIONS;
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.MAX_PENDING_CONNECTION_ACQUIRES;
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.READ_TIMEOUT;
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.WRITE_TIMEOUT;
import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.pool.ChannelPool;
import io.netty.channel.pool.ChannelPoolMap;
import io.netty.handler.codec.http2.Http2SecurityUtil;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import java.net.URI;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManagerFactory;
import software.amazon.awssdk.annotations.ReviewBeforeRelease;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.http.Protocol;
import software.amazon.awssdk.http.SdkHttpConfigurationOption;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.SdkRequestContext;
import software.amazon.awssdk.http.async.AbortableRunnable;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
import software.amazon.awssdk.http.async.SdkHttpRequestProvider;
import software.amazon.awssdk.http.async.SdkHttpResponseHandler;
import software.amazon.awssdk.http.nio.netty.internal.ChannelPipelineInitializer;
import software.amazon.awssdk.http.nio.netty.internal.HandlerRemovingChannelPool;
import software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration;
import software.amazon.awssdk.http.nio.netty.internal.NonManagedEventLoopGroup;
import software.amazon.awssdk.http.nio.netty.internal.ReleaseOnceChannelPool;
import software.amazon.awssdk.http.nio.netty.internal.RequestAdapter;
import software.amazon.awssdk.http.nio.netty.internal.RequestContext;
import software.amazon.awssdk.http.nio.netty.internal.RunnableRequest;
import software.amazon.awssdk.http.nio.netty.internal.SdkChannelPoolMap;
import software.amazon.awssdk.http.nio.netty.internal.SharedSdkEventLoopGroup;
import software.amazon.awssdk.http.nio.netty.internal.http2.HttpOrHttp2ChannelPool;
import software.amazon.awssdk.utils.AttributeMap;
import software.amazon.awssdk.utils.Either;
import software.amazon.awssdk.utils.Validate;

/**
 * An implementation of {@link SdkAsyncHttpClient} that uses a Netty non-blocking HTTP client to communicate with the service.
 *
 * 

This can be created via {@link #builder()}

*/ @SdkPublicApi public final class NettyNioAsyncHttpClient implements SdkAsyncHttpClient { private final RequestAdapter requestAdapter = new RequestAdapter(); private final SdkEventLoopGroup sdkEventLoopGroup; private final ChannelPoolMap pools; private final NettyConfiguration configuration; private final long maxStreams; private Protocol protocol; NettyNioAsyncHttpClient(DefaultBuilder builder, AttributeMap serviceDefaultsMap) { this.configuration = new NettyConfiguration(serviceDefaultsMap); this.protocol = serviceDefaultsMap.get(SdkHttpConfigurationOption.PROTOCOL); this.maxStreams = 200; this.sdkEventLoopGroup = eventLoopGroup(builder); this.pools = createChannelPoolMap(); } private SdkEventLoopGroup eventLoopGroup(DefaultBuilder builder) { Validate.isTrue(builder.eventLoopGroup == null || builder.eventLoopGroupBuilder == null, "The eventLoopGroup and the eventLoopGroupFactory can't both be configured."); return Either.fromNullable(builder.eventLoopGroup, builder.eventLoopGroupBuilder) .map(e -> e.map(this::nonManagedEventLoopGroup, SdkEventLoopGroup.Builder::build)) .orElseGet(SharedSdkEventLoopGroup::get); } public static Builder builder() { return new DefaultBuilder(); } @Override public AbortableRunnable prepareRequest(SdkHttpRequest sdkRequest, SdkRequestContext sdkRequestContext, SdkHttpRequestProvider requestProvider, SdkHttpResponseHandler handler) { RequestContext context = new RequestContext(pools.get(poolKey(sdkRequest)), sdkRequest, requestProvider, requestAdapter.adapt(sdkRequest), handler, configuration); return new RunnableRequest(context); } private static URI poolKey(SdkHttpRequest sdkRequest) { return invokeSafely(() -> new URI(sdkRequest.protocol(), null, sdkRequest.host(), sdkRequest.port(), null, null, null)); } private SslContext sslContext(String protocol) { if (!protocol.equalsIgnoreCase("https")) { return null; } try { return SslContextBuilder.forClient() .sslProvider(SslContext.defaultClientProvider()) .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) .trustManager(getTrustManager()) .build(); } catch (SSLException e) { throw new RuntimeException(e); } } private TrustManagerFactory getTrustManager() { return configuration.trustAllCertificates() ? InsecureTrustManagerFactory.INSTANCE : null; } private ChannelPoolMap createChannelPoolMap() { return new SdkChannelPoolMap() { @Override protected ChannelPool newPool(URI key) { SslContext sslContext = sslContext(key.getScheme()); Bootstrap bootstrap = new Bootstrap() .group(sdkEventLoopGroup.eventLoopGroup()) .channelFactory(sdkEventLoopGroup.channelFactory()) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, configuration.connectTimeoutMillis()) // TODO run some performance tests with and without this. .option(ChannelOption.TCP_NODELAY, true) .remoteAddress(key.getHost(), key.getPort()); AtomicReference channelPoolRef = new AtomicReference<>(); ChannelPipelineInitializer handler = new ChannelPipelineInitializer(protocol, sslContext, maxStreams, channelPoolRef); channelPoolRef.set(new ReleaseOnceChannelPool( new HandlerRemovingChannelPool( new HttpOrHttp2ChannelPool(bootstrap, handler, configuration.maxConnections(), configuration)))); return channelPoolRef.get(); } }; } private SdkEventLoopGroup nonManagedEventLoopGroup(SdkEventLoopGroup eventLoopGroup) { return SdkEventLoopGroup.create(new NonManagedEventLoopGroup(eventLoopGroup.eventLoopGroup()), eventLoopGroup.channelFactory()); } @Override public Optional getConfigurationValue(SdkHttpConfigurationOption key) { return Optional.ofNullable(configuration.attribute(key)); } @Override public void close() { sdkEventLoopGroup.eventLoopGroup().shutdownGracefully(); } /** * Builder that allows configuration of the Netty NIO HTTP implementation. Use {@link #builder()} to configure and construct * a Netty HTTP client. */ public interface Builder extends SdkAsyncHttpClient.Builder { /** * Maximum number of allowed concurrent requests. For HTTP/1.1 this is the same as max connections. For HTTP/2 * the number of connections that will be used depends on the max streams allowed per connection. * *

* If the maximum number of concurrent requests is exceeded they may be queued in the HTTP client (see * {@link #maxPendingConnectionAcquires(Integer)}

) and can cause increased latencies. If the client is overloaded * enough such that the pending connection queue fills up, subsequent requests may be rejected or time out * (see {@link #connectionAcquisitionTimeout(Duration)}). *

* * @param maxConcurrency New value for max concurrency. * @return This builder for method chaining. */ Builder maxConcurrency(Integer maxConcurrency); /** * The maximum number of pending acquires allowed. Once this exceeds, acquire tries will be failed. * * @param maxPendingAcquires Max number of pending acquires * @return This builder for method chaining. */ Builder maxPendingConnectionAcquires(Integer maxPendingAcquires); /** * The amount of time to wait for a read on a socket before an exception is thrown. * * @param readTimeout timeout duration * @return this builder for method chaining. */ Builder readTimeout(Duration readTimeout); /** * The amount of time to wait for a write on a socket before an exception is thrown. * * @param writeTimeout timeout duration * @return this builder for method chaining. */ Builder writeTimeout(Duration writeTimeout); /** * The amount of time to wait when initially establishing a connection before giving up and timing out. * * @param timeout the timeout duration * @return this builder for method chaining. */ Builder connectionTimeout(Duration timeout); /** * The amount of time to wait when acquiring a connection from the pool before giving up and timing out. * @param connectionAcquisitionTimeout the timeout duration * @return this builder for method chaining. */ Builder connectionAcquisitionTimeout(Duration connectionAcquisitionTimeout); /** * Sets the {@link SdkEventLoopGroup} to use for the Netty HTTP client. This event loop group may be shared * across multiple HTTP clients for better resource and thread utilization. The preferred way to create * an {@link EventLoopGroup} is by using the {@link SdkEventLoopGroup#builder()})} method which will choose the * optimal implementation per the platform. * *

The {@link EventLoopGroup} MUST be closed by the caller when it is ready to * be disposed. The SDK will not close the {@link EventLoopGroup} when the HTTP client is closed. See * {@link EventLoopGroup#shutdownGracefully()} to properly close the event loop group.

* *

This configuration method is only recommended when you wish to share an {@link EventLoopGroup} * with multiple clients. If you do not need to share the group it is recommended to use * {@link #eventLoopGroupBuilder(SdkEventLoopGroup.Builder)} as the SDK will handle its cleanup when * the HTTP client is closed.

* * @param eventLoopGroup Netty {@link SdkEventLoopGroup} to use. * @return This builder for method chaining. * @see SdkEventLoopGroup */ Builder eventLoopGroup(SdkEventLoopGroup eventLoopGroup); /** * Sets the {@link SdkEventLoopGroup.Builder} which will be used to create the {@link SdkEventLoopGroup} for the Netty * HTTP client. This allows for custom configuration of the Netty {@link EventLoopGroup}. * *

The {@link EventLoopGroup} created by the builder is managed by the SDK and will be shutdown * when the HTTP client is closed.

* *

This is the preferred configuration method when you just want to customize the {@link EventLoopGroup} * but not share it across multiple HTTP clients. If you do wish to share an {@link EventLoopGroup}, see * {@link #eventLoopGroup(SdkEventLoopGroup)}

* * @param eventLoopGroupBuilder {@link SdkEventLoopGroup.Builder} to use. * @return This builder for method chaining. * @see SdkEventLoopGroup.Builder */ Builder eventLoopGroupBuilder(SdkEventLoopGroup.Builder eventLoopGroupBuilder); /** * Sets the HTTP protocol to use (i.e. HTTP/1.1 or HTTP/2). Not all services support HTTP/2. * * @param protocol Protocol to use. * @return This builder for method chaining. */ @ReviewBeforeRelease("Decide if we want to expose this to customers") Builder protocol(Protocol protocol); } /** * Factory that allows more advanced configuration of the Netty NIO HTTP implementation. Use {@link #builder()} to * configure and construct an immutable instance of the factory. */ private static final class DefaultBuilder implements Builder { private final AttributeMap.Builder standardOptions = AttributeMap.builder(); private SdkEventLoopGroup eventLoopGroup; private SdkEventLoopGroup.Builder eventLoopGroupBuilder; private DefaultBuilder() { } /** * Max allowed connections per endpoint allowed in the connection pool. * * @param maxConcurrency New value for max connections per endpoint. * @return This builder for method chaining. */ @Override public Builder maxConcurrency(Integer maxConcurrency) { standardOptions.put(MAX_CONNECTIONS, maxConcurrency); return this; } public void setMaxConcurrency(Integer maxConnectionsPerEndpoint) { maxConcurrency(maxConnectionsPerEndpoint); } /** * The maximum number of pending acquires allowed. Once this exceeds, acquire tries will be failed. * * @param maxPendingAcquires Max number of pending acquires * @return This builder for method chaining. */ @Override public Builder maxPendingConnectionAcquires(Integer maxPendingAcquires) { standardOptions.put(MAX_PENDING_CONNECTION_ACQUIRES, maxPendingAcquires); return this; } public void setMaxPendingConnectionAcquires(Integer maxPendingAcquires) { maxPendingConnectionAcquires(maxPendingAcquires); } /** * The amount of time to wait for a read on a socket before an exception is thrown. * * @param readTimeout timeout duration * @return this builder for method chaining. */ @Override public Builder readTimeout(Duration readTimeout) { Validate.isPositive(readTimeout, "readTimeout"); standardOptions.put(READ_TIMEOUT, readTimeout); return this; } public void setReadTimeout(Duration readTimeout) { readTimeout(readTimeout); } /** * The amount of time to wait for a write on a socket before an exception is thrown. * * @param writeTimeout timeout duration * @return this builder for method chaining. */ @Override public Builder writeTimeout(Duration writeTimeout) { Validate.isPositive(writeTimeout, "connectionAcquisitionTimeout"); standardOptions.put(WRITE_TIMEOUT, writeTimeout); return this; } public void setWriteTimeout(Duration writeTimeout) { writeTimeout(writeTimeout); } /** * The amount of time to wait when initially establishing a connection before giving up and timing out. * * @param timeout the timeout duration * @return this builder for method chaining. */ @Override public Builder connectionTimeout(Duration timeout) { Validate.isPositive(timeout, "connectionTimeout"); standardOptions.put(CONNECTION_TIMEOUT, timeout); return this; } public void setConnectionTimeout(Duration connectionTimeout) { connectionTimeout(connectionTimeout); } /** * The amount of time to wait when acquiring a connection from the pool before giving up and timing out. * @param connectionAcquisitionTimeout the timeout duration * @return this builder for method chaining. */ @Override public Builder connectionAcquisitionTimeout(Duration connectionAcquisitionTimeout) { Validate.isPositive(connectionAcquisitionTimeout, "connectionAcquisitionTimeout"); standardOptions.put(CONNECTION_ACQUIRE_TIMEOUT, connectionAcquisitionTimeout); return this; } public void setConnectionAcquisitionTimeout(Duration connectionAcquisitionTimeout) { connectionAcquisitionTimeout(connectionAcquisitionTimeout); } @Override public Builder eventLoopGroup(SdkEventLoopGroup eventLoopGroup) { this.eventLoopGroup = eventLoopGroup; return this; } public void setEventLoopGroup(SdkEventLoopGroup eventLoopGroup) { eventLoopGroup(eventLoopGroup); } @Override public Builder eventLoopGroupBuilder(SdkEventLoopGroup.Builder eventLoopGroupBuilder) { this.eventLoopGroupBuilder = eventLoopGroupBuilder; return this; } public void setEventLoopGroupBuilder(SdkEventLoopGroup.Builder eventLoopGroupBuilder) { eventLoopGroupBuilder(eventLoopGroupBuilder); } @Override public Builder protocol(Protocol protocol) { standardOptions.put(SdkHttpConfigurationOption.PROTOCOL, protocol); return this; } public void setProtocol(Protocol protocol) { protocol(protocol); } @Override public SdkAsyncHttpClient buildWithDefaults(AttributeMap serviceDefaults) { return new NettyNioAsyncHttpClient(this, standardOptions.build() .merge(serviceDefaults) .merge(SdkHttpConfigurationOption.GLOBAL_HTTP_DEFAULTS)); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy