From 16d2c81178bdb5d51b9abfa49aa01a6db4d07621 Mon Sep 17 00:00:00 2001 From: Zoe Wang <33073555+zoewangg@users.noreply.github.com> Date: Mon, 23 Mar 2026 18:31:00 -0700 Subject: [PATCH 1/2] Set maxConcurrentStreams on HTTP/2 stream manager so that the MAX_CONNECTIONS setting is respected for HTTP/2. Also improved the error message when HTTP/2 is configured with the sync client to clarify available options. --- .../amazon/awssdk/http/crt/AwsCrtHttpClient.java | 4 +++- .../amazon/awssdk/http/crt/AwsCrtHttpClientBase.java | 9 +++++---- .../amazon/awssdk/http/crt/internal/CrtUtils.java | 10 +++------- .../software/amazon/awssdk/http/crt/H2ErrorTest.java | 5 +++-- pom.xml | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClient.java b/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClient.java index a2eec3bcc625..ca97f6079b89 100644 --- a/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClient.java +++ b/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClient.java @@ -58,7 +58,9 @@ public final class AwsCrtHttpClient extends AwsCrtHttpClientBase implements SdkH private AwsCrtHttpClient(DefaultBuilder builder, AttributeMap config) { super(builder, config); if (this.protocol == Protocol.HTTP2) { - throw new UnsupportedOperationException("HTTP/2 is not supported in sync client. Use AwsCrtAsyncHttpClient instead."); + throw new UnsupportedOperationException( + "HTTP/2 is not supported for sync HTTP clients. Either use HTTP/1.1 (the default) or use an async " + + "HTTP client (e.g., AwsCrtAsyncHttpClient)."); } } diff --git a/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClientBase.java b/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClientBase.java index 228a6086eddf..b7d00688c2ab 100644 --- a/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClientBase.java +++ b/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClientBase.java @@ -69,7 +69,7 @@ abstract class AwsCrtHttpClientBase implements SdkAutoCloseable { private final HttpProxyOptions proxyOptions; private final HttpMonitoringOptions monitoringOptions; private final long maxConnectionIdleInMilliseconds; - private final int maxConnectionsPerEndpoint; + private final int maxStreamsPerEndpoint; private final long connectionAcquisitionTimeout; private final TlsContextOptions tlsContextOptions; private boolean isClosed = false; @@ -95,7 +95,7 @@ abstract class AwsCrtHttpClientBase implements SdkAutoCloseable { this.tlsContext = registerOwnedResource(clientTlsContext); this.readBufferSize = builder.getReadBufferSizeInBytes() == null ? DEFAULT_STREAM_WINDOW_SIZE : builder.getReadBufferSizeInBytes(); - this.maxConnectionsPerEndpoint = config.get(SdkHttpConfigurationOption.MAX_CONNECTIONS); + this.maxStreamsPerEndpoint = config.get(SdkHttpConfigurationOption.MAX_CONNECTIONS); this.monitoringOptions = resolveHttpMonitoringOptions(builder.getConnectionHealthConfiguration()).orElse(null); this.maxConnectionIdleInMilliseconds = config.get(SdkHttpConfigurationOption.CONNECTION_MAX_IDLE_TIMEOUT).toMillis(); this.connectionAcquisitionTimeout = config.get(SdkHttpConfigurationOption.CONNECTION_ACQUIRE_TIMEOUT).toMillis(); @@ -121,7 +121,7 @@ String clientName() { } private HttpStreamManager createConnectionPool(URI uri) { - log.debug(() -> "Creating ConnectionPool for: URI:" + uri + ", MaxConns: " + maxConnectionsPerEndpoint); + log.debug(() -> "Creating ConnectionPool for: URI:" + uri + ", MaxConns: " + maxStreamsPerEndpoint); boolean isHttps = "https".equalsIgnoreCase(uri.getScheme()); TlsContext poolTlsContext = isHttps ? tlsContext : null; @@ -132,7 +132,7 @@ private HttpStreamManager createConnectionPool(URI uri) { .withTlsContext(poolTlsContext) .withUri(uri) .withWindowSize(readBufferSize) - .withMaxConnections(maxConnectionsPerEndpoint) + .withMaxConnections(maxStreamsPerEndpoint) .withManualWindowManagement(true) .withProxyOptions(proxyOptions) .withMonitoringOptions(monitoringOptions) @@ -144,6 +144,7 @@ private HttpStreamManager createConnectionPool(URI uri) { if (protocol == Protocol.HTTP2) { Http2StreamManagerOptions h2Options = new Http2StreamManagerOptions() + .withMaxConcurrentStreams(maxStreamsPerEndpoint) .withConnectionManagerOptions(h1Options); if (!isHttps) { diff --git a/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/internal/CrtUtils.java b/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/internal/CrtUtils.java index 6b660175614a..53f73f2e8d42 100644 --- a/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/internal/CrtUtils.java +++ b/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/internal/CrtUtils.java @@ -28,8 +28,8 @@ import java.util.concurrent.CompletionException; import javax.net.ssl.SSLHandshakeException; import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.crt.CRT; import software.amazon.awssdk.crt.CrtRuntimeException; -import software.amazon.awssdk.crt.http.HttpClientConnection; import software.amazon.awssdk.crt.http.HttpException; import software.amazon.awssdk.crt.http.HttpManagerMetrics; import software.amazon.awssdk.crt.http.HttpStreamManager; @@ -50,10 +50,7 @@ private CrtUtils() { public static Throwable wrapWithIoExceptionIfRetryable(HttpException httpException) { Throwable toThrow = httpException; - // TODO: switch to Crt.awsIsTransientError once https://github.com/awslabs/aws-crt-java/pull/972 is merged - if (HttpClientConnection.isErrorRetryable(httpException)) { - // IOExceptions get retried, and if the CRT says this error is retryable, - // it's semantically an IOException anyway. + if (CRT.awsIsTransientError(httpException.getErrorCode())) { toThrow = new IOException(httpException); } return toThrow; @@ -70,8 +67,7 @@ public static Throwable wrapCrtException(Throwable throwable) { if (httpErrorCode == CRT_TLS_NEGOTIATION_ERROR_CODE) { return new SSLHandshakeException(httpException.getMessage()); } - // TODO: check with CRT team, could CRT_SOCKET_TIMEOUT be thrown - // from processes other than tcp connect? + if (httpErrorCode == CRT_SOCKET_TIMEOUT) { return new ConnectException(httpException.getMessage()); } diff --git a/http-clients/aws-crt-client/src/test/java/software/amazon/awssdk/http/crt/H2ErrorTest.java b/http-clients/aws-crt-client/src/test/java/software/amazon/awssdk/http/crt/H2ErrorTest.java index 926c07414c3f..557419ea426d 100644 --- a/http-clients/aws-crt-client/src/test/java/software/amazon/awssdk/http/crt/H2ErrorTest.java +++ b/http-clients/aws-crt-client/src/test/java/software/amazon/awssdk/http/crt/H2ErrorTest.java @@ -55,6 +55,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import software.amazon.awssdk.crt.http.HttpException; import software.amazon.awssdk.http.Protocol; import software.amazon.awssdk.http.async.SdkAsyncHttpClient; import software.amazon.awssdk.utils.AttributeMap; @@ -82,14 +83,14 @@ public void teardown() { } @Test - public void serverSendsRstStream_shouldThrowIOException() throws Exception { + public void serverSendsRstStream_shouldNotThrowIOException() throws Exception { H2ErrorServer server = new H2ErrorServer(ErrorType.RST_STREAM); server.init(); try { CompletableFuture request = sendGetRequest(server.port(), client); assertThatThrownBy(request::join) .isInstanceOf(CompletionException.class) - .hasCauseInstanceOf(IOException.class) + .hasCauseInstanceOf(HttpException.class) .hasMessageContaining("RST_STREAM"); } finally { server.shutdown(); diff --git a/pom.xml b/pom.xml index 6a8d8b09fed0..52ac0c9ebe13 100644 --- a/pom.xml +++ b/pom.xml @@ -130,7 +130,7 @@ 3.1.5 1.17.1 1.37 - 0.43.5 + 0.43.9 5.10.3 From 22cd422dc7606365bcf273d450b3a757c2f6dc13 Mon Sep 17 00:00:00 2001 From: Zoe Wang <33073555+zoewangg@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:03:26 -0700 Subject: [PATCH 2/2] Update http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClientBase.java Co-authored-by: Dengke Tang <815825145@qq.com> --- .../software/amazon/awssdk/http/crt/AwsCrtHttpClientBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClientBase.java b/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClientBase.java index b7d00688c2ab..41a834c6d7aa 100644 --- a/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClientBase.java +++ b/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/AwsCrtHttpClientBase.java @@ -121,7 +121,7 @@ String clientName() { } private HttpStreamManager createConnectionPool(URI uri) { - log.debug(() -> "Creating ConnectionPool for: URI:" + uri + ", MaxConns: " + maxStreamsPerEndpoint); + log.debug(() -> "Creating ConnectionPool for: URI:" + uri + ", MaxConns: " + maxStreamsPerEndpoint+ ", MaxStreams: " + maxStreamsPerEndpoint); boolean isHttps = "https".equalsIgnoreCase(uri.getScheme()); TlsContext poolTlsContext = isHttps ? tlsContext : null;