2
0
mirror of https://github.com/boostorg/url.git synced 2026-01-19 04:42:15 +00:00

feat: zone_id setters

This commit is contained in:
Alan de Freitas
2025-08-28 19:10:57 -05:00
parent 9b4c4b440e
commit b1f1418b2e
7 changed files with 205 additions and 22 deletions

View File

@@ -384,6 +384,10 @@ public:
static_url& set_host_ipv4(ipv4_address const& addr) { url_base::set_host_ipv4(addr); return *this; }
/// @copydoc url_base::set_host_ipv6
static_url& set_host_ipv6(ipv6_address const& addr) { url_base::set_host_ipv6(addr); return *this; }
/// @copydoc url_base::set_zone_id
static_url& set_zone_id(core::string_view s) { url_base::set_zone_id(s); return *this; }
/// @copydoc url_base::set_encoded_zone_id
static_url& set_encoded_zone_id(pct_string_view const& s) { url_base::set_encoded_zone_id(s); return *this; }
/// @copydoc url_base::set_host_ipvfuture
static_url& set_host_ipvfuture(core::string_view s) { url_base::set_host_ipvfuture(s); return *this; }
/// @copydoc url_base::set_host_name

View File

@@ -450,6 +450,10 @@ public:
url& set_host_ipv4(ipv4_address const& addr) { url_base::set_host_ipv4(addr); return *this; }
/// @copydoc url_base::set_host_ipv6
url& set_host_ipv6(ipv6_address const& addr) { url_base::set_host_ipv6(addr); return *this; }
/// @copydoc url_base::set_zone_id
url& set_zone_id(core::string_view s) { url_base::set_zone_id(s); return *this; }
/// @copydoc url_base::set_encoded_zone_id
url& set_encoded_zone_id(pct_string_view const& s) { url_base::set_encoded_zone_id(s); return *this; }
/// @copydoc url_base::set_host_ipvfuture
url& set_host_ipvfuture(core::string_view s) { url_base::set_host_ipvfuture(s); return *this; }
/// @copydoc url_base::set_host_name

View File

