Skip to content

Commit

Permalink
add configuration to preserve path seperator in URIs
Browse files Browse the repository at this point in the history
  • Loading branch information
sbiscigl committed Mar 15, 2024
1 parent 623da54 commit ecccc1f
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 6 deletions.
6 changes: 6 additions & 0 deletions src/aws-cpp-sdk-core/include/aws/core/Aws.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ namespace Aws
* Disable legacy URL encoding that leaves `$&,:@=` unescaped for legacy purposes.
*/
bool compliantRfc3986Encoding;
/**
* When construting Path sergements in a URI preserve path serperators instead of collapsing
* slashes. This is useful for aligning with other SDKs and tools on key path for S3 objects
* as currently the C++ SDK sanatizes the path.
*/
bool preservePathSerperators = false;
};

/**
Expand Down
8 changes: 7 additions & 1 deletion src/aws-cpp-sdk-core/include/aws/core/http/URI.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ namespace Aws
extern bool s_compliantRfc3986Encoding;
AWS_CORE_API void SetCompliantRfc3986Encoding(bool compliant);

extern bool s_preservePathSeperators;
AWS_CORE_API void SetPreservePathSeperators(bool preservePathSerperators);

//per https://tools.ietf.org/html/rfc3986#section-3.4 there is nothing preventing servers from allowing
//multiple values for the same key. So use a multimap instead of a map.
typedef Aws::MultiMap<Aws::String, Aws::String> QueryStringParameterCollection;
Expand Down Expand Up @@ -135,7 +138,10 @@ namespace Aws
Aws::StringStream ss;
ss << pathSegments;
Aws::String segments = ss.str();
for (const auto& segment : Aws::Utils::StringUtils::Split(segments, '/'))
const auto splitOption = s_preservePathSeperators
? Utils::StringUtils::SplitOptions::PRESERVE_DELIMETERS
: Utils::StringUtils::SplitOptions::NOT_SET;
for (const auto& segment : Aws::Utils::StringUtils::Split(segments, '/', splitOption))
{
m_pathSegments.push_back(segment);
}
Expand Down
15 changes: 14 additions & 1 deletion src/aws-cpp-sdk-core/include/aws/core/utils/StringUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,12 @@ namespace Aws
/**
* Includes empty entries in the vector returned by Split()
*/
INCLUDE_EMPTY_ENTRIES
INCLUDE_EMPTY_ENTRIES,
/**
* Include delimeter in vector returned, however removing leading and trailing
* delimeters.
*/
PRESERVE_DELIMETERS,
};

/**
Expand Down Expand Up @@ -116,6 +121,14 @@ namespace Aws
*/
static Aws::Vector<Aws::String> Split(const Aws::String& toSplit, char splitOn, size_t numOfTargetParts, SplitOptions option);

/**
* Splits a string on delimeter, keeping the delimeter in the vector returned.
* @param toSplit, the original string to split
* @param splitOn, the delimiter you want to use.
* @param numOfTargetParts, how many target parts you want to get, if it is 0, as many as possible.
*/
static Aws::Vector<Aws::String> SplitWithDelimeters(const Aws::String& toSplit, char splitOn, size_t numOfTargetParts);

/**
* Splits a string on new line characters.
*/
Expand Down
1 change: 1 addition & 0 deletions src/aws-cpp-sdk-core/source/Aws.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ namespace Aws
Aws::Http::SetInitCleanupCurlFlag(options.httpOptions.initAndCleanupCurl);
Aws::Http::SetInstallSigPipeHandlerFlag(options.httpOptions.installSigPipeHandler);
Aws::Http::SetCompliantRfc3986Encoding(options.httpOptions.compliantRfc3986Encoding);
Aws::Http::SetPreservePathSeperators(options.httpOptions.preservePathSerperators);
Aws::Http::InitHttp();
Aws::InitializeEnumOverflowContainer();
cJSON_AS4CPP_Hooks hooks;
Expand Down
43 changes: 39 additions & 4 deletions src/aws-cpp-sdk-core/source/http/URI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const char* SEPARATOR = "://";
bool s_compliantRfc3986Encoding = false;
void SetCompliantRfc3986Encoding(bool compliant) { s_compliantRfc3986Encoding = compliant; }

