12bool url::parse_opaque_host(std::string_view input) {
13 ada_log(
"parse_opaque_host ", input,
" [", input.size(),
" bytes]");
14 if (std::ranges::any_of(input.begin(), input.end(),
15 ada::unicode::is_forbidden_host_code_point)) {
21 host = ada::unicode::percent_encode(
26bool url::parse_ipv4(std::string_view input) {
27 ada_log(
"parse_ipv4 ", input,
" [", input.size(),
" bytes]");
28 if (input.back() ==
'.') {
29 input.remove_suffix(1);
31 size_t digit_count{0};
32 int pure_decimal_count = 0;
33 std::string_view original_input =
37 for (; (digit_count < 4) && !(input.empty()); digit_count++) {
41 if (is_hex && ((input.length() == 2) ||
42 ((input.length() > 2) && (input[2] ==
'.')))) {
45 input.remove_prefix(2);
47 std::from_chars_result r{};
49 r = std::from_chars(input.data() + 2, input.data() + input.size(),
51 }
else if ((input.length() >= 2) && input[0] ==
'0' &&
53 r = std::from_chars(input.data() + 1, input.data() + input.size(),
57 r = std::from_chars(input.data(), input.data() + input.size(),
60 if (r.ec != std::errc()) {
63 input.remove_prefix(r.ptr - input.data());
69 if (segment_result >= (uint64_t(1) << (32 - digit_count * 8))) {
72 ipv4 <<= (32 - digit_count * 8);
73 ipv4 |= segment_result;
78 if ((segment_result > 255) || (input[0] !=
'.')) {
82 ipv4 |= segment_result;
83 input.remove_prefix(1);
86 if ((digit_count != 4) || (!input.empty())) {
91 if (pure_decimal_count == 4) {
92 host = original_input;
101bool url::parse_ipv6(std::string_view input) {
102 ada_log(
"parse_ipv6 ", input,
" [", input.size(),
" bytes]");
108 std::array<uint16_t, 8> address{};
114 std::optional<int> compress{};
117 std::string_view::iterator pointer = input.begin();
120 if (input[0] ==
':') {
123 if (input.size() == 1 || input[1] !=
':') {
124 ada_log(
"parse_ipv6 starts with : but the rest does not start with :");
132 compress = ++piece_index;
136 while (pointer != input.end()) {
138 if (piece_index == 8) {
139 ada_log(
"parse_ipv6 piece_index == 8");
144 if (*pointer ==
':') {
146 if (compress.has_value()) {
147 ada_log(
"parse_ipv6 compress is non-null");
154 compress = ++piece_index;
159 uint16_t value = 0, length = 0;
164 while (length < 4 && pointer != input.end() &&
165 unicode::is_ascii_hex_digit(*pointer)) {
167 value = uint16_t(value * 0x10 + unicode::convert_hex_to_binary(*pointer));
173 if (pointer != input.end() && *pointer ==
'.') {
176 ada_log(
"parse_ipv6 length is 0");
184 if (piece_index > 6) {
185 ada_log(
"parse_ipv6 piece_index > 6");
190 int numbers_seen = 0;
193 while (pointer != input.end()) {
195 std::optional<uint16_t> ipv4_piece{};
198 if (numbers_seen > 0) {
201 if (*pointer ==
'.' && numbers_seen < 4) {
206 ada_log(
"parse_ipv6 Otherwise, validation error, return failure");
214 "parse_ipv6 If c is not an ASCII digit, validation error, return "
222 int number = *pointer -
'0';
225 if (!ipv4_piece.has_value()) {
229 else if (ipv4_piece == 0) {
230 ada_log(
"parse_ipv6 if ipv4Piece is 0, validation error");
235 ipv4_piece = *ipv4_piece * 10 + number;
239 if (ipv4_piece > 255) {
240 ada_log(
"parse_ipv6 ipv4_piece > 255");
251 address[piece_index] =
252 uint16_t(address[piece_index] * 0x100 + *ipv4_piece);
258 if (numbers_seen == 2 || numbers_seen == 4) {
264 if (numbers_seen != 4) {
272 else if ((pointer != input.end()) && (*pointer ==
':')) {
277 if (pointer == input.end()) {
279 "parse_ipv6 If c is the EOF code point, validation error, return "
286 else if (pointer != input.end()) {
288 "parse_ipv6 Otherwise, if c is not the EOF code point, validation "
289 "error, return failure");
294 address[piece_index] = value;
301 if (compress.has_value()) {
303 int swaps = piece_index - *compress;
311 while (piece_index != 0 && swaps > 0) {
312 std::swap(address[piece_index], address[*compress + swaps - 1]);
319 else if (piece_index != 8) {
321 "parse_ipv6 if compress is null and pieceIndex is not 8, validation "
322 "error, return failure");
326 ada_log(
"parse_ipv6 ", *host);
331template <
bool has_state_overr
ide>
339 if (is_input_special) {
340 if constexpr (has_state_override) {
357 host.value().empty()) {
364 if constexpr (has_state_override) {
368 if (urls_scheme_port) {
371 if (port.has_value() && *port == urls_scheme_port) {
377 std::string _buffer(input);
382 unicode::to_lower_ascii(_buffer.data(), _buffer.size());
384 if constexpr (has_state_override) {
388 if (
is_special() != ada::scheme::is_special(_buffer)) {
401 host.value().empty()) {
406 set_scheme(std::move(_buffer));
408 if constexpr (has_state_override) {
412 if (urls_scheme_port) {
415 if (port.has_value() && *port == urls_scheme_port) {
426 ada_log(
"parse_host ", input,
" [", input.size(),
" bytes]");
431 if (input[0] ==
'[') {
433 if (input.back() !=
']') {
436 ada_log(
"parse_host ipv6");
440 input.remove_prefix(1);
441 input.remove_suffix(1);
442 return parse_ipv6(input);
448 return parse_opaque_host(input);
455 std::string buffer = std::string(input);
459 unicode::to_lower_ascii(buffer.data(), buffer.size());
460 bool is_forbidden = unicode::contains_forbidden_domain_code_point(
461 buffer.data(), buffer.size());
462 if (is_forbidden == 0 && buffer.find(
"xn-") == std::string_view::npos) {
464 host = std::move(buffer);
465 if (checkers::is_ipv4(host.value())) {
466 ada_log(
"parse_host fast path ipv4");
467 return parse_ipv4(host.value());
469 ada_log(
"parse_host fast path ", *host);
472 ada_log(
"parse_host calling to_ascii");
473 is_valid = ada::unicode::to_ascii(host, input, input.find(
'%'));
475 ada_log(
"parse_host to_ascii returns false");
478 ada_log(
"parse_host to_ascii succeeded ", *host,
" [", host->size(),
481 if (std::any_of(host.value().begin(), host.value().end(),
482 ada::unicode::is_forbidden_domain_code_point)) {
489 if (checkers::is_ipv4(host.value())) {
490 ada_log(
"parse_host got ipv4 ", *host);
491 return parse_ipv4(host.value());
498 ada_log(
"parse_path ", input);
499 std::string tmp_buffer;
500 std::string_view internal_input;
501 if (unicode::has_tabs_or_newline(input)) {
505 helpers::remove_ascii_tab_or_newline(tmp_buffer);
506 internal_input = tmp_buffer;
508 internal_input = input;
513 if (internal_input.empty()) {
515 }
else if ((internal_input[0] ==
'/') || (internal_input[0] ==
'\\')) {
516 helpers::parse_prepared_path(internal_input.substr(1), type, path);
518 helpers::parse_prepared_path(internal_input, type, path);
520 }
else if (!internal_input.empty()) {
521 if (internal_input[0] ==
'/') {
522 helpers::parse_prepared_path(internal_input.substr(1), type, path);
524 helpers::parse_prepared_path(internal_input, type, path);
527 if (!host.has_value()) {
538 auto back = std::back_insert_iterator(answer);
539 answer.append(
"{\n");
540 answer.append(
"\t\"protocol\":\"");
542 answer.append(
"\",\n");
544 answer.append(
"\t\"username\":\"");
545 helpers::encode_json(username, back);
546 answer.append(
"\",\n");
547 answer.append(
"\t\"password\":\"");
548 helpers::encode_json(password, back);
549 answer.append(
"\",\n");
551 if (host.has_value()) {
552 answer.append(
"\t\"host\":\"");
553 helpers::encode_json(host.value(), back);
554 answer.append(
"\",\n");
556 if (port.has_value()) {
557 answer.append(
"\t\"port\":\"");
558 answer.append(std::to_string(port.value()));
559 answer.append(
"\",\n");
561 answer.append(
"\t\"path\":\"");
562 helpers::encode_json(path, back);
563 answer.append(
"\",\n");
564 answer.append(
"\t\"opaque path\":");
567 answer.append(
",\n");
568 answer.append(
"\t\"query\":\"");
569 helpers::encode_json(query.value(), back);
572 if (hash.has_value()) {
573 answer.append(
",\n");
574 answer.append(
"\t\"hash\":\"");
575 helpers::encode_json(hash.value(), back);
578 answer.append(
"\n}");
583 if (!host.has_value()) {
586 return checkers::verify_dns_length(host.value());
598 if (non_special_scheme ==
"blob") {
605 return ada::helpers::concat(
result->get_protocol(),
"//",
620 return helpers::concat(non_special_scheme,
":");
628 if (!host.has_value()) {
631 if (port.has_value()) {
632 return host.value() +
":" +
get_port();
638 return host.value_or(
"");
644 return (!query.has_value() || (query.value().empty())) ?
""
645 :
"?" + query.value();
657 return port.has_value() ? std::to_string(port.value()) :
"";
663 return (!hash.has_value() || (hash.value().empty())) ?
""
664 :
"#" + hash.value();
667template <
bool overr
ide_hostname>
668bool url::set_host_or_hostname(
const std::string_view input) {
673 std::optional<std::string> previous_host = host;
674 std::optional<uint16_t> previous_port = port;
676 size_t host_end_pos = input.find(
'#');
677 std::string _host(input.data(), host_end_pos != std::string_view::npos
680 helpers::remove_ascii_tab_or_newline(_host);
681 std::string_view new_host(_host);
686 std::string_view host_view(_host.data(), _host.length());
687 auto [location, found_colon] =
688 helpers::get_host_delimiter_location(
is_special(), host_view);
694 if constexpr (override_hostname) {
697 std::string_view buffer = new_host.substr(location + 1);
698 if (!buffer.empty()) {
706 else if (host_view.empty() &&
717 bool succeeded = parse_host(host_view);
719 host = previous_host;
720 update_base_port(previous_port);
725 size_t location = new_host.find_first_of(
"/\\?");
726 if (location != std::string_view::npos) {
727 new_host.remove_suffix(new_host.length() - location);
730 if (new_host.empty()) {
735 if (!parse_host(new_host)) {
736 host = previous_host;
737 update_base_port(previous_port);
742 if (host.has_value() && host.value() ==
"localhost") {
750 return set_host_or_hostname<false>(input);
754 return set_host_or_hostname<true>(input);
758 if (cannot_have_credentials_or_port()) {
761 username = ada::unicode::percent_encode(
767 if (cannot_have_credentials_or_port()) {
770 password = ada::unicode::percent_encode(
776 if (cannot_have_credentials_or_port()) {
779 std::string trimmed(input);
780 helpers::remove_ascii_tab_or_newline(trimmed);
781 if (trimmed.empty()) {
786 if (ada::unicode::is_c0_control_or_space(trimmed.front())) {
790 if (input.find_first_of(
"0123456789") == std::string_view::npos) {
795 std::optional<uint16_t> previous_port = port;
800 port = previous_port;
808 helpers::strip_trailing_spaces_from_opaque_path(*
this);
812 std::string new_value;
813 new_value = input[0] ==
'#' ? input.substr(1) : input;
814 helpers::remove_ascii_tab_or_newline(new_value);
815 hash = unicode::percent_encode(new_value,
821 query = std::nullopt;
822 helpers::strip_trailing_spaces_from_opaque_path(*
this);
826 std::string new_value;
827 new_value = input[0] ==
'?' ? input.substr(1) : input;
828 helpers::remove_ascii_tab_or_newline(new_value);
830 auto query_percent_encode_set =
834 query = ada::unicode::percent_encode(new_value, query_percent_encode_set);
847 std::string view(input);
848 helpers::remove_ascii_tab_or_newline(view);
860 std::string::iterator pointer =
861 std::ranges::find_if_not(view, unicode::is_alnum_plus);
863 if (pointer != view.end() && *pointer ==
':') {
864 return parse_scheme<true>(
865 std::string_view(view.data(), pointer - view.begin()));
877 return out.has_value();
Includes all definitions for Ada.
#define ada_really_inline
constexpr uint8_t QUERY_PERCENT_ENCODE[32]
constexpr uint8_t SPECIAL_QUERY_PERCENT_ENCODE[32]
constexpr uint8_t C0_CONTROL_PERCENT_ENCODE[32]
constexpr uint8_t USERINFO_PERCENT_ENCODE[32]
constexpr uint8_t FRAGMENT_PERCENT_ENCODE[32]
constexpr bool has_hex_prefix(std::string_view input)
constexpr bool is_alpha(char x) noexcept
constexpr bool is_digit(char x) noexcept
constexpr std::string_view is_special_list[]
constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept
constexpr uint16_t get_special_port(std::string_view scheme) noexcept
std::string ipv6(const std::array< uint16_t, 8 > &address) noexcept
std::string ipv4(uint64_t address) noexcept
tl::expected< result_type, ada::errors > result
ada_warn_unused ada::result< result_type > parse(std::string_view input, const result_type *base_url=nullptr)
Declarations for the URL scheme.
ada_really_inline constexpr bool is_special() const noexcept
void set_hash(std::string_view input)
std::string get_search() const noexcept
bool set_hostname(std::string_view input)
bool set_host(std::string_view input)
ada_really_inline bool has_credentials() const noexcept
bool set_password(std::string_view input)
void set_search(std::string_view input)
bool set_href(std::string_view input)
bool set_username(std::string_view input)
std::string get_host() const noexcept
std::string get_hash() const noexcept
bool set_pathname(std::string_view input)
std::string get_origin() const noexcept override
std::string get_hostname() const noexcept
const std::string & get_password() const noexcept
bool set_protocol(std::string_view input)
std::string get_port() const noexcept
const std::string & get_username() const noexcept
bool set_port(std::string_view input)
constexpr bool has_search() const noexcept override
std::string to_string() const override
std::string get_protocol() const noexcept
bool has_valid_domain() const noexcept override