diff --git a/doc/javadoc.hpp b/doc/javadoc.hpp new file mode 100644 index 00000000..af8cf7ca --- /dev/null +++ b/doc/javadoc.hpp @@ -0,0 +1,49 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAllinace/url +// + +/** {brief} + + {description} + + @par Example + {description} + @code + {statements} + @endcode + + @par BNF + @code + ELEMENT = {ABNF} + @endcode + + @note {text} + + @par Exception Safety + Does not throw. + No-throw guarantee. + Strong guarantee. + Calls to allocate may throw. + Exceptions thrown on invalid input. + + @return {description}. + + @tparam {name} {description}. + + @param {name} {description}. + + @throw {condition}. + + @par Specification + @li text (rfc#) + + @par Thread Safety + {description} + + @see @ref {refid}, @ref {refid}. +*/ diff --git a/include/boost/url/bnf/impl/parse.hpp b/include/boost/url/bnf/impl/parse.hpp index d7f2bd74..f75f5007 100644 --- a/include/boost/url/bnf/impl/parse.hpp +++ b/include/boost/url/bnf/impl/parse.hpp @@ -124,7 +124,7 @@ parse_string( Tn&&... tn) { error_code ec; - if(parse(s, ec, + if(parse_string(s, ec, std::forward(t0), std::forward(tn)...)) { diff --git a/include/boost/url/detail/except.hpp b/include/boost/url/detail/except.hpp index f4fa2396..debe5212 100644 --- a/include/boost/url/detail/except.hpp +++ b/include/boost/url/detail/except.hpp @@ -20,7 +20,7 @@ namespace detail { BOOST_URL_DECL void BOOST_NORETURN throw_bad_alloc(source_location const& loc); BOOST_URL_DECL void BOOST_NORETURN throw_invalid_argument(char const* what, source_location const& loc); -//BOOST_URL_DECL void BOOST_NORETURN throw_length_error(char const* what, source_location const& loc); +BOOST_URL_DECL void BOOST_NORETURN throw_length_error(char const* what, source_location const& loc); BOOST_URL_DECL void BOOST_NORETURN throw_out_of_range(source_location const& loc); BOOST_URL_DECL void BOOST_NORETURN throw_system_error(error_code const& ec, source_location const& loc); //BOOST_URL_DECL void BOOST_NORETURN throw_system_error(error e, source_location const& loc); diff --git a/include/boost/url/detail/impl/except.ipp b/include/boost/url/detail/impl/except.ipp index 9d3e54fc..e1786bce 100644 --- a/include/boost/url/detail/impl/except.ipp +++ b/include/boost/url/detail/impl/except.ipp @@ -27,7 +27,6 @@ throw_bad_alloc( std::bad_alloc(), loc); } -#if 0 void throw_length_error( char const* what, @@ -36,7 +35,6 @@ throw_length_error( throw_exception( std::length_error(what), loc); } -#endif void throw_invalid_argument( diff --git a/include/boost/url/detail/impl/parse.ipp b/include/boost/url/detail/impl/parse.ipp index 6ef4659c..b1ea94ed 100644 --- a/include/boost/url/detail/impl/parse.ipp +++ b/include/boost/url/detail/impl/parse.ipp @@ -85,7 +85,7 @@ apply_authority( // leading "//" for authority p.resize( part::id_user, - u.username.str.size() + 2); + u.user.str.size() + 2); if(u.password.has_value()) { diff --git a/include/boost/url/detail/parts.hpp b/include/boost/url/detail/parts.hpp index 8c6253fa..16469230 100644 --- a/include/boost/url/detail/parts.hpp +++ b/include/boost/url/detail/parts.hpp @@ -11,6 +11,7 @@ #define BOOST_URL_DETAIL_PARTS_HPP #include +#include #include #include @@ -43,7 +44,17 @@ struct parts std::uint16_t port_number = 0; urls::host_type host_type = urls::host_type::none; + urls::scheme scheme = + urls::scheme::none; + // size excluding null + std::size_t + len() const noexcept + { + return offset[id_end]; + } + + // size of id std::size_t len(int id) const noexcept { @@ -51,6 +62,7 @@ struct parts offset[id]; } + // size of [begin, end) std::size_t len( int begin, @@ -62,6 +74,7 @@ struct parts offset[begin]; } + // return id as string string_view get(int id, char const* s) const noexcept @@ -72,6 +85,7 @@ struct parts offset[id] }; } + // return [begin, end) as string string_view get(int begin, int end, @@ -83,6 +97,7 @@ struct parts offset[begin] }; } + // change id to size n void resize( int id, @@ -100,7 +115,8 @@ struct parts int id, std::size_t n) noexcept { - BOOST_ASSERT(id < detail::id_end - 1); + BOOST_ASSERT( + id < detail::id_end - 1); BOOST_ASSERT(n <= len(id)); offset[id + 1] = offset[id] + static_cast(n); diff --git a/include/boost/url/detail/pct_encoding.hpp b/include/boost/url/detail/pct_encoding.hpp index eef4731d..19ef2b1b 100644 --- a/include/boost/url/detail/pct_encoding.hpp +++ b/include/boost/url/detail/pct_encoding.hpp @@ -138,14 +138,9 @@ pct_decode_size( ++n; continue; } - if(opt.non_normal_is_error) - { - // reserved character in input - ec = error::illegal_reserved_char; - break; - } - ++n; - ++it; + // reserved character in input + ec = error::illegal_reserved_char; + break; } return n; } diff --git a/include/boost/url/impl/scheme.ipp b/include/boost/url/impl/scheme.ipp index 087a9999..83f2f081 100644 --- a/include/boost/url/impl/scheme.ipp +++ b/include/boost/url/impl/scheme.ipp @@ -23,6 +23,9 @@ string_to_scheme( using bnf::ascii_tolower; switch(s.size()) { + case 0: // none + return scheme::none; + case 2: // ws if( ascii_tolower(s[0]) == 'w' && ascii_tolower(s[1]) == 's') @@ -97,11 +100,11 @@ to_string(scheme s) noexcept case scheme::https: return "https"; case scheme::ws: return "ws"; case scheme::wss: return "wss"; - + case scheme::none: return {}; default: break; } - return "unknown"; + return ""; } } // urls diff --git a/include/boost/url/impl/url.ipp b/include/boost/url/impl/url.ipp index 8d2152de..e87b6017 100644 --- a/include/boost/url/impl/url.ipp +++ b/include/boost/url/impl/url.ipp @@ -12,11 +12,16 @@ #include #include +#include +#include #include #include #include +#include +#include #include #include +#include #include #include @@ -70,21 +75,21 @@ copy( char const* s, detail::parts const& pt) { - auto n = pt.len( - id_scheme, id_end); + auto n = pt.len(); if(n == 0 && ! s_) return; if(cap_ < n) { auto cap = growth_impl( cap_, n); - auto p = alloc_impl(cap); + auto s1 = alloc_impl(cap); if(s_) free_impl(s_); - s_ = p; + s_ = s1; cap_ = cap; } - std::memcpy(s_, s, n); + if(n != 0) + std::memcpy(s_, s, n); s_[n] = 0; pt_ = pt; } @@ -109,19 +114,24 @@ growth_impl( std::size_t cap, std::size_t new_size) { - if(new_size >= std::size_t(-1)) - detail::throw_bad_alloc( + if(new_size > max_size()) + detail::throw_length_error( + "url::reserve", BOOST_CURRENT_LOCATION); BOOST_ASSERT(new_size > cap); if(cap == 0) + { + // minimum + if( new_size < 24) + return 24; return new_size; + } // 50% growth factor - auto n = cap + (cap >> 1); + auto n = cap + cap / 2; if(n < cap) { // overflow - return std::size_t(-1) - - 1; // for null + return max_size(); } if(n < new_size) return new_size; @@ -198,9 +208,7 @@ operator=(url_view const& u) } //------------------------------------------------ -//************************************************ -//************************************************ -//************************************************ +//------------------------------------------------ //------------------------------------------------ //------------------------------------------------ @@ -213,8 +221,7 @@ bool url:: empty() const noexcept { - return len( - id_scheme, id_end) == 0; + return pt_.len() == 0; } //------------------------------------------------ @@ -262,6 +269,13 @@ scheme() const noexcept return s; } +urls::scheme +url:: +scheme_id() const noexcept +{ + return pt_.scheme; +} + //---------------------------------------------------------- // // authority @@ -329,7 +343,7 @@ encoded_userinfo() const noexcept string_view url:: -encoded_username() const noexcept +encoded_user() const noexcept { auto s = get(id_user); if(! s.empty()) @@ -585,9 +599,7 @@ encoded_fragment() const noexcept } //------------------------------------------------ -//************************************************ -//************************************************ -//************************************************ +//------------------------------------------------ //------------------------------------------------ string_view @@ -688,36 +700,290 @@ set_encoded_origin( // //------------------------------------------------ +void +url:: +assert_scheme() const noexcept +{ + BOOST_ASSERT( + (len(id_scheme) == 0) || + (len(id_scheme) > 1 && + get(id_scheme).ends_with(':'))); +} + url& url:: -set_scheme( - string_view s) +set_scheme(string_view s) { -#if 0 - if(s.empty()) - { - resize_impl(id_scheme, 0); - return *this; - } - - scheme_bnf b; - bnf::parse(s, b); - auto const n = s.size(); - auto const dest = - resize_impl(id_scheme, n + 1); - s.copy(dest, n); - dest[n] = ':'; -#endif + set_scheme_impl( + s, string_to_scheme(s)); return *this; } -#if 0 +url& +url:: +set_scheme(urls::scheme id) +{ + if(id == urls::scheme::unknown) + detail::throw_invalid_argument( + "url::set_scheme", + BOOST_CURRENT_LOCATION); + if(id == urls::scheme::none) + { + set_scheme_impl("", id); + return *this; + } + set_scheme_impl( + to_string(id), id); + return *this; +} + +void +url:: +set_scheme_impl( + string_view s, + urls::scheme id) +{ + assert_scheme(); + + if(s.empty()) + { + if(len(id_scheme) == 0) + return; + + // remove scheme + // The complicated case is changing + // path-rootless to path-noscheme + bool const need_dot = [this] + { + if(has_authority()) + return false; + auto s = get(id_path); + if(s.empty()) + return false; + if(s.starts_with('/')) + return false; + auto const p = static_cast< + url const*>(this)->path(); + BOOST_ASSERT(! p.empty()); + auto it = p.begin(); + s = it->encoded_segment(); + return s.find_first_of(':') != + string_view::npos; + }(); + + if(! need_dot) + { + // just remove the scheme + resize_impl(id_scheme, 0); + pt_.scheme = id; + assert_scheme(); + return; + } + + // remove the scheme but add "./" + // to the beginning of the path + auto dest = resize_impl( + id_scheme, 2); + dest[0] = '.'; + dest[1] = '/'; + pt_.split(id_scheme, 0); + pt_.scheme = id; + assert_scheme(); + return; + } + + scheme_bnf b; + error_code ec; + bnf::parse_string(s, ec, b); + if(ec.failed()) + detail::throw_invalid_argument( + "url::set_scheme", + BOOST_CURRENT_LOCATION); + auto n = s.size(); + auto dest = resize_impl( + id_scheme, n + 1); + s.copy(dest, n); + dest[n] = ':'; + pt_.scheme = id; + assert_scheme(); + return; +} + //------------------------------------------------ // // authority // //------------------------------------------------ +void +url:: +assert_userinfo() const noexcept +{ + BOOST_ASSERT( + len(id_user) == 0 || + get(id_user).starts_with("//")); + BOOST_ASSERT( + len(id_pass) == 0 || + get(id_user).starts_with("//")); + BOOST_ASSERT( + (len(id_pass) == 0) || + (len(id_pass) == 1 && + get(id_pass) == "@") || + (len(id_pass) == 2 && + get(id_pass) == ":@") || + (len(id_pass) > 2 && + get(id_pass).starts_with(':') && + get(id_pass).ends_with('@'))); +} + +//------------------------------------------------ + +char* +url:: +set_user_impl(std::size_t n) +{ + assert_userinfo(); + if(len(id_pass) != 0) + { + // keep "//" + auto dest = resize_impl( + id_user, 2 + n); + assert_userinfo(); + return dest + 2; + } + // add authority + auto dest = resize_impl( + id_user, 2 + n + 1); + pt_.split(id_user, 2 + n); + dest[0] = '/'; + dest[1] = '/'; + dest[2 + n] = '@'; + assert_userinfo(); + return dest + 2; +} + +url& +url:: +clear_user() noexcept +{ + assert_userinfo(); + if(len(id_user) == 0) + { + // no authority + } + else if(len(id_pass) == 1) + { + // no password, remove '@' + resize_impl( + id_user, id_host, 2); + } + else + { + // keep password + resize_impl(id_user, 2); + } + assert_userinfo(); + return *this; + } + +url& +url:: +set_user(string_view s) +{ + masked_char_set< + unsub_char_mask> cs; + auto const n = + pct_encode_size(s, cs); + auto dest = set_user_impl(n); + dest = detail::pct_encode( + dest, get(id_pass).data(), + s, {}, cs); + BOOST_ASSERT(dest == + get(id_pass).data()); + assert_userinfo(); + return *this; +} + +url& +url:: +set_encoded_user( + string_view s) +{ + if(s.empty()) + { + // remove user + set_user_impl(0); + return *this; + } + error_code ec; + masked_char_set< + unsub_char_mask> cs; + pct_decode_size(s, ec, cs); + if(ec) + detail::throw_invalid_argument( + "url::set_encoded_user", + BOOST_CURRENT_LOCATION); + auto dest = set_user_impl(s.size()); + BOOST_ASSERT(dest != nullptr); + std::memcpy(dest, s.data(), s.size()); + return *this; +} + +//------------------------------------------------ + +char* +url:: +set_password_impl( + std::size_t n) +{ + assert_userinfo(); + if(len(id_user) != 0) + { + // already have authority + auto const dest = resize_impl( + id_pass, 1 + n + 1); + dest[0] = ':'; + dest[n + 1] = '@'; + return dest + 1; + } + // add authority + auto const dest = + resize_impl( + id_user, id_host, + 2 + 1 + n + 1); + pt_.split(id_user, 2); + dest[0] = '/'; + dest[1] = '/'; + dest[2] = ':'; + dest[2 + n + 1] = '@'; + assert_userinfo(); + return dest + 3; +} + +url& +url:: +clear_password() noexcept +{ + assert_userinfo(); + auto const n = + len(id_pass); + if(n == 0) + return *this; + if(len(id_user) == 2) + { + // remove '@' + resize_impl(id_pass, 0); + return *this; + } + // retain '@' + auto dest = + resize_impl(id_pass, 1); + dest[0] = '@'; + return *this; +} + +#if 0 + url& url:: set_encoded_authority( @@ -803,246 +1069,6 @@ set_encoded_userinfo( return *this; } -url& -url:: -set_userinfo_part( - string_view s) -{ - if(! s.empty()) - { - if(s.back() != '@') - invalid_part::raise(); - s.remove_suffix(1); - } - return set_encoded_userinfo(s); -} - -url& -url:: -set_user( - string_view s) -{ - if(s.empty()) - { - if(pt_.len( - id_user) == 0) - return *this; - BOOST_ASSERT(pt_.get( - id_pass, s_).back() == '@'); - BOOST_ASSERT(pt_.get( - id_user, s_).size() >= 2); - BOOST_ASSERT(pt_.get( - id_user, s_)[0] == '/'); - BOOST_ASSERT(pt_.get( - id_user, s_)[1] == '/'); - if(pt_.len( - id_pass) == 1) - { - // remove '@' - resize_impl( - id_user, - id_host, 2); - } - else - { - resize_impl(id_user, 2); - } - return *this; - } - - auto const e = - detail::userinfo_nc_pct_set(); - if(pt_.len( - id_pass) != 0) - { - BOOST_ASSERT(pt_.get( - id_pass, s_).back() == '@'); - // preserve "//" - auto const dest = resize_impl( - id_user, - 2 + e.encoded_size(s)); - e.encode(dest + 2, s); - return *this; - } - auto const n = e.encoded_size(s); - auto const dest = resize_impl( - id_user, 2 + n + 1); - dest[0] = '/'; - dest[1] = '/'; - dest[2 + n] = '@'; - pt_.split( - id_user, - 2 + n); - e.encode(dest + 2, s); - return *this; -} - -url& -url:: -set_encoded_user( - string_view s) -{ - if(s.empty()) - return set_user(s); - - auto const e = - detail::userinfo_nc_pct_set(); - e.validate(s); - - auto const n = s.size(); - if(pt_.len(id_pass) != 0) - { - BOOST_ASSERT(pt_.get( - id_pass, s_).back() == '@'); - // preserve "//" - auto const dest = resize_impl( - id_user, 2 + n); - s.copy(dest + 2, n); - return *this; - } - - // add '@' - auto const dest = resize_impl( - id_user, - 2 + n + 1); - dest[0] = '/'; - dest[1] = '/'; - dest[2 + n] = '@'; - pt_.split( - id_user, - 2 + n); - s.copy(dest + 2, n); - return *this; -} - -url& -url:: -set_password( - string_view s) -{ - if(s.empty()) - { - auto const n = pt_.len( - id_pass); - if(n == 0) - return *this; - BOOST_ASSERT(pt_.get( - id_pass, s_).back() == '@'); - BOOST_ASSERT(pt_.get( - id_user, s_).size() >= 2); - BOOST_ASSERT(pt_.get( - id_user, s_)[0] == '/'); - BOOST_ASSERT(pt_.get( - id_user, s_)[1] == '/'); - if(pt_.len(id_user) == 2) - { - // remove '@' - resize_impl(id_pass, 0); - return *this; - } - // retain '@' - *resize_impl(id_pass, 1) = '@'; - return *this; - } - - auto const e = - detail::userinfo_pct_set(); - auto const n = - e.encoded_size(s); - if(pt_.len(id_user) != 0) - { - auto const dest = resize_impl( - id_pass, 1 + n + 1); - dest[0] = ':'; - dest[n + 1] = '@'; - e.encode(dest + 1, s); - return *this; - } - auto const dest = resize_impl( - id_user, - id_host, - 2 + 1 + n + 1); - dest[0] = '/'; - dest[1] = '/'; - dest[2] = ':'; - dest[2 + n + 1] = '@'; - e.encode(dest + 3, s); - pt_.split(id_user, 2); - return *this; -} - -url& -url:: -set_encoded_password( - string_view s) -{ - if(s.empty()) - return set_password(s); - - auto const e = - detail::userinfo_pct_set(); - if(s[0] == ':') - invalid_part::raise(); - e.validate(s); - - auto const n = s.size(); - if(pt_.len(id_user) != 0) - { - auto const dest = resize_impl( - id_pass, 1 + n + 1); - dest[0] = ':'; - dest[n + 1] = '@'; - s.copy(dest + 1, n); - return *this; - } - auto const dest = resize_impl( - id_user, - id_host, - 2 + 1 + n + 1); - dest[0] = '/'; - dest[1] = '/'; - dest[2] = ':'; - dest[2 + n + 1] = '@'; - s.copy(dest + 3, n); - pt_.split(id_user, 2); - return *this; -} - -url& -url:: -set_password_part( - string_view s) -{ - if(s.empty()) - return set_password(s); - if(s.size() == 1) - { - if(s.front() != ':') - invalid_part::raise(); - if(pt_.len( - id_user) != 0) - { - auto const dest = resize_impl( - id_pass, 2); - dest[0] = ':'; - dest[1] = '@'; - return *this; - } - auto const dest = resize_impl( - id_user, - id_host, 4); - dest[0] = '/'; - dest[1] = '/'; - dest[2] = ':'; - dest[3] = '@'; - pt_.split( - id_user, 2); - } - set_encoded_password( - s.substr(1)); - return *this; -} - //------------------------------------------------ // // host @@ -2060,6 +2086,7 @@ operator[](string_view key) const } //------------------------------------------------ +#endif void url:: @@ -2068,17 +2095,19 @@ resize_impl( { if(new_size > cap_) { +#if 0 // reallocate auto p = static_cast( sp_->allocate(new_size + 1)); if(s_) { BOOST_ASSERT(cap_ != 0); - std::memcpy(p, s_, size() + 1); + std::memcpy(p, s_, pt_.len() + 1); sp_->deallocate(s_, cap_ + 1, 1); } s_ = p; cap_ = new_size; +#endif } s_[new_size] = '\0'; @@ -2100,81 +2129,96 @@ resize_impl( int last, std::size_t new_len) { - auto const len = + auto const n0 = pt_.len(first, last); - if(new_len == 0 && len == 0) + if(new_len == 0 && n0 == 0) { // VFALCO This happens - //BOOST_ASSERT(s_ != nullptr); + BOOST_ASSERT(s_ != nullptr); return s_ + pt_.offset[first]; } - if(new_len <= len) + if(new_len <= n0) { // shrinking - auto const n = static_cast< - std::size_t>(len - new_len); - auto const pos = pt_.offset[last]; + std::size_t n = n0 - new_len; + auto const pos = + pt_.offset[last]; + // adjust chars std::memmove( s_ + pos - n, s_ + pos, pt_.offset[ id_end] - pos + 1); + // collapse [first, last) for(auto i = first + 1; - i < last; ++i) + i < last; ++i) pt_.offset[i] = pt_.offset[last] - n; + // shift [last, end) left for(auto i = last; - i <= id_end; ++i) + i <= id_end; ++i) pt_.offset[i] -= n; - s_[size()] = '\0'; + s_[pt_.len()] = '\0'; return s_ + pt_.offset[first]; } // growing - auto const n = static_cast< - std::size_t>(new_len - len); + std::size_t n = new_len - n0; - // check for exceeding max size - if(n > ( - (std::size_t)-1)/*max_size()*/ - size()) - too_large::raise(); + if(n > max_size() - pt_.len()) + detail::throw_length_error( + "url::resize", + BOOST_CURRENT_LOCATION); - if(cap_ < size() + n) + if(cap_ < pt_.len() + n) { // reallocate - auto p = static_cast( - sp_->allocate(cap_ + n + 1)); + auto new_cap = growth_impl( + cap_, pt_.len() + n); + auto s1 = alloc_impl(new_cap); if(s_) { BOOST_ASSERT(cap_ != 0); - std::memcpy(p, s_, size() + 1); - sp_->deallocate(s_, cap_ + 1, 1); + std::memcpy(s1, s_, pt_.len() + 1); + free_impl(s_); } - s_ = p; - cap_ = cap_ + n; + s_ = s1; + cap_ = new_cap; } auto const pos = pt_.offset[last]; + // adjust chars std::memmove( s_ + pos + n, s_ + pos, pt_.offset[id_end] - pos + 1); + // collapse [first, last) for(auto i = first + 1; i < last; ++i) pt_.offset[i] = pt_.offset[last] + n; + // shift [last, end) right for(auto i = last; i <= id_end; ++i) pt_.offset[i] += n; - - s_[size()] = '\0'; + s_[pt_.len()] = '\0'; return s_ + pt_.offset[first]; } -#endif +//------------------------------------------------ + +std::ostream& +operator<<( + std::ostream& os, + url const& u) +{ + auto const s = u.str(); + os.write(s.data(), s.size()); + return os; +} } // urls } // boost diff --git a/include/boost/url/impl/url_view.ipp b/include/boost/url/impl/url_view.ipp index 36739056..3a698855 100644 --- a/include/boost/url/impl/url_view.ipp +++ b/include/boost/url/impl/url_view.ipp @@ -96,19 +96,11 @@ bool url_view:: empty() const noexcept { - return len( - id_scheme, id_end) == 0; + return pt_.len() == 0; } //------------------------------------------------ -string_view -url_view:: -str() const -{ - return get(id_scheme, id_end); -} - string_view url_view:: encoded_origin() const noexcept @@ -152,6 +144,13 @@ scheme() const noexcept return s; } +urls::scheme +url_view:: +scheme_id() const noexcept +{ + return pt_.scheme; +} + //---------------------------------------------------------- // // authority @@ -219,7 +218,7 @@ encoded_userinfo() const noexcept string_view url_view:: -encoded_username() const noexcept +encoded_user() const noexcept { auto s = get(id_user); if(! s.empty()) @@ -472,6 +471,17 @@ encoded_fragment() const noexcept return s.substr(1); } +//------------------------------------------------ +//------------------------------------------------ +//------------------------------------------------ + +string_view +url_view:: +str() const +{ + return get(id_scheme, id_end); +} + //------------------------------------------------ url_view @@ -487,6 +497,7 @@ parse_uri( detail::parts p; // scheme + p.scheme = t.scheme.id; p.resize( detail::part::id_scheme, t.scheme.str.size() + 1); diff --git a/include/boost/url/pct_encoding.hpp b/include/boost/url/pct_encoding.hpp index 8b5f593f..a0016fd9 100644 --- a/include/boost/url/pct_encoding.hpp +++ b/include/boost/url/pct_encoding.hpp @@ -54,11 +54,6 @@ namespace urls { parameter is omitted, the default options will be used. - @param a An optional allocator the returned - string will use. If this parameter is omitted, - the default allocator is used, and the return - type of the function becomes `std::string`. - @par Specification @li 2.1. Percent-Encoding (rfc3986) diff --git a/include/boost/url/rfc/impl/userinfo_bnf.ipp b/include/boost/url/rfc/impl/userinfo_bnf.ipp index bfa32d83..fc5b474e 100644 --- a/include/boost/url/rfc/impl/userinfo_bnf.ipp +++ b/include/boost/url/rfc/impl/userinfo_bnf.ipp @@ -33,7 +33,7 @@ parse( pct_encoded_bnf< masked_char_set< unsub_char_mask>>{ - t.username})) + t.user})) return false; if( it != end && *it == ':') diff --git a/include/boost/url/rfc/userinfo_bnf.hpp b/include/boost/url/rfc/userinfo_bnf.hpp index 8b483c21..a847f01d 100644 --- a/include/boost/url/rfc/userinfo_bnf.hpp +++ b/include/boost/url/rfc/userinfo_bnf.hpp @@ -23,9 +23,9 @@ namespace urls { @par BNF @code - userinfo = username [ ":" [ password ] ] + userinfo = user [ ":" [ password ] ] - username = *( unreserved / pct-encoded / sub-delims ) + user = *( unreserved / pct-encoded / sub-delims ) password = *( unreserved / pct-encoded / sub-delims / ":" ) @endcode @@ -35,7 +35,7 @@ namespace urls { struct userinfo_bnf { string_view str; - pct_encoded_str username; + pct_encoded_str user; optional password; BOOST_URL_DECL diff --git a/include/boost/url/scheme.hpp b/include/boost/url/scheme.hpp index bf1f6733..21624c92 100644 --- a/include/boost/url/scheme.hpp +++ b/include/boost/url/scheme.hpp @@ -20,9 +20,13 @@ namespace urls { */ enum class scheme : unsigned char { + /** Indicates that no scheme is present + */ + none = 0, + /** Indicates the scheme is not a well-known scheme */ - unknown = 0, + unknown, /** File Transfer Protocol (FTP) diff --git a/include/boost/url/static_url.hpp b/include/boost/url/static_url.hpp index 56a97972..5dd9fede 100644 --- a/include/boost/url/static_url.hpp +++ b/include/boost/url/static_url.hpp @@ -12,6 +12,7 @@ #include #include +#include namespace boost { namespace urls { @@ -73,6 +74,9 @@ class static_url { char buf_[Capacity + 1]; + BOOST_STATIC_ASSERT( + Capacity < max_size()); + public: ~static_url() { diff --git a/include/boost/url/url.hpp b/include/boost/url/url.hpp index 0162d054..4964d500 100644 --- a/include/boost/url/url.hpp +++ b/include/boost/url/url.hpp @@ -15,9 +15,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -46,7 +48,9 @@ class url_view; @li Functions which throw offer the strong exception safety guarantee. - @see @li Uniform Resource Identifier (URI): Generic Syntax + @par Specification + @li + Uniform Resource Identifier (URI): Generic Syntax (rfc3986) */ class BOOST_SYMBOL_VISIBLE url { @@ -140,12 +144,26 @@ public: url& operator=(url_view const& u); + //-------------------------------------------- + //-------------------------------------------- + //-------------------------------------------- + //-------------------------------------------- // // classification // //-------------------------------------------- + /** An integer for the maximum size string that can be represented + */ + static + constexpr + std::size_t + max_size() + { + return 0x7ffffffe; + } + /** Return true if the URL is empty An empty URL is a relative-ref with @@ -170,17 +188,35 @@ public: //-------------------------------------------- /** Return true if a scheme exists + + @par Specification + @li + 3.1. Scheme (rfc3986) */ BOOST_URL_DECL bool has_scheme() const noexcept; /** Return the scheme + + @par Specification + @li + 3.1. Scheme (rfc3986) */ BOOST_URL_DECL string_view scheme() const noexcept; + /** Return a known-scheme constant if a scheme is present + + @par Specification + @li + 3.1. Scheme (rfc3986) + */ + BOOST_URL_DECL + urls::scheme + scheme_id() const noexcept; + //-------------------------------------------- // // authority @@ -222,7 +258,7 @@ public: /** Return the userinfo if it exists, or an empty string Returns the userinfo of the URL as an encoded - string. The userinfo includes the username and + string. The userinfo includes the user and password, with a colon separating the components if the password is not empty. @@ -263,11 +299,11 @@ public: encoded_userinfo(), {}, a); } - /** Return the username if it exists, or an empty string + /** Return the user if it exists, or an empty string - This function returns the username portion of + This function returns the user portion of the userinfo if present, as an encoded string. - The username portion is defined by all of the + The user portion is defined by all of the characters in the userinfo up to but not including the first colon (':"), or the entire userinfo if no colon is present. @@ -278,13 +314,13 @@ public: */ BOOST_URL_DECL string_view - encoded_username() const noexcept; + encoded_user() const noexcept; - /** Return the username if it exists, or an empty string + /** Return the user if it exists, or an empty string - This function returns the username portion of + This function returns the user portion of the userinfo if present, as a decoded string. - The username portion is defined by all of the + The user portion is defined by all of the characters in the userinfo up to but not including the first colon (':"), or the entire userinfo if no colon is present. @@ -306,11 +342,11 @@ public: class Allocator = std::allocator> string_type - username( + user( Allocator const& a = {}) const { return detail::pct_decode_unchecked( - encoded_username(), {}, a); + encoded_user(), {}, a); } /** Return true if a password exists @@ -651,8 +687,6 @@ public: pt_.decoded[id_frag], {}, a); } - //-------------------------------------------- - //-------------------------------------------- //-------------------------------------------- //-------------------------------------------- //-------------------------------------------- @@ -697,52 +731,16 @@ public: void reserve(std::size_t n); - //------------------------------------------------------ - - /** Set the URL. - - @par Exception Safety - - Strong guarantee. - Calls to allocate may throw. - - @param s The URL to set. The contents must - meet the syntactic requirements of a - URI-reference. - - @throw std::exception parsing error. - */ - BOOST_URL_DECL - url& - set_encoded_url( - string_view s); - - /** Set the origin to the specified value. - - The origin consists of the everything from the - beginning of the URL up to but not including - the path. - - @par Exception Safety - - Strong guarantee. - Calls to allocate may throw. - - @param s The origin to set. Special characters - must be percent-encoded, or an exception is - thrown. - */ - BOOST_URL_DECL - url& - set_encoded_origin( - string_view s); - //------------------------------------------------------ // // scheme // //------------------------------------------------------ +private: + void assert_scheme() const noexcept; +public: + /** Set the scheme. This function sets the scheme to the specified @@ -756,157 +754,137 @@ public: contain a valid scheme. A trailing colon is automatically added. - @par ABNF + @par Example + @code + url u; + u.set_scheme( "http" ); // produces "http:" + u.set_scheme( "" ); // produces "" + u.set_scheme( "1forall"); // throws, invalid scheme + @endcode + + @par BNF @code scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) @endcode @par Exception Safety - Strong guarantee. Calls to allocate may throw. + @return A reference to the object, for chaining. + @param s The scheme to set. This string must not include a trailing colon, otherwise an exception is thrown. - @throw std::exception invalid scheme. + @throw std::invalid_argument invalid scheme. + + @par Specification + @li + 3.1. Scheme (rfc3986) */ BOOST_URL_DECL url& set_scheme(string_view s); + /** Set the scheme. + + This function sets the scheme to the specified + string: + + @li If `id` is @ref scheme::none, any existing + scheme is removed along with the trailing + colon (':'), otherwise: + + @li The scheme is set to `id`, which must + not be equal to @ref scheme::unknown. + + @par Example + @code + url u; + u.set_scheme( scheme::http ); // produces "http:" + u.set_scheme( scheme::none ); // produces "" + u.set_scheme( scheme::unknown); // throws, invalid scheme + @endcode + + @par BNF + @code + scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + @endcode + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + + @return A reference to the object, for chaining. + + @param id The scheme to set. + + @throw std::invalid_argument `id == scheme::unknown` + */ + BOOST_URL_DECL + url& + set_scheme(urls::scheme id); + +private: + void set_scheme_impl(string_view s, urls::scheme id); +public: + //------------------------------------------------------ // // authority // //------------------------------------------------------ - /** Set the authority. +private: + void assert_userinfo() const noexcept; + char* set_user_impl(std::size_t n); +public: + + /** Clear the user. + + If a user is present, it is removed. If the + user was the only component present in the + userinfo, then the userinfo is removed without + removing the authority. @par Exception Safety + Does not throw. - Strong guarantee. - Calls to allocate may throw. + @return A reference to the object, for chaining. - @param s The authority to set. This string - must meed the syntactic requirements for - the components of the authority, otherwise - an exception is thrown. - - @throw std::exception invalid authority. + @par Specification + @li + 3.2.1. User Information (rfc3986) */ BOOST_URL_DECL url& - set_encoded_authority( - string_view s); - - //------------------------------------------------------ - // - // userinfo - // - //------------------------------------------------------ - - /** Set the userinfo. - - Sets the userinfo of the URL to the given - encoded string: - - @li If the string is empty, the userinfo is - cleared, else - - @li If the string is not empty, then the userinfo - is set to the given string. The user is set to - the characters up to the first colon if any, - while the password is set to the remaining - characters if any. - If the URL previously did not have an authority - (@ref has_authority returns `false`), a double - slash ("//") is prepended to the userinfo. - The string must meet the syntactic requirements - of userinfo otherwise an exception is - thrown. - - @par ABNF - @code - userinfo = [ [ user ] [ ':' password ] ] - user = *( unreserved / pct-encoded / sub-delims ) - password = *( unreserved / pct-encoded / sub-delims / ":" ) - @endcode - - @par Exception Safety - - Strong guarantee. - Calls to allocate may throw. - - @param s The string to set. - */ - BOOST_URL_DECL - url& - set_encoded_userinfo( - string_view s); - - /** Set the userinfo. - - Sets the userinfo of the URL to the given - encoded string: - - @li If the string is empty, the userinfo is - cleared, else - - @li If the string is not empty, then the userinfo - is set to the given string. The user is set to - the characters up to the first colon if any, - while the password is set to the remaining - characters if any. - If the URL previously did not have an authority - (@ref has_authority returns `false`), a double - slash ("//") is prepended to the userinfo. - The string must meet the syntactic requirements - of userinfo-part otherwise an exception - is thrown. - - @par ABNF - @code - userinfo-part = [ [ user ] [ ':' password ] '@' ] - user = *( unreserved / pct-encoded / sub-delims ) - password = *( unreserved / pct-encoded / sub-delims / ":" ) - @endcode - - @par Exception Safety - - Strong guarantee. - Calls to allocate may throw. - - @param s The string to set. - */ - BOOST_URL_DECL - url& - set_userinfo_part( - string_view s); + clear_user() noexcept; /** Set the user. The user is set to the specified string, - replacing any previous user: + replacing any previous user. If a userinfo + was not present it is added, even if the + user string is empty. The resulting URL + will have an authority if it did not have + one previously. - @li If the string is empty, the user is cleared. - - @li If the string is not empty then the - user is set to the new string. Any special or reserved characters in the string are automatically percent-encoded. - If the URL previously did not have an authority - (@ref has_authority returns `false`), a double - slash ("//") is prepended to the userinfo. @par Exception Safety - Strong guarantee. Calls to allocate may throw. + @return A reference to the object, for chaining. + @param s The string to set. This string may contain any characters, including nulls. + + @par Specification + @li + 3.2.1. User Information (rfc3986) */ BOOST_URL_DECL url& @@ -915,28 +893,25 @@ public: /** Set the user. - The user is set to the specified encoded - string, replacing any previous user: + The user is set to the specified string, + replacing any previous user. If a userinfo + was not present it is added, even if the + user string is empty. The resulting URL + will have an authority if it did not have + one previously. - @li If the string is empty, the user is cleared. + The string must be a valid percent-encoded + string for the user field, otherwise an + exception is thrown. - @li If the string is not empty then the - user is set to the given string. - If the URL previously did not have an authority - (@ref has_authority returns `false`), a double - slash ("//") is prepended to the userinfo. - The string must meet the syntactic requirements - of user otherwise an exception is - thrown. - - @li - @par ABNF + @par BNF @code user = *( unreserved / pct-encoded / sub-delims ) @endcode - @par Exception Safety + @return A reference to the object, for chaining. + @par Exception Safety Strong guarantee. Calls to allocate may throw. @@ -947,6 +922,14 @@ public: set_encoded_user( string_view s); +private: + char* set_password_impl(std::size_t n); +public: + + BOOST_URL_DECL + url& + clear_password() noexcept; + /** Set the password. This function sets the password to the specified @@ -1012,28 +995,69 @@ public: set_encoded_password( string_view s); - /** Set the password. + /** Set the origin to the specified value. - The password part is set to the encoded string - `s`, replacing any previous password: + The origin consists of the everything from the + beginning of the URL up to but not including + the path. - @li If the string is empty, the password is - cleared, and the first occurring colon (':') is - removed from the userinfo if present, otherwise + @par Exception Safety - @li If ths string is not empty then the password - is set to the new string, which must include a - leading colon. + Strong guarantee. + Calls to allocate may throw. + + @param s The origin to set. Special characters + must be percent-encoded, or an exception is + thrown. + */ + BOOST_URL_DECL + url& + set_encoded_origin( + string_view s); + + /** Set the authority. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + + @param s The authority to set. This string + must meed the syntactic requirements for + the components of the authority, otherwise + an exception is thrown. + + @throw std::invalid_argument invalid authority + */ + BOOST_URL_DECL + url& + set_encoded_authority( + string_view s); + + /** Set the userinfo. + + Sets the userinfo of the URL to the given + encoded string: + + @li If the string is empty, the userinfo is + cleared, else + + @li If the string is not empty, then the userinfo + is set to the given string. The user is set to + the characters up to the first colon if any, + while the password is set to the remaining + characters if any. If the URL previously did not have an authority (@ref has_authority returns `false`), a double slash ("//") is prepended to the userinfo. The string must meet the syntactic requirements - of password-part otherwise an exception is + of userinfo otherwise an exception is thrown. - @par ANBF + @par BNF @code - password-part = [ ':' *( unreserved / pct-encoded / sub-delims / ":" ) ] + userinfo = [ [ user ] [ ':' password ] ] + user = *( unreserved / pct-encoded / sub-delims ) + password = *( unreserved / pct-encoded / sub-delims / ":" ) @endcode @par Exception Safety @@ -1045,7 +1069,7 @@ public: */ BOOST_URL_DECL url& - set_password_part( + set_encoded_userinfo( string_view s); //------------------------------------------------------ @@ -1089,7 +1113,7 @@ public: and `s` is not empty, then the authority is added including a leading double slash ("//"). - @par ABNF + @par BNF @code IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet @@ -1139,7 +1163,7 @@ public: then the authority is added including the leading double slash ("//"). - @par ABNF + @par BNF @code host = IP-literal / IPv4address / reg-name @@ -1217,7 +1241,7 @@ public: of port otherwise an exception is thrown. - @par ABNF + @par BNF @code port = *DIGIT @endcode @@ -1254,7 +1278,7 @@ public: of port-part otherwise an exception is thrown. - @par ABNF + @par BNF @code port-part = [ ':' *DIGIT ] @endcode @@ -1302,7 +1326,7 @@ public: If the path does not meet the syntactic requirements, an exception is thrown. - @par ABNF + @par BNF @code path = path-abempty ; begins with "/" or is empty / path-absolute ; begins with "/" but not "//" @@ -1399,7 +1423,7 @@ public: of query otherwise an exception is thrown. - @par ABNF + @par BNF @code query = *( pchar / "/" / "?" ) @endcode @@ -1433,7 +1457,7 @@ public: of query-part otherwise an exception is thrown. - @par ABNF + @par BNF @code query-part = [ "#" *( pchar / "/" / "?" ) ] @endcode @@ -1519,7 +1543,7 @@ public: of fragment otherwise an exception is thrown. - @par ABNF + @par BNF @code fragment = *( pchar / "/" / "?" ) @endcode @@ -1555,7 +1579,7 @@ public: of fragment-part otherwise an exception is thrown. - @par ABNF + @par BNF @code fragment-part = [ "#" *( pchar / "/" / "?" ) ] @endcode @@ -2159,6 +2183,10 @@ private: parse() noexcept; }; +BOOST_URL_DECL +std::ostream& +operator<<(std::ostream& os, url const& u); + } // urls } // boost diff --git a/include/boost/url/url.natvis b/include/boost/url/url.natvis index 3437e6d9..7f42819c 100644 --- a/include/boost/url/url.natvis +++ b/include/boost/url/url.natvis @@ -35,4 +35,34 @@ + + {s_,[pt_.offset[detail::part::id_end]]s} + + + {s_,[pt_.offset[detail::part::id_user]]s} + + + {s_+pt_.offset[detail::id_user],[pt_.offset[detail::part::id_pass]-pt_.offset[detail::part::id_user]]s} + + + {s_+pt_.offset[detail::id_pass],[pt_.offset[detail::part::id_host]-pt_.offset[detail::part::id_pass]]s} + + + {s_+pt_.offset[detail::id_host],[pt_.offset[detail::part::id_port]-pt_.offset[detail::part::id_host]]s} + + + {s_+pt_.offset[detail::id_port],[pt_.offset[detail::part::id_path]-pt_.offset[detail::part::id_port]]s} + + + {s_+pt_.offset[detail::id_path],[pt_.offset[detail::part::id_query]-pt_.offset[detail::part::id_path]]s} + + + {s_+pt_.offset[detail::id_query],[pt_.offset[detail::part::id_frag]-pt_.offset[detail::part::id_query]]s} + + + {s_+pt_.offset[detail::id_frag],[pt_.offset[detail::part::id_end]-pt_.offset[detail::part::id_frag]]s} + + + + diff --git a/include/boost/url/url_view.hpp b/include/boost/url/url_view.hpp index ac24b2a5..2d656d62 100644 --- a/include/boost/url/url_view.hpp +++ b/include/boost/url/url_view.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -106,12 +107,26 @@ public: url_view const> collect() const; + //-------------------------------------------- + //-------------------------------------------- + //-------------------------------------------- + //-------------------------------------------- // // classification // //-------------------------------------------- + /** An integer for the maximum size string that can be represented + */ + static + constexpr + std::size_t + max_size() + { + return 0x7ffffffe; + } + /** Return true if the URL is empty An empty URL is a relative-ref with @@ -123,12 +138,6 @@ public: //-------------------------------------------- - /** Return the complete encoded URL - */ - BOOST_URL_DECL - string_view - str() const; - /** Return the origin */ BOOST_URL_DECL @@ -142,17 +151,35 @@ public: //-------------------------------------------- /** Return true if a scheme exists + + @par Specification + @li + 3.1. Scheme (rfc3986) */ BOOST_URL_DECL bool has_scheme() const noexcept; /** Return the scheme + + @par Specification + @li + 3.1. Scheme (rfc3986) */ BOOST_URL_DECL string_view scheme() const noexcept; + /** Return a known-scheme constant if a scheme is present + + @par Specification + @li + 3.1. Scheme (rfc3986) + */ + BOOST_URL_DECL + urls::scheme + scheme_id() const noexcept; + //-------------------------------------------- // // authority @@ -194,7 +221,7 @@ public: /** Return the userinfo if it exists, or an empty string Returns the userinfo of the URL as an encoded - string. The userinfo includes the username and + string. The userinfo includes the user and password, with a colon separating the components if the password is not empty. @@ -235,11 +262,11 @@ public: encoded_userinfo(), {}, a); } - /** Return the username if it exists, or an empty string + /** Return the user if it exists, or an empty string - This function returns the username portion of + This function returns the user portion of the userinfo if present, as an encoded string. - The username portion is defined by all of the + The user portion is defined by all of the characters in the userinfo up to but not including the first colon (':"), or the entire userinfo if no colon is present. @@ -250,13 +277,13 @@ public: */ BOOST_URL_DECL string_view - encoded_username() const noexcept; + encoded_user() const noexcept; - /** Return the username if it exists, or an empty string + /** Return the user if it exists, or an empty string - This function returns the username portion of + This function returns the user portion of the userinfo if present, as a decoded string. - The username portion is defined by all of the + The user portion is defined by all of the characters in the userinfo up to but not including the first colon (':"), or the entire userinfo if no colon is present. @@ -278,11 +305,11 @@ public: class Allocator = std::allocator> string_type - username( + user( Allocator const& a = {}) const { return detail::pct_decode_unchecked( - encoded_username(), {}, a); + encoded_user(), {}, a); } /** Return true if a password exists @@ -623,6 +650,16 @@ public: pt_.decoded[id_frag], {}, a); } + //-------------------------------------------- + //-------------------------------------------- + //-------------------------------------------- + + /** Return the complete encoded URL + */ + BOOST_URL_DECL + string_view + str() const; + //-------------------------------------------- // // free functions diff --git a/test/pct_encoding.cpp b/test/pct_encoding.cpp index d8e21497..2a5370c4 100644 --- a/test/pct_encoding.cpp +++ b/test/pct_encoding.cpp @@ -122,7 +122,8 @@ public: // null is reserved opt.allow_null = true; opt.non_normal_is_error = false; - good_decode_size(1, string_view("\0", 1), opt); + bad_decode_size(string_view("\0", 1), opt); + good_decode_size(1, string_view("\0", 1), opt, test_chars_null{}); good_decode_size(1, "%00", opt); opt.allow_null = false; bad_decode_size(string_view("\0", 1), opt); @@ -134,12 +135,14 @@ public: pct_decode_opts opt; good_decode_size(1, "A", opt); - good_decode_size(2, "aA", opt); - good_decode_size(3, "ab%41", opt); + good_decode_size(1, "%41", opt); + bad_decode_size("ab%41", opt); + bad_decode_size("aA", opt); opt.non_normal_is_error = true; good_decode_size(1, "A", opt); good_decode_size(2, "A%20", opt); + bad_decode_size("%41", opt); opt.plus_to_space = true; good_decode_size(2, "A+", opt); opt.plus_to_space = false; @@ -228,7 +231,8 @@ public: opt.non_normal_is_error = false; good_decode( string_view("\0", 1), - string_view("\0", 1), opt); + string_view("\0", 1), opt, + test_chars_null{}); good_decode("%00", string_view("\0", 1), opt); opt.allow_null = false; @@ -240,8 +244,9 @@ public: pct_decode_opts opt; good_decode("A", "A", opt); - good_decode("aA", "aA", opt); - good_decode("ab%41", "abA", opt); + good_decode("%42", "B", opt); + bad_decode("aA", opt); + bad_decode("ab%41", opt); opt.non_normal_is_error = true; good_decode("A", "A", opt); diff --git a/test/rfc/authority_bnf.cpp b/test/rfc/authority_bnf.cpp index 122aa281..6c7dce20 100644 --- a/test/rfc/authority_bnf.cpp +++ b/test/rfc/authority_bnf.cpp @@ -65,7 +65,7 @@ public: if(BOOST_TEST(p.has_userinfo)) { BOOST_TEST(p.userinfo.str == "x:y"); - BOOST_TEST(p.userinfo.username.str == "x"); + BOOST_TEST(p.userinfo.user.str == "x"); if(BOOST_TEST(p.userinfo.password.has_value())) BOOST_TEST(p.userinfo.password->str == "y"); } diff --git a/test/rfc/userinfo_bnf.cpp b/test/rfc/userinfo_bnf.cpp index c652a318..9fb942ae 100644 --- a/test/rfc/userinfo_bnf.cpp +++ b/test/rfc/userinfo_bnf.cpp @@ -37,7 +37,7 @@ public: if(! BOOST_TEST(! ec)) return; BOOST_TEST(p.str == s); - BOOST_TEST(p.username.str == s1); + BOOST_TEST(p.user.str == s1); if(s2.has_value()) BOOST_TEST( p.password.has_value() && diff --git a/test/static_url.cpp b/test/static_url.cpp index 077fa455..47627ef8 100644 --- a/test/static_url.cpp +++ b/test/static_url.cpp @@ -12,6 +12,7 @@ #include #include "test_suite.hpp" +#include namespace boost { namespace urls { @@ -135,12 +136,12 @@ public: { url_view uv; BOOST_TEST_NO_THROW( uv = parse_uri( - "http://username:pass@www.boost.org:8080/x/y/z?a=b&c=3#frag")); + "http://user:pass@www.boost.org:8080/x/y/z?a=b&c=3#frag")); url_t u(uv); BOOST_TEST(u.encoded_origin() == - "http://username:pass@www.boost.org:8080"); + "http://user:pass@www.boost.org:8080"); BOOST_TEST(u.scheme() == "http"); - BOOST_TEST(u.username() == "username"); + BOOST_TEST(u.user() == "user"); BOOST_TEST(u.password() == "pass"); BOOST_TEST(u.host() == "www.boost.org"); BOOST_TEST(u.port() == "8080"); @@ -149,11 +150,41 @@ public: BOOST_TEST(u.encoded_fragment() == "frag"); } + void + testSetScheme() + { + { + url_t u; + u = parse_uri("http://www.example.com"); + u.set_scheme(""); + BOOST_TEST(u.str() == "//www.example.com"); + } + { + url_t u; + u = parse_uri("http:live/wire"); + u.set_scheme(""); + BOOST_TEST(u.str() == "live/wire"); + } + { + url_t u; + u = parse_uri("http:my:adidas"); + u.set_scheme(""); + BOOST_TEST(u.str() == "./my:adidas"); + } + { + url_t u; + u = parse_uri("http:my:adidas/"); + u.set_scheme(""); + BOOST_TEST(u.str() == "./my:adidas/"); + } + } + void run() { testSpecial(); testParts(); + testSetScheme(); } }; diff --git a/test/url.cpp b/test/url.cpp index b97ac756..84c5911a 100644 --- a/test/url.cpp +++ b/test/url.cpp @@ -10,7 +10,7 @@ // Test that header file is self-contained. #include -#include +#include #include "test_suite.hpp" @@ -30,7 +30,7 @@ public: log << "href : " << u.str() << "\n" "scheme : " << u.scheme() << "\n" - "user : " << u.encoded_username() << "\n" + "user : " << u.encoded_user() << "\n" "password : " << u.encoded_password() << "\n" "hostname : " << u.encoded_host() << "\n" "port : " << u.port() << "\n" << @@ -69,7 +69,7 @@ public: BOOST_TEST(v.encoded_origin() == "http://user:pass@example.com:80"); BOOST_TEST(v.encoded_authority() == "user:pass@example.com:80"); BOOST_TEST(v.scheme() == "http"); - BOOST_TEST(v.encoded_username() == "user"); + BOOST_TEST(v.encoded_user() == "user"); BOOST_TEST(v.encoded_password() == "pass"); BOOST_TEST(v.encoded_userinfo() == "user:pass"); BOOST_TEST(v.encoded_host() == "example.com"); @@ -78,7 +78,7 @@ public: BOOST_TEST(v.encoded_query() == "k1=v1&k2=v2"); BOOST_TEST(v.encoded_fragment() == ""); - BOOST_TEST(v.username() == "user"); + BOOST_TEST(v.user() == "user"); BOOST_TEST(v.password() == "pass"); BOOST_TEST(v.host() == "example.com"); BOOST_TEST(v.query() == "k1=v1&k2=v2"); @@ -94,24 +94,6 @@ public: BOOST_TEST(url().str() == ""); } - void - testScheme() - { - BOOST_TEST(url().scheme() == ""); - BOOST_TEST(url("http:").scheme() == "http"); - BOOST_TEST(url("http:").str() == "http:"); - BOOST_TEST(url("http:").set_scheme("").scheme() == ""); - BOOST_TEST(url("http:").set_scheme("").str() == ""); - BOOST_TEST(url("http:").set_scheme("ftp").str() == "ftp:"); - BOOST_TEST(url("ws:").set_scheme("gopher").str() == "gopher:"); - BOOST_TEST(url("http://example.com").set_scheme("ftp").str() == "ftp://example.com"); - BOOST_TEST(url("ws://example.com").set_scheme("gopher").str() == "gopher://example.com"); - - BOOST_TEST_THROWS(url().set_scheme("c@t"), invalid_part); - BOOST_TEST_THROWS(url().set_scheme("1cat"), invalid_part); - BOOST_TEST_THROWS(url().set_scheme("http:s"), invalid_part); - } - void testOrigin() { @@ -148,15 +130,15 @@ public: void testUsername() { - BOOST_TEST(url().username() == ""); - BOOST_TEST(url().encoded_username() == ""); - BOOST_TEST(url().set_user("").username() == ""); + BOOST_TEST(url().user() == ""); + BOOST_TEST(url().encoded_user() == ""); + BOOST_TEST(url().set_user("").user() == ""); BOOST_TEST(url().set_user("user").str() == "//user@"); BOOST_TEST(url().set_encoded_user("user%20name").str() == "//user%20name@"); BOOST_TEST(url().set_encoded_user("user%3Aname").str() == "//user%3Aname@"); - BOOST_TEST(url().set_encoded_user("user%3Aname").username() == "user:name"); + BOOST_TEST(url().set_encoded_user("user%3Aname").user() == "user:name"); BOOST_TEST(url().set_encoded_user("user%40name").str() == "//user%40name@"); - BOOST_TEST(url().set_encoded_user("user%40name").username() == "user@name"); + BOOST_TEST(url().set_encoded_user("user%40name").user() == "user@name"); BOOST_TEST(url("http:").set_encoded_user("").str() == "http:"); BOOST_TEST(url("http://@").set_encoded_user("").str() == "http://"); @@ -170,31 +152,6 @@ public: BOOST_TEST_THROWS(url().set_encoded_user("user name"), invalid_part); } - void - testPassword() - { - BOOST_TEST(url().password() == ""); - BOOST_TEST(url().encoded_password() == ""); - BOOST_TEST(url().set_encoded_password("").password() == ""); - BOOST_TEST(url().set_password("pass").str() == "//:pass@"); - BOOST_TEST(url().set_encoded_password("%40pass").str() == "//:%40pass@"); - BOOST_TEST(url().set_encoded_password("pass%20word").str() == "//:pass%20word@"); - BOOST_TEST(url().set_encoded_password("pass%42word").str() == "//:pass%42word@"); - - BOOST_TEST(url("http:").set_encoded_password("").str() == "http:"); - BOOST_TEST(url("http://@").set_encoded_password("").str() == "http://"); - BOOST_TEST(url("http://x@").set_encoded_password("").str() == "http://x@"); - BOOST_TEST(url("http://x@").set_encoded_password("y").str() == "http://x:y@"); - BOOST_TEST(url("http://:@").set_encoded_password("").str() == "http://"); - BOOST_TEST(url("http://:y@").set_password("pass").str() == "http://:pass@"); - BOOST_TEST(url("http://x:y@").set_password("pass").str() == "http://x:pass@"); - BOOST_TEST(url("http://x:pass@").set_password("y").str() == "http://x:y@"); - BOOST_TEST(url("http://x:pass@example.com").set_password("y").str() == "http://x:y@example.com"); - - BOOST_TEST_THROWS(url().set_encoded_password("pass word"), invalid_part); - BOOST_TEST_THROWS(url().set_encoded_password(":pass"), invalid_part); - } - //------------------------------------------------------ void @@ -232,41 +189,6 @@ public: BOOST_TEST(url("http://x:y@z.com/").set_encoded_userinfo("").str() == "http://z.com/"); } - void - testUser() - { - BOOST_TEST(url().username() == ""); - BOOST_TEST(url("//x/").username() == ""); - BOOST_TEST(url("//x@/").username() == "x"); - BOOST_TEST(url("//x:@/").username() == "x"); - BOOST_TEST(url("//x:y@/").username() == "x"); - BOOST_TEST(url("//:y@/").username() == ""); - BOOST_TEST(url("//:@/").username() == ""); - BOOST_TEST(url("//@/").username() == ""); - BOOST_TEST(url("//%3A@/").username() == ":"); - - BOOST_TEST(url().encoded_username() == ""); - BOOST_TEST(url("//x/").encoded_username() == ""); - BOOST_TEST(url("//x@/").encoded_username() == "x"); - BOOST_TEST(url("//x:@/").encoded_username() == "x"); - BOOST_TEST(url("//x:y@/").encoded_username() == "x"); - BOOST_TEST(url("//:y@/").encoded_username() == ""); - BOOST_TEST(url("//:@/").encoded_username() == ""); - BOOST_TEST(url("//@/").encoded_username() == ""); - BOOST_TEST(url("//%3A@/").encoded_username() == "%3A"); - - BOOST_TEST(url("").set_user("").str() == ""); - BOOST_TEST(url("").set_user("x").str() == "//x@"); - BOOST_TEST(url("").set_user("x:").str() == "//x%3A@"); - BOOST_TEST(url("").set_user("x:y").str() == "//x%3Ay@"); - BOOST_TEST(url("//yy@").set_user("x").str() == "//x@"); - BOOST_TEST(url("//:@").set_user("x").str() == "//x:@"); - BOOST_TEST(url("//:p@").set_user("x").str() == "//x:p@"); - //BOOST_TEST(url("//yy@").set_user("").str() == ""); - BOOST_TEST(url("//:p@").set_user("x").str() == "//x:p@"); - BOOST_TEST(url("//yy:p@").set_user("x").str() == "//x:p@"); - } - //------------------------------------------------------ void @@ -862,9 +784,238 @@ public: //------------------------------------------------------ #endif + //-------------------------------------------- + + void + testScheme() + { + { + url u; + u.reserve(40); + BOOST_TEST( + u.set_scheme("http").str() == "http:"); + } + + url u; + BOOST_TEST(u.set_scheme("").str() == ""); + BOOST_TEST(u.set_scheme(scheme::none).str() == ""); + BOOST_TEST(u.set_scheme("http").str() == "http:"); + BOOST_TEST(u.scheme_id() == scheme::http); + BOOST_TEST_THROWS( + u.set_scheme("http:"), std::invalid_argument); + BOOST_TEST(u.str() == "http:"); + BOOST_TEST(u.scheme_id() == scheme::http); + BOOST_TEST_THROWS( + u.set_scheme("1http"), std::invalid_argument); + BOOST_TEST_THROWS( + u.set_scheme(scheme::unknown), std::invalid_argument); + BOOST_TEST(u.scheme_id() == scheme::http); + BOOST_TEST(u.str() == "http:"); + BOOST_TEST(u.scheme_id() == scheme::http); + BOOST_TEST(u.set_scheme("ftp").str() == "ftp:"); + BOOST_TEST(u.scheme_id() == scheme::ftp); + BOOST_TEST(u.set_scheme(scheme::none).str() == ""); + BOOST_TEST(u.scheme_id() == scheme::none); + BOOST_TEST(u.set_scheme(scheme::ws).str() == "ws:"); + BOOST_TEST(u.scheme_id() == scheme::ws); + BOOST_TEST(u.set_scheme("").str() == ""); + BOOST_TEST(u.scheme_id() == scheme::none); + BOOST_TEST(u.set_scheme("x").str() == "x:"); + BOOST_TEST(u.scheme_id() == scheme::unknown); + u = parse_uri("http:/path/to/file.txt"); + BOOST_TEST(u.set_scheme("").str() == "/path/to/file.txt"); + } + + //-------------------------------------------- + + void + testUser() + { + auto const clear = []( + string_view s1, string_view s2) + { + if(s1.empty() || s1.starts_with('/')) + BOOST_TEST(url(parse_relative_ref( + s1)).clear_user().str() == s2); + else + BOOST_TEST(url(parse_uri(s1) + ).clear_user().str() == s2); + }; + + auto const set = []( + string_view s1, string_view s2, + string_view s3) + { + if(s1.empty() || s1.starts_with('/')) + BOOST_TEST(url(parse_relative_ref( + s1)).set_user(s2).str() == s3); + else + BOOST_TEST(url(parse_uri(s1) + ).set_user(s2).str() == s3); + }; + + auto const enc = []( + string_view s1, string_view s2, + string_view s3) + { + if(s1.empty() || s1.starts_with('/')) + BOOST_TEST(url(parse_relative_ref( + s1)).set_encoded_user(s2).str() == s3); + else + BOOST_TEST(url(parse_uri(s1) + ).set_encoded_user(s2).str() == s3); + }; + + clear("", ""); + clear("/x", "/x"); + clear("//", "//"); + clear("//x", "//x"); + clear("//@", "//"); + clear("//:@", "//:@"); + clear("//x@", "//"); + clear("//x@z", "//z"); + clear("//x:@", "//:@"); + clear("//x:y@", "//:y@"); + clear("//x:y@z", "//:y@z"); + + clear("ws:", "ws:"); + clear("ws:/x", "ws:/x"); + clear("ws://", "ws://"); + clear("ws://x", "ws://x"); + clear("ws://@", "ws://"); + clear("ws://:@", "ws://:@"); + clear("ws://x@", "ws://"); + clear("ws://x@z", "ws://z"); + clear("ws://x:@", "ws://:@"); + clear("ws://x:y@", "ws://:y@"); + clear("ws://x:y@z", "ws://:y@z"); + + set("", "", "//@"); + set("/y", "", "//@/y"); + set("//", "", "//@"); + set("//y", "", "//@y"); + set("//@", "", "//@"); + set("//:@", "", "//:@"); + set("//y@", "", "//@"); + set("//y@z", "", "//@z"); + set("//y:@", "", "//:@"); + set("//y:z@", "", "//:z@"); + set("//a:b@c", "", "//:b@c"); + + set("ws:", "", "ws://@"); + set("ws:/y", "", "ws://@/y"); + set("ws://", "", "ws://@"); + set("ws://y", "", "ws://@y"); + set("ws://@", "", "ws://@"); + set("ws://:@", "", "ws://:@"); + set("ws://y@", "", "ws://@"); + set("ws://y@z", "", "ws://@z"); + set("ws://y:@", "", "ws://:@"); + set("ws://y:z@", "", "ws://:z@"); + set("ws://a:b@c", "", "ws://:b@c"); + + set("", "x", "//x@"); + set("/y", "x", "//x@/y"); + set("//", "x", "//x@"); + set("//y", "x", "//x@y"); + set("//@", "x", "//x@"); + set("//:@", "x", "//x:@"); + set("//y@", "x", "//x@"); + set("//y@z", "x", "//x@z"); + set("//y:@", "x", "//x:@"); + set("//y:z@", "x", "//x:z@"); + set("//a:b@c", "x", "//x:b@c"); + + set("ws:", "x", "ws://x@"); + set("ws:/y", "x", "ws://x@/y"); + set("ws://", "x", "ws://x@"); + set("ws://y", "x", "ws://x@y"); + set("ws://@", "x", "ws://x@"); + set("ws://:@", "x", "ws://x:@"); + set("ws://y@", "x", "ws://x@"); + set("ws://y@z", "x", "ws://x@z"); + set("ws://y:@", "x", "ws://x:@"); + set("ws://y:z@", "x", "ws://x:z@"); + set("ws://a:b@c", "x", "ws://x:b@c"); + + enc("", "%41", "//%41@"); + enc("/y", "%41", "//%41@/y"); + enc("//", "%41", "//%41@"); + enc("//y", "%41", "//%41@y"); + enc("//@", "%41", "//%41@"); + enc("//:@", "%41", "//%41:@"); + enc("//y@", "%41", "//%41@"); + enc("//y@z", "%41", "//%41@z"); + enc("//y:@", "%41", "//%41:@"); + enc("//y:z@", "%41", "//%41:z@"); + enc("//a:b@c", "%41", "//%41:b@c"); + + enc("ws:", "%41", "ws://%41@"); + enc("ws:/y", "%41", "ws://%41@/y"); + enc("ws://", "%41", "ws://%41@"); + enc("ws://y", "%41", "ws://%41@y"); + enc("ws://@", "%41", "ws://%41@"); + enc("ws://:@", "%41", "ws://%41:@"); + enc("ws://y@", "%41", "ws://%41@"); + enc("ws://y@z", "%41", "ws://%41@z"); + enc("ws://y:@", "%41", "ws://%41:@"); + enc("ws://y:z@", "%41", "ws://%41:z@"); + enc("ws://a:b@c", "%41", "ws://%41:b@c"); + + BOOST_TEST_THROWS(url().set_encoded_user( + "%2"), std::invalid_argument); + } + + //-------------------------------------------- + + void + testPassword() + { + auto const clear = []( + string_view s1, string_view s2) + { + if(s1.empty() || s1.starts_with('/')) + BOOST_TEST(url(parse_relative_ref( + s1)).clear_password().str() == s2); + else + BOOST_TEST(url(parse_uri(s1) + ).clear_password().str() == s2); + }; + + auto const set = []( + string_view s1, string_view s2, + string_view s3) + { + if(s1.empty() || s1.starts_with('/')) + BOOST_TEST(url(parse_relative_ref( + s1)).set_password(s2).str() == s3); + else + BOOST_TEST(url(parse_uri(s1) + ).set_password(s2).str() == s3); + }; + + auto const enc = []( + string_view s1, string_view s2, + string_view s3) + { + if(s1.empty() || s1.starts_with('/')) + BOOST_TEST(url(parse_relative_ref( + s1)).set_encoded_password(s2).str() == s3); + else + BOOST_TEST(url(parse_uri(s1) + ).set_encoded_password(s2).str() == s3); + }; + } + + //-------------------------------------------- + void run() { + testScheme(); + + testUser(); + testPassword(); #if 0 testObservers(); @@ -878,7 +1029,6 @@ public: testPassword(); testUserinfo(); - testUser(); testHostAndPort(); testHost(); testPort(); diff --git a/test/url_view.cpp b/test/url_view.cpp index b570e26d..ae781c7b 100644 --- a/test/url_view.cpp +++ b/test/url_view.cpp @@ -36,14 +36,14 @@ public: { error_code ec; auto const u = urls::parse_uri( - "http://username:pass@www.boost.org:8080/x/y/z?a=b&c=3#frag", + "http://user:pass@www.boost.org:8080/x/y/z?a=b&c=3#frag", ec); if(! BOOST_TEST(! ec)) return; BOOST_TEST(u.encoded_origin() == - "http://username:pass@www.boost.org:8080"); + "http://user:pass@www.boost.org:8080"); BOOST_TEST(u.scheme() == "http"); - BOOST_TEST(u.username() == "username"); + BOOST_TEST(u.user() == "user"); BOOST_TEST(u.password() == "pass"); BOOST_TEST(u.host() == "www.boost.org"); BOOST_TEST(u.port() == "8080"); @@ -84,12 +84,24 @@ public: "http://"); BOOST_TEST(u.has_scheme()); BOOST_TEST(u.scheme() == "http"); + BOOST_TEST( + u.scheme_id() == scheme::http); + } + { + auto u = parse_uri( + "ou812://"); + BOOST_TEST(u.has_scheme()); + BOOST_TEST(u.scheme() == "ou812"); + BOOST_TEST( + u.scheme_id() == scheme::unknown); } { auto u = parse_relative_ref( "/x"); BOOST_TEST(! u.has_scheme()); BOOST_TEST(u.scheme() == ""); + BOOST_TEST( + u.scheme_id() == scheme::none); } } @@ -187,8 +199,8 @@ public: BOOST_TEST(u.has_userinfo()); BOOST_TEST(u.encoded_userinfo() == ""); BOOST_TEST(u.userinfo() == ""); - BOOST_TEST(u.encoded_username() == ""); - BOOST_TEST(u.username() == ""); + BOOST_TEST(u.encoded_user() == ""); + BOOST_TEST(u.user() == ""); BOOST_TEST(u.has_password() == false); BOOST_TEST(u.encoded_password() == ""); BOOST_TEST(u.password() == ""); @@ -198,8 +210,8 @@ public: BOOST_TEST(u.has_userinfo()); BOOST_TEST(u.encoded_userinfo() == ":"); BOOST_TEST(u.userinfo() == ":"); - BOOST_TEST(u.encoded_username() == ""); - BOOST_TEST(u.username() == ""); + BOOST_TEST(u.encoded_user() == ""); + BOOST_TEST(u.user() == ""); BOOST_TEST(u.has_password() == true); BOOST_TEST(u.encoded_password() == ""); BOOST_TEST(u.password() == ""); @@ -208,8 +220,8 @@ public: auto u = parse_uri("x://a%41:@"); BOOST_TEST(u.has_userinfo()); BOOST_TEST(u.encoded_userinfo() == "a%41:"); - BOOST_TEST(u.encoded_username() == "a%41"); - BOOST_TEST(u.username() == "aA"); + BOOST_TEST(u.encoded_user() == "a%41"); + BOOST_TEST(u.user() == "aA"); BOOST_TEST(u.has_password() == true); BOOST_TEST(u.encoded_password() == ""); BOOST_TEST(u.password() == ""); @@ -218,8 +230,8 @@ public: auto u = parse_uri("x://:b%42@"); BOOST_TEST(u.has_userinfo()); BOOST_TEST(u.encoded_userinfo() == ":b%42"); - BOOST_TEST(u.encoded_username() == ""); - BOOST_TEST(u.username() == ""); + BOOST_TEST(u.encoded_user() == ""); + BOOST_TEST(u.user() == ""); BOOST_TEST(u.has_password() == true); BOOST_TEST(u.encoded_password() == "b%42"); BOOST_TEST(u.password() == "bB"); @@ -228,7 +240,7 @@ public: auto u = parse_uri("x://a:b@"); BOOST_TEST(u.has_userinfo()); BOOST_TEST(u.encoded_userinfo() == "a:b"); - BOOST_TEST(u.encoded_username() == "a"); + BOOST_TEST(u.encoded_user() == "a"); BOOST_TEST(u.has_password() == true); BOOST_TEST(u.encoded_password() == "b"); } @@ -237,8 +249,8 @@ public: BOOST_TEST(u.has_userinfo()); BOOST_TEST(u.encoded_userinfo() == "%3a:%3a"); BOOST_TEST(u.userinfo() == ":::"); - BOOST_TEST(u.encoded_username() == "%3a"); - BOOST_TEST(u.username() == ":"); + BOOST_TEST(u.encoded_user() == "%3a"); + BOOST_TEST(u.user() == ":"); BOOST_TEST(u.has_password() == true); BOOST_TEST(u.encoded_password() == "%3a"); BOOST_TEST(u.password() == ":"); @@ -248,8 +260,8 @@ public: BOOST_TEST(u.has_userinfo()); BOOST_TEST(u.encoded_userinfo() == "%2525"); BOOST_TEST(u.userinfo() == "%25"); - BOOST_TEST(u.encoded_username() == "%2525"); - BOOST_TEST(u.username() == "%25"); + BOOST_TEST(u.encoded_user() == "%2525"); + BOOST_TEST(u.user() == "%25"); BOOST_TEST(u.has_password() == false); BOOST_TEST(u.encoded_password() == ""); BOOST_TEST(u.password() == ""); @@ -648,7 +660,7 @@ public: testCollect() { string_view s = - "http://username:pass@www.boost.org:8080/x/y/z?a=b&c=3#frag"; + "http://user:pass@www.boost.org:8080/x/y/z?a=b&c=3#frag"; std::shared_ptr sp; { auto const u = urls::parse_uri(s);