From 47fe8620eb0696f6c33826a779fd563c8e1e07bc Mon Sep 17 00:00:00 2001 From: kai lin Date: Fri, 17 Apr 2026 11:32:56 -0400 Subject: [PATCH 1/3] feat(core): Add expect100ContinueTimeoutMs to ClientConfiguration Expose CURLOPT_EXPECT_100_TIMEOUT_MS through ClientConfiguration so customers can configure the 100-Continue timeout without needing a custom HTTP client factory. This is useful when operating behind proxies that introduce network delays, where the default 1000ms timeout can cause IncompleteBody errors on uploads. Default of 0 preserves existing behavior (curl's built-in 1000ms). --- .../include/aws/core/client/ClientConfiguration.h | 10 ++++++++++ .../include/aws/core/http/curl/CurlHandleContainer.h | 3 ++- .../source/client/ClientConfiguration.cpp | 1 + .../source/http/curl/CurlHandleContainer.cpp | 8 ++++++-- .../source/http/curl/CurlHttpClient.cpp | 3 ++- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h b/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h index 203022fa2253..d99f3e0290c0 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h +++ b/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h @@ -362,6 +362,16 @@ namespace Aws */ bool disableExpectHeader = false; + /** + * Only works for Curl http client. + * Sets the timeout in milliseconds that Curl will wait for a 100-Continue response from the server + * before sending the request body. This corresponds to CURLOPT_EXPECT_100_TIMEOUT_MS. + * Useful when operating behind proxies that introduce network delays, where the default 1000ms + * may be too short and cause IncompleteBody errors. + * Default 0 means use Curl's built-in default (1000ms). + */ + long expect100ContinueTimeoutMs = 0; + /** * If set to true clock skew will be adjusted after each http attempt, default to true. */ diff --git a/src/aws-cpp-sdk-core/include/aws/core/http/curl/CurlHandleContainer.h b/src/aws-cpp-sdk-core/include/aws/core/http/curl/CurlHandleContainer.h index 3ce921b57db1..406a9527a39a 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/http/curl/CurlHandleContainer.h +++ b/src/aws-cpp-sdk-core/include/aws/core/http/curl/CurlHandleContainer.h @@ -31,7 +31,7 @@ class CurlHandleContainer */ CurlHandleContainer(unsigned maxSize = 50, long httpRequestTimeout = 0, long connectTimeout = 1000, bool tcpKeepAlive = true, unsigned long tcpKeepAliveIntervalMs = 30000, long lowSpeedTime = 3000, unsigned long lowSpeedLimit = 1, - Version version = Version::HTTP_VERSION_2TLS); + Version version = Version::HTTP_VERSION_2TLS, long expect100ContinueTimeoutMs = 0); ~CurlHandleContainer(); /** @@ -71,6 +71,7 @@ class CurlHandleContainer unsigned m_poolSize; std::mutex m_containerLock; Version m_version; + long m_expect100ContinueTimeoutMs; }; } // namespace Http diff --git a/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp b/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp index cc805c69eb99..f1e8de2fd117 100644 --- a/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp +++ b/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp @@ -247,6 +247,7 @@ void setLegacyClientConfigurationParameters(ClientConfiguration& clientConfig) clientConfig.httpClientChunkedMode = HttpClientChunkedMode::CLIENT_IMPLEMENTATION; clientConfig.followRedirects = FollowRedirectsPolicy::DEFAULT; clientConfig.disableExpectHeader = false; + clientConfig.expect100ContinueTimeoutMs = 0; clientConfig.enableClockSkewAdjustment = true; clientConfig.enableHostPrefixInjection = true; clientConfig.enableHttpClientTrace = false; diff --git a/src/aws-cpp-sdk-core/source/http/curl/CurlHandleContainer.cpp b/src/aws-cpp-sdk-core/source/http/curl/CurlHandleContainer.cpp index edc410a4ee67..93bc3dca8f6b 100644 --- a/src/aws-cpp-sdk-core/source/http/curl/CurlHandleContainer.cpp +++ b/src/aws-cpp-sdk-core/source/http/curl/CurlHandleContainer.cpp @@ -16,10 +16,10 @@ static const char* CURL_HANDLE_CONTAINER_TAG = "CurlHandleContainer"; CurlHandleContainer::CurlHandleContainer(unsigned maxSize, long httpRequestTimeout, long connectTimeout, bool enableTcpKeepAlive, unsigned long tcpKeepAliveIntervalMs, long lowSpeedTime, unsigned long lowSpeedLimit, - Version version) : + Version version, long expect100ContinueTimeoutMs) : m_maxPoolSize(maxSize), m_httpRequestTimeout(httpRequestTimeout), m_connectTimeout(connectTimeout), m_enableTcpKeepAlive(enableTcpKeepAlive), m_tcpKeepAliveIntervalMs(tcpKeepAliveIntervalMs), m_lowSpeedTime(lowSpeedTime), m_lowSpeedLimit(lowSpeedLimit), m_poolSize(0), - m_version(version) + m_version(version), m_expect100ContinueTimeoutMs(expect100ContinueTimeoutMs) { AWS_LOGSTREAM_INFO(CURL_HANDLE_CONTAINER_TAG, "Initializing CurlHandleContainer with size " << maxSize); } @@ -174,6 +174,10 @@ void CurlHandleContainer::SetDefaultOptionsOnHandle(CURL* handle) curl_easy_setopt(handle, CURLOPT_TCP_KEEPIDLE, m_tcpKeepAliveIntervalMs / 1000); curl_easy_setopt(handle, CURLOPT_HTTP_VERSION, ConvertHttpVersion(m_version)); curl_easy_setopt(handle, CURLOPT_MAXCONNECTS, m_maxPoolSize); + if (m_expect100ContinueTimeoutMs > 0) + { + curl_easy_setopt(handle, CURLOPT_EXPECT_100_TIMEOUT_MS, m_expect100ContinueTimeoutMs); + } } long CurlHandleContainer::ConvertHttpVersion(Version version) { diff --git a/src/aws-cpp-sdk-core/source/http/curl/CurlHttpClient.cpp b/src/aws-cpp-sdk-core/source/http/curl/CurlHttpClient.cpp index 58fc56875de7..a2df8714f137 100644 --- a/src/aws-cpp-sdk-core/source/http/curl/CurlHttpClient.cpp +++ b/src/aws-cpp-sdk-core/source/http/curl/CurlHttpClient.cpp @@ -602,7 +602,8 @@ const bool FORCE_ENABLE_CURL_LOGGING = false; CurlHttpClient::CurlHttpClient(const ClientConfiguration& clientConfig) : Base(), m_curlHandleContainer(clientConfig.maxConnections, clientConfig.httpRequestTimeoutMs, clientConfig.connectTimeoutMs, clientConfig.enableTcpKeepAlive, - clientConfig.tcpKeepAliveIntervalMs, clientConfig.requestTimeoutMs, clientConfig.lowSpeedLimit, clientConfig.version), + clientConfig.tcpKeepAliveIntervalMs, clientConfig.requestTimeoutMs, clientConfig.lowSpeedLimit, clientConfig.version, + clientConfig.expect100ContinueTimeoutMs), m_isAllowSystemProxy(clientConfig.allowSystemProxy), m_isUsingProxy(!clientConfig.proxyHost.empty()), m_proxyUserName(clientConfig.proxyUserName), m_proxyPassword(clientConfig.proxyPassword), m_proxyScheme(SchemeMapper::ToString(clientConfig.proxyScheme)), m_proxyHost(clientConfig.proxyHost), m_proxySSLCertPath(clientConfig.proxySSLCertPath), m_proxySSLCertType(clientConfig.proxySSLCertType), From 38cd9660894ed14cf161097b8f3c683f28802aa1 Mon Sep 17 00:00:00 2001 From: kai lin Date: Fri, 17 Apr 2026 14:15:48 -0400 Subject: [PATCH 2/3] setting default value to 1000 --- .../include/aws/core/client/ClientConfiguration.h | 4 ++-- src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp | 2 +- .../source/http/curl/CurlHandleContainer.cpp | 5 +---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h b/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h index d99f3e0290c0..b6072387f984 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h +++ b/src/aws-cpp-sdk-core/include/aws/core/client/ClientConfiguration.h @@ -368,9 +368,9 @@ namespace Aws * before sending the request body. This corresponds to CURLOPT_EXPECT_100_TIMEOUT_MS. * Useful when operating behind proxies that introduce network delays, where the default 1000ms * may be too short and cause IncompleteBody errors. - * Default 0 means use Curl's built-in default (1000ms). + * Default 1000ms (Curl's built-in default). */ - long expect100ContinueTimeoutMs = 0; + long expect100ContinueTimeoutMs = 1000; /** * If set to true clock skew will be adjusted after each http attempt, default to true. diff --git a/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp b/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp index f1e8de2fd117..f793bb5d49df 100644 --- a/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp +++ b/src/aws-cpp-sdk-core/source/client/ClientConfiguration.cpp @@ -247,7 +247,7 @@ void setLegacyClientConfigurationParameters(ClientConfiguration& clientConfig) clientConfig.httpClientChunkedMode = HttpClientChunkedMode::CLIENT_IMPLEMENTATION; clientConfig.followRedirects = FollowRedirectsPolicy::DEFAULT; clientConfig.disableExpectHeader = false; - clientConfig.expect100ContinueTimeoutMs = 0; + clientConfig.expect100ContinueTimeoutMs = 1000; clientConfig.enableClockSkewAdjustment = true; clientConfig.enableHostPrefixInjection = true; clientConfig.enableHttpClientTrace = false; diff --git a/src/aws-cpp-sdk-core/source/http/curl/CurlHandleContainer.cpp b/src/aws-cpp-sdk-core/source/http/curl/CurlHandleContainer.cpp index 93bc3dca8f6b..bb70f86248c2 100644 --- a/src/aws-cpp-sdk-core/source/http/curl/CurlHandleContainer.cpp +++ b/src/aws-cpp-sdk-core/source/http/curl/CurlHandleContainer.cpp @@ -174,10 +174,7 @@ void CurlHandleContainer::SetDefaultOptionsOnHandle(CURL* handle) curl_easy_setopt(handle, CURLOPT_TCP_KEEPIDLE, m_tcpKeepAliveIntervalMs / 1000); curl_easy_setopt(handle, CURLOPT_HTTP_VERSION, ConvertHttpVersion(m_version)); curl_easy_setopt(handle, CURLOPT_MAXCONNECTS, m_maxPoolSize); - if (m_expect100ContinueTimeoutMs > 0) - { - curl_easy_setopt(handle, CURLOPT_EXPECT_100_TIMEOUT_MS, m_expect100ContinueTimeoutMs); - } + curl_easy_setopt(handle, CURLOPT_EXPECT_100_TIMEOUT_MS, m_expect100ContinueTimeoutMs); } long CurlHandleContainer::ConvertHttpVersion(Version version) { From dac1c223e14eb9d73f214a16b9b70efeca621509 Mon Sep 17 00:00:00 2001 From: kai lin Date: Fri, 17 Apr 2026 14:17:47 -0400 Subject: [PATCH 3/3] setting default value to 1000 --- .../include/aws/core/http/curl/CurlHandleContainer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aws-cpp-sdk-core/include/aws/core/http/curl/CurlHandleContainer.h b/src/aws-cpp-sdk-core/include/aws/core/http/curl/CurlHandleContainer.h index 406a9527a39a..328b9947430b 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/http/curl/CurlHandleContainer.h +++ b/src/aws-cpp-sdk-core/include/aws/core/http/curl/CurlHandleContainer.h @@ -31,7 +31,7 @@ class CurlHandleContainer */ CurlHandleContainer(unsigned maxSize = 50, long httpRequestTimeout = 0, long connectTimeout = 1000, bool tcpKeepAlive = true, unsigned long tcpKeepAliveIntervalMs = 30000, long lowSpeedTime = 3000, unsigned long lowSpeedLimit = 1, - Version version = Version::HTTP_VERSION_2TLS, long expect100ContinueTimeoutMs = 0); + Version version = Version::HTTP_VERSION_2TLS, long expect100ContinueTimeoutMs = 1000); ~CurlHandleContainer(); /**