Skip to content

Commit b72fd8e

Browse files
wps132230singku
andcommitted
Override Http client handle configuration.
Add virtual functions to override handle configuration for Curl, WinHttp, WinINet and IXMLHTTPRequest2 clients. Co-authored-by: Andrew Tang <[email protected]>
1 parent 726a9a1 commit b72fd8e

File tree

10 files changed

+201
-36
lines changed

10 files changed

+201
-36
lines changed

aws-cpp-sdk-core-tests/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ if (NO_HTTP_CLIENT)
9999
target_compile_definitions(${PROJECT_NAME} PRIVATE "NO_HTTP_CLIENT")
100100
endif()
101101

102-
target_link_libraries(${PROJECT_NAME} ${PROJECT_LIBS})
102+
target_link_libraries(${PROJECT_NAME} ${PROJECT_LIBS} ${CLIENT_LIBS})
103103

104104
add_custom_command(TARGET aws-cpp-sdk-core-tests PRE_BUILD
105105
COMMAND ${CMAKE_COMMAND} -E copy_directory

aws-cpp-sdk-core-tests/http/HttpClientTest.cpp

Lines changed: 168 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
#include <aws/core/http/HttpResponse.h>
99
#include <aws/core/http/HttpClientFactory.h>
1010
#include <aws/core/http/HttpClient.h>
11+
#include <aws/core/http/standard/StandardHttpRequest.h>
1112
#include <aws/core/client/ClientConfiguration.h>
13+
#include <aws/core/utils/logging/LogMacros.h>
1214

1315
using namespace Aws::Http;
1416
using namespace Aws::Utils;
1517
using namespace Aws::Client;
1618

1719
#ifndef NO_HTTP_CLIENT
18-
TEST(HttpClientTest, TestHttpResponseNetworkError)
20+
TEST(HttpClientTest, TestRandomURL)
1921
{
2022
auto request = CreateHttpRequest(Aws::String("http://some.unknown1234xxx.test.aws"),
2123
HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
@@ -33,30 +35,180 @@ TEST(HttpClientTest, TestHttpResponseNetworkError)
3335
{
3436
ASSERT_EQ(HttpResponseCode::FORBIDDEN, response->GetResponseCode());
3537
}
36-
3738
}
3839

39-
//Test CURL HTTP Client Settings.
40-
#if defined(ENABLE_CURL_CLIENT) && defined(ENABLE_HTTP_CLIENT_TESTING)
41-
#include <aws/core/platform/FileSystem.h>
42-
#include <aws/core/utils/DateTime.h>
43-
#include <unistd.h>
40+
// Test Http Client timeout
41+
// Run "scripts/dummy_web_server.py -l localhost -p 8778" to setup a dummy web server first.
42+
#if ENABLE_HTTP_CLIENT_TESTING
43+
static const char ALLOCATION_TAG[] = "HttpClientTest";
44+
45+
#if ENABLE_CURL_CLIENT
46+
#include <aws/core/http/curl/CurlHttpClient.h>
4447
#include <signal.h>
45-
#include <stdlib.h>
46-
#include <thread>
48+
class LongRunningCurlHttpClient : public Aws::Http::CurlHttpClient
49+
{
50+
public:
51+
LongRunningCurlHttpClient(const Aws::Client::ClientConfiguration& clientConfig) : Aws::Http::CurlHttpClient(clientConfig) {}
4752

48-
TEST(CURLHttpClientTest, TestCantResolveHost)
53+
protected:
54+
void OverrideOptionsOnConnectionHandle(CURL* connectionHandle) const override
55+
{
56+
// Override low speed limit and low speed time
57+
curl_easy_setopt(connectionHandle, CURLOPT_LOW_SPEED_LIMIT, 1);
58+
curl_easy_setopt(connectionHandle, CURLOPT_LOW_SPEED_TIME, 10);
59+
}
60+
};
61+
#elif ENABLE_WINDOWS_CLIENT
62+
#include <windows.h>
63+
#if ENABLE_WINDOWS_IXML_HTTP_REQUEST_2_CLIENT
64+
#include <aws/core/http/windows/IXmlHttpRequest2HttpClient.h>
65+
#include <aws/core/platform/refs/IXmlHttpRequest2Ref.h>
66+
class LongRunningIXmlHttpRequest2HttpClient : public Aws::Http::IXmlHttpRequest2HttpClient
4967
{
50-
auto request = CreateHttpRequest(Aws::String("http://some.unknown1234xxx.test.aws"),
68+
public:
69+
LongRunningIXmlHttpRequest2HttpClient(const Aws::Client::ClientConfiguration& clientConfig) : Aws::Http::IXmlHttpRequest2HttpClient(clientConfig) {}
70+
71+
protected:
72+
// Override total timeout.
73+
void OverrideOptionsOnRequestHandle(const Aws::Http::HttpRequestComHandle& handle) const override
74+
{
75+
handle->SetProperty(XHR_PROP_TIMEOUT, 10000);
76+
}
77+
};
78+
#if BYPASS_DEFAULT_PROXY
79+
#include <aws/core/http/windows/WinHttpSyncHttpClient.h>
80+
#include <winhttp.h>
81+
class LongRunningWinHttpSyncHttpClient : public Aws::Http::WinHttpSyncHttpClient
82+
{
83+
public:
84+
LongRunningWinHttpSyncHttpClient(const Aws::Client::ClientConfiguration& clientConfig) : Aws::Http::WinHttpSyncHttpClient(clientConfig) {}
85+
86+
protected:
87+
// Override receive timeout.
88+
void OverrideOptionsOnRequestHandle(void* handle) const override
89+
{
90+
DWORD requestMs = 10000;
91+
if (!WinHttpSetOption(handle, WINHTTP_OPTION_RECEIVE_TIMEOUT, &requestMs, sizeof(requestMs)))
92+
{
93+
AWS_LOGSTREAM_ERROR(ALLOCATION_TAG, "Error setting timeouts " << GetLastError());
94+
}
95+
}
96+
};
97+
#endif
98+
#else
99+
#include <aws/core/http/windows/WinHttpSyncHttpClient.h>
100+
#include <winhttp.h>
101+
class LongRunningWinHttpSyncHttpClient : public Aws::Http::WinHttpSyncHttpClient
102+
{
103+
public:
104+
LongRunningWinHttpSyncHttpClient(const Aws::Client::ClientConfiguration& clientConfig) : Aws::Http::WinHttpSyncHttpClient(clientConfig) {}
105+
106+
protected:
107+
// Override receive timeout.
108+
void OverrideOptionsOnRequestHandle(void* handle) const override
109+
{
110+
DWORD requestMs = 10000;
111+
if (!WinHttpSetOption(handle, WINHTTP_OPTION_RECEIVE_TIMEOUT, &requestMs, sizeof(requestMs)))
112+
{
113+
AWS_LOGSTREAM_ERROR(ALLOCATION_TAG, "Error setting timeouts " << GetLastError());
114+
}
115+
}
116+
};
117+
#endif
118+
#endif
119+
120+
class MockCustomHttpClientFactory : public Aws::Http::HttpClientFactory
121+
{
122+
std::shared_ptr<Aws::Http::HttpClient> CreateHttpClient(const Aws::Client::ClientConfiguration& clientConfiguration) const override
123+
{
124+
#if ENABLE_CURL_CLIENT
125+
return Aws::MakeShared<LongRunningCurlHttpClient>(ALLOCATION_TAG, clientConfiguration);
126+
#elif ENABLE_WINDOWS_CLIENT
127+
#if ENABLE_WINDOWS_IXML_HTTP_REQUEST_2_CLIENT
128+
#if BYPASS_DEFAULT_PROXY
129+
return Aws::MakeShared<LongRunningWinHttpSyncHttpClient>(ALLOCATION_TAG, clientConfiguration);
130+
#else
131+
return Aws::MakeShared<LongRunningIXmlHttpRequest2HttpClient>(ALLOCATION_TAG, clientConfiguration);
132+
#endif // BYPASS_DEFAULT_PROXY
133+
#else
134+
return Aws::MakeShared<LongRunningWinHttpSyncHttpClient>(ALLOCATION_TAG, clientConfiguration);
135+
136+
#endif // ENABLE_WINDOWS_IXML_HTTP_REQUEST_2_CLIENT
137+
#else
138+
AWS_LOGSTREAM_ERROR(ALLOCATION_TAG, "For testing purpose, this factory will not fallback to default http client intentionally.");
139+
return nullptr;
140+
#endif
141+
}
142+
143+
std::shared_ptr<Aws::Http::HttpRequest> CreateHttpRequest(const Aws::String &uri, Aws::Http::HttpMethod method,
144+
const Aws::IOStreamFactory &streamFactory) const override
145+
{
146+
return CreateHttpRequest(Aws::Http::URI(uri), method, streamFactory);
147+
}
148+
149+
std::shared_ptr<Aws::Http::HttpRequest> CreateHttpRequest(const Aws::Http::URI& uri, Aws::Http::HttpMethod method, const Aws::IOStreamFactory& streamFactory) const override
150+
{
151+
auto request = Aws::MakeShared<Aws::Http::Standard::StandardHttpRequest>(ALLOCATION_TAG, uri, method);
152+
request->SetResponseStreamFactory(streamFactory);
153+
154+
return request;
155+
}
156+
157+
void InitStaticState() override
158+
{
159+
#if ENABLE_CURL_CLIENT
160+
LongRunningCurlHttpClient::InitGlobalState();
161+
#elif ENABLE_WINDOWS_IXML_HTTP_REQUEST_2_CLIENT
162+
LongRunningIXmlHttpRequest2HttpClient::InitCOM();
163+
#endif
164+
}
165+
166+
void CleanupStaticState() override
167+
{
168+
#if ENABLE_CURL_CLIENT
169+
LongRunningCurlHttpClient::CleanupGlobalState();
170+
#endif
171+
}
172+
};
173+
174+
TEST(HttpClientTest, TestHttpClientOverride)
175+
{
176+
auto request = CreateHttpRequest(Aws::String("http://127.0.0.1:8778"),
51177
HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
52-
auto httpClient = CreateHttpClient(Aws::Client::ClientConfiguration());
178+
request->SetHeaderValue("WaitSeconds", "5");
179+
Aws::Client::ClientConfiguration config;
180+
config.requestTimeoutMs = 1000; // http server wait 4 seconds to respond
181+
auto httpClient = CreateHttpClient(config);
53182
auto response = httpClient->MakeRequest(request);
54-
ASSERT_NE(nullptr, response);
183+
EXPECT_NE(nullptr, response);
55184
ASSERT_TRUE(response->HasClientError());
56185
ASSERT_EQ(CoreErrors::NETWORK_CONNECTION, response->GetClientErrorType());
57-
ASSERT_EQ(Aws::Http::HttpResponseCode::REQUEST_NOT_MADE, response->GetResponseCode());
186+
EXPECT_EQ(Aws::Http::HttpResponseCode::REQUEST_NOT_MADE, response->GetResponseCode());
187+
188+
// With custom HTTP client factory, the request timeout is 10 seconds for each HTTP client.
189+
SetHttpClientFactory(Aws::MakeShared<MockCustomHttpClientFactory>(ALLOCATION_TAG));
190+
request = CreateHttpRequest(Aws::String("http://127.0.0.1:8778"),
191+
HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
192+
request->SetHeaderValue("WaitSeconds", "5");
193+
httpClient = CreateHttpClient(config);
194+
response = httpClient->MakeRequest(request);
195+
EXPECT_NE(nullptr, response);
196+
ASSERT_FALSE(response->HasClientError());
197+
EXPECT_EQ(Aws::Http::HttpResponseCode::OK, response->GetResponseCode());
198+
199+
CleanupHttp();
200+
InitHttp();
58201
}
59202

203+
//Test CURL HTTP Client specific Settings.
204+
#if ENABLE_CURL_CLIENT
205+
#include <aws/core/platform/FileSystem.h>
206+
#include <aws/core/utils/DateTime.h>
207+
#include <unistd.h>
208+
#include <signal.h>
209+
#include <stdlib.h>
210+
#include <thread>
211+
60212
TEST(CURLHttpClientTest, TestConnectionTimeout)
61213
{
62214
auto request = CreateHttpRequest(Aws::String("https://8.8.8.8:53"),//unless 8.8.8.8 is localhost, it's unlikely to succeed.
@@ -88,22 +240,6 @@ TEST(CURLHttpClientTest, TestHttpRequestTimeout)
88240
EXPECT_TRUE(response->GetClientErrorMessage().find("curlCode: 28") == 0);
89241
}
90242

91-
TEST(CURLHttpClientTest, TestHttpLowSpeedTimeout)
92-
{
93-
auto request = CreateHttpRequest(Aws::String("http://127.0.0.1:8778"),
94-
HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
95-
request->SetHeaderValue("WaitSeconds", "2");
96-
Aws::Client::ClientConfiguration config;
97-
config.requestTimeoutMs = 1000; // http server wait 2 seconds to respond
98-
auto httpClient = CreateHttpClient(config);
99-
auto response = httpClient->MakeRequest(request);
100-
EXPECT_NE(nullptr, response);
101-
ASSERT_TRUE(response->HasClientError());
102-
ASSERT_EQ(CoreErrors::NETWORK_CONNECTION, response->GetClientErrorType());
103-
EXPECT_EQ(Aws::Http::HttpResponseCode::REQUEST_NOT_MADE, response->GetResponseCode());
104-
EXPECT_TRUE(response->GetClientErrorMessage().find("curlCode: 28") == 0);
105-
}
106-
107243
TEST(CURLHttpClientTest, TestHttpRequestTimeoutBeforeFinishing)
108244
{
109245
auto request = CreateHttpRequest(Aws::String("http://127.0.0.1:8778"),
@@ -136,6 +272,6 @@ TEST(CURLHttpClientTest, TestHttpRequestWorksFine)
136272
EXPECT_EQ(Aws::Http::HttpResponseCode::OK, response->GetResponseCode());
137273
EXPECT_EQ("", response->GetClientErrorMessage());
138274
}
139-
#endif // ENABLE_CURL_CLIENT && ENABLE_HTTP_CLIENT_TESTING
140-
275+
#endif // ENABLE_CURL_CLIENT
276+
#endif // ENABLE_HTTP_CLIENT_TESTING
141277
#endif // NO_HTTP_CLIENT

aws-cpp-sdk-core/include/aws/core/http/HttpTypes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ namespace Aws
3535
*/
3636
enum class TransferLibType
3737
{
38-
DEFAULT_CLIENT,
38+
DEFAULT_CLIENT = 0,
3939
CURL_CLIENT,
4040
WIN_INET_CLIENT,
4141
WIN_HTTP_CLIENT

aws-cpp-sdk-core/include/aws/core/http/curl/CurlHttpClient.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ class AWS_CORE_API CurlHttpClient: public HttpClient
4040
static void InitGlobalState();
4141
static void CleanupGlobalState();
4242

43+
protected:
44+
/**
45+
* Override any configuration on CURL handle for each request before sending.
46+
* The usage is to have a subclass of CurlHttpClient and have your own implementation of this function to configure whatever you want on CURL handle.
47+
*/
48+
virtual void OverrideOptionsOnConnectionHandle(CURL*) const {}
49+
4350
private:
4451
mutable CurlHandleContainer m_curlHandleContainer;
4552
bool m_isUsingProxy;

aws-cpp-sdk-core/include/aws/core/http/windows/IXmlHttpRequest2HttpClient.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ namespace Aws
5454
*/
5555
virtual bool SupportsChunkedTransferEncoding() const override { return false; }
5656

57+
protected:
58+
/**
59+
* Override any configuration on request handle.
60+
* The usage is override this function in the subclass to configure whatever you want on request handle.
61+
*/
62+
virtual void OverrideOptionsOnRequestHandle(const HttpRequestComHandle&) const {}
63+
5764
private:
5865
void FillClientSettings(const HttpRequestComHandle&) const;
5966

aws-cpp-sdk-core/include/aws/core/http/windows/WinSyncHttpClient.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@ namespace Aws
6767
* Return call from implementation specific openrequest call.
6868
*/
6969
void* AllocateWindowsHttpRequest(const std::shared_ptr<Aws::Http::HttpRequest>& request, void* connection) const;
70+
/**
71+
* Override any configuration on connection handle.
72+
* The usage is override this function in the subclass to configure whatever you want on connection handle.
73+
*/
74+
virtual void OverrideOptionsOnConnectionHandle(void*) const {}
75+
/**
76+
* Override any configuration on request handle.
77+
* The usage is override this function in the subclass to configure whatever you want on request handle.
78+
*/
79+
virtual void OverrideOptionsOnRequestHandle(void*) const {}
7080
/**
7181
* config flag for whether or not to tell apis to allow redirects.
7282
*/

aws-cpp-sdk-core/source/http/curl/CurlHttpClient.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ std::shared_ptr<HttpResponse> CurlHttpClient::MakeRequest(const std::shared_ptr<
606606
curl_easy_setopt(connectionHandle, CURLOPT_SEEKFUNCTION, SeekBody);
607607
curl_easy_setopt(connectionHandle, CURLOPT_SEEKDATA, &readContext);
608608
}
609+
OverrideOptionsOnConnectionHandle(connectionHandle);
609610
Aws::Utils::DateTime startTransmissionTime = Aws::Utils::DateTime::Now();
610611
CURLcode curlResponseCode = curl_easy_perform(connectionHandle);
611612
bool shouldContinueRequest = ContinueRequest(*request);

aws-cpp-sdk-core/source/http/windows/IXmlHttpRequest2HttpClient.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,8 @@ namespace Aws
392392
}
393393
}
394394

395+
OverrideOptionsOnRequestHandle(requestHandle);
396+
395397
if (writeLimiter)
396398
{
397399
writeLimiter->ApplyAndPayForCost(request->GetSize());

aws-cpp-sdk-core/source/http/windows/WinSyncHttpClient.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,11 +278,13 @@ std::shared_ptr<HttpResponse> WinSyncHttpClient::MakeRequest(const std::shared_p
278278
}
279279

280280
connection = m_connectionPoolMgr->AcquireConnectionForHost(uriRef.GetAuthority(), uriRef.GetPort());
281+
OverrideOptionsOnConnectionHandle(connection);
281282
AWS_LOGSTREAM_DEBUG(GetLogTag(), "Acquired connection " << connection);
282283

283284
hHttpRequest = AllocateWindowsHttpRequest(request, connection);
284285

285286
AddHeadersToRequest(request, hHttpRequest);
287+
OverrideOptionsOnRequestHandle(hHttpRequest);
286288
if (DoSendRequest(hHttpRequest) && StreamPayloadToRequest(request, hHttpRequest, writeLimiter))
287289
{
288290
success = BuildSuccessResponse(request, response, hHttpRequest, readLimiter);

testing-resources/include/aws/testing/mocks/aws/client/MockAWSClient.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ class AmazonWebServiceRequestMock : public Aws::AmazonWebServiceRequest
4949
class CountedRetryStrategy : public Aws::Client::DefaultRetryStrategy
5050
{
5151
public:
52-
CountedRetryStrategy() : m_attemptedRetries(0), m_maxRetries(std::numeric_limits<long>::max()) {}
53-
CountedRetryStrategy(long maxRetires) : m_attemptedRetries(0), m_maxRetries(maxRetires <= 0 ? std::numeric_limits<long>::max() : maxRetires) {}
52+
CountedRetryStrategy() : m_attemptedRetries(0), m_maxRetries((std::numeric_limits<long>::max)()) {}
53+
CountedRetryStrategy(long maxRetires) : m_attemptedRetries(0), m_maxRetries(maxRetires <= 0 ? (std::numeric_limits<long>::max)() : maxRetires) {}
5454

5555
bool ShouldRetry(const Aws::Client::AWSError<Aws::Client::CoreErrors>& error, long attemptedRetries) const override
5656
{

0 commit comments

Comments
 (0)