aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Uri.cpp62
-rw-r--r--test/src/UriTests.cpp7
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) {