1+ /* *
2+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+ * SPDX-License-Identifier: Apache-2.0.
4+ */
5+
6+ #include < aws/testing/AwsTestHelpers.h>
7+ #include < aws/testing/AwsCppSdkGTestSuite.h>
8+ #include < aws/core/Aws.h>
9+ #include < aws/s3/S3Client.h>
10+ #include < aws/s3/model/HeadBucketRequest.h>
11+ #include < aws/core/auth/AWSCredentials.h>
12+ #include < aws/testing/mocks/http/MockHttpClient.h>
13+ #include < aws/core/client/RetryStrategy.h>
14+ #include < aws/core/utils/FileSystemUtils.h>
15+ #include < aws/core/config/ConfigAndCredentialsCacheManager.h>
16+ #include < aws/core/platform/Environment.h>
17+ #include < aws/testing/platform/PlatformTesting.h>
18+
19+ using namespace Aws ;
20+ using namespace Aws ::S3;
21+ using namespace Aws ::S3::Model;
22+ using namespace Aws ::Auth;
23+ using namespace Aws ::Client;
24+ using namespace Aws ::Http;
25+ using namespace Aws ::Utils;
26+ using namespace Aws ::Config;
27+ using namespace Aws ::Environment;
28+
29+ const char * ALLOCATION_TAG = " EndpointResolverIntegrationTest" ;
30+
31+ class S3TestClient : public S3Client
32+ {
33+ public:
34+ template <typename ...Args>
35+ S3TestClient (Args&& ...args): S3Client(std::forward<Args>(args)...){}
36+
37+ XmlOutcome MakeRequest (const Aws::Http::URI& uri,
38+ const Aws::AmazonWebServiceRequest& request,
39+ Aws::Http::HttpMethod method = Aws::Http::HttpMethod::HTTP_POST,
40+ const char * signerName = Aws::Auth::SIGV4_SIGNER,
41+ const char * signerRegionOverride = nullptr ,
42+ const char * signerServiceNameOverride = nullptr ) const
43+ {
44+ return S3Client::MakeRequest (uri, request, method, signerName, signerRegionOverride, signerServiceNameOverride);
45+ }
46+
47+ XmlOutcome MakeRequest (const Aws::Http::URI& uri,
48+ Http::HttpMethod method = Http::HttpMethod::HTTP_POST,
49+ const char * signerName = Aws::Auth::SIGV4_SIGNER,
50+ const char * requestName = " " ,
51+ const char * signerRegionOverride = nullptr ,
52+ const char * signerServiceNameOverride = nullptr ) const
53+ {
54+ return S3Client::MakeRequest (uri, method, signerName, requestName, signerRegionOverride, signerServiceNameOverride);
55+ }
56+
57+ private:
58+ S3TestClient () = default ;
59+ };
60+
61+ class NoRetry : public RetryStrategy
62+ {
63+ public:
64+ bool ShouldRetry (const AWSError<CoreErrors>& error, long attemptedRetries) const override
65+ {
66+ AWS_UNREFERENCED_PARAM (error);
67+ AWS_UNREFERENCED_PARAM (attemptedRetries);
68+ return false ;
69+ }
70+
71+ long CalculateDelayBeforeNextRetry (const AWSError<CoreErrors>& error, long attemptedRetries) const override
72+ {
73+ AWS_UNREFERENCED_PARAM (error);
74+ AWS_UNREFERENCED_PARAM (attemptedRetries);
75+ return 0 ;
76+ }
77+
78+ const char * GetStrategyName () const override {
79+ return " standard" ;
80+ }
81+ };
82+
83+ class EndpointResolverIntegrationTest : public Aws ::Testing::AwsCppSdkGTestSuite
84+ {
85+ protected:
86+ void SetUp () override
87+ {
88+ // Clear environment variables
89+ Aws::Environment::EnvironmentRAII setEnv{{
90+ {" AWS_ENDPOINT_URL" , " " },
91+ {" AWS_ENDPOINT_URL_S3" , " " },
92+ {" AWS_IGNORE_CONFIGURED_ENDPOINT_URLS" , " " },
93+ {" AWS_CONFIG_FILE" , " " }
94+ }};
95+
96+ // Set up mock HTTP client
97+ _mockClientFactory = MakeShared<MockHttpClientFactory>(ALLOCATION_TAG);
98+ _mockHttpClient = MakeShared<MockHttpClient>(ALLOCATION_TAG);
99+ _mockClientFactory->SetClient (_mockHttpClient);
100+ SetHttpClientFactory (_mockClientFactory);
101+ }
102+
103+ void TearDown () override
104+ {
105+ // Clean up mock clients
106+ _mockClientFactory.reset ();
107+ _mockHttpClient.reset ();
108+ }
109+
110+ void SetupMockResponse ()
111+ {
112+ auto mockRequest = MakeShared<Standard::StandardHttpRequest>(ALLOCATION_TAG, " mockuri" , HttpMethod::HTTP_HEAD);
113+ mockRequest->SetResponseStreamFactory ([]() -> IOStream* {
114+ return Aws::New<StringStream>(ALLOCATION_TAG, " response-string" , std::ios_base::in | std::ios_base::binary);
115+ });
116+ auto mockResponse = MakeShared<Standard::StandardHttpResponse>(ALLOCATION_TAG, mockRequest);
117+ mockResponse->SetResponseCode (HttpResponseCode::OK);
118+ _mockHttpClient->AddResponseToReturn (mockResponse);
119+ }
120+
121+ const Aws::Http::HttpRequest& ExecuteHeadBucketRequest (const S3ClientConfiguration& config)
122+ {
123+ SetupMockResponse ();
124+
125+ AWSCredentials credentials{" mock" , " credentials" };
126+ const auto epProvider = MakeShared<S3EndpointProvider>(ALLOCATION_TAG);
127+ auto testClient = MakeShared<S3TestClient>(ALLOCATION_TAG, credentials, epProvider, config);
128+
129+ HeadBucketRequest request;
130+ request.SetBucket (" test-bucket" );
131+
132+ auto response = testClient->HeadBucket (request);
133+ return _mockHttpClient->GetMostRecentHttpRequest ();
134+ }
135+
136+ std::shared_ptr<MockHttpClient> _mockHttpClient;
137+ std::shared_ptr<MockHttpClientFactory> _mockClientFactory;
138+ };
139+
140+ TEST_F (EndpointResolverIntegrationTest, DefaultResolution)
141+ {
142+ S3ClientConfiguration s3Config;
143+ s3Config.region = " us-east-1" ;
144+ s3Config.profileName = " test-default" ;
145+ s3Config.retryStrategy = MakeShared<NoRetry>(ALLOCATION_TAG);
146+
147+ const auto & seenRequest = ExecuteHeadBucketRequest (s3Config);
148+
149+ EXPECT_EQ (" https://test-bucket.s3.us-east-1.amazonaws.com" , seenRequest.GetUri ().GetURIString ());
150+ }
151+
152+ TEST_F (EndpointResolverIntegrationTest, CodeProvidedEndpoint)
153+ {
154+ S3ClientConfiguration s3Config;
155+ s3Config.region = " us-east-1" ;
156+ s3Config.endpointOverride = " https://code-provided.example.com" ;
157+ s3Config.retryStrategy = MakeShared<NoRetry>(ALLOCATION_TAG);
158+
159+ const auto & seenRequest = ExecuteHeadBucketRequest (s3Config);
160+
161+ EXPECT_EQ (" https://test-bucket.code-provided.example.com" , seenRequest.GetUri ().GetURIString ());
162+ }
163+
164+ TEST_F (EndpointResolverIntegrationTest, ServiceSpecificEnvironmentVariable)
165+ {
166+ Aws::Environment::EnvironmentRAII setEnv{{
167+ {" AWS_ENDPOINT_URL_S3" , " https://custom-s3.example.com" }
168+ }};
169+
170+ S3ClientConfiguration s3Config;
171+ s3Config.region = " us-east-1" ;
172+ s3Config.retryStrategy = MakeShared<NoRetry>(ALLOCATION_TAG);
173+
174+ const auto & seenRequest = ExecuteHeadBucketRequest (s3Config);
175+
176+ EXPECT_EQ (" https://test-bucket.custom-s3.example.com" , seenRequest.GetUri ().GetURIString ());
177+ }
178+
179+ TEST_F (EndpointResolverIntegrationTest, GlobalEnvironmentVariable)
180+ {
181+ Aws::Environment::EnvironmentRAII setEnv{{
182+ {" AWS_ENDPOINT_URL" , " https://global-env.example.com" }
183+ }};
184+
185+ S3ClientConfiguration s3Config;
186+ s3Config.region = " us-east-1" ;
187+ s3Config.retryStrategy = MakeShared<NoRetry>(ALLOCATION_TAG);
188+
189+ const auto & seenRequest = ExecuteHeadBucketRequest (s3Config);
190+
191+ EXPECT_EQ (" https://test-bucket.global-env.example.com" , seenRequest.GetUri ().GetURIString ());
192+ }
193+
194+ TEST_F (EndpointResolverIntegrationTest, ServiceSpecificProfile)
195+ {
196+ TempFile configFile (std::ios_base::out | std::ios_base::trunc);
197+ ASSERT_TRUE (configFile.good ());
198+
199+ configFile << " [profile test-profile-service]\n " ;
200+ configFile << " services = test-services\n " ;
201+ configFile << " \n [services test-services]\n " ;
202+ configFile << " s3 =\n " ;
203+ configFile << " endpoint_url = https://s3-profile.example.com\n " ;
204+ configFile.flush ();
205+
206+ Aws::Environment::EnvironmentRAII setEnv{{
207+ {" AWS_CONFIG_FILE" , configFile.GetFileName ()}
208+ }};
209+ ReloadCachedConfigFile ();
210+
211+ S3ClientConfiguration s3Config;
212+ s3Config.region = " us-east-1" ;
213+ s3Config.profileName = " test-profile-service" ;
214+ s3Config.retryStrategy = MakeShared<NoRetry>(ALLOCATION_TAG);
215+
216+ const auto & seenRequest = ExecuteHeadBucketRequest (s3Config);
217+
218+ EXPECT_EQ (" https://test-bucket.s3-profile.example.com" , seenRequest.GetUri ().GetURIString ());
219+ ReloadCachedConfigFile ();
220+ }
221+
222+ TEST_F (EndpointResolverIntegrationTest, GlobalProfile)
223+ {
224+ TempFile configFile (std::ios_base::out | std::ios_base::trunc);
225+ ASSERT_TRUE (configFile.good ());
226+
227+ configFile << " [profile test-profile-global]\n " ;
228+ configFile << " endpoint_url = https://global-profile.example.com\n " ;
229+ configFile.flush ();
230+
231+ Aws::Environment::EnvironmentRAII envGuard{{
232+ {" AWS_CONFIG_FILE" , configFile.GetFileName ()}
233+ }};
234+ ReloadCachedConfigFile ();
235+
236+ S3ClientConfiguration s3Config;
237+ s3Config.region = " us-east-1" ;
238+ s3Config.profileName = " test-profile-global" ;
239+ s3Config.retryStrategy = MakeShared<NoRetry>(ALLOCATION_TAG);
240+
241+ const auto & seenRequest = ExecuteHeadBucketRequest (s3Config);
242+
243+ EXPECT_EQ (" https://test-bucket.global-profile.example.com" , seenRequest.GetUri ().GetURIString ());
244+
245+ ReloadCachedConfigFile ();
246+ }
0 commit comments