diff options
-rw-r--r-- | src/Uri.cpp | 62 | ||||
-rw-r--r-- | test/src/UriTests.cpp | 7 |
2 files changed, 64 insertions, 5 deletions
diff --git a/src/Uri.cpp b/src/Uri.cpp index 4e3cba0..3091b89 100644 --- a/src/Uri.cpp +++ b/src/Uri.cpp @@ -483,6 +483,58 @@ namespace { } /** + * This function returns the hex digit that corresponds + * to the given value. + * + * @param[in] value + * This is the value to convert to a hex digit. + * + * @return + * The hex digit corresponding to the given value is returned. + */ + char MakeHexDigit(unsigned int value) { + if (value < 10) { + return (char)(value + '0'); + } else { + return (char)(value - 10 + 'A'); + } + } + + /** + * This method encodes the given URI element. + * What we are calling a "URI element" is any part of the URI + * which is a sequence of characters that: + * - may be percent-encoded + * - if not percent-encoded, are in a restricted set of characters + * + * @param[in] element + * This is the element to encode. + * + * @param[in] allowedCharacters + * This is the set of characters that do not need to + * be percent-encoded. + * + * @return + * The encoded element is returned. + */ + std::string EncodeElement( + const std::string& element, + const Uri::CharacterSet& allowedCharacters + ) { + std::string encodedElement; + for (auto c: element) { + if (allowedCharacters.Contains(c)) { + encodedElement.push_back(c); + } else { + encodedElement.push_back('%'); + encodedElement.push_back(MakeHexDigit((unsigned int)c >> 4)); + encodedElement.push_back(MakeHexDigit((unsigned int)c & 0x0F)); + } + } + return encodedElement; + } + + /** * This method checks and decodes the given query or fragment. * * @param[in,out] queryOrFragment @@ -1337,13 +1389,13 @@ namespace Uri { if (impl_->HasAuthority()) { buffer << "//"; if (!impl_->userInfo.empty()) { - buffer << impl_->userInfo << '@'; + buffer << EncodeElement(impl_->userInfo, USER_INFO_NOT_PCT_ENCODED) << '@'; } if (!impl_->host.empty()) { if (ValidateIpv6Address(impl_->host)) { buffer << '[' << impl_->host << ']'; } else { - buffer << impl_->host; + buffer << EncodeElement(impl_->host, REG_NAME_NOT_PCT_ENCODED); } } if (impl_->hasPort) { @@ -1359,17 +1411,17 @@ namespace Uri { } size_t i = 0; for (const auto& segment: impl_->path) { - buffer << segment; + buffer << EncodeElement(segment, PCHAR_NOT_PCT_ENCODED); if (i + 1 < impl_->path.size()) { buffer << '/'; } ++i; } if (impl_->hasQuery) { - buffer << '?' << impl_->query; + buffer << '?' << EncodeElement(impl_->query, QUERY_OR_FRAGMENT_NOT_PCT_ENCODED); } if (impl_->hasFragment) { - buffer << '#' << impl_->fragment; + buffer << '#' << EncodeElement(impl_->fragment, QUERY_OR_FRAGMENT_NOT_PCT_ENCODED); } return buffer.str(); } diff --git a/test/src/UriTests.cpp b/test/src/UriTests.cpp index 6e93a4e..2e5f2d6 100644 --- a/test/src/UriTests.cpp +++ b/test/src/UriTests.cpp @@ -796,6 +796,13 @@ TEST(UriTests, GenerateString) { {"http", "bob", "", false, 0, {}, true, "foobar", false, "", "http://bob@?foobar"}, {"", "bob", "", false, 0, {}, true, "foobar", false, "", "//bob@?foobar"}, {"", "bob", "", false, 0, {}, false, "", false, "", "//bob@"}, + + // percent-encoded character test vectors + {"http", "b b", "www.example.com", true, 8080, {"", "abc", "def"}, true, "foobar", true, "ch2", "http://b%20b@www.example.com:8080/abc/def?foobar#ch2"}, + {"http", "bob", "www.e ample.com", true, 8080, {"", "abc", "def"}, true, "foobar", true, "ch2", "http://bob@www.e%20ample.com:8080/abc/def?foobar#ch2"}, + {"http", "bob", "www.example.com", true, 8080, {"", "a c", "def"}, true, "foobar", true, "ch2", "http://bob@www.example.com:8080/a%20c/def?foobar#ch2"}, + {"http", "bob", "www.example.com", true, 8080, {"", "abc", "def"}, true, "foo ar", true, "ch2", "http://bob@www.example.com:8080/abc/def?foo%20ar#ch2"}, + {"http", "bob", "www.example.com", true, 8080, {"", "abc", "def"}, true, "foobar", true, "c 2", "http://bob@www.example.com:8080/abc/def?foobar#c%202"}, }; size_t index = 0; for (const auto& testVector : testVectors) { |