Audit string comparisons (#4197)
* Audit string comparisons * Mac fix * !isxdigit() * Rewrite condition + comment * <3 * Clang-format * 255 * Allow uppercase * stoi() radix * ShouldEncode() Co-authored-by: Anton Kolesnyk <antkmsft@users.noreply.github.com>
This commit is contained in:
parent
8891d30f4e
commit
a70be339e7
@ -3,7 +3,7 @@
|
||||
|
||||
#include "get_env.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <locale>
|
||||
|
||||
#if defined(WINAPI_PARTITION_DESKTOP) && !WINAPI_PARTITION_DESKTOP
|
||||
|
||||
@ -20,7 +20,7 @@ char* std::getenv(const char* name)
|
||||
}
|
||||
|
||||
// We're still trying to match the name.
|
||||
if (std::toupper(*buf) == std::toupper(name[i]))
|
||||
if (std::toupper(*buf, std::locale::classic()) == std::toupper(name[i], std::locale::classic()))
|
||||
{
|
||||
// Matching so far, keep matching name and buffer, char by char, case insensitive.
|
||||
++i;
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include <azure/core/internal/strings.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
@ -193,14 +196,9 @@ public:
|
||||
}
|
||||
hashedThumbprint.resize(hashLength);
|
||||
|
||||
auto hexThumbprint(BinaryToHexString(hashedThumbprint));
|
||||
// HexString uses an "a"-"f" alphabet, but the CLR hex encoder uses an "A"-"F" alphabet,
|
||||
// convert between them.
|
||||
std::transform(
|
||||
hexThumbprint.begin(), hexThumbprint.end(), hexThumbprint.begin(), [](char ch) {
|
||||
return static_cast<char>(std::toupper(ch));
|
||||
});
|
||||
return hexThumbprint;
|
||||
// so we need to uppercase them.
|
||||
return Azure::Core::_internal::StringExtensions::ToUpper(BinaryToHexString(hashedThumbprint));
|
||||
}
|
||||
|
||||
static std::unique_ptr<Cryptography::X509Certificate> Import(
|
||||
|
||||
@ -186,8 +186,7 @@ namespace Azure { namespace Core { namespace Test {
|
||||
// creating
|
||||
std::string GetTestNameLowerCase(bool sanitize = true)
|
||||
{
|
||||
std::string testName(GetTestName(sanitize));
|
||||
return Azure::Core::_internal::StringExtensions::ToLower(testName);
|
||||
return Azure::Core::_internal::StringExtensions::ToLower(GetTestName(sanitize));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -147,7 +147,6 @@ set(
|
||||
src/operation_status.cpp
|
||||
src/private/environment_log_level_listener.hpp
|
||||
src/private/package_version.hpp
|
||||
src/strings.cpp
|
||||
src/tracing/tracing.cpp
|
||||
src/uuid.cpp
|
||||
)
|
||||
|
||||
@ -363,7 +363,7 @@ namespace Azure { namespace Core { namespace Http {
|
||||
}
|
||||
|
||||
// Always toLower() headers
|
||||
auto headerName
|
||||
auto const headerName
|
||||
= Azure::Core::_internal::StringExtensions::ToLower(std::string(start, end));
|
||||
start = end + 1; // start value
|
||||
while (start < last && (*start == ' ' || *start == '\t'))
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace Azure { namespace Core { namespace _internal {
|
||||
@ -19,24 +20,61 @@ namespace Azure { namespace Core { namespace _internal {
|
||||
*/
|
||||
struct StringExtensions final
|
||||
{
|
||||
static constexpr char ToUpper(char c) noexcept
|
||||
{
|
||||
return (c < 'a' || c > 'z') ? c : c - ('a' - 'A');
|
||||
}
|
||||
|
||||
static constexpr char ToLower(char c) noexcept
|
||||
{
|
||||
return (c < 'A' || c > 'Z') ? c : c + ('a' - 'A');
|
||||
}
|
||||
|
||||
struct CaseInsensitiveComparator final
|
||||
{
|
||||
bool operator()(const std::string& lhs, const std::string& rhs) const
|
||||
bool operator()(std::string const& lhs, std::string const& rhs) const
|
||||
{
|
||||
return std::lexicographical_compare(
|
||||
lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](char c1, char c2) {
|
||||
return ToLower(c1) < ToLower(c2);
|
||||
lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](auto l, auto r) {
|
||||
return ToLower(l) < ToLower(r);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
static bool LocaleInvariantCaseInsensitiveEqual(
|
||||
const std::string& lhs,
|
||||
const std::string& rhs) noexcept;
|
||||
static std::string const ToLower(std::string const& src) noexcept;
|
||||
static unsigned char ToLower(unsigned char const src) noexcept;
|
||||
static std::string const ToUpper(std::string const& src) noexcept;
|
||||
static unsigned char ToUpper(unsigned char const src) noexcept;
|
||||
std::string const& lhs,
|
||||
std::string const& rhs) noexcept
|
||||
{
|
||||
auto const rhsSize = rhs.size();
|
||||
if (lhs.size() != rhsSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const lhsData = lhs.c_str();
|
||||
auto const rhsData = rhs.c_str();
|
||||
for (size_t i = 0; i < rhsSize; ++i)
|
||||
{
|
||||
if (ToLower(lhsData[i]) != ToLower(rhsData[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string ToLower(std::string src)
|
||||
{
|
||||
std::transform(src.begin(), src.end(), src.begin(), [](auto c) { return ToLower(c); });
|
||||
return src;
|
||||
}
|
||||
|
||||
static std::string ToUpper(std::string src)
|
||||
{
|
||||
std::transform(src.begin(), src.end(), src.begin(), [](auto c) { return ToUpper(c); });
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
}}} // namespace Azure::Core::_internal
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Azure { namespace Core {
|
||||
namespace _detail {
|
||||
@ -53,10 +52,6 @@ namespace Azure { namespace Core {
|
||||
// query parameters are all encoded
|
||||
std::map<std::string, std::string> m_encodedQueryParameters;
|
||||
|
||||
// List of default non-URL-encode chars. While URL encoding a string, do not escape any chars in
|
||||
// this set.
|
||||
const static std::unordered_set<unsigned char> defaultNonUrlEncodeChars;
|
||||
|
||||
std::string GetUrlWithoutQuery(bool relative) const;
|
||||
|
||||
/**
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
#include "azure/core/platform.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
@ -319,10 +319,10 @@ T ParseNumber(
|
||||
auto i = 0;
|
||||
for (; i < MaxChars; ++i)
|
||||
{
|
||||
int const ch = str[*cursor + i];
|
||||
if (std::isdigit(ch))
|
||||
auto const ch = str[*cursor + i];
|
||||
if (std::isdigit(ch, std::locale::classic()))
|
||||
{
|
||||
value = (value * 10) + (static_cast<decltype(value)>(ch) - '0');
|
||||
value = (value * 10) + (static_cast<decltype(value)>(static_cast<unsigned char>(ch)) - '0');
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -654,10 +654,10 @@ DateTime DateTime::Parse(std::string const& dateTime, DateFormat format)
|
||||
minDateTimeLength += charsRead;
|
||||
if (charsRead == 7 && (DateTimeLength - cursor) > 0)
|
||||
{
|
||||
int const ch = dateTime[cursor];
|
||||
if (std::isdigit(ch))
|
||||
auto const ch = dateTime[cursor];
|
||||
if (std::isdigit(ch, std::locale::classic()))
|
||||
{
|
||||
auto const num = static_cast<int>(ch - '0');
|
||||
auto const num = static_cast<int>(static_cast<unsigned char>(ch) - '0');
|
||||
if (num > 4)
|
||||
{
|
||||
if (fracSec < 9999999)
|
||||
@ -677,7 +677,7 @@ DateTime DateTime::Parse(std::string const& dateTime, DateFormat format)
|
||||
|
||||
for (auto i = DateTimeLength - cursor; i > 0; --i)
|
||||
{
|
||||
if (std::isdigit(static_cast<int>(dateTime[cursor])))
|
||||
if (std::isdigit(dateTime[cursor], std::locale::classic()))
|
||||
{
|
||||
++minDateTimeLength;
|
||||
++cursor;
|
||||
|
||||
@ -25,33 +25,42 @@ Logger::Level const* GetEnvironmentLogLevel()
|
||||
{
|
||||
EnvironmentLogLevelListener::SetInitialized(true);
|
||||
|
||||
auto const envVar = Environment::GetVariable("AZURE_LOG_LEVEL");
|
||||
if (!envVar.empty())
|
||||
auto const logLevelStr = Environment::GetVariable("AZURE_LOG_LEVEL");
|
||||
if (!logLevelStr.empty())
|
||||
{
|
||||
auto const logLevelStr = Azure::Core::_internal::StringExtensions::ToLower(envVar);
|
||||
|
||||
// See https://github.com/Azure/azure-sdk-for-java/wiki/Logging-with-Azure-SDK
|
||||
// And
|
||||
// https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/core/azure-core/src/main/java/com/azure/core/util/logging/LogLevel.java
|
||||
using Azure::Core::_internal::StringExtensions;
|
||||
|
||||
static Logger::Level envLogLevel = {};
|
||||
envLogLevelPtr = &envLogLevel;
|
||||
|
||||
if (logLevelStr == "error" || logLevelStr == "err" || logLevelStr == "4")
|
||||
if (logLevelStr == "4"
|
||||
|| StringExtensions::LocaleInvariantCaseInsensitiveEqual(logLevelStr, "error")
|
||||
|| StringExtensions::LocaleInvariantCaseInsensitiveEqual(logLevelStr, "err"))
|
||||
{
|
||||
envLogLevel = Logger::Level::Error;
|
||||
}
|
||||
else if (logLevelStr == "warning" || logLevelStr == "warn" || logLevelStr == "3")
|
||||
else if (
|
||||
logLevelStr == "3"
|
||||
|| StringExtensions::LocaleInvariantCaseInsensitiveEqual(logLevelStr, "warning")
|
||||
|| StringExtensions::LocaleInvariantCaseInsensitiveEqual(logLevelStr, "warn"))
|
||||
{
|
||||
envLogLevel = Logger::Level::Warning;
|
||||
}
|
||||
else if (
|
||||
logLevelStr == "informational" || logLevelStr == "info" || logLevelStr == "information"
|
||||
|| logLevelStr == "2")
|
||||
logLevelStr == "2"
|
||||
|| StringExtensions::LocaleInvariantCaseInsensitiveEqual(logLevelStr, "informational")
|
||||
|| StringExtensions::LocaleInvariantCaseInsensitiveEqual(logLevelStr, "information")
|
||||
|| StringExtensions::LocaleInvariantCaseInsensitiveEqual(logLevelStr, "info"))
|
||||
{
|
||||
envLogLevel = Logger::Level::Informational;
|
||||
}
|
||||
else if (logLevelStr == "verbose" || logLevelStr == "debug" || logLevelStr == "1")
|
||||
else if (
|
||||
logLevelStr == "1"
|
||||
|| StringExtensions::LocaleInvariantCaseInsensitiveEqual(logLevelStr, "verbose")
|
||||
|| StringExtensions::LocaleInvariantCaseInsensitiveEqual(logLevelStr, "debug"))
|
||||
{
|
||||
envLogLevel = Logger::Level::Verbose;
|
||||
}
|
||||
|
||||
@ -70,6 +70,7 @@
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@ -1315,9 +1316,10 @@ void DumpCurlInfoToLog(std::string const& text, uint8_t* ptr, size_t size)
|
||||
{
|
||||
// Log the contents of the buffer as text, if it's printable, print the character, otherwise
|
||||
// print '.'
|
||||
if (isprint(ptr[i + c]))
|
||||
auto const ch = static_cast<char>(ptr[i + c]);
|
||||
if (std::isprint(ch, std::locale::classic()))
|
||||
{
|
||||
ss << ptr[i + c];
|
||||
ss << ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -6,6 +6,9 @@
|
||||
#include "azure/core/internal/io/null_body_stream.hpp"
|
||||
#include "azure/core/url.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <locale>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
using namespace Azure::Core;
|
||||
@ -25,153 +28,30 @@ const HttpMethod HttpMethod::Put("PUT");
|
||||
const HttpMethod HttpMethod::Delete("DELETE");
|
||||
const HttpMethod HttpMethod::Patch("PATCH");
|
||||
|
||||
namespace {
|
||||
bool IsInvalidHeaderNameChar(char c)
|
||||
{
|
||||
static std::unordered_set<char> const HeaderNameExtraValidChars
|
||||
= {' ', '!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '_', '`', '|', '~'};
|
||||
|
||||
return !std::isalnum(c, std::locale::classic())
|
||||
&& HeaderNameExtraValidChars.find(c) == HeaderNameExtraValidChars.end();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Azure::Core::Http::_detail::RawResponseHelpers::InsertHeaderWithValidation(
|
||||
Azure::Core::CaseInsensitiveMap& headers,
|
||||
std::string const& headerName,
|
||||
std::string const& headerValue)
|
||||
{
|
||||
// Static table for validating header names. It is created just once for the program and reused
|
||||
// each time SetHeader is called
|
||||
static const uint8_t validChars[256] = {
|
||||
0, /* 0 - null */
|
||||
0, /* 1 - start of heading */
|
||||
0, /* 2 - start of text */
|
||||
0, /* 3 - end of text */
|
||||
0, /* 4 - end of transmission */
|
||||
0, /* 5 - enquiry */
|
||||
0, /* 6 - acknowledge */
|
||||
0, /* 7 - bell */
|
||||
0, /* 8 - backspace */
|
||||
0, /* 9 - horizontal tab */
|
||||
0, /* 10 - new line */
|
||||
0, /* 11 - vertical tab */
|
||||
0, /* 12 - new page */
|
||||
0, /* 13 - carriage return */
|
||||
0, /* 14 - shift out */
|
||||
0, /* 15 - shift in */
|
||||
0, /* 16 - data link escape */
|
||||
0, /* 17 - device control 1 */
|
||||
0, /* 18 - device control 2 */
|
||||
0, /* 19 - device control 3 */
|
||||
0, /* 20 - device control 4 */
|
||||
0, /* 21 - negative acknowledge */
|
||||
0, /* 22 - synchronous idle */
|
||||
0, /* 23 - end of trans. block */
|
||||
0, /* 24 - cancel */
|
||||
0, /* 25 - end of medium */
|
||||
0, /* 26 - substitute */
|
||||
0, /* 27 - escape */
|
||||
0, /* 28 - file separator */
|
||||
0, /* 29 - group separator */
|
||||
0, /* 30 - record separator */
|
||||
0, /* 31 - unit separator */
|
||||
' ', /* 32 - space */
|
||||
'!', /* 33 - ! */
|
||||
0, /* 34 - " */
|
||||
'#', /* 35 - # */
|
||||
'$', /* 36 - $ */
|
||||
'%', /* 37 - % */
|
||||
'&', /* 38 - & */
|
||||
'\'', /* 39 - ' */
|
||||
0, /* 40 - ( */
|
||||
0, /* 41 - ) */
|
||||
'*', /* 42 - * */
|
||||
'+', /* 43 - + */
|
||||
0, /* 44 - , */
|
||||
'-', /* 45 - - */
|
||||
'.', /* 46 - . */
|
||||
0, /* 47 - / */
|
||||
'0', /* 48 - 0 */
|
||||
'1', /* 49 - 1 */
|
||||
'2', /* 50 - 2 */
|
||||
'3', /* 51 - 3 */
|
||||
'4', /* 52 - 4 */
|
||||
'5', /* 53 - 5 */
|
||||
'6', /* 54 - 6 */
|
||||
'7', /* 55 - 7 */
|
||||
'8', /* 56 - 8 */
|
||||
'9', /* 57 - 9 */
|
||||
0, /* 58 - : */
|
||||
0, /* 59 - ; */
|
||||
0, /* 60 - < */
|
||||
0, /* 61 - = */
|
||||
0, /* 62 - > */
|
||||
0, /* 63 - ? */
|
||||
0, /* 64 - @ */
|
||||
'a', /* 65 - A */
|
||||
'b', /* 66 - B */
|
||||
'c', /* 67 - C */
|
||||
'd', /* 68 - D */
|
||||
'e', /* 69 - E */
|
||||
'f', /* 70 - F */
|
||||
'g', /* 71 - G */
|
||||
'h', /* 72 - H */
|
||||
'i', /* 73 - I */
|
||||
'j', /* 74 - J */
|
||||
'k', /* 75 - K */
|
||||
'l', /* 76 - L */
|
||||
'm', /* 77 - M */
|
||||
'n', /* 78 - N */
|
||||
'o', /* 79 - O */
|
||||
'p', /* 80 - P */
|
||||
'q', /* 81 - Q */
|
||||
'r', /* 82 - R */
|
||||
's', /* 83 - S */
|
||||
't', /* 84 - T */
|
||||
'u', /* 85 - U */
|
||||
'v', /* 86 - V */
|
||||
'w', /* 87 - W */
|
||||
'x', /* 88 - X */
|
||||
'y', /* 89 - Y */
|
||||
'z', /* 90 - Z */
|
||||
0, /* 91 - [ */
|
||||
0, /* 92 - comment */
|
||||
0, /* 93 - ] */
|
||||
'^', /* 94 - ^ */
|
||||
'_', /* 95 - _ */
|
||||
'`', /* 96 - ` */
|
||||
'a', /* 97 - a */
|
||||
'b', /* 98 - b */
|
||||
'c', /* 99 - c */
|
||||
'd', /* 100 - d */
|
||||
'e', /* 101 - e */
|
||||
'f', /* 102 - f */
|
||||
'g', /* 103 - g */
|
||||
'h', /* 104 - h */
|
||||
'i', /* 105 - i */
|
||||
'j', /* 106 - j */
|
||||
'k', /* 107 - k */
|
||||
'l', /* 108 - l */
|
||||
'm', /* 109 - m */
|
||||
'n', /* 110 - n */
|
||||
'o', /* 111 - o */
|
||||
'p', /* 112 - p */
|
||||
'q', /* 113 - q */
|
||||
'r', /* 114 - r */
|
||||
's', /* 115 - s */
|
||||
't', /* 116 - t */
|
||||
'u', /* 117 - u */
|
||||
'v', /* 118 - v */
|
||||
'w', /* 119 - w */
|
||||
'x', /* 120 - x */
|
||||
'y', /* 121 - y */
|
||||
'z', /* 122 - z */
|
||||
0, /* 123 - { */
|
||||
'|', /* 124 - | */
|
||||
0, /* 125 - } */
|
||||
'~', /* 126 - ~ */
|
||||
0 /* 127 - DEL */
|
||||
// ...128-255 is all zeros (not valid) characters}
|
||||
};
|
||||
|
||||
// Check all chars in name are valid
|
||||
for (size_t index = 0; index < headerName.size(); index++)
|
||||
if (std::find_if(headerName.begin(), headerName.end(), IsInvalidHeaderNameChar)
|
||||
!= headerName.end())
|
||||
{
|
||||
if (validChars[static_cast<int>(headerName[index])] == 0)
|
||||
{
|
||||
throw std::invalid_argument("Invalid header: " + headerName);
|
||||
}
|
||||
throw std::invalid_argument("Invalid header name: " + headerName);
|
||||
}
|
||||
|
||||
// insert (override if duplicated)
|
||||
headers[headerName] = headerValue;
|
||||
}
|
||||
|
||||
@ -3,12 +3,6 @@
|
||||
|
||||
#include "azure/core/http/raw_response.hpp"
|
||||
#include "azure/core/http/http.hpp"
|
||||
#include "azure/core/internal/strings.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace Azure::Core::IO;
|
||||
using namespace Azure::Core::Http;
|
||||
|
||||
@ -24,29 +24,24 @@ static Azure::Core::CaseInsensitiveMap MergeMaps(
|
||||
|
||||
Azure::Nullable<std::string> Request::GetHeader(std::string const& name)
|
||||
{
|
||||
std::vector<std::string> returnedHeaders;
|
||||
auto headerNameLowerCase = Azure::Core::_internal::StringExtensions::ToLower(name);
|
||||
for (auto const& hdrs : {m_retryHeaders, m_headers})
|
||||
{
|
||||
auto const header = hdrs.find(name);
|
||||
if (header != hdrs.end())
|
||||
{
|
||||
return header->second;
|
||||
}
|
||||
}
|
||||
|
||||
auto retryHeader = this->m_retryHeaders.find(headerNameLowerCase);
|
||||
if (retryHeader != this->m_retryHeaders.end())
|
||||
{
|
||||
return retryHeader->second;
|
||||
}
|
||||
auto header = this->m_headers.find(headerNameLowerCase);
|
||||
if (header != this->m_headers.end())
|
||||
{
|
||||
return header->second;
|
||||
}
|
||||
return Azure::Nullable<std::string>{};
|
||||
return {};
|
||||
}
|
||||
|
||||
void Request::SetHeader(std::string const& name, std::string const& value)
|
||||
{
|
||||
auto headerNameLowerCase = Azure::Core::_internal::StringExtensions::ToLower(name);
|
||||
return this->m_retryModeEnabled ? _detail::RawResponseHelpers::InsertHeaderWithValidation(
|
||||
this->m_retryHeaders, headerNameLowerCase, value)
|
||||
: _detail::RawResponseHelpers::InsertHeaderWithValidation(
|
||||
this->m_headers, headerNameLowerCase, value);
|
||||
return _detail::RawResponseHelpers::InsertHeaderWithValidation(
|
||||
m_retryModeEnabled ? m_retryHeaders : m_headers,
|
||||
Azure::Core::_internal::StringExtensions::ToLower(name),
|
||||
value);
|
||||
}
|
||||
|
||||
void Request::RemoveHeader(std::string const& name)
|
||||
|
||||
@ -5,145 +5,165 @@
|
||||
#include "azure/core/internal/strings.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <locale>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace Azure::Core;
|
||||
|
||||
Url::Url(const std::string& url)
|
||||
Url::Url(std::string const& url)
|
||||
{
|
||||
std::string::const_iterator pos = url.begin();
|
||||
auto urlIter = url.cbegin();
|
||||
|
||||
const std::string schemeEnd = "://";
|
||||
auto schemeIter = url.find(schemeEnd);
|
||||
if (schemeIter != std::string::npos)
|
||||
{
|
||||
std::transform(url.begin(), url.begin() + schemeIter, std::back_inserter(m_scheme), [](char c) {
|
||||
return static_cast<char>(
|
||||
Azure::Core::_internal::StringExtensions::ToLower(static_cast<unsigned char>(c)));
|
||||
});
|
||||
std::string const SchemeEnd = "://";
|
||||
auto const schemePos = url.find(SchemeEnd);
|
||||
|
||||
pos = url.begin() + schemeIter + schemeEnd.length();
|
||||
if (schemePos != std::string::npos)
|
||||
{
|
||||
m_scheme = Azure::Core::_internal::StringExtensions::ToLower(url.substr(0, schemePos));
|
||||
urlIter += schemePos + SchemeEnd.length();
|
||||
}
|
||||
}
|
||||
|
||||
auto hostIter
|
||||
= std::find_if(pos, url.end(), [](char c) { return c == '/' || c == '?' || c == ':'; });
|
||||
m_host = std::string(pos, hostIter);
|
||||
pos = hostIter;
|
||||
|
||||
if (pos != url.end() && *pos == ':')
|
||||
{
|
||||
auto port_ite = std::find_if_not(
|
||||
pos + 1, url.end(), [](char c) { return std::isdigit(static_cast<unsigned char>(c)); });
|
||||
auto portNumber = std::stoi(std::string(pos + 1, port_ite));
|
||||
auto const hostIter
|
||||
= std::find_if(urlIter, url.end(), [](auto c) { return c == '/' || c == '?' || c == ':'; });
|
||||
|
||||
m_host = std::string(urlIter, hostIter);
|
||||
urlIter = hostIter;
|
||||
}
|
||||
|
||||
if (urlIter == url.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (*urlIter == ':')
|
||||
{
|
||||
++urlIter;
|
||||
auto const portIter = std::find_if_not(
|
||||
urlIter, url.end(), [](auto c) { return std::isdigit(c, std::locale::classic()); });
|
||||
|
||||
auto const portNumber = std::stoi(std::string(urlIter, portIter));
|
||||
|
||||
// stoi will throw out_of_range when `int` is overflow, but we need to throw if uint16 is
|
||||
// overflow
|
||||
auto maxPortNumberSupported = std::numeric_limits<uint16_t>::max();
|
||||
if (portNumber > maxPortNumberSupported)
|
||||
{
|
||||
throw std::out_of_range(
|
||||
"The port number is out of range. The max supported number is "
|
||||
+ std::to_string(maxPortNumberSupported) + ".");
|
||||
constexpr auto const MaxPortNumberSupported = std::numeric_limits<uint16_t>::max();
|
||||
if (portNumber > MaxPortNumberSupported)
|
||||
{
|
||||
throw std::out_of_range(
|
||||
"The port number is out of range. The max supported number is "
|
||||
+ std::to_string(MaxPortNumberSupported) + ".");
|
||||
}
|
||||
}
|
||||
|
||||
// cast is safe because the overflow was detected before
|
||||
m_port = static_cast<uint16_t>(portNumber);
|
||||
pos = port_ite;
|
||||
urlIter = portIter;
|
||||
}
|
||||
|
||||
if (pos != url.end() && (*pos != '/') && (*pos != '?'))
|
||||
if (urlIter == url.end())
|
||||
{
|
||||
// only char `\` or `?` is valid after the port (or the end of the URL). Any other char is an
|
||||
return;
|
||||
}
|
||||
|
||||
if (*urlIter != '/' && *urlIter != '?')
|
||||
{
|
||||
// only char '/' or '?' is valid after the port (or the end of the URL). Any other char is an
|
||||
// invalid input
|
||||
throw std::invalid_argument("The port number contains invalid characters.");
|
||||
}
|
||||
|
||||
if (pos != url.end() && (*pos == '/'))
|
||||
if (*urlIter == '/')
|
||||
{
|
||||
auto pathIter = std::find(pos + 1, url.end(), '?');
|
||||
m_encodedPath = std::string(pos + 1, pathIter);
|
||||
pos = pathIter;
|
||||
++urlIter;
|
||||
|
||||
auto const pathIter = std::find(urlIter, url.end(), '?');
|
||||
m_encodedPath = std::string(urlIter, pathIter);
|
||||
|
||||
urlIter = pathIter;
|
||||
}
|
||||
|
||||
if (pos != url.end() && *pos == '?')
|
||||
if (urlIter != url.end() && *urlIter == '?')
|
||||
{
|
||||
auto queryIter = std::find(pos + 1, url.end(), '#');
|
||||
AppendQueryParameters(std::string(pos + 1, queryIter));
|
||||
pos = queryIter;
|
||||
++urlIter;
|
||||
AppendQueryParameters(std::string(urlIter, std::find(urlIter, url.end(), '#')));
|
||||
}
|
||||
}
|
||||
|
||||
std::string Url::Decode(const std::string& value)
|
||||
std::string Url::Decode(std::string const& value)
|
||||
{
|
||||
const static std::vector<int> hexTable = []() {
|
||||
std::vector<int> t(256, -1);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
t[static_cast<size_t>('0') + i] = i;
|
||||
}
|
||||
for (int i = 10; i < 16; ++i)
|
||||
{
|
||||
t[static_cast<size_t>('A') + i - 10] = i;
|
||||
t[static_cast<size_t>('a') + i - 10] = i;
|
||||
}
|
||||
return t;
|
||||
}();
|
||||
|
||||
std::string decodedValue;
|
||||
for (size_t i = 0; i < value.size();)
|
||||
auto const valueSize = value.size();
|
||||
for (size_t i = 0; i < valueSize; ++i)
|
||||
{
|
||||
char c = value[i];
|
||||
if (c == '+')
|
||||
auto const c = value[i];
|
||||
switch (c)
|
||||
{
|
||||
decodedValue += ' ';
|
||||
++i;
|
||||
}
|
||||
else if (c == '%')
|
||||
{
|
||||
if (i + 2 >= value.size() || hexTable[value[i + 1]] < 0 || hexTable[value[i + 2]] < 0)
|
||||
{
|
||||
throw std::runtime_error("failed when decoding URL component");
|
||||
}
|
||||
int v = (hexTable[value[i + 1]] << 4) + hexTable[value[i + 2]];
|
||||
decodedValue += static_cast<std::string::value_type>(v);
|
||||
i += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
decodedValue += value[i];
|
||||
++i;
|
||||
case '%':
|
||||
if ((valueSize - i) < 3 // need at least 3 characters: "%XY"
|
||||
|| !std::isxdigit(value[i + 1], std::locale::classic())
|
||||
|| !std::isxdigit(value[i + 2], std::locale::classic()))
|
||||
{
|
||||
throw std::runtime_error("failed when decoding URL component");
|
||||
}
|
||||
|
||||
decodedValue += static_cast<char>(std::stoi(value.substr(i + 1, 2), nullptr, 16));
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case '+':
|
||||
decodedValue += ' ';
|
||||
break;
|
||||
|
||||
default:
|
||||
decodedValue += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return decodedValue;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool ShouldEncode(char c)
|
||||
{
|
||||
static std::unordered_set<char> const ExtraNonEncodableChars = {'-', '.', '_', '~'};
|
||||
|
||||
return !std::isalnum(c, std::locale::classic())
|
||||
&& ExtraNonEncodableChars.find(c) == ExtraNonEncodableChars.end();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string Url::Encode(const std::string& value, const std::string& doNotEncodeSymbols)
|
||||
{
|
||||
const char* hex = "0123456789ABCDEF";
|
||||
std::unordered_set<unsigned char> noEncodingSymbolsSet(
|
||||
auto const Hex = "0123456789ABCDEF";
|
||||
|
||||
std::unordered_set<char> const doNotEncodeSymbolsSet(
|
||||
doNotEncodeSymbols.begin(), doNotEncodeSymbols.end());
|
||||
|
||||
std::string encoded;
|
||||
for (char c : value)
|
||||
for (auto const c : value)
|
||||
{
|
||||
unsigned char uc = c;
|
||||
// encode if char is not in the default non-encoding set AND if it is NOT in chars to ignore
|
||||
// from user input
|
||||
if (defaultNonUrlEncodeChars.find(uc) == defaultNonUrlEncodeChars.end()
|
||||
&& noEncodingSymbolsSet.find(uc) == noEncodingSymbolsSet.end())
|
||||
if (ShouldEncode(c) && doNotEncodeSymbolsSet.find(c) == doNotEncodeSymbolsSet.end())
|
||||
{
|
||||
auto const u8 = static_cast<uint8_t>(c);
|
||||
|
||||
encoded += '%';
|
||||
encoded += hex[(uc >> 4) & 0x0f];
|
||||
encoded += hex[uc & 0x0f];
|
||||
encoded += Hex[(u8 >> 4) & 0x0f];
|
||||
encoded += Hex[u8 & 0x0f];
|
||||
}
|
||||
else
|
||||
{
|
||||
encoded += c;
|
||||
}
|
||||
}
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
@ -219,9 +239,3 @@ std::string Url::GetAbsoluteUrl() const
|
||||
return GetUrlWithoutQuery(false)
|
||||
+ _detail::FormatEncodedUrlQueryParameters(m_encodedQueryParameters);
|
||||
}
|
||||
|
||||
const std::unordered_set<unsigned char> Url::defaultNonUrlEncodeChars
|
||||
= {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
|
||||
'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
|
||||
'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.', '_', '~'};
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
#include "azure/core/http/policies/policy.hpp"
|
||||
#include "azure/core/internal/tracing/service_tracing.hpp"
|
||||
#include "azure/core/platform.hpp"
|
||||
#include <cctype>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
@ -133,10 +133,10 @@ std::string GetOSVersion()
|
||||
|
||||
std::string TrimString(std::string s)
|
||||
{
|
||||
auto const isSpace = [](int c) { return !std::isspace(c); };
|
||||
auto const isNotSpace = [](char c) { return !std::isspace(c, std::locale::classic()); };
|
||||
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), isSpace));
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), isSpace).base(), s.end());
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), isNotSpace));
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), isNotSpace).base(), s.end());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -1,133 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "azure/core/internal/strings.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
// The locale invariant case table is generated with the following program
|
||||
// followed by clang-format.
|
||||
#if 0
|
||||
// generate with
|
||||
// cl /EHsc /W4 /WX .\casetable.cpp && .\casetable.exe
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <stdio.h>
|
||||
|
||||
const int *PrintUpToLine(const int *first, const int *const last) {
|
||||
if (first != last) {
|
||||
printf("0x%02X,", *first);
|
||||
for (ptrdiff_t idx = 1; ++first != last && idx < 8; ++idx) {
|
||||
printf(" 0x%02X,", *first);
|
||||
}
|
||||
|
||||
puts("");
|
||||
}
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
int main() {
|
||||
using namespace std;
|
||||
int characters[256];
|
||||
iota(begin(characters), end(characters), 0);
|
||||
for (ptrdiff_t idx = 'a'; idx <= 'z'; ++idx) {
|
||||
characters[idx] = idx - ('a' - 'A');
|
||||
}
|
||||
|
||||
const int *first = begin(characters);
|
||||
const auto last = first + size(characters);
|
||||
printf("{");
|
||||
while (first != last) {
|
||||
first = PrintUpToLine(first, last);
|
||||
}
|
||||
printf("};");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const unsigned char LocaleInvariantLowercaseTable[256] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
|
||||
0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
|
||||
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
|
||||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
|
||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
|
||||
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
|
||||
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
|
||||
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
|
||||
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
|
||||
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
|
||||
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
|
||||
};
|
||||
const unsigned char LocaleInvariantUppercaseTable[256] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
|
||||
0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
|
||||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
|
||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
|
||||
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
|
||||
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
|
||||
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
|
||||
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
|
||||
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
|
||||
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
|
||||
};
|
||||
} // unnamed namespace
|
||||
|
||||
namespace Azure { namespace Core { namespace _internal {
|
||||
|
||||
unsigned char StringExtensions::ToLower(unsigned char const symbol) noexcept
|
||||
{
|
||||
return LocaleInvariantLowercaseTable[symbol];
|
||||
}
|
||||
|
||||
std::string const StringExtensions::ToLower(const std::string& src) noexcept
|
||||
{
|
||||
auto result = std::string(src);
|
||||
std::transform(result.begin(), result.end(), result.begin(), [](char const ch) {
|
||||
return StringExtensions::ToLower(ch);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned char StringExtensions::ToUpper(unsigned char const symbol) noexcept
|
||||
{
|
||||
return LocaleInvariantUppercaseTable[symbol];
|
||||
}
|
||||
|
||||
std::string const StringExtensions::ToUpper(const std::string& src) noexcept
|
||||
{
|
||||
auto result = std::string(src);
|
||||
std::transform(result.begin(), result.end(), result.begin(), [](char const ch) {
|
||||
return StringExtensions::ToUpper(ch);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StringExtensions::LocaleInvariantCaseInsensitiveEqual(
|
||||
const std::string& lhs,
|
||||
const std::string& rhs) noexcept
|
||||
{
|
||||
return std::equal(
|
||||
lhs.begin(),
|
||||
lhs.end(),
|
||||
rhs.begin(),
|
||||
rhs.end(),
|
||||
[](const char left, const char right) noexcept {
|
||||
return LocaleInvariantLowercaseTable[static_cast<unsigned char>(left)]
|
||||
== LocaleInvariantLowercaseTable[static_cast<unsigned char>(right)];
|
||||
});
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Core::_internal
|
||||
@ -5,7 +5,6 @@
|
||||
#include "azure/core/http/policies/policy.hpp"
|
||||
#include "azure/core/internal/tracing/service_tracing.hpp"
|
||||
#include "azure/core/internal/tracing/tracing_impl.hpp"
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
|
||||
namespace Azure { namespace Core { namespace Tracing { namespace _internal {
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
|
||||
#include <azure/core/internal/strings.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <locale>
|
||||
#include <string>
|
||||
|
||||
TEST(String, invariantCompare)
|
||||
@ -23,18 +25,20 @@ TEST(String, invariantCompare)
|
||||
TEST(String, toLowerC)
|
||||
{
|
||||
using Azure::Core::_internal::StringExtensions;
|
||||
for (unsigned char ch = 0; ch < 255; ch += 1)
|
||||
for (unsigned i = 0; i <= 255; ++i)
|
||||
{
|
||||
EXPECT_TRUE(StringExtensions::ToLower(ch) == std::tolower(ch));
|
||||
auto const c = static_cast<char>(static_cast<unsigned char>(i));
|
||||
EXPECT_TRUE(StringExtensions::ToLower(c) == std::tolower(c, std::locale::classic()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(String, toUpperC)
|
||||
{
|
||||
using Azure::Core::_internal::StringExtensions;
|
||||
for (unsigned char ch = 0; ch < 255; ch += 1)
|
||||
for (unsigned i = 0; i <= 255; ++i)
|
||||
{
|
||||
EXPECT_TRUE(StringExtensions::ToUpper(ch) == std::toupper(ch));
|
||||
auto const c = static_cast<char>(static_cast<unsigned char>(i));
|
||||
EXPECT_TRUE(StringExtensions::ToUpper(c) == std::toupper(c, std::locale::classic()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
#include <azure/core/internal/unique_handle.hpp>
|
||||
#include <azure/core/platform.hpp>
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <locale>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
@ -63,7 +63,7 @@ void ThrowIfNotSafeCmdLineInput(std::string const& input, std::string const& des
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!std::isalnum(c))
|
||||
if (!std::isalnum(c, std::locale::classic()))
|
||||
{
|
||||
throw AuthenticationException(
|
||||
"AzureCliCredential: Unsafe command line input found in " + description + ": "
|
||||
|
||||
@ -62,17 +62,16 @@ KeyRotationPolicy _detail::KeyRotationPolicySerializer::KeyRotationPolicyDeseria
|
||||
action[_detail::TriggerActionsValue],
|
||||
_detail::TBEActionsValue);
|
||||
|
||||
auto actionType = action[_detail::ActionActionsValue][TypeActionsValue].get<std::string>();
|
||||
actionType = Azure::Core::_internal::StringExtensions::ToLower(actionType);
|
||||
auto const actionType
|
||||
= action[_detail::ActionActionsValue][TypeActionsValue].get<std::string>();
|
||||
|
||||
if (actionType
|
||||
== Azure::Core::_internal::StringExtensions::ToLower(_detail::RotateActionsValue))
|
||||
if (Azure::Core::_internal::StringExtensions::LocaleInvariantCaseInsensitiveEqual(
|
||||
actionType, _detail::RotateActionsValue))
|
||||
{
|
||||
currentAction.Action = LifetimeActionType::Rotate;
|
||||
}
|
||||
else if (
|
||||
actionType
|
||||
== Azure::Core::_internal::StringExtensions::ToLower(_detail::NotifyActionsValue))
|
||||
else if (Azure::Core::_internal::StringExtensions::LocaleInvariantCaseInsensitiveEqual(
|
||||
actionType, _detail::NotifyActionsValue))
|
||||
{
|
||||
currentAction.Action = LifetimeActionType::Notify;
|
||||
}
|
||||
|
||||
@ -13,12 +13,12 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <azure/core/case_insensitive_containers.hpp>
|
||||
#include <azure/core/context.hpp>
|
||||
#include <azure/core/datetime.hpp>
|
||||
#include <azure/core/etag.hpp>
|
||||
#include <azure/core/http/http.hpp>
|
||||
#include <azure/core/internal/http/pipeline.hpp>
|
||||
#include <azure/core/internal/strings.hpp>
|
||||
#include <azure/core/io/body_stream.hpp>
|
||||
#include <azure/core/nullable.hpp>
|
||||
#include <azure/core/response.hpp>
|
||||
@ -409,11 +409,7 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
/**
|
||||
* A set of name-value pairs associated with this blob or blob container.
|
||||
*/
|
||||
std::map<
|
||||
std::string,
|
||||
std::string,
|
||||
Core::_internal::StringExtensions::CaseInsensitiveComparator>
|
||||
Metadata;
|
||||
Core::CaseInsensitiveMap Metadata;
|
||||
};
|
||||
/**
|
||||
* @brief An Azure Storage container.
|
||||
@ -648,11 +644,7 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
/**
|
||||
* A set of name-value pair associated with this blob container.
|
||||
*/
|
||||
std::map<
|
||||
std::string,
|
||||
std::string,
|
||||
Core::_internal::StringExtensions::CaseInsensitiveComparator>
|
||||
Metadata;
|
||||
Core::CaseInsensitiveMap Metadata;
|
||||
/**
|
||||
* The ETag contains a value that you can use to perform operations conditionally. If the
|
||||
* request version is 2011-08-18 or newer, the ETag value will be in quotes.
|
||||
@ -1289,11 +1281,7 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
/**
|
||||
* A set of name-value pairs associated with this blob or blob container.
|
||||
*/
|
||||
std::map<
|
||||
std::string,
|
||||
std::string,
|
||||
Core::_internal::StringExtensions::CaseInsensitiveComparator>
|
||||
Metadata;
|
||||
Core::CaseInsensitiveMap Metadata;
|
||||
/**
|
||||
* User-defined tags for this blob.
|
||||
*/
|
||||
@ -1480,11 +1468,7 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
/**
|
||||
* A set of name-value pairs associated with this blob or blob container.
|
||||
*/
|
||||
std::map<
|
||||
std::string,
|
||||
std::string,
|
||||
Core::_internal::StringExtensions::CaseInsensitiveComparator>
|
||||
Metadata;
|
||||
Core::CaseInsensitiveMap Metadata;
|
||||
/**
|
||||
* The current sequence number for a page blob.
|
||||
*/
|
||||
@ -1653,11 +1637,7 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
/**
|
||||
* A set of name-value pair associated with this blob.
|
||||
*/
|
||||
std::map<
|
||||
std::string,
|
||||
std::string,
|
||||
Core::_internal::StringExtensions::CaseInsensitiveComparator>
|
||||
Metadata;
|
||||
Core::CaseInsensitiveMap Metadata;
|
||||
/**
|
||||
* Optional. Only valid when Object Replication is enabled for the storage container and on
|
||||
* the destination blob of the replication.
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
#include "azure/storage/common/internal/shared_key_policy.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
#include <azure/core/http/http.hpp>
|
||||
#include <azure/core/internal/strings.hpp>
|
||||
@ -32,7 +31,7 @@ namespace Azure { namespace Storage { namespace _internal {
|
||||
"If-Unmodified-Since",
|
||||
"Range"})
|
||||
{
|
||||
auto ite = headers.find(Azure::Core::_internal::StringExtensions::ToLower(headerName));
|
||||
auto ite = headers.find(headerName);
|
||||
if (ite != headers.end())
|
||||
{
|
||||
if (headerName == "Content-Length" && ite->second == "0")
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
#include "test_base.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
@ -13,12 +13,12 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <azure/core/case_insensitive_containers.hpp>
|
||||
#include <azure/core/context.hpp>
|
||||
#include <azure/core/datetime.hpp>
|
||||
#include <azure/core/etag.hpp>
|
||||
#include <azure/core/http/http.hpp>
|
||||
#include <azure/core/internal/http/pipeline.hpp>
|
||||
#include <azure/core/internal/strings.hpp>
|
||||
#include <azure/core/io/body_stream.hpp>
|
||||
#include <azure/core/nullable.hpp>
|
||||
#include <azure/core/response.hpp>
|
||||
@ -316,11 +316,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
/**
|
||||
* A set of name-value pairs associated with the share or file.
|
||||
*/
|
||||
std::map<
|
||||
std::string,
|
||||
std::string,
|
||||
Core::_internal::StringExtensions::CaseInsensitiveComparator>
|
||||
Metadata;
|
||||
Core::CaseInsensitiveMap Metadata;
|
||||
/**
|
||||
* Properties of a share.
|
||||
*/
|
||||
@ -409,11 +405,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
/**
|
||||
* A set of name-value pairs that contain the user-defined metadata of the share.
|
||||
*/
|
||||
std::map<
|
||||
std::string,
|
||||
std::string,
|
||||
Core::_internal::StringExtensions::CaseInsensitiveComparator>
|
||||
Metadata;
|
||||
Core::CaseInsensitiveMap Metadata;
|
||||
/**
|
||||
* The ETag contains a value that you can use to perform operations conditionally, in quotes.
|
||||
*/
|
||||
@ -886,11 +878,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
/**
|
||||
* A set of name-value pairs that contain metadata for the directory.
|
||||
*/
|
||||
std::map<
|
||||
std::string,
|
||||
std::string,
|
||||
Core::_internal::StringExtensions::CaseInsensitiveComparator>
|
||||
Metadata;
|
||||
Core::CaseInsensitiveMap Metadata;
|
||||
/**
|
||||
* The ETag contains a value that you can use to perform operations conditionally, in quotes.
|
||||
*/
|
||||
@ -1290,11 +1278,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
/**
|
||||
* A set of name-value pairs associated with the share or file.
|
||||
*/
|
||||
std::map<
|
||||
std::string,
|
||||
std::string,
|
||||
Core::_internal::StringExtensions::CaseInsensitiveComparator>
|
||||
Metadata;
|
||||
Core::CaseInsensitiveMap Metadata;
|
||||
/**
|
||||
* String identifier for this copy operation. Use with Get File Properties to check the
|
||||
* status of this copy operation, or pass to Abort Copy File to abort a pending copy.
|
||||
@ -1404,11 +1388,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
|
||||
/**
|
||||
* A set of name-value pairs associated with this file as user-defined metadata.
|
||||
*/
|
||||
std::map<
|
||||
std::string,
|
||||
std::string,
|
||||
Core::_internal::StringExtensions::CaseInsensitiveComparator>
|
||||
Metadata;
|
||||
Core::CaseInsensitiveMap Metadata;
|
||||
/**
|
||||
* The size of the file in bytes. This header returns the value of the 'x-ms-content-length'
|
||||
* header that is stored with the file.
|
||||
|
||||
@ -12,10 +12,10 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <azure/core/case_insensitive_containers.hpp>
|
||||
#include <azure/core/context.hpp>
|
||||
#include <azure/core/datetime.hpp>
|
||||
#include <azure/core/internal/http/pipeline.hpp>
|
||||
#include <azure/core/internal/strings.hpp>
|
||||
#include <azure/core/nullable.hpp>
|
||||
#include <azure/core/response.hpp>
|
||||
#include <azure/core/url.hpp>
|
||||
@ -211,11 +211,7 @@ namespace Azure { namespace Storage { namespace Queues {
|
||||
/**
|
||||
* A set of name-value pairs associated with this queue.
|
||||
*/
|
||||
std::map<
|
||||
std::string,
|
||||
std::string,
|
||||
Core::_internal::StringExtensions::CaseInsensitiveComparator>
|
||||
Metadata;
|
||||
Core::CaseInsensitiveMap Metadata;
|
||||
};
|
||||
/**
|
||||
* @brief Include this parameter to specify that the queues' metadata be returned as part of the
|
||||
@ -290,11 +286,7 @@ namespace Azure { namespace Storage { namespace Queues {
|
||||
*/
|
||||
struct QueueProperties final
|
||||
{
|
||||
std::map<
|
||||
std::string,
|
||||
std::string,
|
||||
Core::_internal::StringExtensions::CaseInsensitiveComparator>
|
||||
Metadata;
|
||||
Core::CaseInsensitiveMap Metadata;
|
||||
/**
|
||||
* The approximate number of messages in the queue. This number is not lower than the actual
|
||||
* number of messages in the queue, but could be higher.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user