size_t CurlHttpClient::WriteHeader(char* ptr, size_t size, size_t nmemb, void* userdata)
{
    if (ptr)
    {
        AWS_LOGSTREAM_TRACE(CURL_HTTP_CLIENT_TAG, ptr);
        HttpResponse* response = (HttpResponse*) userdata;
        Aws::String headerLine(ptr);
        Aws::Vector<Aws::String> keyValuePair = StringUtils::Split(headerLine, ':');


        if (keyValuePair.size() > 1)
        {
            Aws::String headerName = keyValuePair[0];
            headerName = StringUtils::Trim(headerName.c_str());


            Aws::String headerValue = headerLine.substr(headerName.length() + 1).c_str();
            headerValue = StringUtils::Trim(headerValue.c_str());


            response->AddHeader(headerName, headerValue);
        }
        return size * nmemb;
    }
    return 0;
}
void VerifyAllLogsAtOrBelow(LogLevel logLevel, const Aws::String& tag, const Aws::Vector<Aws::String>& loggedStatements)
{
    static const uint32_t STATEMENTS_PER_LEVEL = 3;
    uint32_t expectedLogLevels = static_cast<uint32_t>(logLevel);
    uint32_t expectedStatementCount = expectedLogLevels * STATEMENTS_PER_LEVEL;
    ASSERT_EQ(expectedStatementCount, loggedStatements.size());

    for(uint32_t i = 0; i < expectedLogLevels; ++i)
    {
        LogLevel currentLevel = static_cast<LogLevel>(i + 1);
        Aws::String levelTag = "[" + GetLogLevelName(currentLevel) + "]";

        for(uint32_t j = 0; j < STATEMENTS_PER_LEVEL; ++j)
        {
            uint32_t statementIndex = i * STATEMENTS_PER_LEVEL + j;
            ASSERT_TRUE(loggedStatements[statementIndex].find(levelTag) != Aws::String::npos);
            ASSERT_TRUE(loggedStatements[statementIndex].find(tag) != Aws::String::npos);
        }

        Aws::String logText1 = "test " + StringUtils::ToLower(GetLogLevelName(currentLevel).c_str()) + " level";
        ASSERT_TRUE(loggedStatements[i * STATEMENTS_PER_LEVEL].find(logText1) != Aws::String::npos);

        Aws::String logText2 = "test " + StringUtils::ToLower(GetLogLevelName(currentLevel).c_str()) + " format level";
        ASSERT_TRUE(loggedStatements[i * STATEMENTS_PER_LEVEL + 1].find(logText2) != Aws::String::npos);

        Aws::String logText3 = "test " + StringUtils::ToLower(GetLogLevelName(currentLevel).c_str()) + " stream level";
        ASSERT_TRUE(loggedStatements[i * STATEMENTS_PER_LEVEL + 2].find(logText3) != Aws::String::npos);
    }
}
TEST(StringUtilsTest, TestSplitDelimiterNotFound)
{
    AWS_BEGIN_MEMORY_TEST(16, 10)

    Aws::String toSplit = "BlahBlahBlah";

    Aws::Vector<Aws::String> splits = StringUtils::Split(toSplit, ',');

    ASSERT_EQ(1uL, splits.size());

    AWS_END_MEMORY_TEST
}
TEST(StringUtilsTest, TestSplitWithEmptyString)
{
    AWS_BEGIN_MEMORY_TEST(16, 10)

    Aws::String toSplit = "";

    Aws::Vector<Aws::String> splits = StringUtils::Split(toSplit, ',');

    ASSERT_EQ(0uL, splits.size());

    AWS_END_MEMORY_TEST
}
TEST(StringUtilsTest, TestSplitHappyPath)
{
    AWS_BEGIN_MEMORY_TEST(16, 10)

    Aws::String toSplit = "test1,test2,test3,test4";
    Aws::Vector<Aws::String> splits = StringUtils::Split(toSplit, ',');

    ASSERT_EQ(4uL, splits.size());
    EXPECT_STREQ("test1", splits[0].c_str());
    EXPECT_STREQ("test2", splits[1].c_str());
    EXPECT_STREQ("test3", splits[2].c_str());
    EXPECT_STREQ("test4", splits[3].c_str());

    AWS_END_MEMORY_TEST
}
TEST(StringUtilsTest, TestSplitWithDelimiterOnTheFrontAndBack)
{
    AWS_BEGIN_MEMORY_TEST(16, 10)

    Aws::String toSplit = ",test1,test2,test3,test4,";

    Aws::Vector<Aws::String> splits = StringUtils::Split(toSplit, ',');

    ASSERT_EQ(4uL, splits.size());
    EXPECT_STREQ("test1", splits[0].c_str());
    EXPECT_STREQ("test2", splits[1].c_str());
    EXPECT_STREQ("test3", splits[2].c_str());
    EXPECT_STREQ("test4", splits[3].c_str());

    AWS_END_MEMORY_TEST
}
TEST(StringUtilsTest, TestSplitOnLineHappyPath)
{
    AWS_BEGIN_MEMORY_TEST(16, 10)

    Aws::StringStream ss;
    ss << "test1" << std::endl << "test2" << std::endl << "test3" << std::endl << "test4";
    Aws::String toSplit = ss.str();

    Aws::Vector<Aws::String> splits = StringUtils::SplitOnLine(toSplit);

    ASSERT_EQ(4uL, splits.size());
    EXPECT_STREQ("test1", splits[0].c_str());
    EXPECT_STREQ("test2", splits[1].c_str());
    EXPECT_STREQ("test3", splits[2].c_str());
    EXPECT_STREQ("test4", splits[3].c_str());

    AWS_END_MEMORY_TEST
}
Aws::Map<Aws::String, Aws::String> ProfileConfigFileAWSCredentialsProvider::ParseProfileConfigFile(const Aws::String& filename)
{
    std::ifstream profileFile(filename.c_str());
    Aws::Map<Aws::String, Aws::String> propertyValueMap;

    Aws::String profile = "";
    if (profileFile.good() && profileFile.is_open())
    {
        Aws::String line;
        while (std::getline(profileFile, line))
        {
            Aws::String trimmedLine(StringUtils::Trim(line.c_str()));

            if (trimmedLine.empty() || trimmedLine.front() == '#')
                continue;

            if (trimmedLine.front() == '[' && trimmedLine.back() == ']')
            {
                profile = StringUtils::Trim(trimmedLine.substr(1, trimmedLine.length() - 2).c_str());
                AWS_LOGSTREAM_DEBUG(profileLogTag, "Found profile " << profile);
            }

            Aws::Vector<Aws::String> propertyPair = StringUtils::Split(trimmedLine, '=');

            if (propertyPair.size() == 2)
            {
                Aws::String key(StringUtils::Trim(propertyPair[0].c_str()));
                Aws::String value(StringUtils::Trim(propertyPair[1].c_str()));

                AWS_LOGSTREAM_TRACE(profileLogTag, "Found property " << key << "for profile " << profile);
                if (key == AWS_ACCESS_KEY_ID || key == AWS_SECRET_ACCESS_KEY || key == AWS_SESSION_TOKEN || key == AWS_ACCOUNT_ID)
                    propertyValueMap[profile + ":" + key] = value;
            }
        }
    }

    if (profileFile.is_open())
        profileFile.close();

    return std::move(propertyValueMap);
}
std::shared_ptr<HttpResponse> WinSyncHttpClient::BuildSuccessResponse(const Aws::Http::HttpRequest& request, void* hHttpRequest, Aws::Utils::RateLimits::RateLimiterInterface* readLimiter) const
{
    auto response = Aws::MakeShared<StandardHttpResponse>(GetLogTag(), request);
    Aws::StringStream ss;
    uint64_t read = 0;

    DoQueryHeaders(hHttpRequest, response, ss, read);

    if(readLimiter != nullptr && read > 0)
    {
        readLimiter->ApplyAndPayForCost(read);
    }

    Aws::Vector<Aws::String> rawHeaders = StringUtils::SplitOnLine(ss.str());

    for (auto& header : rawHeaders)
    {
        Aws::Vector<Aws::String> keyValuePair = StringUtils::Split(header, ':');
        if (keyValuePair.size() > 1)
        {
            Aws::String headerName = keyValuePair[0];
            headerName = StringUtils::Trim(headerName.c_str());

            Aws::String headerValue(keyValuePair[1]);

            for (unsigned i = 2; i < keyValuePair.size(); ++i)
            {
                headerValue += ":";
                headerValue += keyValuePair[i];                 
            }

            response->AddHeader(headerName, StringUtils::Trim(headerValue.c_str()));
        }
    }

    if (request.GetMethod() != HttpMethod::HTTP_HEAD)
    {
        char body[1024];
        uint64_t bodySize = sizeof(body);
        read = 0;
        bool success = true;

        while (DoReadData(hHttpRequest, body, bodySize, read) && read > 0 && success)
        {
            response->GetResponseBody().write(body, read);
            if (read > 0)
            {
                if (readLimiter != nullptr)
                {
                    readLimiter->ApplyAndPayForCost(read);
                }
                auto& receivedHandler = request.GetDataReceivedEventHandler();
                if (receivedHandler)
                {
                    receivedHandler(&request, response.get(), (long long)read);
                }
            }

            success = success && IsRequestProcessingEnabled();
        }

        if(!success)
        {
            return nullptr;
        }
    }

    //go ahead and flush the response body.
    response->GetResponseBody().flush();

    return response;
}