/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include #include #include #include #include #include #include using namespace Aws::Utils; using namespace Aws::Client; using namespace Aws::Http; using namespace Aws::Http::Standard; using namespace Aws::Internal; static const char ALLOCATION_TAG[] = "AWSHttpResourceClientTest"; namespace Aws { namespace Client { AWS_CORE_API Aws::String ComputeUserAgentString(); } } namespace { class AWSHttpResourceClientTest : public Aws::Testing::AwsCppSdkGTestSuite { public: AWSHttpResourceClientTest() { // Default retry strategy set maxRetry to be 4, and interval factor to be 1000ms // In order to speed up our unit tests, we reset it here. clientConfig.retryStrategy = Aws::MakeShared(ALLOCATION_TAG, 2/*maxRetry*/, 1/*Interval factor between retry (exp backoff), first retry wait 1ms, second retry wait 2ms*/); } protected: std::shared_ptr mockHttpClient; std::shared_ptr mockHttpClientFactory; ClientConfiguration clientConfig; void SetUp() { mockHttpClient = Aws::MakeShared(ALLOCATION_TAG); mockHttpClientFactory = Aws::MakeShared(ALLOCATION_TAG); mockHttpClientFactory->SetClient(mockHttpClient); SetHttpClientFactory(mockHttpClientFactory); } void TearDown() { mockHttpClient = nullptr; mockHttpClientFactory = nullptr; CleanupHttp(); InitHttp(); } void addGetCredentialsSecurelyMock() { std::shared_ptr tokenRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/api/token"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr tokenResponse = Aws::MakeShared(ALLOCATION_TAG, tokenRequest); tokenResponse->SetResponseCode(HttpResponseCode::OK); tokenResponse->GetResponseBody() << "tokenstring"; mockHttpClient->Reset(); std::shared_ptr profileRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr profileResponse = Aws::MakeShared(ALLOCATION_TAG, profileRequest); profileResponse->SetResponseCode(HttpResponseCode::OK); profileResponse->GetResponseBody() << "profilestring"; mockHttpClient->Reset(); std::shared_ptr credsRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr credsResponse = Aws::MakeShared(ALLOCATION_TAG, credsRequest); credsResponse->SetResponseCode(HttpResponseCode::OK); credsResponse->GetResponseBody() << "credsstring"; mockHttpClient->Reset(); mockHttpClient->AddResponseToReturn(tokenResponse); mockHttpClient->AddResponseToReturn(profileResponse); mockHttpClient->AddResponseToReturn(credsResponse); } }; static const char EC2_IMDS_TOKEN_TTL_HEADER[] = "x-aws-ec2-metadata-token-ttl-seconds"; static const char EC2_IMDS_TOKEN_HEADER[] = "x-aws-ec2-metadata-token"; TEST_F(AWSHttpResourceClientTest, TestSetErrorMarshallerCorrectly) { auto awsHttpResourceClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, ALLOCATION_TAG); ASSERT_EQ(nullptr, awsHttpResourceClient->GetErrorMarshaller()); awsHttpResourceClient->SetErrorMarshaller(Aws::MakeUnique(ALLOCATION_TAG)); ASSERT_NE(nullptr, awsHttpResourceClient->GetErrorMarshaller()); } TEST_F(AWSHttpResourceClientTest, TestErrorMarshallerWorksWithSTSRetryStrategyExpectedly) { Aws::Client::ClientConfiguration config; Aws::Vector retryableErrors; retryableErrors.push_back("IDPCommunicationError"); retryableErrors.push_back("InvalidIdentityToken"); config.retryStrategy = Aws::MakeShared(ALLOCATION_TAG, retryableErrors, 3/*max retries*/); auto awsHttpResourceClient = Aws::MakeShared(ALLOCATION_TAG, config, ALLOCATION_TAG); awsHttpResourceClient->SetErrorMarshaller(Aws::MakeUnique(ALLOCATION_TAG)); // Retry 3 times without response, nullptr response is treated as NETWORK_CONNECTION_ERROR and is retryable Aws::String result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", nullptr/*authToken*/); ASSERT_EQ(4u, mockHttpClient->GetAllRequestsMade().size()); ASSERT_EQ("", result); std::shared_ptr request = CreateHttpRequest(URI("https://www.uri.com/path/to/res"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); Aws::String retryError = "IDPCommunicationErrorThe resource you requested does not exist/path/to/resource.jpg4442587FB7D0A2F9"; std::shared_ptr retryableResponse = Aws::MakeShared(ALLOCATION_TAG, request); retryableResponse->SetResponseCode(HttpResponseCode::BAD_REQUEST); retryableResponse->GetResponseBody() << retryError; Aws::String retryError2 = "InvalidIdentityTokenThe resource you requested does not exist/path/to/resource.jpg4442587FB7D0A2F9"; std::shared_ptr retryableResponse2 = Aws::MakeShared(ALLOCATION_TAG, request); retryableResponse2->SetResponseCode(HttpResponseCode::BAD_REQUEST); retryableResponse2->GetResponseBody() << retryError2; Aws::String nonRetryError = "OtherErrorThe resource you requested does not exist/path/to/resource.jpg4442587FB7D0A2F9"; std::shared_ptr nonRetryResponse = Aws::MakeShared(ALLOCATION_TAG, request); nonRetryResponse->SetResponseCode(HttpResponseCode::BAD_REQUEST); nonRetryResponse->GetResponseBody() << nonRetryError; //Made up credentials from https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html Aws::String goodXml = "AQoDYXdzEE0a8ANXXXXXXXXNO1ewxE5TijQyp+IEXAMPLEwJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY2014-10-24T23:00:23ZASgeIAIOSFODNN7EXAMPLE"; std::shared_ptr goodResponse = Aws::MakeShared(ALLOCATION_TAG, request); goodResponse->SetResponseCode(HttpResponseCode::OK); goodResponse->GetResponseBody() << goodXml; // Encounter 1 non retryable error mockHttpClient->Reset(); mockHttpClient->AddResponseToReturn(nonRetryResponse); result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", nullptr/*authToken*/); ASSERT_EQ(1u, mockHttpClient->GetAllRequestsMade().size()); ASSERT_EQ("", result); // Encounter 2 IDPCommunicationError and InvalidIdentityToken in response and 1 other error, will retry 2 times. mockHttpClient->Reset(); mockHttpClient->AddResponseToReturn(retryableResponse); mockHttpClient->AddResponseToReturn(retryableResponse2); nonRetryResponse->GetResponseBody() << nonRetryError; // response has been read out. need to refill. mockHttpClient->AddResponseToReturn(nonRetryResponse); result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", nullptr/*authToken*/); ASSERT_EQ(3u, mockHttpClient->GetAllRequestsMade().size()); ASSERT_EQ("", result); // Encounter 2 retryable errors and 1 good response, will retry 2 times. mockHttpClient->Reset(); retryableResponse->GetResponseBody() << retryError; retryableResponse2->GetResponseBody() << retryError; mockHttpClient->AddResponseToReturn(retryableResponse); mockHttpClient->AddResponseToReturn(retryableResponse2); mockHttpClient->AddResponseToReturn(goodResponse); result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", nullptr/*authToken*/); ASSERT_EQ(3u, mockHttpClient->GetAllRequestsMade().size()); ASSERT_EQ(goodXml, result); } TEST_F(AWSHttpResourceClientTest, TestAWSHttpResourceClientWithPermanentNullResponse) { auto awsHttpResourceClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, ALLOCATION_TAG); Aws::String result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", ""/*authToken*/); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://www.uri.com/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("www.uri.com", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_STREQ(ComputeUserAgentString().c_str(), mockRequest.GetUserAgent().c_str()); ASSERT_EQ("", result); // 1 initial request + 2 retries should be done when request can't be made at all ASSERT_EQ(3u, mockHttpClient->GetAllRequestsMade().size()); } TEST_F(AWSHttpResourceClientTest, TestAWSHttpResourceClientWithEmptyResponse) { // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr request = CreateHttpRequest(URI("http://www.uri.com/path/to/res"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr response = Aws::MakeShared(ALLOCATION_TAG, request); response->SetResponseCode(HttpResponseCode::OK); response->GetResponseBody() << ""; mockHttpClient->AddResponseToReturn(response); auto awsHttpResourceClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, ALLOCATION_TAG); Aws::String result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", ""/*authToken*/); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://www.uri.com/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("www.uri.com", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", result); // No retries should be made ASSERT_EQ(1u, mockHttpClient->GetAllRequestsMade().size()); } TEST_F(AWSHttpResourceClientTest, TestAWSHttpResourceClientWithHttpBadRequest) { // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr request = CreateHttpRequest(URI("http://www.uri.com/path/to/res"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr response = Aws::MakeShared(ALLOCATION_TAG, request); response->SetResponseCode(HttpResponseCode::BAD_REQUEST); response->GetResponseBody() << "{ \"Resource\": \"TestResource\" }"; mockHttpClient->AddResponseToReturn(response); auto awsHttpResourceClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, ALLOCATION_TAG); Aws::String result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", ""/*authToken*/); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://www.uri.com/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("www.uri.com", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", result); // No retries should be made for status code < 500 ASSERT_EQ(1u, mockHttpClient->GetAllRequestsMade().size()); } TEST_F(AWSHttpResourceClientTest, TestAWSHttpResourceClientWithPermanentInternalServerError) { // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr request = CreateHttpRequest(URI("http://www.uri.com/path/to/res"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr response = Aws::MakeShared(ALLOCATION_TAG, request); response->SetResponseCode(HttpResponseCode::INTERNAL_SERVER_ERROR); response->GetResponseBody() << "{ \"Resource\": \"TestResource\" }"; // Add enough to respond to 1 initial request and 2 retries for (int i = 0; i < 3; ++i) { mockHttpClient->AddResponseToReturn(response); } auto awsHttpResourceClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, ALLOCATION_TAG); Aws::String result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", ""/*authToken*/); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://www.uri.com/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("www.uri.com", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", result); // 1 initial request + 2 retries should be done for status code >= 500 ASSERT_EQ(3u, mockHttpClient->GetAllRequestsMade().size()); } TEST_F(AWSHttpResourceClientTest, TestAWSHttpResourceClientGetExpectedResource) { // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr request = CreateHttpRequest(URI("http://www.uri.com/path/to/res"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr response = Aws::MakeShared(ALLOCATION_TAG, request); response->SetResponseCode(HttpResponseCode::OK); response->GetResponseBody() << "{ \"Resource\": \"TestResource\" }"; mockHttpClient->AddResponseToReturn(response); auto awsHttpResourceClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, ALLOCATION_TAG); Aws::String result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", ""/*authToken*/); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://www.uri.com/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("www.uri.com", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("{ \"Resource\": \"TestResource\" }", result); // No retries should be made ASSERT_EQ(1u, mockHttpClient->GetAllRequestsMade().size()); } TEST_F(AWSHttpResourceClientTest, TestAWSHttpResourceClientWithSuccessResponseAfterOneClientError) { // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr request = CreateHttpRequest(URI("http://www.uri.com/path/to/res"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr response1 = Aws::MakeShared(ALLOCATION_TAG, request); //response code is REQUEST_NOT_MADE, indicates error, and we set this as client error. response1->SetClientErrorType(Aws::Client::CoreErrors::NETWORK_CONNECTION); response1->SetClientErrorMessage("Mock Client Error"); mockHttpClient->AddResponseToReturn(response1); std::shared_ptr response2 = Aws::MakeShared(ALLOCATION_TAG, request); response2->SetResponseCode(HttpResponseCode::OK); response2->GetResponseBody() << "{ \"Resource\": \"TestResource\" }"; mockHttpClient->AddResponseToReturn(response2); auto awsHttpResourceClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, ALLOCATION_TAG); Aws::String result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", ""/*authToken*/); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://www.uri.com/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("www.uri.com", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("{ \"Resource\": \"TestResource\" }", result); ASSERT_EQ(2u, mockHttpClient->GetAllRequestsMade().size()); } TEST_F(AWSHttpResourceClientTest, TestAWSHttpResourceClientWithBadRequestResponseAfterOneResponseError) { // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr request = CreateHttpRequest(URI("http://www.uri.com/path/to/res"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr response1 = Aws::MakeShared(ALLOCATION_TAG, request); //response code is REQUEST_NOT_MADE, indicates error, and we set this as unparsable error. mockHttpClient->AddResponseToReturn(response1); auto response2 = Aws::MakeShared(ALLOCATION_TAG, request); response2->SetResponseCode(HttpResponseCode::BAD_REQUEST); response2->GetResponseBody() << "Any test string"; mockHttpClient->AddResponseToReturn(response2); auto awsHttpResourceClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, ALLOCATION_TAG); Aws::String result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", ""/*authToken*/); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://www.uri.com/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("www.uri.com", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", result); ASSERT_EQ(2u, mockHttpClient->GetAllRequestsMade().size()); } TEST_F(AWSHttpResourceClientTest, TestAWSHttpResourceClientWithSuccessResponseAfterOneInternalServerError) { // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr request = CreateHttpRequest(URI("http://www.uri.com/path/to/res"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr response1 = Aws::MakeShared(ALLOCATION_TAG, request); response1->SetResponseCode(HttpResponseCode::INTERNAL_SERVER_ERROR); response1->GetResponseBody() << "{ \"Resource\": \"TestResource\" }"; mockHttpClient->AddResponseToReturn(response1); auto response2 = Aws::MakeShared(ALLOCATION_TAG, request); response2->SetResponseCode(HttpResponseCode::OK); response2->GetResponseBody() << "{ \"Resource\": \"TestResource\" }"; mockHttpClient->AddResponseToReturn(response2); auto awsHttpResourceClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, ALLOCATION_TAG); Aws::String result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", ""/*authToken*/); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://www.uri.com/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("www.uri.com", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("{ \"Resource\": \"TestResource\" }", result); // 1 initial request + 1 retry ASSERT_EQ(2u, mockHttpClient->GetAllRequestsMade().size()); } TEST_F(AWSHttpResourceClientTest, TestAWSHttpResourceClientWithBadRequestResponseAfterOneInternalServerError) { // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr request = CreateHttpRequest(URI("http://www.uri.com/path/to/res"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr response1 = Aws::MakeShared(ALLOCATION_TAG, request); response1->SetResponseCode(HttpResponseCode::INTERNAL_SERVER_ERROR); response1->GetResponseBody() << "{ \"Resource\": \"TestResource\" }"; mockHttpClient->AddResponseToReturn(response1); auto response2 = Aws::MakeShared(ALLOCATION_TAG, request); response2->SetResponseCode(HttpResponseCode::BAD_REQUEST); response2->GetResponseBody() << "Any test string"; mockHttpClient->AddResponseToReturn(response2); auto awsHttpResourceClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, ALLOCATION_TAG); Aws::String result = awsHttpResourceClient->GetResource("http://www.uri.com", "/path/to/res", ""/*authToken*/); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://www.uri.com/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("www.uri.com", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", result); // 1 initial request + 1 retry ASSERT_EQ(2u, mockHttpClient->GetAllRequestsMade().size()); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientWithNullSecurityCredentialsStringResponse) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/iam/security-credentials", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientWithEmptySecurityCredentialsStringResponse) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr profileRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr profileResponse = Aws::MakeShared(ALLOCATION_TAG, profileRequest); profileResponse->SetResponseCode(HttpResponseCode::OK); profileResponse->GetResponseBody() << ""; mockHttpClient->AddResponseToReturn(profileResponse); auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/iam/security-credentials", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientWithMalformedSecurityCredentialsString) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr profileRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr profileResponse = Aws::MakeShared(ALLOCATION_TAG, profileRequest); profileResponse->SetResponseCode(HttpResponseCode::OK); profileResponse->GetResponseBody() << " \n \t "; mockHttpClient->AddResponseToReturn(profileResponse); auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/iam/security-credentials", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientWithBadHttpRequest) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr tokenRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/api/iam/token"), HttpMethod::HTTP_PUT, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr tokenResponse = Aws::MakeShared(ALLOCATION_TAG, tokenRequest); tokenResponse->SetResponseCode(HttpResponseCode::BAD_REQUEST); tokenResponse->GetResponseBody() << "Any test string"; mockHttpClient->AddResponseToReturn(tokenResponse); auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/latest/api/token", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequest.GetMethod()); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientGetExpectedCredentialsFromSecureServerWithRetry) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr tokenRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/api/token"), HttpMethod::HTTP_PUT, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr tokenResponse = Aws::MakeShared(ALLOCATION_TAG, tokenRequest); std::shared_ptr tokenResponseRetry = Aws::MakeShared(ALLOCATION_TAG, tokenRequest); tokenResponseRetry->SetResponseCode(HttpResponseCode::SERVICE_UNAVAILABLE); tokenResponseRetry->GetResponseBody() << "Throttling"; mockHttpClient->AddResponseToReturn(tokenResponseRetry); tokenResponse->SetResponseCode(HttpResponseCode::OK); tokenResponse->GetResponseBody() << "123321--=random token information xxx==="; mockHttpClient->AddResponseToReturn(tokenResponse); std::shared_ptr profileRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr profileResponse = Aws::MakeShared(ALLOCATION_TAG, profileRequest); std::shared_ptr profileResponseRetry = Aws::MakeShared(ALLOCATION_TAG, profileRequest); profileResponseRetry->SetResponseCode(HttpResponseCode::SERVICE_UNAVAILABLE); profileResponseRetry->GetResponseBody() << "Throttling"; mockHttpClient->AddResponseToReturn(profileResponseRetry); profileResponse->SetResponseCode(HttpResponseCode::OK); profileResponse->GetResponseBody() << "credentials"; mockHttpClient->AddResponseToReturn(profileResponse); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr credRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials/credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr credResponse = Aws::MakeShared(ALLOCATION_TAG, credRequest); credResponse->SetResponseCode(HttpResponseCode::OK); credResponse->GetResponseBody() << "{ \"AccessKeyId\": \"goodAccessKey\", \"SecretAccessKey\": \"goodSecretKey\", \"Token\": \"goodToken\" }"; mockHttpClient->AddResponseToReturn(credResponse); auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely(); auto mockRequests = mockHttpClient->GetAllRequestsMade(); ASSERT_EQ(5u, mockRequests.size()); ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequests[0].GetURIString()); ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequests[1].GetURIString()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequests[0].GetMethod()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequests[1].GetMethod()); ASSERT_EQ("21600", mockRequests[0].GetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER)); ASSERT_EQ("21600", mockRequests[1].GetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER)); ASSERT_EQ("123321--=random token information xxx===", mockRequests[2].GetHeaderValue(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[2].GetURIString()); ASSERT_EQ("123321--=random token information xxx===", mockRequests[3].GetHeaderValue(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[3].GetURIString()); ASSERT_EQ("123321--=random token information xxx===", mockRequests[4].GetHeaderValue(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials/credentials", mockRequests[4].GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequests[4].GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequests[4].GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/iam/security-credentials/credentials", mockRequests[4].GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequests[4].GetMethod()); ASSERT_EQ(cred, "{ \"AccessKeyId\": \"goodAccessKey\", \"SecretAccessKey\": \"goodSecretKey\", \"Token\": \"goodToken\" }"); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientFailedGettingCredentialsFromSecureServer) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr tokenRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/api/token"), HttpMethod::HTTP_PUT, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr tokenResponse = Aws::MakeShared(ALLOCATION_TAG, tokenRequest); tokenResponse->SetResponseCode(HttpResponseCode::BAD_REQUEST); tokenResponse->GetResponseBody() << "bad request info"; mockHttpClient->AddResponseToReturn(tokenResponse); auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely(); auto mockRequests = mockHttpClient->GetAllRequestsMade(); ASSERT_EQ(1u, mockRequests.size()); ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequests[0].GetURIString()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequests[0].GetMethod()); ASSERT_EQ("21600", mockRequests[0].GetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER)); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientFailedGettingCredentialsFromSecureServerWithExpiredToken) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr tokenRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/api/token"), HttpMethod::HTTP_PUT, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr tokenResponse = Aws::MakeShared(ALLOCATION_TAG, tokenRequest); tokenResponse->SetResponseCode(HttpResponseCode::OK); tokenResponse->GetResponseBody() << "123321--=random token information xxx==="; mockHttpClient->AddResponseToReturn(tokenResponse); std::shared_ptr profileRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr profileResponse = Aws::MakeShared(ALLOCATION_TAG, profileRequest); profileResponse->SetResponseCode(HttpResponseCode::UNAUTHORIZED); profileResponse->GetResponseBody() << "expired token"; mockHttpClient->AddResponseToReturn(profileResponse); auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely(); auto mockRequests = mockHttpClient->GetAllRequestsMade(); ASSERT_EQ(2u, mockRequests.size()); ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequests[0].GetURIString()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequests[0].GetMethod()); ASSERT_EQ("21600", mockRequests[0].GetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER)); ASSERT_EQ("123321--=random token information xxx===", mockRequests[1].GetHeaderValue(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[1].GetURIString()); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientFailedGettingCredentialsFromSecureServerWithTokenAndRetries) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr tokenRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/api/token"), HttpMethod::HTTP_PUT, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr tokenResponse = Aws::MakeShared(ALLOCATION_TAG, tokenRequest); tokenResponse->SetResponseCode(HttpResponseCode::OK); tokenResponse->GetResponseBody() << "123321--=random token information xxx==="; mockHttpClient->AddResponseToReturn(tokenResponse); std::shared_ptr profileRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); Aws::Vector> profileResponses(3, Aws::MakeShared(ALLOCATION_TAG, profileRequest)); for (auto& response: profileResponses) { response->SetResponseCode(HttpResponseCode::SERVICE_UNAVAILABLE); response->GetResponseBody() << "Throttling"; mockHttpClient->AddResponseToReturn(response); } auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely(); auto mockRequests = mockHttpClient->GetAllRequestsMade(); ASSERT_EQ(4u, mockRequests.size()); ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequests[0].GetURIString()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequests[0].GetMethod()); ASSERT_EQ("21600", mockRequests[0].GetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER)); ASSERT_EQ("123321--=random token information xxx===", mockRequests[1].GetHeaderValue(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("123321--=random token information xxx===", mockRequests[2].GetHeaderValue(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("123321--=random token information xxx===", mockRequests[3].GetHeaderValue(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[1].GetURIString()); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[2].GetURIString()); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[3].GetURIString()); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientSkipsCallForConfiguration) { clientConfig.disableIMDS = true; auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); ASSERT_EQ("", ec2MetadataClient->GetDefaultCredentialsSecurely()); #if !defined(DISABLE_IMDSV1) ASSERT_EQ("", ec2MetadataClient->GetDefaultCredentials()); #endif ASSERT_EQ("", ec2MetadataClient->GetCurrentRegion()); } #if !defined(DISABLE_IMDSV1) TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientFailedGettingCredentialsFromSecureServerWithRetries) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr tokenRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/api/token"), HttpMethod::HTTP_PUT, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); // max try 2 times Aws::Vector> tokenResponses(3, Aws::MakeShared(ALLOCATION_TAG, tokenRequest)); for (auto& response: tokenResponses) { response->SetResponseCode(HttpResponseCode::SERVICE_UNAVAILABLE); response->GetResponseBody() << "Throttling"; mockHttpClient->AddResponseToReturn(response); } // falling back to insecure way std::shared_ptr profileRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr profileResponse = Aws::MakeShared(ALLOCATION_TAG, profileRequest); profileResponse->SetResponseCode(HttpResponseCode::UNAUTHORIZED); // Do not send a body as IMDS also doesn't send one. Just the HTTP error code. mockHttpClient->AddResponseToReturn(profileResponse); auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely(); auto mockRequests = mockHttpClient->GetAllRequestsMade(); ASSERT_EQ(4u, mockRequests.size()); ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequests[0].GetURIString()); ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequests[1].GetURIString()); ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequests[2].GetURIString()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequests[0].GetMethod()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequests[1].GetMethod()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequests[2].GetMethod()); ASSERT_EQ("21600", mockRequests[0].GetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER)); ASSERT_EQ("21600", mockRequests[1].GetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER)); ASSERT_EQ("21600", mockRequests[2].GetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER)); ASSERT_FALSE(mockRequests[3].HasHeader(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[3].GetURIString()); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientGetExpectedCredentialsFromInSecureServerWithRetry) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr tokenRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/api/token"), HttpMethod::HTTP_PUT, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr tokenResponse = Aws::MakeShared(ALLOCATION_TAG, tokenRequest); tokenResponse->SetResponseCode(HttpResponseCode::NOT_FOUND); tokenResponse->GetResponseBody() << "error information"; // empty body will be treated as NETWORK_ERROR, hence retryable error. mockHttpClient->AddResponseToReturn(tokenResponse); std::shared_ptr profileRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr profileResponse = Aws::MakeShared(ALLOCATION_TAG, profileRequest); std::shared_ptr profileResponseRetry = Aws::MakeShared(ALLOCATION_TAG, profileRequest); profileResponseRetry->SetResponseCode(HttpResponseCode::SERVICE_UNAVAILABLE); profileResponseRetry->GetResponseBody() << "Throttling"; mockHttpClient->AddResponseToReturn(profileResponseRetry); profileResponse->SetResponseCode(HttpResponseCode::OK); profileResponse->GetResponseBody() << "credentials"; mockHttpClient->AddResponseToReturn(profileResponse); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr credRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials/credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr credResponse = Aws::MakeShared(ALLOCATION_TAG, credRequest); credResponse->SetResponseCode(HttpResponseCode::OK); credResponse->GetResponseBody() << "{ \"AccessKeyId\": \"goodAccessKey\", \"SecretAccessKey\": \"goodSecretKey\", \"Token\": \"goodToken\" }"; mockHttpClient->AddResponseToReturn(credResponse); auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely(); auto mockRequests = mockHttpClient->GetAllRequestsMade(); ASSERT_EQ(4u, mockRequests.size()); ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequests[0].GetURIString()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequests[0].GetMethod()); ASSERT_EQ("21600", mockRequests[0].GetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER)); ASSERT_FALSE(mockRequests[1].HasHeader(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[1].GetURIString()); ASSERT_FALSE(mockRequests[2].HasHeader(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[2].GetURIString()); ASSERT_FALSE(mockRequests[3].HasHeader(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials/credentials", mockRequests[3].GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequests[3].GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequests[3].GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/iam/security-credentials/credentials", mockRequests[3].GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequests[3].GetMethod()); ASSERT_EQ(cred, "{ \"AccessKeyId\": \"goodAccessKey\", \"SecretAccessKey\": \"goodSecretKey\", \"Token\": \"goodToken\" }"); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientFailedGettingCredentialsFromInSecureServer) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr tokenRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/api/token"), HttpMethod::HTTP_PUT, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr tokenResponse = Aws::MakeShared(ALLOCATION_TAG, tokenRequest); tokenResponse->SetResponseCode(HttpResponseCode::NOT_FOUND); tokenResponse->GetResponseBody() << "error information"; // empty body will be treated as NETWORK_ERROR, hence retryable error. mockHttpClient->AddResponseToReturn(tokenResponse); std::shared_ptr profileRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr profileResponse = Aws::MakeShared(ALLOCATION_TAG, profileRequest); profileResponse->SetResponseCode(HttpResponseCode::NOT_FOUND); profileResponse->GetResponseBody() << "profile not found"; mockHttpClient->AddResponseToReturn(profileResponse); auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely(); auto mockRequests = mockHttpClient->GetAllRequestsMade(); ASSERT_EQ(2u, mockRequests.size()); ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequests[0].GetURIString()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequests[0].GetMethod()); ASSERT_EQ("21600", mockRequests[0].GetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER)); ASSERT_FALSE(mockRequests[1].HasHeader(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[1].GetURIString()); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientFailedGettingCredentialsFromInSecureServerWithRetries) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr tokenRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/api/token"), HttpMethod::HTTP_PUT, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr tokenResponse = Aws::MakeShared(ALLOCATION_TAG, tokenRequest); tokenResponse->SetResponseCode(HttpResponseCode::NOT_FOUND); tokenResponse->GetResponseBody() << "error information"; // empty body will be treated as NETWORK_ERROR, hence retryable error. mockHttpClient->AddResponseToReturn(tokenResponse); std::shared_ptr profileRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/iam/security-credentials"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); Aws::Vector> profileResponses(3, Aws::MakeShared(ALLOCATION_TAG, profileRequest)); for (auto& response: profileResponses) { response->SetResponseCode(HttpResponseCode::SERVICE_UNAVAILABLE); response->GetResponseBody() << "Throttling"; mockHttpClient->AddResponseToReturn(response); } auto cred = ec2MetadataClient->GetDefaultCredentialsSecurely(); auto mockRequests = mockHttpClient->GetAllRequestsMade(); ASSERT_EQ(4u, mockRequests.size()); ASSERT_EQ("http://169.254.169.254/latest/api/token", mockRequests[0].GetURIString()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_PUT, mockRequests[0].GetMethod()); ASSERT_EQ("21600", mockRequests[0].GetHeaderValue(EC2_IMDS_TOKEN_TTL_HEADER)); ASSERT_FALSE(mockRequests[1].HasHeader(EC2_IMDS_TOKEN_HEADER)); ASSERT_FALSE(mockRequests[2].HasHeader(EC2_IMDS_TOKEN_HEADER)); ASSERT_FALSE(mockRequests[3].HasHeader(EC2_IMDS_TOKEN_HEADER)); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[1].GetURIString()); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[2].GetURIString()); ASSERT_EQ("http://169.254.169.254/latest/meta-data/iam/security-credentials", mockRequests[3].GetURIString()); ASSERT_EQ("", cred); } #endif TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientGetRegionWithNullResponse) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); auto region = ec2MetadataClient->GetCurrentRegion(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.169.254/latest/meta-data/placement/availability-zone", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/placement/availability-zone", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", region); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientGetRegionWithEmptyResponse) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr regionRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/placement/availability-zone"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr regionResponse = Aws::MakeShared(ALLOCATION_TAG, regionRequest); regionResponse->SetResponseCode(HttpResponseCode::OK); regionResponse->GetResponseBody() << ""; mockHttpClient->AddResponseToReturn(regionResponse); auto region = ec2MetadataClient->GetCurrentRegion(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.169.254/latest/meta-data/placement/availability-zone", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/placement/availability-zone", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", region); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientGetRegionWithHttpBadRequest) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr regionRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/placement/availability-zone"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr regionResponse = Aws::MakeShared(ALLOCATION_TAG, regionRequest); regionResponse->SetResponseCode(HttpResponseCode::BAD_REQUEST); regionResponse->GetResponseBody() << "Any test string"; mockHttpClient->AddResponseToReturn(regionResponse); auto region = ec2MetadataClient->GetCurrentRegion(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.169.254/latest/meta-data/placement/availability-zone", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/placement/availability-zone", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", region); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientGetRegionWithResponseHasSingleDigitSubStringInsideString) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); addGetCredentialsSecurelyMock(); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr regionRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/placement/availability-zone"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr regionResponse = Aws::MakeShared(ALLOCATION_TAG, regionRequest); regionResponse->SetResponseCode(HttpResponseCode::OK); regionResponse->GetResponseBody() << "us-west-123abc"; mockHttpClient->AddResponseToReturn(regionResponse); auto region = ec2MetadataClient->GetCurrentRegion(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.169.254/latest/meta-data/placement/availability-zone", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/placement/availability-zone", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("us-west-123", region); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientGetRegionWithResponseHasMultipleDigitSubStrings) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); addGetCredentialsSecurelyMock(); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr regionRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/placement/availability-zone"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr regionResponse = Aws::MakeShared(ALLOCATION_TAG, regionRequest); regionResponse->SetResponseCode(HttpResponseCode::OK); regionResponse->GetResponseBody() << "us-west-123abc321def"; mockHttpClient->AddResponseToReturn(regionResponse); auto region = ec2MetadataClient->GetCurrentRegion(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.169.254/latest/meta-data/placement/availability-zone", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/placement/availability-zone", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("us-west-123", region); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientGetRegionWithResponseStartWithDigitSubString) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); addGetCredentialsSecurelyMock(); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr regionRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/placement/availability-zone"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr regionResponse = Aws::MakeShared(ALLOCATION_TAG, regionRequest); regionResponse->SetResponseCode(HttpResponseCode::OK); regionResponse->GetResponseBody() << "123456-us-west-123abc321def"; mockHttpClient->AddResponseToReturn(regionResponse); auto region = ec2MetadataClient->GetCurrentRegion(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.169.254/latest/meta-data/placement/availability-zone", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/placement/availability-zone", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("123456", region); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientGetRegionWithResponseHasSingleDigitSubstringAtEnd) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); addGetCredentialsSecurelyMock(); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr regionRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/placement/availability-zone"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr regionResponse = Aws::MakeShared(ALLOCATION_TAG, regionRequest); regionResponse->SetResponseCode(HttpResponseCode::OK); regionResponse->GetResponseBody() << "us-west-123321"; mockHttpClient->AddResponseToReturn(regionResponse); auto region = ec2MetadataClient->GetCurrentRegion(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.169.254/latest/meta-data/placement/availability-zone", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/placement/availability-zone", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("us-west-123321", region); } TEST_F(AWSHttpResourceClientTest, TestEC2MetadataClientGetRegionWithResponseHasNoDigit) { // Create EC2MetadataClient with default endpoint http://169.254.169.254 auto ec2MetadataClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig); addGetCredentialsSecurelyMock(); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr regionRequest = CreateHttpRequest(URI("http://169.254.169.254/latest/meta-data/placement/availability-zone"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr regionResponse = Aws::MakeShared(ALLOCATION_TAG, regionRequest); regionResponse->SetResponseCode(HttpResponseCode::OK); regionResponse->GetResponseBody() << "us-west-abccba"; mockHttpClient->AddResponseToReturn(regionResponse); auto region = ec2MetadataClient->GetCurrentRegion(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.169.254/latest/meta-data/placement/availability-zone", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.169.254", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/latest/meta-data/placement/availability-zone", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("us-west-abccba", region); } TEST_F(AWSHttpResourceClientTest, TestECSCredentialsClientWithNullResponse) { // Create EC2CredentialsClient with default endpoint http://169.254.170.2 auto ecsCredentialsClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, "/path/to/res"); auto cred = ecsCredentialsClient->GetECSCredentials(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.170.2/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.170.2", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestECSCredentialsClientWithEmptyResponse) { // Create EC2CredentialsClient with default endpoint http://169.254.170.2 auto ecsCredentialsClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, "/path/to/res"); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr credRequest = CreateHttpRequest(URI("http://169.254.170.2/path/to/res"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr credResponse = Aws::MakeShared(ALLOCATION_TAG, credRequest); credResponse->SetResponseCode(HttpResponseCode::OK); credResponse->GetResponseBody() << ""; mockHttpClient->AddResponseToReturn(credResponse); auto cred = ecsCredentialsClient->GetECSCredentials(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.170.2/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.170.2", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestECSCredentialsClientUsingFullUriAndAuthToken) { auto ecsCredentialsClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, ""/*resource*/, "http://10.1.1.1/super/secret"/*endpoint*/, "TrustMeItIsMe"/*authToken*/); auto cred = ecsCredentialsClient->GetECSCredentials(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_STREQ("http://10.1.1.1/super/secret", mockRequest.GetURIString().c_str()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("10.1.1.1", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/super/secret", mockRequest.GetUri().GetPath()); ASSERT_STREQ("TrustMeItIsMe", mockRequest.GetHeaderValue(Aws::Http::AWS_AUTHORIZATION_HEADER).c_str()); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestECSCredentialsClientWithHttpBadRequest) { // Create EC2CredentialsClient with default endpoint http://169.254.170.2 auto ecsCredentialsClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, "/path/to/res"); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr credRequest = CreateHttpRequest(URI("http://169.254.170.2/path/to/res"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr credResponse = Aws::MakeShared(ALLOCATION_TAG, credRequest); credResponse->SetResponseCode(HttpResponseCode::BAD_REQUEST); credResponse->GetResponseBody() << "Any test string"; mockHttpClient->AddResponseToReturn(credResponse); auto cred = ecsCredentialsClient->GetECSCredentials(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.170.2/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.170.2", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ("", cred); } TEST_F(AWSHttpResourceClientTest, TestECSCredentialsClientGetExpectedCredentials) { // Create EC2CredentialsClient with default endpoint http://169.254.170.2 auto ecsCredentialsClient = Aws::MakeShared(ALLOCATION_TAG, clientConfig, "/path/to/res"); // This mocked URI is used to initiate http response and has nothing to do with the requested URI actually sent out. std::shared_ptr credRequest = CreateHttpRequest(URI("http://169.254.170.2/path/to/res"), HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); std::shared_ptr credResponse = Aws::MakeShared(ALLOCATION_TAG, credRequest); credResponse->SetResponseCode(HttpResponseCode::OK); credResponse->GetResponseBody() << "{ \"AccessKeyId\": \"betterAccessKey\", \"SecretAccessKey\": \"betterSecretKey\", \"Token\": \"betterToken\", \"Expiration\": \"2020-02-25T06:03:31Z\" }"; mockHttpClient->AddResponseToReturn(credResponse); auto cred = ecsCredentialsClient->GetECSCredentials(); auto mockRequest = mockHttpClient->GetMostRecentHttpRequest(); ASSERT_EQ("http://169.254.170.2/path/to/res", mockRequest.GetURIString()); ASSERT_EQ(Aws::Http::Scheme::HTTP, mockRequest.GetUri().GetScheme()); ASSERT_EQ("169.254.170.2", mockRequest.GetUri().GetAuthority()); ASSERT_EQ("/path/to/res", mockRequest.GetUri().GetPath()); ASSERT_EQ(Aws::Http::HttpMethod::HTTP_GET, mockRequest.GetMethod()); ASSERT_EQ(cred, "{ \"AccessKeyId\": \"betterAccessKey\", \"SecretAccessKey\": \"betterSecretKey\", \"Token\": \"betterToken\", \"Expiration\": \"2020-02-25T06:03:31Z\" }"); } }