bool s_preservePathSeperators = false;
void SetPreservePathSeperators(bool preservePathSerperators) { s_preservePathSeperators = preservePathSerperators; }

Aws::String urlEncodeSegment(const Aws::String& segment, bool rfcEncoded = false)
{
// consolidates legacy escaping logic into one local method
Expand Down Expand Up @@ -199,8 +202,18 @@ Aws::String URI::GetPath() const

for (auto const& segment : m_pathSegments)
{
path.push_back('/');
path.append(segment);
if((s_preservePathSeperators && segment == "/"))
{
path.push_back('/');
}
else
{
if (!s_preservePathSeperators)
{
path.push_back('/');
}
path.append(segment);
}
}

if (m_pathSegments.empty() || m_pathHasTrailingSlash)
Expand All @@ -217,7 +230,18 @@ Aws::String URI::GetURLEncodedPath() const

for (auto const& segment : m_pathSegments)
{
ss << '/' << StringUtils::URLEncode(segment.c_str());
if((s_preservePathSeperators && segment == "/"))
{
ss << "/";
}
else
{
if (!s_preservePathSeperators)
{
ss << "/";
}
ss << StringUtils::URLEncode(segment.c_str());
}
}

if (m_pathSegments.empty() || m_pathHasTrailingSlash)
Expand All @@ -237,7 +261,18 @@ Aws::String URI::GetURLEncodedPathRFC3986() const
// (mostly; there is some non-standards legacy support that can be disabled)
for (const auto& segment : m_pathSegments)
{
ss << '/' << urlEncodeSegment(segment, m_useRfcEncoding);
if((s_preservePathSeperators && segment == "/"))
{
ss << "/";
}
else
{
if (!s_preservePathSeperators)
{
ss << "/";
}
ss << urlEncodeSegment(segment, m_useRfcEncoding);
}
}

if (m_pathSegments.empty() || m_pathHasTrailingSlash)
Expand Down
27 changes: 27 additions & 0 deletions src/aws-cpp-sdk-core/source/utils/StringUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char spl

Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char splitOn, size_t numOfTargetParts, SplitOptions option)
{
if (option == SplitOptions::PRESERVE_DELIMETERS)
{
return StringUtils::SplitWithDelimeters(toSplit, splitOn, numOfTargetParts);
}

Aws::Vector<Aws::String> returnValues;
Aws::StringStream input(toSplit);
Aws::String item;
Expand Down Expand Up @@ -128,6 +133,28 @@ Aws::Vector<Aws::String> StringUtils::Split(const Aws::String& toSplit, char spl
return returnValues;
}

Aws::Vector<Aws::String> StringUtils::SplitWithDelimeters(const Aws::String& toSplit, char splitOn, size_t numOfTargetParts)
{
Aws::Vector<Aws::String> returnValues;
size_t start = 0;
size_t found = toSplit.find_first_of(splitOn, start);

while (returnValues.size() < numOfTargetParts && found != std::string::npos) {
if (found > start) {
returnValues.push_back(toSplit.substr(start, found - start));
}
returnValues.push_back(toSplit.substr(found, 1));
start = found + 1;
found = toSplit.find_first_of(splitOn, start);
}

if (start < toSplit.length()) {
returnValues.push_back(toSplit.substr(start));
}

return returnValues;
}

Aws::Vector<Aws::String> StringUtils::SplitOnLine(const Aws::String& toSplit)
{
Aws::StringStream input(toSplit);
Expand Down

0 comments on commit ecccc1f

Please sign in to comment.