Skip to content

Update Apache HttpClient 5 to use recommended APIs and remove deprecated code #6312

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 57 commits into from
Aug 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
f3fc974
Add initial empty module for Apache5x for seting up package (#6075)
joviegas Apr 30, 2025
5092cc4
Baseline or Copy all the ApacheSDKHttpClient classes to newly added A…
joviegas May 6, 2025
6e9939f
Phase 2 , getting Apache 5 compilation and Junit ready along with cle…
joviegas May 22, 2025
4dda3f7
Merge branch 'master' into feature/master/apache5x
joviegas May 25, 2025
3053d09
Update the snap shot
joviegas May 25, 2025
985ca35
Fix HTTP authentication retry failures by improving RepeatableInputSt…
joviegas May 28, 2025
b97c9da
Merge branch 'master' into feature/master/apache5x
joviegas May 29, 2025
64f7d33
Updated snap shot after merge from master
joviegas May 29, 2025
24438e8
Revert "Updated snap shot after merge from master"
joviegas May 29, 2025
62150d6
Updated snap shot after merge from master
joviegas May 29, 2025
fbfb70f
Fix architecture test failures for apache5.x (#6140)
joviegas May 29, 2025
3c4433c
Merge branch 'master' into feature/master/apache5x
joviegas May 30, 2025
6d8b5f4
Updated snap shot after merge from master
joviegas May 30, 2025
13c5796
Use reference of PoolingHttpClientConnectionManager instead of HttpCl…
joviegas Jun 2, 2025
0bdf878
Merge branch 'master' into feature/master/apache5x
joviegas Jun 2, 2025
265976d
Fix Apache5 HTTP client retry failures with non-resettable streams (#…
joviegas Jun 5, 2025
4671831
Merge branch 'master' into feature/master/apache5x
joviegas Jun 5, 2025
2f72282
Merge branch 'master' into feature/master/apache5x
joviegas Jun 17, 2025
79f6f0f
Merge PR#6165 https://github.com/aws/aws-sdk-java-v2/pull/6165
joviegas Jun 17, 2025
fcbc3c0
Disable Client based retries and define httpcore5 httpclient5 in .bra…
joviegas Jun 19, 2025
716a6db
Merge branch 'master' into feature/master/apache5x
joviegas Jun 21, 2025
09e8ee4
Update snapshots
joviegas Jun 21, 2025
ad30c25
Merge branch 'feature/master/apache5x' of github.com:aws/aws-sdk-java…
joviegas Jun 21, 2025
3cedfb1
Do not buffer the Response stream using BufferedHttpEntity (#6200)
joviegas Jun 24, 2025
959605c
Merge branch 'master' into feature/master/apache5x
joviegas Jun 24, 2025
a959581
Merge from master
joviegas Jun 24, 2025
e188995
Apache5x SDkBenhmark Tests (#6206)
joviegas Jun 26, 2025
3cc94bb
Clean up unused APIs and add test to make sure it can be handled with…
joviegas Jun 26, 2025
45f630d
Merge branch 'master' into feature/master/apache5x
joviegas Jun 26, 2025
67d6690
Upgrade Apache5 org.apache.httpcomponents.client5 to latest available…
joviegas Jun 26, 2025
c3cb46f
Preview API annotation added for Public APIs and TODOs addressed (#6215)
joviegas Jun 30, 2025
bb372da
Merge branch 'master' into feature/master/apache5x
joviegas Jun 30, 2025
d336053
Updated the snapshot
joviegas Jun 30, 2025
397f330
Updated thr Brazil package nma e to have preview as suffix
joviegas Jun 30, 2025
a21356a
Updated Version as -PREVIEW to release apache5 as preview release (#6…
joviegas Jul 1, 2025
4aa1fdb
Handled Surface API review comments (#6224)
joviegas Jul 3, 2025
198a814
Merge branch 'master' into feature/master/apache5x
joviegas Jul 7, 2025
661b152
update pom.xml for apache5.x
joviegas Jul 7, 2025
251490c
Handled commed for merge to master PR 6220 (#6240)
joviegas Jul 8, 2025
7a7e4cd
Merge branch 'master' into feature/master/apache5x
joviegas Jul 10, 2025
6383c44
Added change logs
joviegas Jul 10, 2025
ee93292
Merge branch 'master' into feature/master/apache5x
joviegas Jul 14, 2025
061b5fb
Review comment
joviegas Jul 14, 2025
f26f7bb
Handled Review comments
joviegas Jul 14, 2025
dfcfdcb
Merge branch 'master' into feature/master/apache5x
joviegas Jul 14, 2025
236c102
merge from master
joviegas Jul 21, 2025
2590e6e
Move connection timeout configuration from RequestConfig to Connectio…
joviegas Jul 23, 2025
e88d20d
Replace httpclient.execute call with httpclient.executeOpen as mentio…
joviegas Jul 25, 2025
5c6cdb2
Replace deprecated SSLConnectionSocketFactory with recommended API (#…
joviegas Aug 1, 2025
7e329e6
Merge branch 'master' into feature/master/apache5x
joviegas Aug 1, 2025
0da1dc3
Added change logs
joviegas Aug 1, 2025
a93013f
removed old change logs
joviegas Aug 1, 2025
122e35b
Merge branch 'master' into feature/master/apache5x
joviegas Aug 4, 2025
5b2fc0d
remove unused imports
joviegas Aug 4, 2025
b031aba
inermittent port used in wiremock fixed
joviegas Aug 4, 2025
228caac
update change logs
joviegas Aug 4, 2025
25a61cb
Merge branch 'master' into feature/master/apache5x
joviegas Aug 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "Apache HTTP Client 5",
"contributor": "",
"description": "Replace deprecated APIs from Apache HttpClient 5.5.x with corresponding recommended APIs"
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.hc.client5.http.ClientProtocolException;
import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.auth.AuthSchemeFactory;
import org.apache.hc.client5.http.auth.CredentialsProvider;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.classic.HttpClients;
Expand All @@ -53,9 +55,11 @@
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
import org.apache.hc.client5.http.routing.RoutingSupport;
import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
Expand All @@ -70,6 +74,7 @@
import org.apache.hc.core5.pool.PoolStats;
import org.apache.hc.core5.ssl.SSLInitializationException;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
import software.amazon.awssdk.annotations.SdkPreviewApi;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.SdkTestInternalApi;
Expand Down Expand Up @@ -285,14 +290,25 @@ private HttpExecuteResponse execute(HttpUriRequestBase apacheRequest, MetricColl
HttpClientContext localRequestContext = Apache5Utils.newClientContext(requestConfig.proxyConfiguration());
THREAD_LOCAL_REQUEST_METRIC_COLLECTOR.set(metricCollector);
try {
HttpResponse httpResponse = httpClient.execute(apacheRequest, localRequestContext);
// Create a connection-aware input stream that closes the response when closed
HttpHost target = determineTarget(apacheRequest);
ClassicHttpResponse httpResponse = httpClient.executeOpen(target, apacheRequest, localRequestContext);
return createResponse(httpResponse, apacheRequest);
} finally {
THREAD_LOCAL_REQUEST_METRIC_COLLECTOR.remove();
}
}

/**
* Determines the target host from the request using Apache HttpClient's official routing support utility.
*/
private static HttpHost determineTarget(ClassicHttpRequest request) throws IOException {
try {
return RoutingSupport.determineHost(request);
} catch (HttpException ex) {
throw new ClientProtocolException(ex);
}
}

private HttpUriRequestBase toApacheRequest(HttpExecuteRequest request) {
return apacheHttpRequestFactory.create(request, requestConfig);
}
Expand Down Expand Up @@ -355,7 +371,6 @@ private Apache5HttpRequestConfig createRequestConfig(DefaultBuilder builder,
AttributeMap resolvedOptions) {
return Apache5HttpRequestConfig.builder()
.socketTimeout(resolvedOptions.get(SdkHttpConfigurationOption.READ_TIMEOUT))
.connectionTimeout(resolvedOptions.get(SdkHttpConfigurationOption.CONNECTION_TIMEOUT))
.connectionAcquireTimeout(
resolvedOptions.get(SdkHttpConfigurationOption.CONNECTION_ACQUIRE_TIMEOUT))
.proxyConfiguration(builder.proxyConfiguration)
Expand Down Expand Up @@ -464,12 +479,15 @@ public interface Builder extends SdkHttpClient.Builder<Apache5HttpClient.Builder
Builder dnsResolver(DnsResolver dnsResolver);

/**
* Configuration that defines a custom Socket factory. If set to a null value, a default factory is used.
* <p>
* 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.
* Configure a custom TLS strategy for SSL/TLS connections.
* This is the preferred method over the ConnectionSocketFactory.
*
* @param tlsSocketStrategy The TLS strategy to use for upgrading connections to TLS.
* If null, default TLS configuration will be used.
* @return This builder for method chaining

*/
Builder socketFactory(SSLConnectionSocketFactory socketFactory);
Builder tlsSocketStrategy(TlsSocketStrategy tlsSocketStrategy);

/**
* Configuration that defines an HTTP route planner that computes the route an HTTP request should take.
Expand Down Expand Up @@ -527,7 +545,7 @@ private static final class DefaultBuilder implements Builder {
private HttpRoutePlanner httpRoutePlanner;
private CredentialsProvider credentialsProvider;
private DnsResolver dnsResolver;
private SSLConnectionSocketFactory socketFactory;
private TlsSocketStrategy tlsStrategy;

private DefaultBuilder() {
}
Expand Down Expand Up @@ -649,15 +667,11 @@ public void setDnsResolver(DnsResolver dnsResolver) {
}

@Override
public Builder socketFactory(SSLConnectionSocketFactory socketFactory) {
this.socketFactory = socketFactory;
public Builder tlsSocketStrategy(TlsSocketStrategy tlsSocketStrategy) {
this.tlsStrategy = tlsSocketStrategy;
return this;
}

public void setSocketFactory(SSLConnectionSocketFactory socketFactory) {
socketFactory(socketFactory);
}

@Override
public Builder httpRoutePlanner(HttpRoutePlanner httpRoutePlanner) {
this.httpRoutePlanner = httpRoutePlanner;
Expand Down Expand Up @@ -731,31 +745,44 @@ public SdkHttpClient buildWithDefaults(AttributeMap serviceDefaults) {
private static class ApacheConnectionManagerFactory {

public PoolingHttpClientConnectionManager create(Apache5HttpClient.DefaultBuilder configuration,
AttributeMap standardOptions) {
// TODO : Deprecated method needs to be removed with new replacements
SSLConnectionSocketFactory sslsf = getPreferredSocketFactory(configuration, standardOptions);
AttributeMap standardOptions) {

TlsSocketStrategy tlsStrategy = getPreferredTlsStrategy(configuration, standardOptions);

PoolingHttpClientConnectionManagerBuilder builder =
PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(sslsf)
.setTlsSocketStrategy(tlsStrategy)
.setSchemePortResolver(DefaultSchemePortResolver.INSTANCE)
.setDnsResolver(configuration.dnsResolver);
Duration connectionTtl = standardOptions.get(SdkHttpConfigurationOption.CONNECTION_TIME_TO_LIVE);
if (!connectionTtl.isZero()) {
// Skip TTL=0 to maintain backward compatibility (infinite in 4.x vs immediate expiration in 5.x)
builder.setConnectionTimeToLive(TimeValue.of(connectionTtl.toMillis(), TimeUnit.MILLISECONDS));
}
builder.setMaxConnPerRoute(standardOptions.get(SdkHttpConfigurationOption.MAX_CONNECTIONS));
builder.setMaxConnTotal(standardOptions.get(SdkHttpConfigurationOption.MAX_CONNECTIONS));
builder.setDefaultSocketConfig(buildSocketConfig(standardOptions));
builder.setDefaultConnectionConfig(getConnectionConfig(standardOptions));
return builder.build();
}

private SSLConnectionSocketFactory getPreferredSocketFactory(Apache5HttpClient.DefaultBuilder configuration,
AttributeMap standardOptions) {
return Optional.ofNullable(configuration.socketFactory)
.orElseGet(() -> new SdkTlsSocketFactory(getSslContext(standardOptions),
getHostNameVerifier(standardOptions)));
private static ConnectionConfig getConnectionConfig(AttributeMap standardOptions) {
ConnectionConfig.Builder connectionConfigBuilder =
ConnectionConfig.custom()
.setConnectTimeout(Timeout.ofMilliseconds(
standardOptions.get(SdkHttpConfigurationOption.CONNECTION_TIMEOUT).toMillis()))
.setSocketTimeout(Timeout.ofMilliseconds(
standardOptions.get(SdkHttpConfigurationOption.READ_TIMEOUT).toMillis()));
Duration connectionTtl = standardOptions.get(SdkHttpConfigurationOption.CONNECTION_TIME_TO_LIVE);
if (!connectionTtl.isZero()) {
// Skip TTL=0 to maintain backward compatibility (infinite in 4.x vs immediate expiration in 5.x)
connectionConfigBuilder.setTimeToLive(TimeValue.ofMilliseconds(connectionTtl.toMillis()));
}
return connectionConfigBuilder.build();
}

private TlsSocketStrategy getPreferredTlsStrategy(Apache5HttpClient.DefaultBuilder configuration,
AttributeMap standardOptions) {
if (configuration.tlsStrategy != null) {
return configuration.tlsStrategy;
}
return new SdkTlsSocketFactory(getSslContext(standardOptions),
getHostNameVerifier(standardOptions));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,12 @@
public final class Apache5HttpRequestConfig {

private final Duration socketTimeout;
private final Duration connectionTimeout;
private final Duration connectionAcquireTimeout;
private final boolean expectContinueEnabled;
private final ProxyConfiguration proxyConfiguration;

private Apache5HttpRequestConfig(Builder builder) {
this.socketTimeout = builder.socketTimeout;
this.connectionTimeout = builder.connectionTimeout;
this.connectionAcquireTimeout = builder.connectionAcquireTimeout;
this.expectContinueEnabled = builder.expectContinueEnabled;
this.proxyConfiguration = builder.proxyConfiguration;
Expand All @@ -44,10 +42,6 @@ public Duration socketTimeout() {
return socketTimeout;
}

public Duration connectionTimeout() {
return connectionTimeout;
}

public Duration connectionAcquireTimeout() {
return connectionAcquireTimeout;
}
Expand All @@ -73,7 +67,6 @@ public static Builder builder() {
public static final class Builder {

private Duration socketTimeout;
private Duration connectionTimeout;
private Duration connectionAcquireTimeout;
private boolean expectContinueEnabled;
private ProxyConfiguration proxyConfiguration;
Expand All @@ -86,11 +79,6 @@ public Builder socketTimeout(Duration socketTimeout) {
return this;
}

public Builder connectionTimeout(Duration connectionTimeout) {
this.connectionTimeout = connectionTimeout;
return this;
}

public Builder connectionAcquireTimeout(Duration connectionAcquireTimeout) {
this.connectionAcquireTimeout = connectionAcquireTimeout;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static HttpClientConnectionManager wrap(HttpClientConnectionManager orig)
/**
* Further wraps {@link LeaseRequest} to capture performance metrics.
*/
private static class InstrumentedHttpClientConnectionManager extends DelegatingHttpClientConnectionManager {
private static final class InstrumentedHttpClientConnectionManager extends DelegatingHttpClientConnectionManager {

private InstrumentedHttpClientConnectionManager(HttpClientConnectionManager delegate) {
super(delegate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ static LeaseRequest wrap(LeaseRequest orig) {
/**
* Measures the latency of {@link LeaseRequest#get(Timeout)}.
*/
private static class InstrumentedConnectionRequest extends DelegatingConnectionRequest {
private static final class InstrumentedConnectionRequest extends DelegatingConnectionRequest {

private InstrumentedConnectionRequest(LeaseRequest delegate) {
super(delegate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,50 @@
package software.amazon.awssdk.http.apache5.internal.conn;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Arrays;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.TimeValue;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.http.apache5.internal.net.SdkSocket;
import software.amazon.awssdk.http.apache5.internal.net.SdkSslSocket;
import software.amazon.awssdk.utils.Logger;

@SdkInternalApi
public class SdkTlsSocketFactory extends SSLConnectionSocketFactory {
public class SdkTlsSocketFactory extends DefaultClientTlsStrategy {

private static final Logger log = Logger.loggerFor(SdkTlsSocketFactory.class);

public SdkTlsSocketFactory(SSLContext sslContext, HostnameVerifier hostnameVerifier) {
super(sslContext, hostnameVerifier);
if (sslContext == null) {
throw new IllegalArgumentException(
"sslContext must not be null. " + "Use SSLContext.getDefault() if you are unsure.");
"sslContext must not be null. Use SSLContext.getDefault() if you are unsure.");
}
}

@Override
protected final void prepareSocket(SSLSocket socket) {
protected void initializeSocket(SSLSocket socket) {
super.initializeSocket(socket);
log.debug(() -> String.format("socket.getSupportedProtocols(): %s, socket.getEnabledProtocols(): %s",
Arrays.toString(socket.getSupportedProtocols()),
Arrays.toString(socket.getEnabledProtocols())));
}

@Override
public Socket connectSocket(TimeValue connectTimeout,
Socket socket,
HttpHost host,
InetSocketAddress remoteAddress,
InetSocketAddress localAddress,
HttpContext context) throws IOException {
log.trace(() -> String.format("Connecting to %s:%s", remoteAddress.getAddress(), remoteAddress.getPort()));
public SSLSocket upgrade(Socket socket,
String target,
int port,
Object attachment,
HttpContext context) throws IOException {
log.trace(() -> String.format("Upgrading socket to TLS for %s:%s", target, port));

Socket connectSocket = super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context);
return new SdkSocket(connectSocket);
SSLSocket upgradedSocket = super.upgrade(socket, target, port, attachment, context);

// Wrap the upgraded SSLSocket in SdkSSLSocket for logging
return new SdkSslSocket(upgradedSocket);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public class Apache5HttpRequestFactory {
private static final List<String> IGNORE_HEADERS = Arrays.asList(HttpHeaders.CONTENT_LENGTH, HttpHeaders.HOST,
HttpHeaders.TRANSFER_ENCODING);

public HttpUriRequestBase create(final HttpExecuteRequest request, final Apache5HttpRequestConfig requestConfig) {
public HttpUriRequestBase create(HttpExecuteRequest request, Apache5HttpRequestConfig requestConfig) {
HttpUriRequestBase base = createApacheRequest(request, sanitizeUri(request.httpRequest()));
addHeadersToRequest(base, request.httpRequest());
addRequestConfig(base, request.httpRequest(), requestConfig);
Expand Down Expand Up @@ -90,12 +90,10 @@ private URI sanitizeUri(SdkHttpRequest request) {
private void addRequestConfig(HttpUriRequestBase base,
SdkHttpRequest request,
Apache5HttpRequestConfig requestConfig) {
int connectTimeout = saturatedCast(requestConfig.connectionTimeout().toMillis());
int connectAcquireTimeout = saturatedCast(requestConfig.connectionAcquireTimeout().toMillis());
RequestConfig.Builder requestConfigBuilder = RequestConfig
.custom()
.setConnectionRequestTimeout(connectAcquireTimeout, TimeUnit.MILLISECONDS)
.setConnectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.setResponseTimeout(saturatedCast(requestConfig.socketTimeout().toMillis()), TimeUnit.MILLISECONDS);

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ public ClassicHttpResponse execute(HttpHost target, ClassicHttpRequest request)
return delegate.execute(target, request);
}

@Override
public ClassicHttpResponse executeOpen(HttpHost target, ClassicHttpRequest request, HttpContext context) throws IOException {
return delegate.executeOpen(target, request, context);
}

@Override
public HttpResponse execute(HttpHost target, ClassicHttpRequest request, HttpContext context) throws IOException {
return delegate.execute(target, request, context);
Expand Down
Loading
Loading