This implementation leverages Apache HttpClient 5.x, offering improved performance characteristics and better compliance - * with HTTP standards compared to the Apache 4.x-based.
+ * An implementation of {@link SdkHttpClient} that uses Apache5 HTTP client to communicate with the service. This is the most + * powerful synchronous client that adds an extra dependency and additional startup latency in exchange for more functionality, + * like support for HTTP proxies. * - *See software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient for a lighter alternative implementation - * with fewer dependencies but more limited functionality.
+ *See software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient for an alternative implementation.
* + *This can be created via {@link #builder()}
*/ @SdkPublicApi -public class Apache5HttpClient implements SdkHttpClient { +public final class Apache5HttpClient implements SdkHttpClient { public static final String CLIENT_NAME = "Apache5"; + private static final Logger log = Logger.loggerFor(Apache5HttpClient.class); + + private final Apache5HttpRequestFactory apacheHttpRequestFactory = new Apache5HttpRequestFactory(); + private final ConnectionManagerAwareHttpClient httpClient; + private final Apache5HttpRequestConfig requestConfig; + private final AttributeMap resolvedOptions; + + @SdkTestInternalApi + Apache5HttpClient(ConnectionManagerAwareHttpClient httpClient, + Apache5HttpRequestConfig requestConfig, + AttributeMap resolvedOptions) { + this.httpClient = httpClient; + this.requestConfig = requestConfig; + this.resolvedOptions = resolvedOptions; + } + + private Apache5HttpClient(DefaultBuilder builder, AttributeMap resolvedOptions) { + this.httpClient = createClient(builder, resolvedOptions); + this.requestConfig = createRequestConfig(builder, resolvedOptions); + this.resolvedOptions = resolvedOptions; + } + + public static Builder builder() { + return new DefaultBuilder(); + } + + /** + * Create a {@link Apache5HttpClient} with the default properties + * + * @return an {@link Apache5HttpClient} + */ + public static SdkHttpClient create() { + return new DefaultBuilder().build(); + } + + private ConnectionManagerAwareHttpClient createClient(Apache5HttpClient.DefaultBuilder configuration, + AttributeMap standardOptions) { + ApacheConnectionManagerFactory cmFactory = new ApacheConnectionManagerFactory(); + + HttpClientBuilder builder = HttpClients.custom(); + // Note that it is important we register the original connection manager with the + // IdleConnectionReaper as it's required for the successful deregistration of managers + // from the reaper. See https://github.com/aws/aws-sdk-java/issues/722. + HttpClientConnectionManager cm = cmFactory.create(configuration, standardOptions); + + Registry+ * SdkHttpClient httpClient = + * Apache5HttpClient.builder() + * .socketTimeout(Duration.ofSeconds(10)) + * .build(); + *+ */ + public interface Builder extends SdkHttpClient.Builder
+ * When enabled, connections left idling for longer than {@link #connectionMaxIdleTime(Duration)} will be + * closed. This will not close connections currently in use. By default, this is enabled. + */ + Builder useIdleConnectionReaper(Boolean useConnectionReaper); + + /** + * Configuration that defines a DNS resolver. If no matches are found, the default resolver is used. + */ + Builder dnsResolver(DnsResolver dnsResolver); + + /** + * Configuration that defines a custom Socket factory. If set to a null value, a default factory is used. + *
+ * When set to a non-null value, the use of a custom factory implies the configuration options TRUST_ALL_CERTIFICATES, + * TLS_TRUST_MANAGERS_PROVIDER, and TLS_KEY_MANAGERS_PROVIDER are ignored. + */ + Builder socketFactory(ConnectionSocketFactory socketFactory); + + /** + * Configuration that defines an HTTP route planner that computes the route an HTTP request should take. + * May not be used in conjunction with {@link #proxyConfiguration(ProxyConfiguration)}. + */ + Builder httpRoutePlanner(HttpRoutePlanner proxyConfiguration); + + /** + * Configuration that defines a custom credential provider for HTTP requests. + * May not be used in conjunction with {@link ProxyConfiguration#username()} and {@link ProxyConfiguration#password()}. + */ + Builder credentialsProvider(CredentialsProvider credentialsProvider); + + /** + * Configure whether to enable or disable TCP KeepAlive. + * The configuration will be passed to the socket option {@link java.net.SocketOptions#SO_KEEPALIVE}. + *
+ * By default, this is disabled. + *
+ * When enabled, the actual KeepAlive mechanism is dependent on the Operating System and therefore additional TCP + * KeepAlive values (like timeout, number of packets, etc) must be configured via the Operating System (sysctl on + * Linux/Mac, and Registry values on Windows). + */ + Builder tcpKeepAlive(Boolean keepConnectionAlive); + + /** + * Configure the {@link TlsKeyManagersProvider} that will provide the {@link javax.net.ssl.KeyManager}s to use + * when constructing the SSL context. + *
+ * The default used by the client will be {@link SystemPropertyTlsKeyManagersProvider}. Configure an instance of
+ * {@link software.amazon.awssdk.internal.http.NoneTlsKeyManagersProvider} or another implementation of
+ * {@link TlsKeyManagersProvider} to override it.
+ */
+ Builder tlsKeyManagersProvider(TlsKeyManagersProvider tlsKeyManagersProvider);
+
+ /**
+ * Configure the {@link TlsTrustManagersProvider} that will provide the {@link javax.net.ssl.TrustManager}s to use
+ * when constructing the SSL context.
+ */
+ Builder tlsTrustManagersProvider(TlsTrustManagersProvider tlsTrustManagersProvider);
+
+ /**
+ * Configure the authentication scheme registry that can be used to obtain the corresponding authentication scheme
+ * implementation for a given type of authorization challenge.
+ */
+ Builder authSchemeProviderRegistry(Registry All implementations of this interface are mutable and not thread safe.
+ * This value is set to "true" by default which means SDK will automatically use system property values for options that
+ * are not provided during building the {@link ProxyConfiguration} object. To disable this behavior, set this value to
+ * "false".It is important to note that when this property is set to "true," all proxy settings will exclusively originate
+ * from system properties, and no partial settings will be obtained from EnvironmentVariableValues.
+ */
+ Builder useSystemPropertyValues(Boolean useSystemPropertyValues);
+
+ /**
+ * Option whether to use environment variable values for proxy configuration if any of the config options are missing.
+ *
+ * This value is set to "true" by default, which means the SDK will automatically use environment variable values for
+ * proxy configuration options that are not provided during the building of the {@link ProxyConfiguration} object. To
+ * disable this behavior, set this value to "false". It is important to note that when this property is set to "true," all
+ * proxy settings will exclusively originate from environment variableValues, and no partial settings will be obtained
+ * from SystemPropertyValues.
+ * Comma-separated host names in the NO_PROXY environment variable indicate multiple hosts to exclude from
+ * proxy settings.
+ *
+ * @param useEnvironmentVariableValues The option whether to use environment variable values.
+ * @return This object for method chaining.
+ */
+ Builder useEnvironmentVariableValues(Boolean useEnvironmentVariableValues);
+
+ /**
+ * The HTTP scheme to use for connecting to the proxy. Valid values are {@code http} and {@code https}.
+ *
+ * The client defaults to {@code http} if none is given.
+ *
+ * @param scheme The proxy scheme.
+ * @return This object for method chaining.
+ */
+ Builder scheme(String scheme);
+
+ }
+
+ /**
+ * An SDK-internal implementation of {@link Builder}.
+ */
+ private static final class DefaultClientProxyConfigurationBuilder implements Builder {
+
+ private URI endpoint;
+ private String username;
+ private String password;
+ private String ntlmDomain;
+ private String ntlmWorkstation;
+ private Set
+ * If an error is encountered the first time we try to write the request
+ * entity, we remember the original exception, and report that as the root
+ * cause if we continue to encounter errors, rather than masking the
+ * original error.
+ */
+ @Override
+ public void writeTo(OutputStream output) throws IOException {
+ try {
+ if (!firstAttempt && isRepeatable()) {
+ content.reset();
+ }
+
+ firstAttempt = false;
+ inputStreamRequestEntity.writeTo(output);
+ } catch (IOException ioe) {
+ if (originalException == null) {
+ originalException = ioe;
+ }
+ throw originalException;
+ }
+ }
+
+}
diff --git a/http-clients/apache5-client/src/main/java/software/amazon/awssdk/http/apache5/internal/SdkProxyRoutePlanner.java b/http-clients/apache5-client/src/main/java/software/amazon/awssdk/http/apache5/internal/SdkProxyRoutePlanner.java
new file mode 100644
index 000000000000..64cae495fba2
--- /dev/null
+++ b/http-clients/apache5-client/src/main/java/software/amazon/awssdk/http/apache5/internal/SdkProxyRoutePlanner.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 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.apache5.internal;
+
+import static software.amazon.awssdk.utils.StringUtils.lowerCase;
+
+import java.util.Set;
+import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
+import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+
+/**
+ * SdkProxyRoutePlanner delegates a Proxy Route Planner from the settings instead of the
+ * system properties. It will use the proxy created from proxyHost and proxyPort and
+ * filter the hosts who matches nonProxyHosts pattern.
+ */
+@SdkInternalApi
+public class SdkProxyRoutePlanner extends DefaultRoutePlanner {
+
+ private HttpHost proxy;
+ private Set
+ * setNormalizeUri is added only in 4.5.8, so customers using the latest version of SDK with old versions (4.5.6 or less)
+ * of Apache httpclient will see NoSuchMethodError. Hence this method will suppress the error.
+ *
+ * Do not use Apache version 4.5.7 as it breaks URI paths with special characters and there is no option
+ * to disable normalization.
+ *