From b102775f247a5c3301ebe716d81d123ef226d10c Mon Sep 17 00:00:00 2001 From: Daniel Griffing Date: Wed, 7 Nov 2018 14:02:39 -0800 Subject: [PATCH 01/10] add append_path_raw() to uri_builder --- Release/include/cpprest/uri_builder.h | 14 +++++++++++ Release/src/uri/uri_builder.cpp | 20 +++++++++++++++ .../functional/uri/uri_builder_tests.cpp | 25 +++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/Release/include/cpprest/uri_builder.h b/Release/include/cpprest/uri_builder.h index 6e03e5e2c0..b52ba829a5 100644 --- a/Release/include/cpprest/uri_builder.h +++ b/Release/include/cpprest/uri_builder.h @@ -188,6 +188,20 @@ namespace web /// A reference to this uri_builder to support chaining. _ASYNCRTIMP uri_builder &append_path(const utility::string_t &path, bool do_encoding = false); + /// + /// Appends the raw contents of the path argument to the path of this uri_builder with no separator de-duplication. + /// + /// + /// The path argument is appended after adding a '/' separator without regards to the contents of path. If an empty string + /// is provided, this function will immediately return without changes to the stored path value. + /// For example: if the current contents are "/abc" and path="/xyz", the result will be "/abc//xyz". + /// + /// Path to append as a already encoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + _ASYNCRTIMP uri_builder &append_path_raw(const utility::string_t &path, bool do_encoding = false); + + /// /// Appends another query to the query of this uri_builder. /// diff --git a/Release/src/uri/uri_builder.cpp b/Release/src/uri/uri_builder.cpp index f99923c487..9c62624637 100644 --- a/Release/src/uri/uri_builder.cpp +++ b/Release/src/uri/uri_builder.cpp @@ -55,6 +55,26 @@ uri_builder &uri_builder::append_path(const utility::string_t &path, bool is_enc return *this; } +uri_builder &uri_builder::append_path_literal(const utility::string_t &path, bool is_encode) +{ + if(path.empty()) + { + return *this; + } + + auto encoded_path = is_encode ? uri::encode_uri(path, uri::components::path) : path; + auto thisPath = this->path(); + if(thisPath.empty()) + { + set_path(_XPLATSTR('/') + encoded_path); + } + else + { + set_path(thisPath + _XPLATSTR('/') + encoded_path); + } + return *this; +} + uri_builder &uri_builder::append_query(const utility::string_t &query, bool is_encode) { if(query.empty()) diff --git a/Release/tests/functional/uri/uri_builder_tests.cpp b/Release/tests/functional/uri/uri_builder_tests.cpp index d4b792e091..78ebd7004a 100644 --- a/Release/tests/functional/uri/uri_builder_tests.cpp +++ b/Release/tests/functional/uri/uri_builder_tests.cpp @@ -271,6 +271,31 @@ TEST(append_path_string) VERIFY_ARE_EQUAL(U("/path1/path2/path3/path4"), builder.path()); } +TEST(append_path_literal_string) +{ + // empty uri builder path + uri_builder builder; + builder.append_path_literal(U("path1")); + VERIFY_ARE_EQUAL(U("/path1"), builder.path()); + + // empty append path + builder.append_path_literal(U("")); + VERIFY_ARE_EQUAL(U("/path1"), builder.path()); + + // uri builder with slash + builder.append_path_literal(U("/")); + builder.append_path_literal(U("path2")); + VERIFY_ARE_EQUAL(U("/path1///path2"), builder.path()); + + // both with slash + builder.append_path_literal(U("/path3")); + VERIFY_ARE_EQUAL(U("/path1///path2//path3"), builder.path()); + + // both without slash + builder.append_path_literal(U("path4")); + VERIFY_ARE_EQUAL(U("/path1///path2//path3/path4"), builder.path()); +} + TEST(append_query_string) { // empty uri builder query From f1a99268f7b0117e9cc1c75139be04d3ef82aebf Mon Sep 17 00:00:00 2001 From: Daniel Griffing Date: Wed, 7 Nov 2018 14:04:13 -0800 Subject: [PATCH 02/10] modify implementation details --- Release/src/uri/uri_builder.cpp | 2 +- Release/tests/functional/uri/uri_builder_tests.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Release/src/uri/uri_builder.cpp b/Release/src/uri/uri_builder.cpp index 9c62624637..ce0729beef 100644 --- a/Release/src/uri/uri_builder.cpp +++ b/Release/src/uri/uri_builder.cpp @@ -55,7 +55,7 @@ uri_builder &uri_builder::append_path(const utility::string_t &path, bool is_enc return *this; } -uri_builder &uri_builder::append_path_literal(const utility::string_t &path, bool is_encode) +uri_builder &uri_builder::append_path_raw(const utility::string_t &path, bool is_encode) { if(path.empty()) { diff --git a/Release/tests/functional/uri/uri_builder_tests.cpp b/Release/tests/functional/uri/uri_builder_tests.cpp index 78ebd7004a..502c540e3f 100644 --- a/Release/tests/functional/uri/uri_builder_tests.cpp +++ b/Release/tests/functional/uri/uri_builder_tests.cpp @@ -271,28 +271,28 @@ TEST(append_path_string) VERIFY_ARE_EQUAL(U("/path1/path2/path3/path4"), builder.path()); } -TEST(append_path_literal_string) +TEST(append_path_raw_string) { // empty uri builder path uri_builder builder; - builder.append_path_literal(U("path1")); + builder.append_path_raw(U("path1")); VERIFY_ARE_EQUAL(U("/path1"), builder.path()); // empty append path - builder.append_path_literal(U("")); + builder.append_path_raw(U("")); VERIFY_ARE_EQUAL(U("/path1"), builder.path()); // uri builder with slash - builder.append_path_literal(U("/")); - builder.append_path_literal(U("path2")); + builder.append_path_raw(U("/")); + builder.append_path_raw(U("path2")); VERIFY_ARE_EQUAL(U("/path1///path2"), builder.path()); // both with slash - builder.append_path_literal(U("/path3")); + builder.append_path_raw(U("/path3")); VERIFY_ARE_EQUAL(U("/path1///path2//path3"), builder.path()); // both without slash - builder.append_path_literal(U("path4")); + builder.append_path_raw(U("path4")); VERIFY_ARE_EQUAL(U("/path1///path2//path3/path4"), builder.path()); } From 52b2082d9c847549b5776ffec939de384ad4b2c8 Mon Sep 17 00:00:00 2001 From: Daniel Griffing Date: Wed, 7 Nov 2018 14:18:01 -0800 Subject: [PATCH 03/10] modified test case --- Release/tests/functional/uri/uri_builder_tests.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Release/tests/functional/uri/uri_builder_tests.cpp b/Release/tests/functional/uri/uri_builder_tests.cpp index 502c540e3f..e9d7cff440 100644 --- a/Release/tests/functional/uri/uri_builder_tests.cpp +++ b/Release/tests/functional/uri/uri_builder_tests.cpp @@ -287,13 +287,14 @@ TEST(append_path_raw_string) builder.append_path_raw(U("path2")); VERIFY_ARE_EQUAL(U("/path1///path2"), builder.path()); - // both with slash + // leading slash (should result in "//") builder.append_path_raw(U("/path3")); VERIFY_ARE_EQUAL(U("/path1///path2//path3"), builder.path()); - // both without slash - builder.append_path_raw(U("path4")); - VERIFY_ARE_EQUAL(U("/path1///path2//path3/path4"), builder.path()); + // trailing slash + builder.append_path_raw(U("path4/")); + builder.append_path_raw(U("path5")); + VERIFY_ARE_EQUAL(U("/path1///path2//path3/path4//path5"), builder.path()); } TEST(append_query_string) From a8c61176ee0cf0ec54ccba54593af6f37e657551 Mon Sep 17 00:00:00 2001 From: Daniel Griffing Date: Thu, 8 Nov 2018 10:02:37 -0800 Subject: [PATCH 04/10] fixed append_path_raw and included a testscase for a trailing slash with that API --- Release/src/uri/uri_builder.cpp | 2 +- Release/tests/functional/uri/uri_builder_tests.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Release/src/uri/uri_builder.cpp b/Release/src/uri/uri_builder.cpp index ce0729beef..bae42bf17f 100644 --- a/Release/src/uri/uri_builder.cpp +++ b/Release/src/uri/uri_builder.cpp @@ -64,7 +64,7 @@ uri_builder &uri_builder::append_path_raw(const utility::string_t &path, bool is auto encoded_path = is_encode ? uri::encode_uri(path, uri::components::path) : path; auto thisPath = this->path(); - if(thisPath.empty()) + if(thisPath.empty() || thisPath == _XPLATSTR("/")) { set_path(_XPLATSTR('/') + encoded_path); } diff --git a/Release/tests/functional/uri/uri_builder_tests.cpp b/Release/tests/functional/uri/uri_builder_tests.cpp index e9d7cff440..db5f77f2cb 100644 --- a/Release/tests/functional/uri/uri_builder_tests.cpp +++ b/Release/tests/functional/uri/uri_builder_tests.cpp @@ -293,8 +293,8 @@ TEST(append_path_raw_string) // trailing slash builder.append_path_raw(U("path4/")); - builder.append_path_raw(U("path5")); - VERIFY_ARE_EQUAL(U("/path1///path2//path3/path4//path5"), builder.path()); + builder.append_path_raw(U("path5")); + VERIFY_ARE_EQUAL(U("/path1///path2//path3/path4//path5"), builder.path()); } TEST(append_query_string) From 0f213ec4d850772a71994f993a632dcadba2c7f8 Mon Sep 17 00:00:00 2001 From: Daniel Griffing Date: Thu, 8 Nov 2018 13:03:46 -0800 Subject: [PATCH 05/10] update submodule to vcpkg master due to the NuGet hash changes --- vcpkg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcpkg b/vcpkg index 76ea5e321c..1d7fa4c74a 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit 76ea5e321cf88cf1c99a7bd0fcebae7c8f990ee0 +Subproject commit 1d7fa4c74a1cfdc962d5a58408b8f431e4f6ba52 From f0dd0e76a009861b985647658a0478c8977b90ad Mon Sep 17 00:00:00 2001 From: "Billy O'Neal (VC LIBS)" Date: Sat, 10 Nov 2018 16:31:18 -0800 Subject: [PATCH 06/10] Avoid double encoding through set_path and add tests. Extract single slash string comparison. Reduce string copy count. Add VS Code settings and launch. --- .vscode/launch.json | 19 +++++ .vscode/settings.json | 8 ++ Release/src/uri/uri_builder.cpp | 85 +++++++++---------- .../functional/uri/uri_builder_tests.cpp | 57 ++++++++----- 4 files changed, 101 insertions(+), 68 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..655ea37edf --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(Windows) Launch Debug Tests", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceFolder}/build.debug/Release/Binaries/test_runner.exe", + "args": ["*testd.dll"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/build.debug/Release/Binaries", + "environment": [], + "externalConsole": true + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..2fc5e0c5fe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.watcherExclude": { + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "**/node_modules/*/**": true, + "**/vcpkg/**": true + } +} diff --git a/Release/src/uri/uri_builder.cpp b/Release/src/uri/uri_builder.cpp index bae42bf17f..ea7a7efbca 100644 --- a/Release/src/uri/uri_builder.cpp +++ b/Release/src/uri/uri_builder.cpp @@ -13,39 +13,38 @@ #include "stdafx.h" -#include +static const utility::string_t oneSlash = _XPLATSTR("/"); namespace web { - -uri_builder &uri_builder::append_path(const utility::string_t &path, bool is_encode) +uri_builder& uri_builder::append_path(const utility::string_t& path, bool do_encode) { - if(path.empty() || path == _XPLATSTR("/")) + if (path.empty() || path == oneSlash) { return *this; } - auto encoded_path = is_encode ? uri::encode_uri(path, uri::components::path) : path; + auto encoded_path = do_encode ? uri::encode_uri(path, uri::components::path) : path; auto thisPath = this->path(); - if(thisPath.empty() || thisPath == _XPLATSTR("/")) + if (thisPath.empty() || thisPath == oneSlash) { - if(encoded_path.front() != _XPLATSTR('/')) + if (encoded_path.front() != _XPLATSTR('/')) { - set_path(_XPLATSTR("/") + encoded_path); + set_path(oneSlash + encoded_path); } else { set_path(encoded_path); } } - else if(thisPath.back() == _XPLATSTR('/') && encoded_path.front() == _XPLATSTR('/')) + else if (thisPath.back() == _XPLATSTR('/') && encoded_path.front() == _XPLATSTR('/')) { thisPath.pop_back(); set_path(thisPath + encoded_path); } - else if(thisPath.back() != _XPLATSTR('/') && encoded_path.front() != _XPLATSTR('/')) + else if (thisPath.back() != _XPLATSTR('/') && encoded_path.front() != _XPLATSTR('/')) { - set_path(thisPath + _XPLATSTR("/") + encoded_path); + set_path(thisPath + oneSlash + encoded_path); } else { @@ -55,45 +54,48 @@ uri_builder &uri_builder::append_path(const utility::string_t &path, bool is_enc return *this; } -uri_builder &uri_builder::append_path_raw(const utility::string_t &path, bool is_encode) +uri_builder& uri_builder::append_path_raw(const utility::string_t& toAppend, bool do_encode) { - if(path.empty()) + if (!toAppend.empty()) { - return *this; - } + auto& thisPath = m_uri.m_path; + if (thisPath != oneSlash) + { + thisPath.push_back(_XPLATSTR('/')); + } - auto encoded_path = is_encode ? uri::encode_uri(path, uri::components::path) : path; - auto thisPath = this->path(); - if(thisPath.empty() || thisPath == _XPLATSTR("/")) - { - set_path(_XPLATSTR('/') + encoded_path); - } - else - { - set_path(thisPath + _XPLATSTR('/') + encoded_path); + if (do_encode) + { + thisPath.append(uri::encode_uri(toAppend, uri::components::path)); + } + else + { + thisPath.append(toAppend); + } } + return *this; } -uri_builder &uri_builder::append_query(const utility::string_t &query, bool is_encode) +uri_builder& uri_builder::append_query(const utility::string_t& query, bool do_encode) { - if(query.empty()) + if (query.empty()) { return *this; } - auto encoded_query = is_encode ? uri::encode_uri(query, uri::components::query) : query; + auto encoded_query = do_encode ? uri::encode_uri(query, uri::components::query) : query; auto thisQuery = this->query(); if (thisQuery.empty()) { this->set_query(encoded_query); } - else if(thisQuery.back() == _XPLATSTR('&') && encoded_query.front() == _XPLATSTR('&')) + else if (thisQuery.back() == _XPLATSTR('&') && encoded_query.front() == _XPLATSTR('&')) { thisQuery.pop_back(); this->set_query(thisQuery + encoded_query); } - else if(thisQuery.back() != _XPLATSTR('&') && encoded_query.front() != _XPLATSTR('&')) + else if (thisQuery.back() != _XPLATSTR('&') && encoded_query.front() != _XPLATSTR('&')) { this->set_query(thisQuery + _XPLATSTR("&") + encoded_query); } @@ -105,7 +107,7 @@ uri_builder &uri_builder::append_query(const utility::string_t &query, bool is_e return *this; } -uri_builder &uri_builder::set_port(const utility::string_t &port) +uri_builder& uri_builder::set_port(const utility::string_t& port) { utility::istringstream_t portStream(port); portStream.imbue(std::locale::classic()); @@ -119,7 +121,7 @@ uri_builder &uri_builder::set_port(const utility::string_t &port) return *this; } -uri_builder &uri_builder::append(const http::uri &relative_uri) +uri_builder& uri_builder::append(const http::uri& relative_uri) { append_path(relative_uri.path()); append_query(relative_uri.query()); @@ -127,32 +129,23 @@ uri_builder &uri_builder::append(const http::uri &relative_uri) return *this; } -utility::string_t uri_builder::to_string() const -{ - return to_uri().to_string(); -} +utility::string_t uri_builder::to_string() const { return to_uri().to_string(); } -uri uri_builder::to_uri() const -{ - return uri(m_uri); -} +uri uri_builder::to_uri() const { return uri(m_uri); } -bool uri_builder::is_valid() -{ - return uri::validate(m_uri.join()); -} +bool uri_builder::is_valid() { return uri::validate(m_uri.join()); } -void uri_builder::append_query_encode_impl(const utility::string_t & name, const utf8string & value) +void uri_builder::append_query_encode_impl(const utility::string_t& name, const utf8string& value) { utility::string_t encodedQuery = uri::encode_query_impl(utility::conversions::to_utf8string(name)); - encodedQuery.append(_XPLATSTR("=")); + encodedQuery.push_back(_XPLATSTR('=')); encodedQuery.append(uri::encode_query_impl(value)); // The query key value pair was already encoded by us or the user separately. append_query(encodedQuery, false); } -void uri_builder::append_query_no_encode_impl(const utility::string_t & name, const utility::string_t & value) +void uri_builder::append_query_no_encode_impl(const utility::string_t& name, const utility::string_t& value) { append_query(name + _XPLATSTR("=") + value, false); } diff --git a/Release/tests/functional/uri/uri_builder_tests.cpp b/Release/tests/functional/uri/uri_builder_tests.cpp index db5f77f2cb..a2cba03a5b 100644 --- a/Release/tests/functional/uri/uri_builder_tests.cpp +++ b/Release/tests/functional/uri/uri_builder_tests.cpp @@ -129,7 +129,7 @@ TEST(component_assignment) const utility::string_t path(U("jklajsd")); const utility::string_t query(U("key1=val1")); const utility::string_t fragment(U("last")); - + builder.set_scheme(scheme); builder.set_user_info(uinfo); builder.set_host(host); @@ -152,7 +152,7 @@ TEST(component_assignment_encode) const utility::string_t path(U("jklajsd/yes no")); const utility::string_t query(U("key1=va%l1")); const utility::string_t fragment(U("las t")); - + builder.set_scheme(scheme); builder.set_user_info(uinfo, true); builder.set_host(host, true); @@ -162,13 +162,13 @@ TEST(component_assignment_encode) builder.set_fragment(fragment, true); VERIFY_URI_BUILDER( - builder, - scheme, - utility::string_t(U("johndoe:test")), - utility::string_t(U("localhost")), - port, - utility::string_t(U("jklajsd/yes%20no")), - utility::string_t(U("key1=va%25l1")), + builder, + scheme, + utility::string_t(U("johndoe:test")), + utility::string_t(U("localhost")), + port, + utility::string_t(U("jklajsd/yes%20no")), + utility::string_t(U("key1=va%25l1")), utility::string_t(U("las%20t"))); } { @@ -180,7 +180,7 @@ TEST(component_assignment_encode) const utility::string_t path(U("jklajsd/yes no")); const utility::string_t query(U("key1=va%l1")); const utility::string_t fragment(U("las t")); - + builder.set_scheme(scheme); builder.set_user_info(uinfo, true); builder.set_host(host, true); @@ -190,13 +190,13 @@ TEST(component_assignment_encode) builder.set_fragment(fragment, true); VERIFY_URI_BUILDER( - builder, - scheme, - utility::string_t(U("johndoe:test")), - utility::string_t(U("localhost")), - port, - utility::string_t(U("jklajsd/yes%20no")), - utility::string_t(U("key1=va%25l1")), + builder, + scheme, + utility::string_t(U("johndoe:test")), + utility::string_t(U("localhost")), + port, + utility::string_t(U("jklajsd/yes%20no")), + utility::string_t(U("key1=va%25l1")), utility::string_t(U("las%20t"))); } } @@ -255,7 +255,7 @@ TEST(append_path_string) // empty append path builder.append_path(U("")); VERIFY_ARE_EQUAL(U("/path1"), builder.path()); - + // uri builder with slash builder.append_path(U("/")); builder.append_path(U("path2")); @@ -281,7 +281,7 @@ TEST(append_path_raw_string) // empty append path builder.append_path_raw(U("")); VERIFY_ARE_EQUAL(U("/path1"), builder.path()); - + // uri builder with slash builder.append_path_raw(U("/")); builder.append_path_raw(U("path2")); @@ -295,6 +295,19 @@ TEST(append_path_raw_string) builder.append_path_raw(U("path4/")); builder.append_path_raw(U("path5")); VERIFY_ARE_EQUAL(U("/path1///path2//path3/path4//path5"), builder.path()); + + // encoding + builder.clear(); + builder.append_path_raw(U("encode%things")); + VERIFY_ARE_EQUAL(U("/encode%things"), builder.path()); + + builder.clear(); + builder.append_path_raw(U("encode%things"), false); + VERIFY_ARE_EQUAL(U("/encode%things"), builder.path()); + + builder.clear(); + builder.append_path_raw(U("encode%things"), true); + VERIFY_ARE_EQUAL(U("/encode%25things"), builder.path()); } TEST(append_query_string) @@ -307,7 +320,7 @@ TEST(append_query_string) // empty append query builder.append_query(U("")); VERIFY_ARE_EQUAL(U("key1=value1"), builder.query()); - + // uri builder with ampersand builder.append_query(U("&")); builder.append_query(U("key2=value2")); @@ -424,8 +437,8 @@ TEST(host_encoding) uri_builder ub1; ub1.set_scheme(U("http")).set_host(U("????dfasddsf!@#$%^&*()_+")).set_port(80); - - VERIFY_IS_FALSE(ub1.is_valid()); + + VERIFY_IS_FALSE(ub1.is_valid()); } TEST(clear) From a0c6da98f3a645806c74cb3e50198802ecbbc1a7 Mon Sep 17 00:00:00 2001 From: "Billy O'Neal (VC LIBS)" Date: Sat, 10 Nov 2018 16:47:53 -0800 Subject: [PATCH 07/10] Optimize append_path similarly. --- Release/src/uri/uri_builder.cpp | 74 ++++++++++--------- .../functional/uri/uri_builder_tests.cpp | 13 ++++ 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/Release/src/uri/uri_builder.cpp b/Release/src/uri/uri_builder.cpp index ea7a7efbca..4907f33842 100644 --- a/Release/src/uri/uri_builder.cpp +++ b/Release/src/uri/uri_builder.cpp @@ -1,15 +1,15 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Builder for constructing URIs. -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Builder for constructing URIs. + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #include "stdafx.h" @@ -17,40 +17,42 @@ static const utility::string_t oneSlash = _XPLATSTR("/"); namespace web { -uri_builder& uri_builder::append_path(const utility::string_t& path, bool do_encode) +uri_builder& uri_builder::append_path(const utility::string_t& toAppend, bool do_encode) { - if (path.empty() || path == oneSlash) + if (!toAppend.empty() && toAppend != oneSlash) { - return *this; - } + auto& thisPath = m_uri.m_path; + if (thisPath.empty() || thisPath == oneSlash) + { + thisPath.clear(); + if (toAppend.front() != _XPLATSTR('/')) + { + thisPath.push_back(_XPLATSTR('/')); + } + } + else if (thisPath.back() == _XPLATSTR('/') && toAppend.front() == _XPLATSTR('/')) + { + thisPath.pop_back(); + } + else if (thisPath.back() != _XPLATSTR('/') && toAppend.front() != _XPLATSTR('/')) + { + thisPath.push_back(_XPLATSTR('/')); + } + else + { + // Only one slash. + } - auto encoded_path = do_encode ? uri::encode_uri(path, uri::components::path) : path; - auto thisPath = this->path(); - if (thisPath.empty() || thisPath == oneSlash) - { - if (encoded_path.front() != _XPLATSTR('/')) + if (do_encode) { - set_path(oneSlash + encoded_path); + thisPath.append(uri::encode_uri(toAppend, uri::components::path)); } else { - set_path(encoded_path); + thisPath.append(toAppend); } } - else if (thisPath.back() == _XPLATSTR('/') && encoded_path.front() == _XPLATSTR('/')) - { - thisPath.pop_back(); - set_path(thisPath + encoded_path); - } - else if (thisPath.back() != _XPLATSTR('/') && encoded_path.front() != _XPLATSTR('/')) - { - set_path(thisPath + oneSlash + encoded_path); - } - else - { - // Only one slash. - set_path(thisPath + encoded_path); - } + return *this; } diff --git a/Release/tests/functional/uri/uri_builder_tests.cpp b/Release/tests/functional/uri/uri_builder_tests.cpp index a2cba03a5b..258ebb9259 100644 --- a/Release/tests/functional/uri/uri_builder_tests.cpp +++ b/Release/tests/functional/uri/uri_builder_tests.cpp @@ -269,6 +269,19 @@ TEST(append_path_string) // both without slash builder.append_path(U("path4")); VERIFY_ARE_EQUAL(U("/path1/path2/path3/path4"), builder.path()); + + // encoding + builder.clear(); + builder.append_path(U("encode%things")); + VERIFY_ARE_EQUAL(U("/encode%things"), builder.path()); + + builder.clear(); + builder.append_path(U("encode%things"), false); + VERIFY_ARE_EQUAL(U("/encode%things"), builder.path()); + + builder.clear(); + builder.append_path(U("encode%things"), true); + VERIFY_ARE_EQUAL(U("/encode%25things"), builder.path()); } TEST(append_path_raw_string) From f478e54caeb98e04c4d7bb1264ec15e36ef3122f Mon Sep 17 00:00:00 2001 From: "Billy O'Neal (VC LIBS)" Date: Sat, 10 Nov 2018 16:57:07 -0800 Subject: [PATCH 08/10] Also optimize append_query. --- Release/src/uri/uri_builder.cpp | 51 ++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/Release/src/uri/uri_builder.cpp b/Release/src/uri/uri_builder.cpp index 4907f33842..98f571a9ad 100644 --- a/Release/src/uri/uri_builder.cpp +++ b/Release/src/uri/uri_builder.cpp @@ -79,33 +79,38 @@ uri_builder& uri_builder::append_path_raw(const utility::string_t& toAppend, boo return *this; } -uri_builder& uri_builder::append_query(const utility::string_t& query, bool do_encode) +uri_builder& uri_builder::append_query(const utility::string_t& toAppend, bool do_encode) { - if (query.empty()) + if (!toAppend.empty()) { - return *this; - } + auto& thisQuery = m_uri.m_query; + if (thisQuery.empty()) + { + thisQuery.clear(); + } + else if (thisQuery.back() == _XPLATSTR('&') && toAppend.front() == _XPLATSTR('&')) + { + thisQuery.pop_back(); + } + else if (thisQuery.back() != _XPLATSTR('&') && toAppend.front() != _XPLATSTR('&')) + { + thisQuery.push_back(_XPLATSTR('&')); + } + else + { + // Only one ampersand. + } - auto encoded_query = do_encode ? uri::encode_uri(query, uri::components::query) : query; - auto thisQuery = this->query(); - if (thisQuery.empty()) - { - this->set_query(encoded_query); - } - else if (thisQuery.back() == _XPLATSTR('&') && encoded_query.front() == _XPLATSTR('&')) - { - thisQuery.pop_back(); - this->set_query(thisQuery + encoded_query); - } - else if (thisQuery.back() != _XPLATSTR('&') && encoded_query.front() != _XPLATSTR('&')) - { - this->set_query(thisQuery + _XPLATSTR("&") + encoded_query); - } - else - { - // Only one ampersand. - this->set_query(thisQuery + encoded_query); + if (do_encode) + { + thisQuery.append(uri::encode_uri(toAppend, uri::components::query)); + } + else + { + thisQuery.append(toAppend); + } } + return *this; } From 458658ddb0e7eb591521d0d2e93507682e418360 Mon Sep 17 00:00:00 2001 From: "Billy O'Neal (VC LIBS)" Date: Sat, 10 Nov 2018 17:06:47 -0800 Subject: [PATCH 09/10] Also optimize other uri_builder things. --- Release/include/cpprest/base_uri.h | 5 +- Release/include/cpprest/uri_builder.h | 481 ++++++++++++++------------ 2 files changed, 260 insertions(+), 226 deletions(-) diff --git a/Release/include/cpprest/base_uri.h b/Release/include/cpprest/base_uri.h index 1aaefe9c2f..fd82a3985e 100644 --- a/Release/include/cpprest/base_uri.h +++ b/Release/include/cpprest/base_uri.h @@ -14,10 +14,9 @@ #pragma once #include -#include #include #include -#include +#include #include "cpprest/asyncrt_utils.h" #include "cpprest/details/basic_types.h" @@ -421,4 +420,4 @@ namespace web { details::uri_components m_components; }; -} // namespace web +} // namespace web diff --git a/Release/include/cpprest/uri_builder.h b/Release/include/cpprest/uri_builder.h index b52ba829a5..9f3fe756d5 100644 --- a/Release/include/cpprest/uri_builder.h +++ b/Release/include/cpprest/uri_builder.h @@ -1,261 +1,296 @@ /*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Builder style class for creating URIs. -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ + * Copyright (C) Microsoft. All rights reserved. + * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + * + * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * + * Builder style class for creating URIs. + * + * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk + * + * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + ****/ #pragma once -#include #include -#include #include "cpprest/base_uri.h" namespace web { +/// +/// Builder for constructing URIs incrementally. +/// +class uri_builder +{ +public: + /// + /// Creates a builder with an initially empty URI. + /// + uri_builder() = default; + + /// + /// Creates a builder with a existing URI object. + /// + /// Encoded string containing the URI. + uri_builder(const uri& uri_str) : m_uri(uri_str.m_components) {} + + /// + /// Get the scheme component of the URI as an encoded string. + /// + /// The URI scheme as a string. + const utility::string_t& scheme() const { return m_uri.m_scheme; } + /// - /// Builder for constructing URIs incrementally. + /// Get the user information component of the URI as an encoded string. /// - class uri_builder + /// The URI user information as a string. + const utility::string_t& user_info() const { return m_uri.m_user_info; } + + /// + /// Get the host component of the URI as an encoded string. + /// + /// The URI host as a string. + const utility::string_t& host() const { return m_uri.m_host; } + + /// + /// Get the port component of the URI. Returns -1 if no port is specified. + /// + /// The URI port as an integer. + int port() const { return m_uri.m_port; } + + /// + /// Get the path component of the URI as an encoded string. + /// + /// The URI path as a string. + const utility::string_t& path() const { return m_uri.m_path; } + + /// + /// Get the query component of the URI as an encoded string. + /// + /// The URI query as a string. + const utility::string_t& query() const { return m_uri.m_query; } + + /// + /// Get the fragment component of the URI as an encoded string. + /// + /// The URI fragment as a string. + const utility::string_t& fragment() const { return m_uri.m_fragment; } + + /// + /// Set the scheme of the URI. + /// + /// Uri scheme. + /// A reference to this uri_builder to support chaining. + uri_builder& set_scheme(const utility::string_t& scheme) { - public: - - /// - /// Creates a builder with an initially empty URI. - /// - uri_builder() = default; - - /// - /// Creates a builder with a existing URI object. - /// - /// Encoded string containing the URI. - uri_builder(const uri &uri_str): m_uri(uri_str.m_components) {} - - /// - /// Get the scheme component of the URI as an encoded string. - /// - /// The URI scheme as a string. - const utility::string_t &scheme() const { return m_uri.m_scheme; } - - /// - /// Get the user information component of the URI as an encoded string. - /// - /// The URI user information as a string. - const utility::string_t &user_info() const { return m_uri.m_user_info; } - - /// - /// Get the host component of the URI as an encoded string. - /// - /// The URI host as a string. - const utility::string_t &host() const { return m_uri.m_host; } - - /// - /// Get the port component of the URI. Returns -1 if no port is specified. - /// - /// The URI port as an integer. - int port() const { return m_uri.m_port; } - - /// - /// Get the path component of the URI as an encoded string. - /// - /// The URI path as a string. - const utility::string_t &path() const { return m_uri.m_path; } - - /// - /// Get the query component of the URI as an encoded string. - /// - /// The URI query as a string. - const utility::string_t &query() const { return m_uri.m_query; } - - /// - /// Get the fragment component of the URI as an encoded string. - /// - /// The URI fragment as a string. - const utility::string_t &fragment() const { return m_uri.m_fragment; } - - /// - /// Set the scheme of the URI. - /// - /// Uri scheme. - /// A reference to this uri_builder to support chaining. - uri_builder & set_scheme(const utility::string_t &scheme) + m_uri.m_scheme = scheme; + return *this; + } + + /// + /// Set the user info component of the URI. + /// + /// User info as a decoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + uri_builder& set_user_info(const utility::string_t& user_info, bool do_encoding = false) + { + if (do_encoding) { - m_uri.m_scheme = scheme; - return *this; + m_uri.m_user_info = uri::encode_uri(user_info, uri::components::user_info); } - - /// - /// Set the user info component of the URI. - /// - /// User info as a decoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - uri_builder & set_user_info(const utility::string_t &user_info, bool do_encoding = false) + else { - m_uri.m_user_info = do_encoding ? uri::encode_uri(user_info, uri::components::user_info) : user_info; - return *this; + m_uri.m_user_info = user_info; } - /// - /// Set the host component of the URI. - /// - /// Host as a decoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - uri_builder & set_host(const utility::string_t &host, bool do_encoding = false) + return *this; + } + + /// + /// Set the host component of the URI. + /// + /// Host as a decoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + uri_builder& set_host(const utility::string_t& host, bool do_encoding = false) + { + if (do_encoding) { - m_uri.m_host = do_encoding ? uri::encode_uri(host, uri::components::host) : host; - return *this; + m_uri.m_host = uri::encode_uri(host, uri::components::host); } - - /// - /// Set the port component of the URI. - /// - /// Port as an integer. - /// A reference to this uri_builder to support chaining. - uri_builder & set_port(int port) + else { - m_uri.m_port = port; - return *this; + m_uri.m_host = host; } - /// - /// Set the port component of the URI. - /// - /// Port as a string. - /// A reference to this uri_builder to support chaining. - /// When string can't be converted to an integer the port is left unchanged. - _ASYNCRTIMP uri_builder & set_port(const utility::string_t &port); - - /// - /// Set the path component of the URI. - /// - /// Path as a decoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - uri_builder & set_path(const utility::string_t &path, bool do_encoding = false) + return *this; + } + + /// + /// Set the port component of the URI. + /// + /// Port as an integer. + /// A reference to this uri_builder to support chaining. + uri_builder& set_port(int port) + { + m_uri.m_port = port; + return *this; + } + + /// + /// Set the port component of the URI. + /// + /// Port as a string. + /// A reference to this uri_builder to support chaining. + /// When string can't be converted to an integer the port is left unchanged. + _ASYNCRTIMP uri_builder& set_port(const utility::string_t& port); + + /// + /// Set the path component of the URI. + /// + /// Path as a decoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + uri_builder& set_path(const utility::string_t& path, bool do_encoding = false) + { + if (do_encoding) + { + m_uri.m_path = uri::encode_uri(path, uri::components::path); + } + else { - m_uri.m_path = do_encoding ? uri::encode_uri(path, uri::components::path) : path; - return *this; + m_uri.m_path = path; } + return *this; + } - /// - /// Set the query component of the URI. - /// - /// Query as a decoded string. - /// Specify whether apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - uri_builder & set_query(const utility::string_t &query, bool do_encoding = false) + /// + /// Set the query component of the URI. + /// + /// Query as a decoded string. + /// Specify whether apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + uri_builder& set_query(const utility::string_t& query, bool do_encoding = false) + { + if (do_encoding) { - m_uri.m_query = do_encoding ? uri::encode_uri(query, uri::components::query) : query; - return *this; + m_uri.m_query = uri::encode_uri(query, uri::components::query); } - - /// - /// Set the fragment component of the URI. - /// - /// Fragment as a decoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - uri_builder & set_fragment(const utility::string_t &fragment, bool do_encoding = false) + else { - m_uri.m_fragment = do_encoding ? uri::encode_uri(fragment, uri::components::fragment) : fragment; - return *this; + m_uri.m_query = query; } - /// - /// Clears all components of the underlying URI in this uri_builder. - /// - void clear() + return *this; + } + + /// + /// Set the fragment component of the URI. + /// + /// Fragment as a decoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + uri_builder& set_fragment(const utility::string_t& fragment, bool do_encoding = false) + { + if (do_encoding) { - m_uri = details::uri_components(); + m_uri.m_fragment = uri::encode_uri(fragment, uri::components::fragment); } - - /// - /// Appends another path to the path of this uri_builder. - /// - /// Path to append as a already encoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - _ASYNCRTIMP uri_builder &append_path(const utility::string_t &path, bool do_encoding = false); - - /// - /// Appends the raw contents of the path argument to the path of this uri_builder with no separator de-duplication. - /// - /// - /// The path argument is appended after adding a '/' separator without regards to the contents of path. If an empty string - /// is provided, this function will immediately return without changes to the stored path value. - /// For example: if the current contents are "/abc" and path="/xyz", the result will be "/abc//xyz". - /// - /// Path to append as a already encoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - _ASYNCRTIMP uri_builder &append_path_raw(const utility::string_t &path, bool do_encoding = false); - - - /// - /// Appends another query to the query of this uri_builder. - /// - /// Query to append as a decoded string. - /// Specify whether to apply URI encoding to the given string. - /// A reference to this uri_builder to support chaining. - _ASYNCRTIMP uri_builder &append_query(const utility::string_t &query, bool do_encoding = false); - - /// - /// Appends an relative uri (Path, Query and fragment) at the end of the current uri. - /// - /// The relative uri to append. - /// A reference to this uri_builder to support chaining. - _ASYNCRTIMP uri_builder &append(const uri &relative_uri); - - /// - /// Appends another query to the query of this uri_builder, encoding it first. This overload is useful when building a query segment of - /// the form "element=10", where the right hand side of the query is stored as a type other than a string, for instance, an integral type. - /// - /// The name portion of the query string - /// The value portion of the query string - /// A reference to this uri_builder to support chaining. - template - uri_builder &append_query(const utility::string_t &name, const T &value, bool do_encoding = true) + else { - if (do_encoding) - append_query_encode_impl(name, utility::conversions::details::print_utf8string(value)); - else - append_query_no_encode_impl(name, utility::conversions::details::print_string(value)); - return *this; + m_uri.m_fragment = fragment; } - /// - /// Combine and validate the URI components into a encoded string. An exception will be thrown if the URI is invalid. - /// - /// The created URI as a string. - _ASYNCRTIMP utility::string_t to_string() const; - - /// - /// Combine and validate the URI components into a URI class instance. An exception will be thrown if the URI is invalid. - /// - /// The create URI as a URI class instance. - _ASYNCRTIMP uri to_uri() const; - - /// - /// Validate the generated URI from all existing components of this uri_builder. - /// - /// Whether the URI is valid. - _ASYNCRTIMP bool is_valid(); - - private: - _ASYNCRTIMP void append_query_encode_impl(const utility::string_t &name, const utf8string &value); - _ASYNCRTIMP void append_query_no_encode_impl(const utility::string_t &name, const utility::string_t &value); - - details::uri_components m_uri; - }; + return *this; + } + + /// + /// Clears all components of the underlying URI in this uri_builder. + /// + void clear() { m_uri = details::uri_components(); } + + /// + /// Appends another path to the path of this uri_builder. + /// + /// Path to append as a already encoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + _ASYNCRTIMP uri_builder& append_path(const utility::string_t& path, bool do_encoding = false); + + /// + /// Appends the raw contents of the path argument to the path of this uri_builder with no separator de-duplication. + /// + /// + /// The path argument is appended after adding a '/' separator without regards to the contents of path. If an empty + /// string is provided, this function will immediately return without changes to the stored path value. For example: + /// if the current contents are "/abc" and path="/xyz", the result will be "/abc//xyz". + /// + /// Path to append as a already encoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + _ASYNCRTIMP uri_builder& append_path_raw(const utility::string_t& path, bool do_encoding = false); + + /// + /// Appends another query to the query of this uri_builder. + /// + /// Query to append as a decoded string. + /// Specify whether to apply URI encoding to the given string. + /// A reference to this uri_builder to support chaining. + _ASYNCRTIMP uri_builder& append_query(const utility::string_t& query, bool do_encoding = false); + + /// + /// Appends an relative uri (Path, Query and fragment) at the end of the current uri. + /// + /// The relative uri to append. + /// A reference to this uri_builder to support chaining. + _ASYNCRTIMP uri_builder& append(const uri& relative_uri); + + /// + /// Appends another query to the query of this uri_builder, encoding it first. This overload is useful when building + /// a query segment of the form "element=10", where the right hand side of the query is stored as a type other than + /// a string, for instance, an integral type. + /// + /// The name portion of the query string + /// The value portion of the query string + /// A reference to this uri_builder to support chaining. + template + uri_builder& append_query(const utility::string_t& name, const T& value, bool do_encoding = true) + { + if (do_encoding) + append_query_encode_impl(name, utility::conversions::details::print_utf8string(value)); + else + append_query_no_encode_impl(name, utility::conversions::details::print_string(value)); + return *this; + } + + /// + /// Combine and validate the URI components into a encoded string. An exception will be thrown if the URI is + /// invalid. + /// + /// The created URI as a string. + _ASYNCRTIMP utility::string_t to_string() const; + + /// + /// Combine and validate the URI components into a URI class instance. An exception will be thrown if the URI is + /// invalid. + /// + /// The create URI as a URI class instance. + _ASYNCRTIMP uri to_uri() const; + + /// + /// Validate the generated URI from all existing components of this uri_builder. + /// + /// Whether the URI is valid. + _ASYNCRTIMP bool is_valid(); + +private: + _ASYNCRTIMP void append_query_encode_impl(const utility::string_t& name, const utf8string& value); + _ASYNCRTIMP void append_query_no_encode_impl(const utility::string_t& name, const utility::string_t& value); + + details::uri_components m_uri; +}; } // namespace web From ee6d8a4472d10e0954e871398408621b57bc3b1e Mon Sep 17 00:00:00 2001 From: "Billy O'Neal (VC LIBS)" Date: Sat, 10 Nov 2018 17:35:01 -0800 Subject: [PATCH 10/10] Avoid self references. --- Release/src/uri/uri_builder.cpp | 18 +++++++++ .../functional/uri/uri_builder_tests.cpp | 39 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/Release/src/uri/uri_builder.cpp b/Release/src/uri/uri_builder.cpp index 98f571a9ad..0135610466 100644 --- a/Release/src/uri/uri_builder.cpp +++ b/Release/src/uri/uri_builder.cpp @@ -22,6 +22,12 @@ uri_builder& uri_builder::append_path(const utility::string_t& toAppend, bool do if (!toAppend.empty() && toAppend != oneSlash) { auto& thisPath = m_uri.m_path; + if (&thisPath == &toAppend) + { + auto appendCopy = toAppend; + return append_path(appendCopy, do_encode); + } + if (thisPath.empty() || thisPath == oneSlash) { thisPath.clear(); @@ -61,6 +67,12 @@ uri_builder& uri_builder::append_path_raw(const utility::string_t& toAppend, boo if (!toAppend.empty()) { auto& thisPath = m_uri.m_path; + if (&thisPath == &toAppend) + { + auto appendCopy = toAppend; + return append_path_raw(appendCopy, do_encode); + } + if (thisPath != oneSlash) { thisPath.push_back(_XPLATSTR('/')); @@ -84,6 +96,12 @@ uri_builder& uri_builder::append_query(const utility::string_t& toAppend, bool d if (!toAppend.empty()) { auto& thisQuery = m_uri.m_query; + if (&thisQuery == &toAppend) + { + auto appendCopy = toAppend; + return append_query(appendCopy, do_encode); + } + if (thisQuery.empty()) { thisQuery.clear(); diff --git a/Release/tests/functional/uri/uri_builder_tests.cpp b/Release/tests/functional/uri/uri_builder_tests.cpp index 258ebb9259..1a4f2a41c6 100644 --- a/Release/tests/functional/uri/uri_builder_tests.cpp +++ b/Release/tests/functional/uri/uri_builder_tests.cpp @@ -282,6 +282,19 @@ TEST(append_path_string) builder.clear(); builder.append_path(U("encode%things"), true); VERIFY_ARE_EQUAL(U("/encode%25things"), builder.path()); + + // self references + builder.set_path(U("example")); + builder.append_path(builder.path()); + VERIFY_ARE_EQUAL(U("example/example"), builder.path()); + + builder.set_path(U("/example")); + builder.append_path(builder.path()); + VERIFY_ARE_EQUAL(U("/example/example"), builder.path()); + + builder.set_path(U("/example/")); + builder.append_path(builder.path()); + VERIFY_ARE_EQUAL(U("/example/example/"), builder.path()); } TEST(append_path_raw_string) @@ -321,6 +334,19 @@ TEST(append_path_raw_string) builder.clear(); builder.append_path_raw(U("encode%things"), true); VERIFY_ARE_EQUAL(U("/encode%25things"), builder.path()); + + // self references + builder.set_path(U("example")); + builder.append_path_raw(builder.path()); + VERIFY_ARE_EQUAL(U("example/example"), builder.path()); + + builder.set_path(U("/example")); + builder.append_path_raw(builder.path()); + VERIFY_ARE_EQUAL(U("/example//example"), builder.path()); + + builder.set_path(U("/example/")); + builder.append_path_raw(builder.path()); + VERIFY_ARE_EQUAL(U("/example///example/"), builder.path()); } TEST(append_query_string) @@ -359,6 +385,19 @@ TEST(append_query_string) // key and value separate with '=', '&', and ';' builder.append_query(U("key=&;"), U("=&;value")); VERIFY_ARE_EQUAL(U("key1=value1&key2=value2&key3=value3&key4=value4&key5=1&key6=val6&key%3D%26%3B=%3D%26%3Bvalue"), builder.query()); + + // self references + builder.set_query(U("example")); + builder.append_query(builder.query()); + VERIFY_ARE_EQUAL(U("example&example"), builder.query()); + + builder.set_query(U("&example")); + builder.append_query(builder.query()); + VERIFY_ARE_EQUAL(U("&example&example"), builder.query()); + + builder.set_query(U("&example&")); + builder.append_query(builder.query()); + VERIFY_ARE_EQUAL(U("&example&example&"), builder.query()); } TEST(append_query_string_no_encode)