/
auroraos
/
mirror_ada
Обзор
Документация
Войти
/
auroraos
/
mirror_ada
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
src/url_pattern.cpp
381 строка
15 KB
Abdul Rawoof Khan
keep query and fragment delimiter in canonicalize search and hash (#1171)
09 июл 2026, 14:42
Не верифицирован
09 июл 2026, 14:42
761db09
Код
Авторство
О чём код?
#if ADA_INCLUDE_URL_PATTERN #include "ada/url_pattern-inl.h" #include <algorithm> #include <optional> #include <string> namespace ada { tl::expected<url_pattern_init, errors> url_pattern_init::process( const url_pattern_init& init, url_pattern_init::process_type type, std::optional<std::string_view> protocol, std::optional<std::string_view> username, std::optional<std::string_view> password, std::optional<std::string_view> hostname, std::optional<std::string_view> port, std::optional<std::string_view> pathname, std::optional<std::string_view> search, std::optional<std::string_view> hash) { // Let result be the result of creating a new URLPatternInit. auto result = url_pattern_init{}; // If protocol is not null, set result["protocol"] to protocol. if (protocol.has_value()) result.protocol = *protocol; // If username is not null, set result["username"] to username. if (username.has_value()) result.username = *username; // If password is not null, set result["password"] to password. if (password.has_value()) result.password = *password; // If hostname is not null, set result["hostname"] to hostname. if (hostname.has_value()) result.hostname = *hostname; // If port is not null, set result["port"] to port. if (port.has_value()) result.port = *port; // If pathname is not null, set result["pathname"] to pathname. if (pathname.has_value()) result.pathname = *pathname; // If search is not null, set result["search"] to search. if (search.has_value()) result.search = *search; // If hash is not null, set result["hash"] to hash. if (hash.has_value()) result.hash = *hash; // Let baseURL be null. std::optional<url_aggregator> base_url{}; // If init["baseURL"] exists: if (init.base_url.has_value()) { // Set baseURL to the result of parsing init["baseURL"]. auto parsing_result = ada::parse<url_aggregator>(*init.base_url); // If baseURL is failure, then throw a TypeError. if (!parsing_result) { return tl::unexpected(errors::type_error); } base_url = std::move(*parsing_result); // If init["protocol"] does not exist, then set result["protocol"] to the // result of processing a base URL string given baseURL's scheme and type. if (!init.protocol.has_value()) { ADA_ASSERT_TRUE(base_url.has_value()); std::string_view base_url_protocol = base_url->get_protocol(); if (base_url_protocol.ends_with(":")) base_url_protocol.remove_suffix(1); result.protocol = url_pattern_helpers::process_base_url_string(base_url_protocol, type); } // If type is not "pattern" and init contains none of "protocol", // "hostname", "port" and "username", then set result["username"] to the // result of processing a base URL string given baseURL's username and type. if (type != process_type::pattern && !init.protocol && !init.hostname && !init.port && !init.username) { result.username = url_pattern_helpers::process_base_url_string( base_url->get_username(), type); } // TODO: Optimization opportunity: Merge this with the previous check. // If type is not "pattern" and init contains none of "protocol", // "hostname", "port", "username" and "password", then set // result["password"] to the result of processing a base URL string given // baseURL's password and type. if (type != process_type::pattern && !init.protocol && !init.hostname && !init.port && !init.username && !init.password) { result.password = url_pattern_helpers::process_base_url_string( base_url->get_password(), type); } // If init contains neither "protocol" nor "hostname", then: if (!init.protocol && !init.hostname) { // Let baseHost be baseURL's host. // If baseHost is null, then set baseHost to the empty string. auto base_host = base_url->get_hostname(); // Set result["hostname"] to the result of processing a base URL string // given baseHost and type. result.hostname = url_pattern_helpers::process_base_url_string(base_host, type); } // If init contains none of "protocol", "hostname", and "port", then: if (!init.protocol && !init.hostname && !init.port) { // If baseURL's port is null, then set result["port"] to the empty string. // Otherwise, set result["port"] to baseURL's port, serialized. result.port = base_url->get_port(); } // If init contains none of "protocol", "hostname", "port", and "pathname", // then set result["pathname"] to the result of processing a base URL string // given the result of URL path serializing baseURL and type. if (!init.protocol && !init.hostname && !init.port && !init.pathname) { result.pathname = url_pattern_helpers::process_base_url_string( base_url->get_pathname(), type); } // If init contains none of "protocol", "hostname", "port", "pathname", and // "search", then: if (!init.protocol && !init.hostname && !init.port && !init.pathname && !init.search) { // Let baseQuery be baseURL's query. get_search() carries the leading "?" // delimiter, but the spec inherits the query itself, so drop it. std::string_view base_query = base_url->get_search(); if (base_query.starts_with("?")) base_query.remove_prefix(1); // Set result["search"] to the result of processing a base URL string // given baseQuery and type. result.search = url_pattern_helpers::process_base_url_string(base_query, type); } // If init contains none of "protocol", "hostname", "port", "pathname", // "search", and "hash", then: if (!init.protocol && !init.hostname && !init.port && !init.pathname && !init.search && !init.hash) { // Let baseFragment be baseURL's fragment. get_hash() carries the leading // "#" delimiter, but the spec inherits the fragment itself, so drop it. std::string_view base_fragment = base_url->get_hash(); if (base_fragment.starts_with("#")) base_fragment.remove_prefix(1); // Set result["hash"] to the result of processing a base URL string given // baseFragment and type. result.hash = url_pattern_helpers::process_base_url_string(base_fragment, type); } } // If init["protocol"] exists, then set result["protocol"] to the result of // process protocol for init given init["protocol"] and type. if (init.protocol) { auto process_result = process_protocol(*init.protocol, type); if (!process_result) { return tl::unexpected(process_result.error()); } result.protocol = std::move(*process_result); } // If init["username"] exists, then set result["username"] to the result of // process username for init given init["username"] and type. if (init.username.has_value()) { auto process_result = process_username(*init.username, type); if (!process_result) { return tl::unexpected(process_result.error()); } result.username = std::move(*process_result); } // If init["password"] exists, then set result["password"] to the result of // process password for init given init["password"] and type. if (init.password.has_value()) { auto process_result = process_password(*init.password, type); if (!process_result) { return tl::unexpected(process_result.error()); } result.password = std::move(*process_result); } // If init["hostname"] exists, then set result["hostname"] to the result of // process hostname for init given init["hostname"] and type. if (init.hostname.has_value()) { auto process_result = process_hostname(*init.hostname, type); if (!process_result) { return tl::unexpected(process_result.error()); } result.hostname = std::move(*process_result); } // If init["port"] exists, then set result["port"] to the result of process // port for init given init["port"], result["protocol"], and type. if (init.port) { auto process_result = process_port(*init.port, result.protocol.value_or("fake"), type); if (!process_result) { return tl::unexpected(process_result.error()); } result.port = std::move(*process_result); } // If init["pathname"] exists: if (init.pathname.has_value()) { // Set result["pathname"] to init["pathname"]. result.pathname = init.pathname; // If the following are all true: // - baseURL is not null; // - baseURL has an opaque path; and // - the result of running is an absolute pathname given result["pathname"] // and type is false, if (base_url && !base_url->has_opaque_path && !url_pattern_helpers::is_absolute_pathname(*result.pathname, type)) { // Let baseURLPath be the result of running process a base URL string // given the result of URL path serializing baseURL and type. // TODO: Optimization opportunity: Avoid returning a string if no slash // exist. std::string base_url_path = url_pattern_helpers::process_base_url_string( base_url->get_pathname(), type); // Let slash index be the index of the last U+002F (/) code point found in // baseURLPath, interpreted as a sequence of code points, or null if there // are no instances of the code point. auto slash_index = base_url_path.find_last_of('/'); // If slash index is not null: if (slash_index != std::string::npos) { // Let new pathname be the code point substring from 0 to slash index + // 1 within baseURLPath. base_url_path.resize(slash_index + 1); // Append result["pathname"] to the end of new pathname. ADA_ASSERT_TRUE(result.pathname.has_value()); base_url_path.append(std::move(*result.pathname)); // Set result["pathname"] to new pathname. result.pathname = std::move(base_url_path); } } // Set result["pathname"] to the result of process pathname for init given // result["pathname"], result["protocol"], and type. auto pathname_processing_result = process_pathname(*result.pathname, result.protocol.value_or(""), type); if (!pathname_processing_result) { return tl::unexpected(pathname_processing_result.error()); } result.pathname = std::move(*pathname_processing_result); } // If init["search"] exists then set result["search"] to the result of process // search for init given init["search"] and type. if (init.search) { auto process_result = process_search(*init.search, type); if (!process_result) { return tl::unexpected(process_result.error()); } result.search = std::move(*process_result); } // If init["hash"] exists then set result["hash"] to the result of process // hash for init given init["hash"] and type. if (init.hash) { auto process_result = process_hash(*init.hash, type); if (!process_result) { return tl::unexpected(process_result.error()); } result.hash = std::move(*process_result); } // Return result. return result; } tl::expected<std::string, errors> url_pattern_init::process_protocol( std::string_view value, process_type type) { ada_log("process_protocol=", value, " [", type, "]"); // Let strippedValue be the given value with a single trailing U+003A (:) // removed, if any. if (value.ends_with(":")) { value.remove_suffix(1); } // If type is "pattern" then return strippedValue. if (type == process_type::pattern) { return std::string(value); } // Return the result of running canonicalize a protocol given strippedValue. return url_pattern_helpers::canonicalize_protocol(value); } tl::expected<std::string, errors> url_pattern_init::process_username( std::string_view value, process_type type) { // If type is "pattern" then return value. if (type == process_type::pattern) { return std::string(value); } // Return the result of running canonicalize a username given value. return url_pattern_helpers::canonicalize_username(value); } tl::expected<std::string, errors> url_pattern_init::process_password( std::string_view value, process_type type) { // If type is "pattern" then return value. if (type == process_type::pattern) { return std::string(value); } // Return the result of running canonicalize a password given value. return url_pattern_helpers::canonicalize_password(value); } tl::expected<std::string, errors> url_pattern_init::process_hostname( std::string_view value, process_type type) { ada_log("process_hostname value=", value, " type=", type); // If type is "pattern" then return value. if (type == process_type::pattern) { return std::string(value); } // Return the result of running canonicalize a hostname given value. return url_pattern_helpers::canonicalize_hostname(value); } tl::expected<std::string, errors> url_pattern_init::process_port( std::string_view port, std::string_view protocol, process_type type) { // If type is "pattern" then return portValue. if (type == process_type::pattern) { return std::string(port); } // Return the result of running canonicalize a port given portValue and // protocolValue. return url_pattern_helpers::canonicalize_port_with_protocol(port, protocol); } tl::expected<std::string, errors> url_pattern_init::process_pathname( std::string_view value, std::string_view protocol, process_type type) { // If type is "pattern" then return pathnameValue. if (type == process_type::pattern) { return std::string(value); } // If protocolValue is a special scheme or the empty string, then return the // result of running canonicalize a pathname given pathnameValue. if (protocol.empty() || scheme::is_special(protocol)) { return url_pattern_helpers::canonicalize_pathname(value); } // Return the result of running canonicalize an opaque pathname given // pathnameValue. return url_pattern_helpers::canonicalize_opaque_pathname(value); } tl::expected<std::string, errors> url_pattern_init::process_search( std::string_view value, process_type type) { // Let strippedValue be the given value with a single leading U+003F (?) // removed, if any. if (value.starts_with("?")) { value.remove_prefix(1); } // We cannot assert that the value is no longer starting with a single // question mark because technically it can start. The question is whether or // not we should remove the first question mark. Ref: // https://github.com/ada-url/ada/pull/992 The spec is not clear on this. // If type is "pattern" then return strippedValue. if (type == process_type::pattern) { return std::string(value); } // Return the result of running canonicalize a search given strippedValue. return url_pattern_helpers::canonicalize_search(value); } tl::expected<std::string, errors> url_pattern_init::process_hash( std::string_view value, process_type type) { // Let strippedValue be the given value with a single leading U+0023 (#) // removed, if any. if (value.starts_with("#")) { value.remove_prefix(1); } ADA_ASSERT_TRUE(!value.starts_with("#")); // If type is "pattern" then return strippedValue. if (type == process_type::pattern) { return std::string(value); } // Return the result of running canonicalize a hash given strippedValue. return url_pattern_helpers::canonicalize_hash(value); } } // namespace ada #endif // ADA_INCLUDE_URL_PATTERN