24# include <string_view>
25# include <type_traits>
43class UPA_SO_VISIBLE host_output {
45 host_output() =
default;
46 inline host_output(
bool need_save)
47 : need_save_{ need_save } {}
49 host_output(
const host_output&) =
delete;
50 host_output& operator=(
const host_output&) =
delete;
51 virtual ~host_output() =
default;
53 virtual std::string& hostStart() = 0;
54 virtual void hostDone(HostType ) = 0;
55 inline bool need_save() const noexcept {
return need_save_; }
57 bool need_save_ =
true;
62 template <
typename CharT>
63 static validation_errc parse_host(
const CharT* first,
const CharT* last,
bool is_opaque, host_output& dest);
65 template <
typename CharT>
66 static validation_errc parse_opaque_host(
const CharT* first,
const CharT* last, host_output& dest);
68 template <
typename CharT>
69 static validation_errc parse_ipv4(
const CharT* first,
const CharT* last, host_output& dest);
71 template <
typename CharT>
72 static validation_errc parse_ipv6(
const CharT* first,
const CharT* last, host_output& dest);
93 template <class StrT, enable_if_str_arg_t<StrT> = 0>
97 const auto inp = make_str_arg(str);
98 const auto res = host_parser::parse_host(inp.begin(), inp.end(),
false, out);
100 throw url_error(res,
"Host parse error");
116 [[nodiscard]]
inline std::string_view
name() const UPA_LIFETIMEBOUND {
128 class UPA_SO_VISIBLE host_out :
public host_output {
130 inline explicit host_out(
url_host& host)
133 inline std::string& hostStart()
override {
134 return host_.host_str_;
136 inline void hostDone(HostType ht)
override {
144 std::string host_str_;
153template <
typename CharT>
154UPA_CONSTEXPR_20
bool contains_forbidden_domain_char(
const CharT* first,
const CharT* last) {
155 return std::any_of(first, last, detail::is_forbidden_domain_char<CharT>);
158template <
typename CharT>
159UPA_CONSTEXPR_20
bool contains_forbidden_host_char(
const CharT* first,
const CharT* last) {
160 return std::any_of(first, last, detail::is_forbidden_host_char<CharT>);
164template <
typename CharT>
165inline bool domain_parser_to_ascii(std::string& domain,
const CharT* input,
const CharT* input_end,
166 bool be_strict,
bool is_input_ascii =
false)
168 return idna::to_ascii(domain, input, input_end,
169 idna::domain_options(be_strict, is_input_ascii));
188template <
class StrT, enable_if_str_arg_t<StrT> = 0>
191 const auto inp = make_str_arg(input);
193 const bool is_ascii = util::is_ascii(inp.begin(), inp.end());
195 return detail::domain_parser_to_ascii(output, inp.begin(), inp.end(),
true, is_ascii)
199 if (inp.empty() || detail::contains_forbidden_domain_char(inp.begin(), inp.end()))
202 util::append_ascii_lowercase(output, inp.begin(), inp.end());
207 if (detail::domain_parser_to_ascii(output, inp.begin(), inp.end(),
false,
false) &&
209 !detail::contains_forbidden_domain_char(output.data(), output.data() + output.size()))
227UPA_EXPORT
template <
class CharT,
class StrT, enable_if_str_arg_t<StrT> = 0>
229 bool be_strict =
false,
bool is_input_ascii =
false)
231 static constexpr auto to_unicode =
232 [](std::u32string& domain,
const auto* input,
const auto* input_end,
233 bool be_strict,
bool is_input_ascii) {
234 return idna::to_unicode(domain, input, input_end, idna::domain_options(
235 be_strict, is_input_ascii) | idna::Option::FailFast);
238 const auto inp = make_str_arg(input);
240 if constexpr (std::is_same_v<CharT, char32_t>) {
241 const auto len = output.length();
242 if (to_unicode(output, inp.begin(), inp.end(), be_strict, is_input_ascii))
246 std::u32string domain;
247 if (to_unicode(domain, inp.begin(), inp.end(), be_strict, is_input_ascii)) {
248 if constexpr (
sizeof(CharT) ==
sizeof(char)) {
250 for (
auto cp : domain)
251 url_utf::append_utf8<std::basic_string<CharT>, detail::append_to_string>(cp, output);
252 }
else if constexpr (
sizeof(CharT) ==
sizeof(char16_t)) {
254 for (
auto cp : domain)
255 url_utf::append_utf16(cp, output);
256 }
else if constexpr (
sizeof(CharT) ==
sizeof(char32_t)) {
258 util::append(output, domain);
260 static_assert(util::false_v<CharT>,
"unsupported output character type");
267 using InpCharT =
typename decltype(inp)::value_type;
268 if constexpr (
sizeof(CharT) ==
sizeof(InpCharT)) {
270 util::append(output, inp);
273 const auto* last = inp.end();
274 for (
const auto* ptr = inp.begin(); ptr != last;) {
275 const auto cp = url_utf::read_utf_char(ptr, last).value;
276 if constexpr (
sizeof(CharT) ==
sizeof(char)) {
278 url_utf::append_utf8<std::basic_string<CharT>, detail::append_to_string>(cp, output);
279 }
else if constexpr (
sizeof(CharT) ==
sizeof(char16_t)) {
281 url_utf::append_utf16(cp, output);
282 }
else if constexpr (
sizeof(CharT) ==
sizeof(char32_t)) {
284 output.push_back(
static_cast<CharT
>(cp));
286 static_assert(util::false_v<CharT>,
"unsupported output character type");
296template <
typename CharT>
297inline validation_errc host_parser::parse_host(
const CharT* first,
const CharT* last,
bool is_opaque, host_output& dest) {
298 using UCharT = std::make_unsigned_t<CharT>;
300 static constexpr auto to_ascii_non_empty =
301 [](std::string& domain,
const auto* input,
const auto* input_end) {
302 return idna::to_ascii(domain, input, input_end, idna::domain_options(
false,
false)) &&
318 assert(first < last);
321 if (*(last - 1) ==
']') {
322 return parse_ipv6(first + 1, last - 1, dest);
330 return parse_opaque_host(first, last, dest);
333 const auto ptr = std::find_if_not(first, last, detail::is_ascii_domain_char<CharT>);
338 if (hostname_ends_in_a_number(first, last))
339 return parse_ipv4(first, last, dest);
341 if (dest.need_save()) {
343 std::string& str_host = dest.hostStart();
344 util::append_ascii_lowercase(str_host, first, last);
349 if (
static_cast<UCharT
>(*ptr) < 0x80 && *ptr !=
'%') {
353 if (!(*ptr >= 0x3C && *ptr <= 0x3E && ptr + 1 < last &&
static_cast<UCharT
>(ptr[1]) >= 0x80))
359 std::string buff_ascii;
361 const auto pes = std::find(ptr, last,
'%');
364 if (!to_ascii_non_empty(buff_ascii, first, last))
368 simple_buffer<char32_t> buff_uc;
371 for (
auto it = first; it != ptr; ++it) {
372 const auto uch =
static_cast<UCharT
>(*it);
373 buff_uc.push_back(
static_cast<char32_t>(uch));
378 bool is_ascii =
true;
379 for (
auto it = ptr; it != last;) {
380 const auto uch =
static_cast<UCharT
>(*it++);
383 buff_uc.push_back(
static_cast<char32_t>(uch));
388 if (detail::decode_hex_to_byte(it, last, uc8)) {
393 buff_uc.push_back(
static_cast<char32_t>(uc8));
400 simple_buffer<char> buff_utf8;
401 buff_utf8.push_back(
static_cast<char>(uc8));
402 while (it != last && *it ==
'%') {
404 if (!detail::decode_hex_to_byte(it, last, uc8))
406 buff_utf8.push_back(
static_cast<char>(uc8));
409 const auto* last_utf8 = buff_utf8.data() + buff_utf8.size();
410 for (
const auto* it_utf8 = buff_utf8.data(); it_utf8 < last_utf8;)
411 buff_uc.push_back(url_utf::read_utf_char(it_utf8, last_utf8).value);
417 buff_uc.push_back(
'%');
421 buff_uc.push_back(url_utf::read_utf_char(it, last).value);
425 util::append_ascii_lowercase(buff_ascii, buff_uc.begin(), buff_uc.end());
426 else if (!to_ascii_non_empty(buff_ascii, buff_uc.begin(), buff_uc.end()))
430 if (detail::contains_forbidden_domain_char(buff_ascii.data(), buff_ascii.data() + buff_ascii.size())) {
438 if (hostname_ends_in_a_number(buff_ascii.data(), buff_ascii.data() + buff_ascii.size()))
439 return parse_ipv4(buff_ascii.data(), buff_ascii.data() + buff_ascii.size(), dest);
441 if (dest.need_save()) {
443 std::string& str_host = dest.hostStart();
444 str_host.append(buff_ascii);
453template <
typename CharT>
454inline validation_errc host_parser::parse_opaque_host(
const CharT* first,
const CharT* last, host_output& dest) {
457 if (detail::contains_forbidden_host_char(first, last))
466 if (dest.need_save()) {
467 std::string& str_host = dest.hostStart();
471 using UCharT = std::make_unsigned_t<CharT>;
473 const CharT* pointer = first;
474 while (pointer < last) {
476 const auto uch =
static_cast<UCharT
>(*pointer);
479 detail::append_utf8_percent_encoded_char(pointer, last, str_host);
482 const auto uc =
static_cast<unsigned char>(uch);
484 detail::append_percent_encoded_byte(uc, str_host);
486 str_host.push_back(uc);
496template <
typename CharT>
497inline validation_errc host_parser::parse_ipv4(
const CharT* first,
const CharT* last, host_output& dest) {
500 const auto res = ipv4_parse(first, last, ipv4);
502 std::string& str_ipv4 = dest.hostStart();
503 ipv4_serialize(ipv4, str_ipv4);
509template <
typename CharT>
510inline validation_errc host_parser::parse_ipv6(
const CharT* first,
const CharT* last, host_output& dest) {
511 std::uint16_t ipv6addr[8];
513 const auto res = ipv6_parse(first, last, ipv6addr);
515 std::string& str_ipv6 = dest.hostStart();
516 str_ipv6.push_back(
'[');
517 ipv6_serialize(ipv6addr, str_ipv6);
518 str_ipv6.push_back(
']');
url_host(const url_host &)=default
~url_host()=default
destructor
std::string to_string() const
std::string_view name() const
url_host(url_host &&) noexcept=default
HostType
Host representation.
@ IPv4
host is an IPv4 address
@ Empty
empty host is the empty string
@ Opaque
opaque host is a non-empty ASCII string used in a not special URL
@ IPv6
host is an IPv6 address
validation_errc
URL validation and other error codes.
@ host_invalid_code_point
An opaque host contains a forbidden host code point.
@ host_missing
The input has a special scheme, but does not contain a host.
@ domain_to_ascii
Unicode ToASCII records an error or returns the empty string.
@ ipv6_unclosed
An IPv6 address is missing the closing U+005D (]).
validation_errc domain_parser(std::string &output, const StrT &input, bool be_strict)
Implements the domain parser algorithm.
bool domain_to_unicode(std::basic_string< CharT > &output, const StrT &input, bool be_strict=false, bool is_input_ascii=false)
Implements the domain to Unicode algorithm.