@@ -1355,6 +1355,63 @@ public:
set_host_ipv6(
ipv6_address const& addr);
/** Set the zone ID for an IPv6 address.
This function sets the zone ID for the host if the host is an IPv6 address.
Reserved characters in the string are percent-escaped in the result.
@par Example
@code
assert( u.set_host_ipv6( ipv6_address( "fe80::1" ) ).set_zone_id( "eth0" ).buffer() == "https://[fe80::1%25eth0]" );
@endcode
@par Complexity
Linear in `this->size()`.
@par Exception Safety
Strong guarantee. Calls to allocate may throw.
@param s The zone ID to set.
@return `*this`
@par Specification
@li <a href="https://datatracker.ietf.org/doc/html/rfc6874">RFC 6874</a>
*/
url_base&
set_zone_id(core::string_view s);
/** Set the zone ID for an IPv6 address (percent-encoded).
This function sets the zone ID for the host if the host is an IPv6 address.
Escapes in the string are preserved, and reserved characters in the string
are percent-escaped in the result.
@par Example
@code
assert( u.set_host_ipv6( ipv6_address( "fe80::1" ) ).set_encoded_zone_id( "eth0" ).buffer() == "https://[fe80::1%25eth0]" );
@endcode
@par Complexity
Linear in `this->size()`.
@par Exception Safety
Strong guarantee. Calls to allocate may throw.
Exceptions thrown on invalid input.
@throw system_error
`s` contains an invalid percent-encoding.
@param s The zone ID to set.
@return `*this`
@par Specification
@li <a href="https://datatracker.ietf.org/doc/html/rfc6874">RFC 6874</a>
*/
url_base&
set_encoded_zone_id(pct_string_view s);
/** Set the host to an address
The host is set to the specified IPvFuture
@@ -2861,6 +2918,16 @@ private:
char* set_port_impl(std::size_t n, op_t& op);
char* set_path_impl(std::size_t n, op_t& op);
void
set_host_ipv6_and_zone_id(
ipv6_address const& addr,
core::string_view zone_id);
void
set_host_ipv6_and_encoded_zone_id(
ipv6_address const& addr,
pct_string_view zone_id);
core::string_view
first_segment() const noexcept;

View File

@@ -736,7 +736,7 @@ public:
encoding_opts opt;
opt.space_as_plus = false;
return encoded_userinfo().decode(
opt, std::move(token));
opt, std::forward<StringToken>(token));
}
/** Return the userinfo
@@ -836,7 +836,7 @@ public:
encoding_opts opt;
opt.space_as_plus = false;
return encoded_user().decode(
opt, std::move(token));
opt, std::forward<StringToken>(token));
}
/** Return the user
@@ -935,7 +935,7 @@ public:
encoding_opts opt;
opt.space_as_plus = false;
return encoded_password().decode(
opt, std::move(token));
opt, std::forward<StringToken>(token));
}
/** Return the password
@@ -1064,7 +1064,7 @@ public:
encoding_opts opt;
opt.space_as_plus = false;
return encoded_host().decode(
opt, std::move(token));
opt, std::forward<StringToken>(token));
}
/** Return the host
@@ -1162,7 +1162,7 @@ public:
encoding_opts opt;
opt.space_as_plus = false;
return encoded_host_address().decode(
opt, std::move(token));
opt, std::forward<StringToken>(token));
}
/** Return the host
@@ -1383,7 +1383,7 @@ public:
encoding_opts opt;
opt.space_as_plus = false;
return encoded_host_name().decode(
opt, std::move(token));
opt, std::forward<StringToken>(token));
}
/** Return the host name
@@ -1468,7 +1468,7 @@ public:
encoding_opts opt;
opt.space_as_plus = false;
return encoded_zone_id().decode(
opt, std::move(token));
opt, std::forward<StringToken>(token));
}
/** Return the IPv6 Zone ID
@@ -1732,7 +1732,7 @@ public:
encoding_opts opt;
opt.space_as_plus = false;
return encoded_path().decode(
opt, std::move(token));
opt, std::forward<StringToken>(token));
}
/** Return the path
@@ -1977,7 +1977,7 @@ public:
encoding_opts opt;
opt.space_as_plus = false;
return encoded_query().decode(
opt, std::move(token));
opt, std::forward<StringToken>(token));
}
/** Return the query
@@ -2220,7 +2220,7 @@ public:
encoding_opts opt;
opt.space_as_plus = false;
return encoded_fragment().decode(
opt, std::move(token));
opt, std::forward<StringToken>(token));
}
/** Return the fragment

View File

@@ -57,7 +57,7 @@ parse(
grammar::delim_rule(']'))));
if(! rv)
{
// IPv6addrz
// IPv6addrz: https://datatracker.ietf.org/doc/html/rfc6874
it = it0;
auto rv2 = grammar::parse(
it, end,

View File

@@ -810,24 +810,97 @@ url_base::
set_host_ipv6(
ipv6_address const& addr)
{
op_t op(*this);
char buf[2 +
urls::ipv6_address::max_str_len];
auto s = addr.to_buffer(
buf + 1, sizeof(buf) - 2);
buf[0] = '[';
buf[s.size() + 1] = ']';
auto const n = s.size() + 2;
set_host_ipv6_and_encoded_zone_id(addr, encoded_zone_id());
return *this;
}
url_base&
url_base::
set_zone_id(core::string_view s)
{
set_host_ipv6_and_zone_id(host_ipv6_address(), s);
return *this;
}
url_base&
url_base::
set_encoded_zone_id(pct_string_view s)
{
set_host_ipv6_and_encoded_zone_id(host_ipv6_address(), s);
return *this;
}
void
url_base::
set_host_ipv6_and_zone_id(
ipv6_address const& addr,
core::string_view zone_id)
{
op_t op(*this, &zone_id);
char ipv6_str_buf[urls::ipv6_address::max_str_len];
auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
bool const has_zone_id = !zone_id.empty();
encoding_opts opt;
auto const ipn = ipv6_str.size();
auto const zn = encoded_size(zone_id, unreserved_chars, opt);
auto const n = ipn + 2 + has_zone_id * (3 + zn);
auto dest = set_host_impl(n, op);
std::memcpy(dest, buf, n);
impl_.decoded_[id_host] = n;
*dest++ = '[';
std::memcpy(dest, ipv6_str.data(), ipn);
dest += ipn;
if (has_zone_id)
{
*dest++ = '%';
*dest++ = '2';
*dest++ = '5';
encode(dest, zn, zone_id, unreserved_chars, opt);
dest += zn;
}
*dest++ = ']';
// ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
impl_.decoded_[id_host] = ipn + 2 + has_zone_id * (1 + zone_id.size());
impl_.host_type_ = urls::host_type::ipv6;
auto bytes = addr.to_bytes();
std::memcpy(
impl_.ip_addr_,
bytes.data(),
bytes.size());
}
void
url_base::
set_host_ipv6_and_encoded_zone_id(
ipv6_address const& addr,
pct_string_view zone_id)
{
op_t op(*this, &detail::ref(zone_id));
char ipv6_str_buf[urls::ipv6_address::max_str_len];
auto ipv6_str = addr.to_buffer(ipv6_str_buf, sizeof(ipv6_str_buf));
bool const has_zone_id = !zone_id.empty();
auto const ipn = ipv6_str.size();
auto const zn = detail::re_encoded_size_unsafe(zone_id, unreserved_chars);
auto const n = ipn + 2 + has_zone_id * (3 + zn);
auto dest = set_host_impl(n, op);
*dest++ = '[';
std::memcpy(dest, ipv6_str.data(), ipn);
dest += ipn;
std::size_t dzn = 0;
if (has_zone_id)
{
*dest++ = '%';
*dest++ = '2';
*dest++ = '5';
dzn = detail::re_encode_unsafe(dest, dest + zn, zone_id, unreserved_chars);
}
*dest++ = ']';
// ipn + |"["| + |"]"| + (has_zone_id ? |"%"| + zn : 0)
impl_.decoded_[id_host] = ipn + 2 + has_zone_id * (1 + dzn);
impl_.host_type_ = urls::host_type::ipv6;
auto bytes = addr.to_bytes();
std::memcpy(
impl_.ip_addr_,
bytes.data(),
bytes.size());
return *this;
}
url_base&

View File

@@ -1192,6 +1192,41 @@ struct url_base_test
set_host_ipv6("1::6:c0a8:1", "//[1::6:c0a8:1]");
// set_zone_id
{
// Round-trip: set and get zone id
url u;
BOOST_TEST_NO_THROW(u.set_host_ipv6(ipv6_address("fe80::1")));
BOOST_TEST_NO_THROW(u.set_zone_id("eth0"));
BOOST_TEST_EQ(u.host_ipv6_address().to_string(), "fe80::1");
BOOST_TEST_EQ(u.zone_id(), "eth0");
BOOST_TEST_EQ(u.buffer(), "//[fe80::1%25eth0]");
// set_zone_id when no IPv6 host: should create default
// constructed IPv6
url u2;
BOOST_TEST_NO_THROW(u2.set_zone_id("zone42"));
BOOST_TEST_EQ(u2.host_type(), host_type::ipv6);
BOOST_TEST_EQ(u2.zone_id(), "zone42");
BOOST_TEST_EQ(u2.buffer(), "//[::%25zone42]");
// set_encoded_zone_id: round-trip
url u3;
BOOST_TEST_NO_THROW(u3.set_host_ipv6(ipv6_address("fe80::2")));
BOOST_TEST_NO_THROW(u3.set_encoded_zone_id("en%30"));
BOOST_TEST_EQ(u3.zone_id(), "en0");
BOOST_TEST_EQ(u3.encoded_zone_id(), "en%30");
BOOST_TEST_EQ(u3.buffer(), "//[fe80::2%25en%30]");
// set_encoded_zone_id when no IPv6 host: should create default
// constructed IPv6
url u4;
BOOST_TEST_NO_THROW(u4.set_encoded_zone_id("zone%34"));
BOOST_TEST_EQ(u4.host_type(), host_type::ipv6);
BOOST_TEST_EQ(u4.zone_id(), "zone4");
BOOST_TEST_EQ(u4.buffer(), "//[::%25zone%34]");
}
set_host_ipvfuture("v42.69", "//[v42.69]");
BOOST_TEST_THROWS(url().set_host_ipvfuture("127.0.0.1"), system::system_error);