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/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 6e03e5e2c0..9f3fe756d5 100644 --- a/Release/include/cpprest/uri_builder.h +++ b/Release/include/cpprest/uri_builder.h @@ -1,247 +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 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 diff --git a/Release/src/uri/uri_builder.cpp b/Release/src/uri/uri_builder.cpp index f99923c487..0135610466 100644 --- a/Release/src/uri/uri_builder.cpp +++ b/Release/src/uri/uri_builder.cpp @@ -1,91 +1,138 @@ /*** -* 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" -#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& toAppend, bool do_encode) { - if(path.empty() || path == _XPLATSTR("/")) + if (!toAppend.empty() && toAppend != oneSlash) { - return *this; - } + auto& thisPath = m_uri.m_path; + if (&thisPath == &toAppend) + { + auto appendCopy = toAppend; + return append_path(appendCopy, do_encode); + } - auto encoded_path = is_encode ? uri::encode_uri(path, uri::components::path) : path; - auto thisPath = this->path(); - if(thisPath.empty() || thisPath == _XPLATSTR("/")) - { - if(encoded_path.front() != _XPLATSTR('/')) + 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('/')) { - set_path(_XPLATSTR("/") + encoded_path); + thisPath.push_back(_XPLATSTR('/')); } else { - set_path(encoded_path); + // Only one slash. + } + + if (do_encode) + { + thisPath.append(uri::encode_uri(toAppend, uri::components::path)); + } + else + { + 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 + _XPLATSTR("/") + encoded_path); - } - else - { - // Only one slash. - set_path(thisPath + encoded_path); - } + return *this; } -uri_builder &uri_builder::append_query(const utility::string_t &query, bool is_encode) +uri_builder& uri_builder::append_path_raw(const utility::string_t& toAppend, bool do_encode) { - if(query.empty()) + if (!toAppend.empty()) { - return *this; - } + auto& thisPath = m_uri.m_path; + if (&thisPath == &toAppend) + { + auto appendCopy = toAppend; + return append_path_raw(appendCopy, do_encode); + } - auto encoded_query = is_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); + if (thisPath != oneSlash) + { + thisPath.push_back(_XPLATSTR('/')); + } + + if (do_encode) + { + thisPath.append(uri::encode_uri(toAppend, uri::components::path)); + } + else + { + thisPath.append(toAppend); + } } - else + + return *this; +} + +uri_builder& uri_builder::append_query(const utility::string_t& toAppend, bool do_encode) +{ + if (!toAppend.empty()) { - // Only one ampersand. - this->set_query(thisQuery + encoded_query); + auto& thisQuery = m_uri.m_query; + if (&thisQuery == &toAppend) + { + auto appendCopy = toAppend; + return append_query(appendCopy, do_encode); + } + + 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. + } + + if (do_encode) + { + thisQuery.append(uri::encode_uri(toAppend, uri::components::query)); + } + else + { + thisQuery.append(toAppend); + } } + 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()); @@ -99,7 +146,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()); @@ -107,32 +154,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 d4b792e091..1a4f2a41c6 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")); @@ -269,6 +269,84 @@ 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()); + + // 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) +{ + // empty uri builder path + uri_builder builder; + builder.append_path_raw(U("path1")); + VERIFY_ARE_EQUAL(U("/path1"), builder.path()); + + // 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")); + VERIFY_ARE_EQUAL(U("/path1///path2"), builder.path()); + + // leading slash (should result in "//") + builder.append_path_raw(U("/path3")); + VERIFY_ARE_EQUAL(U("/path1///path2//path3"), 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()); + + // 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()); + + // 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) @@ -281,7 +359,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")); @@ -307,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) @@ -398,8 +489,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)