714 lines
15 KiB
C++
714 lines
15 KiB
C++
//
|
|
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
|
|
// Copyright (c) 2022 Alan de Freitas (alandefreitas@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/boostorg/url
|
|
//
|
|
|
|
#ifndef BOOST_URL_IMPL_URL_VIEW_BASE_IPP
|
|
#define BOOST_URL_IMPL_URL_VIEW_BASE_IPP
|
|
|
|
#include <boost/url/url_view_base.hpp>
|
|
#include <boost/url/detail/except.hpp>
|
|
#include <boost/url/detail/over_allocator.hpp>
|
|
|
|
namespace boost {
|
|
namespace urls {
|
|
|
|
// construct empty view
|
|
url_view_base::
|
|
url_view_base() noexcept
|
|
: impl_(from::url)
|
|
, pi_(&impl_)
|
|
{
|
|
}
|
|
|
|
// construct reference
|
|
url_view_base::
|
|
url_view_base(
|
|
detail::url_impl const& impl) noexcept
|
|
: impl_(impl)
|
|
, pi_(&impl_)
|
|
{
|
|
}
|
|
|
|
//------------------------------------------------
|
|
|
|
std::size_t
|
|
url_view_base::
|
|
digest(std::size_t salt) const noexcept
|
|
{
|
|
detail::fnv_1a h(salt);
|
|
detail::ci_digest(pi_->get(id_scheme), h);
|
|
detail::digest_encoded(pi_->get(id_user), h);
|
|
detail::digest_encoded(pi_->get(id_pass), h);
|
|
detail::ci_digest_encoded(pi_->get(id_host), h);
|
|
h.put(pi_->get(id_port));
|
|
detail::normalized_path_digest(
|
|
pi_->get(id_path), is_path_absolute(), h);
|
|
detail::digest_encoded(pi_->get(id_query), h);
|
|
detail::digest_encoded(pi_->get(id_frag), h);
|
|
return h.digest();
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// Observers
|
|
//
|
|
//------------------------------------------------
|
|
|
|
struct url_view_base::shared_impl
|
|
: url_view
|
|
{
|
|
virtual
|
|
~shared_impl()
|
|
{
|
|
}
|
|
|
|
shared_impl(
|
|
url_view const& u) noexcept
|
|
: url_view(u)
|
|
{
|
|
impl_.cs_ = reinterpret_cast<
|
|
char const*>(this + 1);
|
|
}
|
|
};
|
|
|
|
std::shared_ptr<url_view const>
|
|
url_view_base::
|
|
persist() const
|
|
{
|
|
using T = shared_impl;
|
|
using Alloc = std::allocator<char>;
|
|
Alloc a;
|
|
auto p = std::allocate_shared<T>(
|
|
detail::over_allocator<T, Alloc>(
|
|
size(), a), url_view(*pi_));
|
|
std::memcpy(
|
|
reinterpret_cast<char*>(
|
|
p.get() + 1), data(), size());
|
|
return p;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// Scheme
|
|
//
|
|
//------------------------------------------------
|
|
|
|
bool
|
|
url_view_base::
|
|
has_scheme() const noexcept
|
|
{
|
|
auto const n = pi_->len(
|
|
id_scheme);
|
|
if(n == 0)
|
|
return false;
|
|
BOOST_ASSERT(n > 1);
|
|
BOOST_ASSERT(
|
|
pi_->get(id_scheme
|
|
).ends_with(':'));
|
|
return true;
|
|
}
|
|
|
|
string_view
|
|
url_view_base::
|
|
scheme() const noexcept
|
|
{
|
|
auto s = pi_->get(id_scheme);
|
|
if(! s.empty())
|
|
{
|
|
BOOST_ASSERT(s.size() > 1);
|
|
BOOST_ASSERT(s.ends_with(':'));
|
|
s.remove_suffix(1);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
urls::scheme
|
|
url_view_base::
|
|
scheme_id() const noexcept
|
|
{
|
|
return pi_->scheme_;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// Authority
|
|
//
|
|
//------------------------------------------------
|
|
|
|
authority_view
|
|
url_view_base::
|
|
authority() const noexcept
|
|
{
|
|
detail::url_impl u(from::authority);
|
|
u.cs_ = encoded_authority().data();
|
|
if(has_authority())
|
|
{
|
|
u.set_size(id_user, pi_->len(id_user) - 2);
|
|
u.set_size(id_pass, pi_->len(id_pass));
|
|
u.set_size(id_host, pi_->len(id_host));
|
|
u.set_size(id_port, pi_->len(id_port));
|
|
}
|
|
else
|
|
{
|
|
u.set_size(id_user, pi_->len(id_user));
|
|
BOOST_ASSERT(pi_->len(id_pass) == 0);
|
|
BOOST_ASSERT(pi_->len(id_host) == 0);
|
|
BOOST_ASSERT(pi_->len(id_port) == 0);
|
|
}
|
|
u.decoded_[id_user] = pi_->decoded_[id_user];
|
|
u.decoded_[id_pass] = pi_->decoded_[id_pass];
|
|
u.decoded_[id_host] = pi_->decoded_[id_host];
|
|
for (int i = 0; i < 16; ++i)
|
|
u.ip_addr_[i] = pi_->ip_addr_[i];
|
|
u.port_number_ = pi_->port_number_;
|
|
u.host_type_ = pi_->host_type_;
|
|
return u.construct_authority();
|
|
}
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_authority() const noexcept
|
|
{
|
|
auto s = pi_->get(id_user, id_path);
|
|
if(! s.empty())
|
|
{
|
|
BOOST_ASSERT(has_authority());
|
|
s.remove_prefix(2);
|
|
}
|
|
return make_pct_string_view_unsafe(
|
|
s.data(),
|
|
s.size(),
|
|
pi_->decoded_[id_user] +
|
|
pi_->decoded_[id_pass] +
|
|
pi_->decoded_[id_host] +
|
|
pi_->decoded_[id_port] +
|
|
has_password());
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// Userinfo
|
|
//
|
|
//------------------------------------------------
|
|
|
|
bool
|
|
url_view_base::
|
|
has_userinfo() const noexcept
|
|
{
|
|
auto n = pi_->len(id_pass);
|
|
if(n == 0)
|
|
return false;
|
|
BOOST_ASSERT(has_authority());
|
|
BOOST_ASSERT(pi_->get(
|
|
id_pass).ends_with('@'));
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
url_view_base::
|
|
has_password() const noexcept
|
|
{
|
|
auto const n = pi_->len(id_pass);
|
|
if(n > 1)
|
|
{
|
|
BOOST_ASSERT(pi_->get(id_pass
|
|
).starts_with(':'));
|
|
BOOST_ASSERT(pi_->get(id_pass
|
|
).ends_with('@'));
|
|
return true;
|
|
}
|
|
BOOST_ASSERT(n == 0 || pi_->get(
|
|
id_pass).ends_with('@'));
|
|
return false;
|
|
}
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_userinfo() const noexcept
|
|
{
|
|
auto s = pi_->get(
|
|
id_user, id_host);
|
|
if(s.empty())
|
|
return s;
|
|
BOOST_ASSERT(
|
|
has_authority());
|
|
s.remove_prefix(2);
|
|
if(s.empty())
|
|
return s;
|
|
BOOST_ASSERT(
|
|
s.ends_with('@'));
|
|
s.remove_suffix(1);
|
|
return make_pct_string_view_unsafe(
|
|
s.data(),
|
|
s.size(),
|
|
pi_->decoded_[id_user] +
|
|
pi_->decoded_[id_pass] +
|
|
has_password());
|
|
}
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_user() const noexcept
|
|
{
|
|
auto s = pi_->get(id_user);
|
|
if(! s.empty())
|
|
{
|
|
BOOST_ASSERT(
|
|
has_authority());
|
|
s.remove_prefix(2);
|
|
}
|
|
return make_pct_string_view_unsafe(
|
|
s.data(),
|
|
s.size(),
|
|
pi_->decoded_[id_user]);
|
|
}
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_password() const noexcept
|
|
{
|
|
auto s = pi_->get(id_pass);
|
|
switch(s.size())
|
|
{
|
|
case 1:
|
|
BOOST_ASSERT(
|
|
s.starts_with('@'));
|
|
s.remove_prefix(1);
|
|
BOOST_FALLTHROUGH;
|
|
case 0:
|
|
return make_pct_string_view_unsafe(
|
|
s.data(), s.size(), 0);
|
|
default:
|
|
break;
|
|
}
|
|
BOOST_ASSERT(s.ends_with('@'));
|
|
BOOST_ASSERT(s.starts_with(':'));
|
|
return make_pct_string_view_unsafe(
|
|
s.data() + 1,
|
|
s.size() - 2,
|
|
pi_->decoded_[id_pass]);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// Host
|
|
//
|
|
//------------------------------------------------
|
|
/*
|
|
host_type host_type() // ipv4, ipv6, ipvfuture, name
|
|
|
|
std::string host() // return encoded_host().decode()
|
|
pct_string_view encoded_host() // return host part, as-is
|
|
std::string host_address() // return encoded_host_address().decode()
|
|
pct_string_view encoded_host_address() // ipv4, ipv6, ipvfut, or encoded name, no brackets
|
|
|
|
ipv4_address host_ipv4_address() // return ipv4_address or {}
|
|
ipv6_address host_ipv6_address() // return ipv6_address or {}
|
|
string_view host_ipvfuture() // return ipvfuture or {}
|
|
std::string host_name() // return decoded name or ""
|
|
pct_string_view encoded_host_name() // return encoded host name or ""
|
|
*/
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_host() const noexcept
|
|
{
|
|
return pi_->pct_get(id_host);
|
|
}
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_host_address() const noexcept
|
|
{
|
|
string_view s = pi_->get(id_host);
|
|
std::size_t n;
|
|
switch(pi_->host_type_)
|
|
{
|
|
default:
|
|
case urls::host_type::none:
|
|
BOOST_ASSERT(s.empty());
|
|
n = 0;
|
|
break;
|
|
|
|
case urls::host_type::name:
|
|
case urls::host_type::ipv4:
|
|
n = pi_->decoded_[id_host];
|
|
break;
|
|
|
|
case urls::host_type::ipv6:
|
|
case urls::host_type::ipvfuture:
|
|
{
|
|
BOOST_ASSERT(
|
|
pi_->decoded_[id_host] ==
|
|
s.size());
|
|
BOOST_ASSERT(s.size() >= 2);
|
|
BOOST_ASSERT(s.front() == '[');
|
|
BOOST_ASSERT(s.back() == ']');
|
|
s = s.substr(1, s.size() - 2);
|
|
n = pi_->decoded_[id_host] - 2;
|
|
break;
|
|
}
|
|
}
|
|
return make_pct_string_view_unsafe(
|
|
s.data(),
|
|
s.size(),
|
|
n);
|
|
}
|
|
|
|
urls::ipv4_address
|
|
url_view_base::
|
|
host_ipv4_address() const noexcept
|
|
{
|
|
if(pi_->host_type_ !=
|
|
urls::host_type::ipv4)
|
|
return {};
|
|
ipv4_address::bytes_type b{{}};
|
|
std::memcpy(
|
|
&b[0], &pi_->ip_addr_[0], b.size());
|
|
return urls::ipv4_address(b);
|
|
}
|
|
|
|
urls::ipv6_address
|
|
url_view_base::
|
|
host_ipv6_address() const noexcept
|
|
{
|
|
if(pi_->host_type_ !=
|
|
urls::host_type::ipv6)
|
|
return {};
|
|
ipv6_address::bytes_type b{{}};
|
|
std::memcpy(
|
|
&b[0], &pi_->ip_addr_[0], b.size());
|
|
return urls::ipv6_address(b);
|
|
}
|
|
|
|
string_view
|
|
url_view_base::
|
|
host_ipvfuture() const noexcept
|
|
{
|
|
if(pi_->host_type_ !=
|
|
urls::host_type::ipvfuture)
|
|
return {};
|
|
string_view s = pi_->get(id_host);
|
|
BOOST_ASSERT(s.size() >= 6);
|
|
BOOST_ASSERT(s.front() == '[');
|
|
BOOST_ASSERT(s.back() == ']');
|
|
s = s.substr(1, s.size() - 2);
|
|
return s;
|
|
}
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_host_name() const noexcept
|
|
{
|
|
if(pi_->host_type_ !=
|
|
urls::host_type::name)
|
|
return {};
|
|
string_view s = pi_->get(id_host);
|
|
return make_pct_string_view_unsafe(
|
|
s.data(),
|
|
s.size(),
|
|
pi_->decoded_[id_host]);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
|
|
bool
|
|
url_view_base::
|
|
has_port() const noexcept
|
|
{
|
|
auto const n = pi_->len(id_port);
|
|
if(n == 0)
|
|
return false;
|
|
BOOST_ASSERT(
|
|
pi_->get(id_port).starts_with(':'));
|
|
return true;
|
|
}
|
|
|
|
string_view
|
|
url_view_base::
|
|
port() const noexcept
|
|
{
|
|
auto s = pi_->get(id_port);
|
|
if(s.empty())
|
|
return s;
|
|
BOOST_ASSERT(has_port());
|
|
return s.substr(1);
|
|
}
|
|
|
|
std::uint16_t
|
|
url_view_base::
|
|
port_number() const noexcept
|
|
{
|
|
BOOST_ASSERT(
|
|
has_port() ||
|
|
pi_->port_number_ == 0);
|
|
return pi_->port_number_;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// Path
|
|
//
|
|
//------------------------------------------------
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_path() const noexcept
|
|
{
|
|
return pi_->pct_get(id_path);
|
|
}
|
|
|
|
segments_view
|
|
url_view_base::
|
|
segments() const noexcept
|
|
{
|
|
return {detail::path_ref(*pi_)};
|
|
}
|
|
|
|
segments_encoded_view
|
|
url_view_base::
|
|
encoded_segments() const noexcept
|
|
{
|
|
return segments_encoded_view(
|
|
detail::path_ref(*pi_));
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// Query
|
|
//
|
|
//------------------------------------------------
|
|
|
|
bool
|
|
url_view_base::
|
|
has_query() const noexcept
|
|
{
|
|
auto const n = pi_->len(
|
|
id_query);
|
|
if(n == 0)
|
|
return false;
|
|
BOOST_ASSERT(
|
|
pi_->get(id_query).
|
|
starts_with('?'));
|
|
return true;
|
|
}
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_query() const noexcept
|
|
{
|
|
auto s = pi_->get(id_query);
|
|
if(s.empty())
|
|
return s;
|
|
BOOST_ASSERT(
|
|
s.starts_with('?'));
|
|
return s.substr(1);
|
|
}
|
|
|
|
params_encoded_view
|
|
url_view_base::
|
|
encoded_params() const noexcept
|
|
{
|
|
return params_encoded_view(*pi_);
|
|
}
|
|
|
|
params_view
|
|
url_view_base::
|
|
params() const noexcept
|
|
{
|
|
return params_view(
|
|
*pi_,
|
|
encoding_opts{
|
|
true,false,false});
|
|
}
|
|
|
|
params_view
|
|
url_view_base::
|
|
params(encoding_opts opt) const noexcept
|
|
{
|
|
return params_view(*pi_, opt);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// Fragment
|
|
//
|
|
//------------------------------------------------
|
|
|
|
bool
|
|
url_view_base::
|
|
has_fragment() const noexcept
|
|
{
|
|
auto const n = pi_->len(id_frag);
|
|
if(n == 0)
|
|
return false;
|
|
BOOST_ASSERT(
|
|
pi_->get(id_frag).
|
|
starts_with('#'));
|
|
return true;
|
|
}
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_fragment() const noexcept
|
|
{
|
|
auto s = pi_->get(id_frag);
|
|
if(! s.empty())
|
|
{
|
|
BOOST_ASSERT(
|
|
s.starts_with('#'));
|
|
s.remove_prefix(1);
|
|
}
|
|
return make_pct_string_view_unsafe(
|
|
s.data(),
|
|
s.size(),
|
|
pi_->decoded_[id_frag]);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// Compound Fields
|
|
//
|
|
//------------------------------------------------
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_host_and_port() const noexcept
|
|
{
|
|
return pi_->pct_get(id_host, id_path);
|
|
}
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_origin() const noexcept
|
|
{
|
|
if(pi_->len(id_user) < 2)
|
|
return {};
|
|
return pi_->get(id_scheme, id_path);
|
|
}
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_resource() const noexcept
|
|
{
|
|
auto n =
|
|
pi_->decoded_[id_path] +
|
|
pi_->decoded_[id_query] +
|
|
pi_->decoded_[id_frag];
|
|
if(has_query())
|
|
++n;
|
|
if(has_fragment())
|
|
++n;
|
|
BOOST_ASSERT(pct_string_view(
|
|
pi_->get(id_path, id_end)
|
|
).decoded_size() == n);
|
|
auto s = pi_->get(id_path, id_end);
|
|
return make_pct_string_view_unsafe(
|
|
s.data(), s.size(), n);
|
|
}
|
|
|
|
pct_string_view
|
|
url_view_base::
|
|
encoded_target() const noexcept
|
|
{
|
|
auto n =
|
|
pi_->decoded_[id_path] +
|
|
pi_->decoded_[id_query];
|
|
if(has_query())
|
|
++n;
|
|
BOOST_ASSERT(pct_string_view(
|
|
pi_->get(id_path, id_frag)
|
|
).decoded_size() == n);
|
|
auto s = pi_->get(id_path, id_frag);
|
|
return make_pct_string_view_unsafe(
|
|
s.data(), s.size(), n);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// Comparisons
|
|
//
|
|
//------------------------------------------------
|
|
|
|
int
|
|
url_view_base::
|
|
compare(const url_view_base& other) const noexcept
|
|
{
|
|
int comp =
|
|
static_cast<int>(has_scheme()) -
|
|
static_cast<int>(other.has_scheme());
|
|
if ( comp != 0 )
|
|
return comp;
|
|
|
|
if (has_scheme())
|
|
{
|
|
comp = detail::ci_compare(
|
|
scheme(),
|
|
other.scheme());
|
|
if ( comp != 0 )
|
|
return comp;
|
|
}
|
|
|
|
comp =
|
|
static_cast<int>(has_authority()) -
|
|
static_cast<int>(other.has_authority());
|
|
if ( comp != 0 )
|
|
return comp;
|
|
|
|
if (has_authority())
|
|
{
|
|
comp = authority().compare(other.authority());
|
|
if ( comp != 0 )
|
|
return comp;
|
|
}
|
|
|
|
comp = detail::segments_compare(
|
|
encoded_segments(),
|
|
other.encoded_segments());
|
|
if ( comp != 0 )
|
|
return comp;
|
|
|
|
comp =
|
|
static_cast<int>(has_query()) -
|
|
static_cast<int>(other.has_query());
|
|
if ( comp != 0 )
|
|
return comp;
|
|
|
|
if (has_query())
|
|
{
|
|
comp = detail::compare_encoded(
|
|
encoded_query(),
|
|
other.encoded_query());
|
|
if ( comp != 0 )
|
|
return comp;
|
|
}
|
|
|
|
comp =
|
|
static_cast<int>(has_fragment()) -
|
|
static_cast<int>(other.has_fragment());
|
|
if ( comp != 0 )
|
|
return comp;
|
|
|
|
if (has_fragment())
|
|
{
|
|
comp = detail::compare_encoded(
|
|
encoded_fragment(),
|
|
other.encoded_fragment());
|
|
if ( comp != 0 )
|
|
return comp;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
} // urls
|
|
} // boost
|
|
|
|
#endif
|