From 557b025ad5999ac530d69aaeea6d4f197f5cb630 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Tue, 9 Oct 2018 22:51:54 +0300 Subject: [PATCH 1/6] added URI resolution according to RFC3986, Section 5 (https://tools.ietf.org/html/rfc3986#section-5) --- Release/include/cpprest/base_uri.h | 9 +- Release/src/uri/uri.cpp | 90 +++++++++++++++++++ Release/tests/functional/uri/CMakeLists.txt | 1 + .../functional/uri/resolve_uri_tests.cpp | 69 ++++++++++++++ 4 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 Release/tests/functional/uri/resolve_uri_tests.cpp diff --git a/Release/include/cpprest/base_uri.h b/Release/include/cpprest/base_uri.h index b5fc8fcfd0..bf3523026e 100644 --- a/Release/include/cpprest/base_uri.h +++ b/Release/include/cpprest/base_uri.h @@ -379,12 +379,18 @@ namespace web { /// /// Returns the full (encoded) URI as a string. /// - /// The full encoded URI string. + /// The full encoded URI string. utility::string_t to_string() const { return m_uri; } + /// + /// Returns an URI resolved according to RFC3986, Section 5 (https://tools.ietf.org/html/rfc3986#section-5). + /// + /// The new resolved URI. + _ASYNCRTIMP utility::string_t resolve_uri(const utility::string_t &relativeUri) const; + _ASYNCRTIMP bool operator == (const uri &other) const; bool operator < (const uri &other) const @@ -414,3 +420,4 @@ namespace web { }; } // namespace web + diff --git a/Release/src/uri/uri.cpp b/Release/src/uri/uri.cpp index 737ed12e1b..5b5aa8bbf5 100644 --- a/Release/src/uri/uri.cpp +++ b/Release/src/uri/uri.cpp @@ -423,6 +423,53 @@ namespace return encoded; } + // 5.2.3. Merge Paths https://tools.ietf.org/html/rfc3986#section-5.2.3 + std::wstring mergePaths(const std::wstring &base, const std::wstring &relative) + { + const auto lastSlash = base.rfind(L'/'); + if (lastSlash == std::wstring::npos) + { + return base + L'/' + relative; + } + else if (lastSlash == base.size() - 1) + { + return base + relative; + } + // path contains and does not end with '/', we remove segment after last '/' + return base.substr(0, lastSlash + 1) + relative; + } + + // 5.2.4. Remove Dot Segments https://tools.ietf.org/html/rfc3986#section-5.2.4 + void removeDotSegments(web::uri_builder &builder) + { + if (builder.path().find(L'.') == std::wstring::npos) + return; + + const auto segments = web::uri::split_path(builder.path()); + std::vector> result; + for (auto& segment : segments) + { + if (segment == _XPLATSTR(".")) + continue; + else if (segment != _XPLATSTR("..")) + result.push_back(segment); + else if (!result.empty()) + result.pop_back(); + } + if (result.empty()) + { + builder.set_path(utility::string_t()); + return; + } + utility::stringstream_t path; + path << result.front().get(); + for (size_t i = 1; i != result.size(); ++i) + path << L'/' << result[i].get(); + if (segments.back() == L".." || segments.back() == L"." || builder.path().back() == L'/') + path << L'/'; + + builder.set_path(path.str()); + } } utility::string_t uri_components::join() @@ -784,4 +831,47 @@ bool uri::operator == (const uri &other) const return true; } +//resolving URI according to RFC3986, Section 5 https://tools.ietf.org/html/rfc3986#section-5 +utility::string_t uri::resolve_uri(const utility::string_t &relativeUri) const +{ + if (relativeUri.empty()) + return to_string(); + + if (relativeUri[0] == _XPLATSTR('/')) // starts with '/' + { + if (relativeUri.size() >= 2 && relativeUri[1] == _XPLATSTR('/')) // starts with '//' + return scheme() + L':' + relativeUri; + + // otherwise relative to root + auto builder = web::uri_builder(this->authority()); + builder.append(relativeUri); + details::removeDotSegments(builder); + return builder.to_string(); + } + + const auto url = web::uri(relativeUri); + if (!url.scheme().empty()) + return relativeUri; + + if (!url.authority().is_empty()) + return web::uri_builder(url).set_scheme(this->scheme()).to_string(); + + // relative url + auto builder = web::uri_builder(*this); + if (url.path() == L"/" || url.path().empty()) // web::uri considers empty path as '/' + { + if (!url.query().empty()) + builder.set_query(url.query()); + } + else if (!this->path().empty()) + { + builder.set_path(details::mergePaths(this->path(), url.path())); + details::removeDotSegments(builder); + builder.set_query(url.query()); + } + + return builder.set_fragment(url.fragment()).to_string(); +} + } + diff --git a/Release/tests/functional/uri/CMakeLists.txt b/Release/tests/functional/uri/CMakeLists.txt index f869e24d18..54d80ac254 100644 --- a/Release/tests/functional/uri/CMakeLists.txt +++ b/Release/tests/functional/uri/CMakeLists.txt @@ -8,6 +8,7 @@ set(SOURCES operator_tests.cpp splitting_tests.cpp uri_builder_tests.cpp + resolve_uri_tests.cpp stdafx.cpp ) diff --git a/Release/tests/functional/uri/resolve_uri_tests.cpp b/Release/tests/functional/uri/resolve_uri_tests.cpp new file mode 100644 index 0000000000..fc2ef899e6 --- /dev/null +++ b/Release/tests/functional/uri/resolve_uri_tests.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" + +using namespace web; +using namespace utility; + +namespace tests { namespace functional { namespace uri_tests { + +//testing resolution against examples from Section 5.4 https://tools.ietf.org/html/rfc3986#section-5.4 +SUITE(resolve_uri_tests) +{ +//5.4.1. Normal Examples https://tools.ietf.org/html/rfc3986#section-5.4.1 +TEST(resolve_uri_normal) +{ + const uri baseUri = U("http://a/b/c/d;p?q"); + + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g:h")), U("g:h")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g")), U("http://a/b/c/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./g")), U("http://a/b/c/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g/")), U("http://a/b/c/g/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("/g")), U("http://a/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("//g")), U("http://g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("?y")), U("http://a/b/c/d;p?y")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y")), U("http://a/b/c/g?y")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("#s")), U("http://a/b/c/d;p?q#s")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g#s")), U("http://a/b/c/g#s")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y#s")), U("http://a/b/c/g?y#s")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U(";x")), U("http://a/b/c/;x")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x")), U("http://a/b/c/g;x")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x?y#s")), U("http://a/b/c/g;x?y#s")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("")), U("http://a/b/c/d;p?q")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U(".")), U("http://a/b/c/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./")), U("http://a/b/c/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("..")), U("http://a/b/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../")), U("http://a/b/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../g")), U("http://a/b/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../..")), U("http://a/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../")), U("http://a/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../g")), U("http://a/g")); +} +//5.4.2. Abnormal Examples https://tools.ietf.org/html/rfc3986#section-5.4.2 +TEST(resolve_uri_abnormal) +{ + const uri baseUri = U("http://a/b/c/d;p?q"); + + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../../g")), U("http://a/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../../../g")), U("http://a/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("/./g")), U("http://a/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("/../g")), U("http://a/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g.")), U("http://a/b/c/g.")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U(".g")), U("http://a/b/c/.g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g..")), U("http://a/b/c/g..")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("..g")), U("http://a/b/c/..g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./../g")), U("http://a/b/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./g/.")), U("http://a/b/c/g/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g/./h")), U("http://a/b/c/g/h")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g/../h")), U("http://a/b/c/h")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x=1/./y")), U("http://a/b/c/g;x=1/y")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x=1/../y")), U("http://a/b/c/y")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y/./x")), U("http://a/b/c/g?y/./x")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y/../x")), U("http://a/b/c/g?y/../x")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g#s/./x")), U("http://a/b/c/g#s/./x")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g#s/../x")), U("http://a/b/c/g#s/../x")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("http:g")), U("http:g")); +} + +} // SUITE(resolve_uri_tests) + +}}} + From a34749c70059da43df34f832e5e57c0388dd5b1d Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Tue, 9 Oct 2018 23:07:18 +0300 Subject: [PATCH 2/6] fixed std::wstring -> utility::string_t --- Release/src/uri/uri.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Release/src/uri/uri.cpp b/Release/src/uri/uri.cpp index 5b5aa8bbf5..861f97929f 100644 --- a/Release/src/uri/uri.cpp +++ b/Release/src/uri/uri.cpp @@ -424,10 +424,10 @@ namespace } // 5.2.3. Merge Paths https://tools.ietf.org/html/rfc3986#section-5.2.3 - std::wstring mergePaths(const std::wstring &base, const std::wstring &relative) + utility::string_t mergePaths(const utility::string_t &base, const utility::string_t &relative) { const auto lastSlash = base.rfind(L'/'); - if (lastSlash == std::wstring::npos) + if (lastSlash == utility::string_t::npos) { return base + L'/' + relative; } @@ -442,7 +442,7 @@ namespace // 5.2.4. Remove Dot Segments https://tools.ietf.org/html/rfc3986#section-5.2.4 void removeDotSegments(web::uri_builder &builder) { - if (builder.path().find(L'.') == std::wstring::npos) + if (builder.path().find(L'.') == utility::string_t::npos) return; const auto segments = web::uri::split_path(builder.path()); From a218343ac45541ba8b9dd959bf1ed46b9f84dda0 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Tue, 9 Oct 2018 23:11:44 +0300 Subject: [PATCH 3/6] fixed literals --- Release/src/uri/uri.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Release/src/uri/uri.cpp b/Release/src/uri/uri.cpp index 861f97929f..e5f584cdc5 100644 --- a/Release/src/uri/uri.cpp +++ b/Release/src/uri/uri.cpp @@ -426,10 +426,10 @@ namespace // 5.2.3. Merge Paths https://tools.ietf.org/html/rfc3986#section-5.2.3 utility::string_t mergePaths(const utility::string_t &base, const utility::string_t &relative) { - const auto lastSlash = base.rfind(L'/'); + const auto lastSlash = base.rfind(_XPLATSTR('/')); if (lastSlash == utility::string_t::npos) { - return base + L'/' + relative; + return base + _XPLATSTR('/') + relative; } else if (lastSlash == base.size() - 1) { @@ -442,7 +442,7 @@ namespace // 5.2.4. Remove Dot Segments https://tools.ietf.org/html/rfc3986#section-5.2.4 void removeDotSegments(web::uri_builder &builder) { - if (builder.path().find(L'.') == utility::string_t::npos) + if (builder.path().find(_XPLATSTR('.')) == utility::string_t::npos) return; const auto segments = web::uri::split_path(builder.path()); @@ -464,9 +464,9 @@ namespace utility::stringstream_t path; path << result.front().get(); for (size_t i = 1; i != result.size(); ++i) - path << L'/' << result[i].get(); - if (segments.back() == L".." || segments.back() == L"." || builder.path().back() == L'/') - path << L'/'; + path << _XPLATSTR('/') << result[i].get(); + if (segments.back() == _XPLATSTR("..") || segments.back() == _XPLATSTR(".") || builder.path().back() == _XPLATSTR('/')) + path << _XPLATSTR('/'); builder.set_path(path.str()); } @@ -840,7 +840,7 @@ utility::string_t uri::resolve_uri(const utility::string_t &relativeUri) const if (relativeUri[0] == _XPLATSTR('/')) // starts with '/' { if (relativeUri.size() >= 2 && relativeUri[1] == _XPLATSTR('/')) // starts with '//' - return scheme() + L':' + relativeUri; + return scheme() + _XPLATSTR(':') + relativeUri; // otherwise relative to root auto builder = web::uri_builder(this->authority()); @@ -858,7 +858,7 @@ utility::string_t uri::resolve_uri(const utility::string_t &relativeUri) const // relative url auto builder = web::uri_builder(*this); - if (url.path() == L"/" || url.path().empty()) // web::uri considers empty path as '/' + if (url.path() == _XPLATSTR("/") || url.path().empty()) // web::uri considers empty path as '/' { if (!url.query().empty()) builder.set_query(url.query()); From f78bc6970a29ebf635f3903fdd048d07a1a10df3 Mon Sep 17 00:00:00 2001 From: Pavel Novikov Date: Wed, 10 Oct 2018 13:29:47 +0300 Subject: [PATCH 4/6] implemented suggestions by @BillyONeal; improved doc, cleaned up code, ensured line ending consistency --- Release/include/cpprest/base_uri.h | 9 ++- Release/src/uri/uri.cpp | 45 ++++++----- .../functional/uri/resolve_uri_tests.cpp | 75 +++++++++---------- 3 files changed, 67 insertions(+), 62 deletions(-) diff --git a/Release/include/cpprest/base_uri.h b/Release/include/cpprest/base_uri.h index bf3523026e..91a4d47bd0 100644 --- a/Release/include/cpprest/base_uri.h +++ b/Release/include/cpprest/base_uri.h @@ -386,9 +386,11 @@ namespace web { } /// - /// Returns an URI resolved according to RFC3986, Section 5 (https://tools.ietf.org/html/rfc3986#section-5). + /// Returns an URI resolved against this as the base URI + /// according to RFC3986, Section 5 (https://tools.ietf.org/html/rfc3986#section-5). /// - /// The new resolved URI. + /// The relative URI to be resolved against this as base. + /// The new resolved URI string. _ASYNCRTIMP utility::string_t resolve_uri(const utility::string_t &relativeUri) const; _ASYNCRTIMP bool operator == (const uri &other) const; @@ -419,5 +421,4 @@ namespace web { details::uri_components m_components; }; -} // namespace web - +} // namespace web diff --git a/Release/src/uri/uri.cpp b/Release/src/uri/uri.cpp index e5f584cdc5..7053818921 100644 --- a/Release/src/uri/uri.cpp +++ b/Release/src/uri/uri.cpp @@ -440,18 +440,22 @@ namespace } // 5.2.4. Remove Dot Segments https://tools.ietf.org/html/rfc3986#section-5.2.4 - void removeDotSegments(web::uri_builder &builder) + void removeDotSegments(uri_builder &builder) { if (builder.path().find(_XPLATSTR('.')) == utility::string_t::npos) return; - const auto segments = web::uri::split_path(builder.path()); + const utility::string_t + dotSegment = _XPLATSTR("."), + dotDotSegment = _XPLATSTR(".."); + + const auto segments = uri::split_path(builder.path()); std::vector> result; for (auto& segment : segments) { - if (segment == _XPLATSTR(".")) + if (segment == dotSegment) continue; - else if (segment != _XPLATSTR("..")) + else if (segment != dotDotSegment) result.push_back(segment); else if (!result.empty()) result.pop_back(); @@ -461,16 +465,18 @@ namespace builder.set_path(utility::string_t()); return; } - utility::stringstream_t path; - path << result.front().get(); + utility::string_t path = result.front().get(); for (size_t i = 1; i != result.size(); ++i) - path << _XPLATSTR('/') << result[i].get(); - if (segments.back() == _XPLATSTR("..") || segments.back() == _XPLATSTR(".") || builder.path().back() == _XPLATSTR('/')) - path << _XPLATSTR('/'); + { + path += _XPLATSTR('/'); + path += result[i].get(); + } + if (segments.back() == dotDotSegment || segments.back() == dotSegment || builder.path().back() == _XPLATSTR('/')) + path += _XPLATSTR('/'); - builder.set_path(path.str()); + builder.set_path(std::move(path)); } -} +} // namespace utility::string_t uri_components::join() { @@ -538,7 +544,7 @@ utility::string_t uri_components::join() return ret; } -} +} // namespace details uri::uri(const details::uri_components &components) : m_components(components) { @@ -840,25 +846,25 @@ utility::string_t uri::resolve_uri(const utility::string_t &relativeUri) const if (relativeUri[0] == _XPLATSTR('/')) // starts with '/' { if (relativeUri.size() >= 2 && relativeUri[1] == _XPLATSTR('/')) // starts with '//' - return scheme() + _XPLATSTR(':') + relativeUri; + return this->scheme() + _XPLATSTR(':') + relativeUri; // otherwise relative to root - auto builder = web::uri_builder(this->authority()); + auto builder = uri_builder(this->authority()); builder.append(relativeUri); details::removeDotSegments(builder); return builder.to_string(); } - const auto url = web::uri(relativeUri); + const auto url = uri(relativeUri); if (!url.scheme().empty()) return relativeUri; if (!url.authority().is_empty()) - return web::uri_builder(url).set_scheme(this->scheme()).to_string(); + return uri_builder(url).set_scheme(this->scheme()).to_string(); // relative url - auto builder = web::uri_builder(*this); - if (url.path() == _XPLATSTR("/") || url.path().empty()) // web::uri considers empty path as '/' + auto builder = uri_builder(*this); + if (url.path() == _XPLATSTR("/") || url.path().empty()) // web::uri considers empty path as '/' { if (!url.query().empty()) builder.set_query(url.query()); @@ -873,5 +879,4 @@ utility::string_t uri::resolve_uri(const utility::string_t &relativeUri) const return builder.set_fragment(url.fragment()).to_string(); } -} - +} // namespace web diff --git a/Release/tests/functional/uri/resolve_uri_tests.cpp b/Release/tests/functional/uri/resolve_uri_tests.cpp index fc2ef899e6..963be8656f 100644 --- a/Release/tests/functional/uri/resolve_uri_tests.cpp +++ b/Release/tests/functional/uri/resolve_uri_tests.cpp @@ -13,28 +13,28 @@ TEST(resolve_uri_normal) { const uri baseUri = U("http://a/b/c/d;p?q"); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g:h")), U("g:h")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g")), U("http://a/b/c/g")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./g")), U("http://a/b/c/g")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g/")), U("http://a/b/c/g/")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("/g")), U("http://a/g")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("//g")), U("http://g")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("?y")), U("http://a/b/c/d;p?y")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y")), U("http://a/b/c/g?y")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("#s")), U("http://a/b/c/d;p?q#s")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g#s")), U("http://a/b/c/g#s")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y#s")), U("http://a/b/c/g?y#s")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U(";x")), U("http://a/b/c/;x")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x")), U("http://a/b/c/g;x")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x?y#s")), U("http://a/b/c/g;x?y#s")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("")), U("http://a/b/c/d;p?q")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U(".")), U("http://a/b/c/")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./")), U("http://a/b/c/")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("..")), U("http://a/b/")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../")), U("http://a/b/")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../g")), U("http://a/b/g")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../..")), U("http://a/")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../")), U("http://a/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g:h")), U("g:h")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g")), U("http://a/b/c/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./g")), U("http://a/b/c/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g/")), U("http://a/b/c/g/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("/g")), U("http://a/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("//g")), U("http://g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("?y")), U("http://a/b/c/d;p?y")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y")), U("http://a/b/c/g?y")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("#s")), U("http://a/b/c/d;p?q#s")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g#s")), U("http://a/b/c/g#s")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y#s")), U("http://a/b/c/g?y#s")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U(";x")), U("http://a/b/c/;x")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x")), U("http://a/b/c/g;x")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x?y#s")), U("http://a/b/c/g;x?y#s")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("")), U("http://a/b/c/d;p?q")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U(".")), U("http://a/b/c/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./")), U("http://a/b/c/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("..")), U("http://a/b/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../")), U("http://a/b/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../g")), U("http://a/b/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../..")), U("http://a/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../")), U("http://a/")); VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../g")), U("http://a/g")); } //5.4.2. Abnormal Examples https://tools.ietf.org/html/rfc3986#section-5.4.2 @@ -42,28 +42,27 @@ TEST(resolve_uri_abnormal) { const uri baseUri = U("http://a/b/c/d;p?q"); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../../g")), U("http://a/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../../g")), U("http://a/g")); VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("../../../../g")), U("http://a/g")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("/./g")), U("http://a/g")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("/../g")), U("http://a/g")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g.")), U("http://a/b/c/g.")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U(".g")), U("http://a/b/c/.g")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g..")), U("http://a/b/c/g..")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("/./g")), U("http://a/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("/../g")), U("http://a/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g.")), U("http://a/b/c/g.")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U(".g")), U("http://a/b/c/.g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g..")), U("http://a/b/c/g..")); VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("..g")), U("http://a/b/c/..g")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./../g")), U("http://a/b/g")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./g/.")), U("http://a/b/c/g/")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g/./h")), U("http://a/b/c/g/h")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g/../h")), U("http://a/b/c/h")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x=1/./y")), U("http://a/b/c/g;x=1/y")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./../g")), U("http://a/b/g")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("./g/.")), U("http://a/b/c/g/")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g/./h")), U("http://a/b/c/g/h")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g/../h")), U("http://a/b/c/h")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x=1/./y")), U("http://a/b/c/g;x=1/y")); VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g;x=1/../y")), U("http://a/b/c/y")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y/./x")), U("http://a/b/c/g?y/./x")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y/../x")), U("http://a/b/c/g?y/../x")); - VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g#s/./x")), U("http://a/b/c/g#s/./x")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y/./x")), U("http://a/b/c/g?y/./x")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g?y/../x")), U("http://a/b/c/g?y/../x")); + VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g#s/./x")), U("http://a/b/c/g#s/./x")); VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("g#s/../x")), U("http://a/b/c/g#s/../x")); VERIFY_ARE_EQUAL(baseUri.resolve_uri(U("http:g")), U("http:g")); } } // SUITE(resolve_uri_tests) -}}} - +}}} From f0d67cc9dd7aecc1b40bfb8868e33f17c1073f3a Mon Sep 17 00:00:00 2001 From: Billy Robert O'Neal III Date: Wed, 10 Oct 2018 14:43:22 -0700 Subject: [PATCH 5/6] Make dotSegment and dotDotSegment global constants to pay for their construction once instead of every call. --- Release/src/uri/uri.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Release/src/uri/uri.cpp b/Release/src/uri/uri.cpp index 7053818921..0bbb7aa772 100644 --- a/Release/src/uri/uri.cpp +++ b/Release/src/uri/uri.cpp @@ -19,6 +19,9 @@ namespace web { namespace details { namespace { + const ::utility::string_t dotSegment = _XPLATSTR("."); + const ::utility::string_t dotDotSegment = _XPLATSTR(".."); + /// /// Unreserved characters are those that are allowed in a URI but do not have a reserved purpose. They include: /// - A-Z @@ -445,10 +448,6 @@ namespace if (builder.path().find(_XPLATSTR('.')) == utility::string_t::npos) return; - const utility::string_t - dotSegment = _XPLATSTR("."), - dotDotSegment = _XPLATSTR(".."); - const auto segments = uri::split_path(builder.path()); std::vector> result; for (auto& segment : segments) From 7963710f85b69a6793644b037e231313ba858b6f Mon Sep 17 00:00:00 2001 From: Billy Robert O'Neal III Date: Wed, 10 Oct 2018 14:43:54 -0700 Subject: [PATCH 6/6] Use push_back instead of initializer_list, minor whitespace fixups. --- Release/src/uri/uri.cpp | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/Release/src/uri/uri.cpp b/Release/src/uri/uri.cpp index 0bbb7aa772..1953564a9f 100644 --- a/Release/src/uri/uri.cpp +++ b/Release/src/uri/uri.cpp @@ -470,8 +470,12 @@ namespace path += _XPLATSTR('/'); path += result[i].get(); } - if (segments.back() == dotDotSegment || segments.back() == dotSegment || builder.path().back() == _XPLATSTR('/')) + if (segments.back() == dotDotSegment + || segments.back() == dotSegment + || builder.path().back() == _XPLATSTR('/')) + { path += _XPLATSTR('/'); + } builder.set_path(std::move(path)); } @@ -500,7 +504,8 @@ utility::string_t uri_components::join() if (!m_scheme.empty()) { - ret.append(m_scheme).append({ _XPLATSTR(':') }); + ret.append(m_scheme); + ret.push_back(_XPLATSTR(':')); } if (!m_host.empty()) @@ -525,7 +530,7 @@ utility::string_t uri_components::join() // only add the leading slash when the host is present if (!m_host.empty() && m_path.front() != _XPLATSTR('/')) { - ret.append({ _XPLATSTR('/') }); + ret.push_back(_XPLATSTR('/')); } ret.append(m_path); @@ -533,12 +538,14 @@ utility::string_t uri_components::join() if (!m_query.empty()) { - ret.append({ _XPLATSTR('?') }).append(m_query); + ret.push_back(_XPLATSTR('?')); + ret.append(m_query); } if (!m_fragment.empty()) { - ret.append({ _XPLATSTR('#') }).append(m_fragment); + ret.push_back(_XPLATSTR('#')); + ret.append(m_fragment); } return ret; @@ -767,7 +774,7 @@ std::map uri::split_query(const utility::s utility::string_t key(key_value_pair.begin(), key_value_pair.begin() + equals_index); utility::string_t value(key_value_pair.begin() + equals_index + 1, key_value_pair.end()); results[key] = value; - } + } } return results; @@ -840,12 +847,16 @@ bool uri::operator == (const uri &other) const utility::string_t uri::resolve_uri(const utility::string_t &relativeUri) const { if (relativeUri.empty()) + { return to_string(); + } if (relativeUri[0] == _XPLATSTR('/')) // starts with '/' { if (relativeUri.size() >= 2 && relativeUri[1] == _XPLATSTR('/')) // starts with '//' + { return this->scheme() + _XPLATSTR(':') + relativeUri; + } // otherwise relative to root auto builder = uri_builder(this->authority()); @@ -859,14 +870,18 @@ utility::string_t uri::resolve_uri(const utility::string_t &relativeUri) const return relativeUri; if (!url.authority().is_empty()) + { return uri_builder(url).set_scheme(this->scheme()).to_string(); + } // relative url auto builder = uri_builder(*this); if (url.path() == _XPLATSTR("/") || url.path().empty()) // web::uri considers empty path as '/' { if (!url.query().empty()) + { builder.set_query(url.query()); + } } else if (!this->path().empty()) { @@ -878,4 +893,4 @@ utility::string_t uri::resolve_uri(const utility::string_t &relativeUri) const return builder.set_fragment(url.fragment()).to_string(); } -} // namespace web +} // namespace web