From 9fdcd30ea92185fa77efe4a8862617b0d161de83 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Tue, 15 Oct 2024 16:54:37 -0400 Subject: [PATCH] merge url implementation to single file --- src/ada.cpp | 4 +- src/url-getters.cpp | 91 -------------- src/url-setters.cpp | 226 ---------------------------------- src/url.cpp | 291 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 292 insertions(+), 320 deletions(-) delete mode 100644 src/url-getters.cpp delete mode 100644 src/url-setters.cpp diff --git a/src/ada.cpp b/src/ada.cpp index 164f37d74..26090909f 100644 --- a/src/ada.cpp +++ b/src/ada.cpp @@ -5,9 +5,7 @@ #include "implementation.cpp" #include "helpers.cpp" #include "url.cpp" -#include "url-getters.cpp" -#include "url-setters.cpp" #include "parser.cpp" #include "url_components.cpp" #include "url_aggregator.cpp" -#include "ada_c.cpp" \ No newline at end of file +#include "ada_c.cpp" diff --git a/src/url-getters.cpp b/src/url-getters.cpp deleted file mode 100644 index 54a7b2a34..000000000 --- a/src/url-getters.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/** - * @file url-getters.cpp - * Includes all the getters of `ada::url` - */ -#include "ada.h" -#include "ada/implementation.h" -#include "ada/helpers.h" -#include "ada/scheme.h" - -#include - -namespace ada { -[[nodiscard]] std::string url::get_origin() const noexcept { - if (is_special()) { - // Return a new opaque origin. - if (type == scheme::FILE) { - return "null"; - } - return ada::helpers::concat(get_protocol(), "//", get_host()); - } - - if (non_special_scheme == "blob") { - if (!path.empty()) { - auto result = ada::parse(path); - if (result && - (result->type == scheme::HTTP || result->type == scheme::HTTPS)) { - // If pathURL's scheme is not "http" and not "https", then return a - // new opaque origin. - return ada::helpers::concat(result->get_protocol(), "//", - result->get_host()); - } - } - } - - // Return a new opaque origin. - return "null"; -} - -[[nodiscard]] std::string url::get_protocol() const noexcept { - if (is_special()) { - return helpers::concat(ada::scheme::details::is_special_list[type], ":"); - } - // We only move the 'scheme' if it is non-special. - return helpers::concat(non_special_scheme, ":"); -} - -[[nodiscard]] std::string url::get_host() const noexcept { - // If url's host is null, then return the empty string. - // If url's port is null, return url's host, serialized. - // Return url's host, serialized, followed by U+003A (:) and url's port, - // serialized. - if (!host.has_value()) { - return ""; - } - if (port.has_value()) { - return host.value() + ":" + get_port(); - } - return host.value(); -} - -[[nodiscard]] std::string url::get_hostname() const noexcept { - return host.value_or(""); -} - -[[nodiscard]] std::string url::get_search() const noexcept { - // If this's URL's query is either null or the empty string, then return the - // empty string. Return U+003F (?), followed by this's URL's query. - return (!query.has_value() || (query.value().empty())) ? "" - : "?" + query.value(); -} - -[[nodiscard]] const std::string& url::get_username() const noexcept { - return username; -} - -[[nodiscard]] const std::string& url::get_password() const noexcept { - return password; -} - -[[nodiscard]] std::string url::get_port() const noexcept { - return port.has_value() ? std::to_string(port.value()) : ""; -} - -[[nodiscard]] std::string url::get_hash() const noexcept { - // If this's URL's fragment is either null or the empty string, then return - // the empty string. Return U+0023 (#), followed by this's URL's fragment. - return (!hash.has_value() || (hash.value().empty())) ? "" - : "#" + hash.value(); -} - -} // namespace ada diff --git a/src/url-setters.cpp b/src/url-setters.cpp deleted file mode 100644 index 62b717335..000000000 --- a/src/url-setters.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/** - * @file url-setters.cpp - * Includes all the setters of `ada::url` - */ -#include "ada.h" -#include "ada/helpers.h" - -#include -#include - -namespace ada { - -template -bool url::set_host_or_hostname(const std::string_view input) { - if (has_opaque_path) { - return false; - } - - std::optional previous_host = host; - std::optional previous_port = port; - - size_t host_end_pos = input.find('#'); - std::string _host(input.data(), host_end_pos != std::string_view::npos - ? host_end_pos - : input.size()); - helpers::remove_ascii_tab_or_newline(_host); - std::string_view new_host(_host); - - // If url's scheme is "file", then set state to file host state, instead of - // host state. - if (type != ada::scheme::type::FILE) { - std::string_view host_view(_host.data(), _host.length()); - auto [location, found_colon] = - helpers::get_host_delimiter_location(is_special(), host_view); - - // Otherwise, if c is U+003A (:) and insideBrackets is false, then: - // Note: the 'found_colon' value is true if and only if a colon was - // encountered while not inside brackets. - if (found_colon) { - if constexpr (override_hostname) { - return false; - } - std::string_view buffer = new_host.substr(location + 1); - if (!buffer.empty()) { - set_port(buffer); - } - } - // If url is special and host_view is the empty string, validation error, - // return failure. Otherwise, if state override is given, host_view is the - // empty string, and either url includes credentials or url's port is - // non-null, return. - else if (host_view.empty() && - (is_special() || has_credentials() || port.has_value())) { - return false; - } - - // Let host be the result of host parsing host_view with url is not special. - if (host_view.empty() && !is_special()) { - host = ""; - return true; - } - - bool succeeded = parse_host(host_view); - if (!succeeded) { - host = previous_host; - update_base_port(previous_port); - } - return succeeded; - } - - size_t location = new_host.find_first_of("/\\?"); - if (location != std::string_view::npos) { - new_host.remove_suffix(new_host.length() - location); - } - - if (new_host.empty()) { - // Set url's host to the empty string. - host = ""; - } else { - // Let host be the result of host parsing buffer with url is not special. - if (!parse_host(new_host)) { - host = previous_host; - update_base_port(previous_port); - return false; - } - - // If host is "localhost", then set host to the empty string. - if (host.has_value() && host.value() == "localhost") { - host = ""; - } - } - return true; -} - -bool url::set_host(const std::string_view input) { - return set_host_or_hostname(input); -} - -bool url::set_hostname(const std::string_view input) { - return set_host_or_hostname(input); -} - -bool url::set_username(const std::string_view input) { - if (cannot_have_credentials_or_port()) { - return false; - } - username = ada::unicode::percent_encode( - input, character_sets::USERINFO_PERCENT_ENCODE); - return true; -} - -bool url::set_password(const std::string_view input) { - if (cannot_have_credentials_or_port()) { - return false; - } - password = ada::unicode::percent_encode( - input, character_sets::USERINFO_PERCENT_ENCODE); - return true; -} - -bool url::set_port(const std::string_view input) { - if (cannot_have_credentials_or_port()) { - return false; - } - std::string trimmed(input); - helpers::remove_ascii_tab_or_newline(trimmed); - if (trimmed.empty()) { - port = std::nullopt; - return true; - } - // Input should not start with control characters. - if (ada::unicode::is_c0_control_or_space(trimmed.front())) { - return false; - } - // Input should contain at least one ascii digit. - if (input.find_first_of("0123456789") == std::string_view::npos) { - return false; - } - - // Revert changes if parse_port fails. - std::optional previous_port = port; - parse_port(trimmed); - if (is_valid) { - return true; - } - port = previous_port; - is_valid = true; - return false; -} - -void url::set_hash(const std::string_view input) { - if (input.empty()) { - hash = std::nullopt; - helpers::strip_trailing_spaces_from_opaque_path(*this); - return; - } - - std::string new_value; - new_value = input[0] == '#' ? input.substr(1) : input; - helpers::remove_ascii_tab_or_newline(new_value); - hash = unicode::percent_encode(new_value, - ada::character_sets::FRAGMENT_PERCENT_ENCODE); -} - -void url::set_search(const std::string_view input) { - if (input.empty()) { - query = std::nullopt; - helpers::strip_trailing_spaces_from_opaque_path(*this); - return; - } - - std::string new_value; - new_value = input[0] == '?' ? input.substr(1) : input; - helpers::remove_ascii_tab_or_newline(new_value); - - auto query_percent_encode_set = - is_special() ? ada::character_sets::SPECIAL_QUERY_PERCENT_ENCODE - : ada::character_sets::QUERY_PERCENT_ENCODE; - - query = ada::unicode::percent_encode(new_value, query_percent_encode_set); -} - -bool url::set_pathname(const std::string_view input) { - if (has_opaque_path) { - return false; - } - path = ""; - parse_path(input); - return true; -} - -bool url::set_protocol(const std::string_view input) { - std::string view(input); - helpers::remove_ascii_tab_or_newline(view); - if (view.empty()) { - return true; - } - - // Schemes should start with alpha values. - if (!checkers::is_alpha(view[0])) { - return false; - } - - view.append(":"); - - std::string::iterator pointer = - std::ranges::find_if_not(view, unicode::is_alnum_plus); - - if (pointer != view.end() && *pointer == ':') { - return parse_scheme( - std::string_view(view.data(), pointer - view.begin())); - } - return false; -} - -bool url::set_href(const std::string_view input) { - ada::result out = ada::parse(input); - - if (out) { - *this = *out; - } - - return out.has_value(); -} - -} // namespace ada diff --git a/src/url.cpp b/src/url.cpp index 04d7580cd..e14b37b75 100644 --- a/src/url.cpp +++ b/src/url.cpp @@ -586,4 +586,295 @@ ada_really_inline void url::parse_path(std::string_view input) { return checkers::verify_dns_length(host.value()); } +[[nodiscard]] std::string url::get_origin() const noexcept { + if (is_special()) { + // Return a new opaque origin. + if (type == scheme::FILE) { + return "null"; + } + return ada::helpers::concat(get_protocol(), "//", get_host()); + } + + if (non_special_scheme == "blob") { + if (!path.empty()) { + auto result = ada::parse(path); + if (result && + (result->type == scheme::HTTP || result->type == scheme::HTTPS)) { + // If pathURL's scheme is not "http" and not "https", then return a + // new opaque origin. + return ada::helpers::concat(result->get_protocol(), "//", + result->get_host()); + } + } + } + + // Return a new opaque origin. + return "null"; +} + +[[nodiscard]] std::string url::get_protocol() const noexcept { + if (is_special()) { + return helpers::concat(ada::scheme::details::is_special_list[type], ":"); + } + // We only move the 'scheme' if it is non-special. + return helpers::concat(non_special_scheme, ":"); +} + +[[nodiscard]] std::string url::get_host() const noexcept { + // If url's host is null, then return the empty string. + // If url's port is null, return url's host, serialized. + // Return url's host, serialized, followed by U+003A (:) and url's port, + // serialized. + if (!host.has_value()) { + return ""; + } + if (port.has_value()) { + return host.value() + ":" + get_port(); + } + return host.value(); +} + +[[nodiscard]] std::string url::get_hostname() const noexcept { + return host.value_or(""); +} + +[[nodiscard]] std::string url::get_search() const noexcept { + // If this's URL's query is either null or the empty string, then return the + // empty string. Return U+003F (?), followed by this's URL's query. + return (!query.has_value() || (query.value().empty())) ? "" + : "?" + query.value(); +} + +[[nodiscard]] const std::string& url::get_username() const noexcept { + return username; +} + +[[nodiscard]] const std::string& url::get_password() const noexcept { + return password; +} + +[[nodiscard]] std::string url::get_port() const noexcept { + return port.has_value() ? std::to_string(port.value()) : ""; +} + +[[nodiscard]] std::string url::get_hash() const noexcept { + // If this's URL's fragment is either null or the empty string, then return + // the empty string. Return U+0023 (#), followed by this's URL's fragment. + return (!hash.has_value() || (hash.value().empty())) ? "" + : "#" + hash.value(); +} + +template +bool url::set_host_or_hostname(const std::string_view input) { + if (has_opaque_path) { + return false; + } + + std::optional previous_host = host; + std::optional previous_port = port; + + size_t host_end_pos = input.find('#'); + std::string _host(input.data(), host_end_pos != std::string_view::npos + ? host_end_pos + : input.size()); + helpers::remove_ascii_tab_or_newline(_host); + std::string_view new_host(_host); + + // If url's scheme is "file", then set state to file host state, instead of + // host state. + if (type != ada::scheme::type::FILE) { + std::string_view host_view(_host.data(), _host.length()); + auto [location, found_colon] = + helpers::get_host_delimiter_location(is_special(), host_view); + + // Otherwise, if c is U+003A (:) and insideBrackets is false, then: + // Note: the 'found_colon' value is true if and only if a colon was + // encountered while not inside brackets. + if (found_colon) { + if constexpr (override_hostname) { + return false; + } + std::string_view buffer = new_host.substr(location + 1); + if (!buffer.empty()) { + set_port(buffer); + } + } + // If url is special and host_view is the empty string, validation error, + // return failure. Otherwise, if state override is given, host_view is the + // empty string, and either url includes credentials or url's port is + // non-null, return. + else if (host_view.empty() && + (is_special() || has_credentials() || port.has_value())) { + return false; + } + + // Let host be the result of host parsing host_view with url is not special. + if (host_view.empty() && !is_special()) { + host = ""; + return true; + } + + bool succeeded = parse_host(host_view); + if (!succeeded) { + host = previous_host; + update_base_port(previous_port); + } + return succeeded; + } + + size_t location = new_host.find_first_of("/\\?"); + if (location != std::string_view::npos) { + new_host.remove_suffix(new_host.length() - location); + } + + if (new_host.empty()) { + // Set url's host to the empty string. + host = ""; + } else { + // Let host be the result of host parsing buffer with url is not special. + if (!parse_host(new_host)) { + host = previous_host; + update_base_port(previous_port); + return false; + } + + // If host is "localhost", then set host to the empty string. + if (host.has_value() && host.value() == "localhost") { + host = ""; + } + } + return true; +} + +bool url::set_host(const std::string_view input) { + return set_host_or_hostname(input); +} + +bool url::set_hostname(const std::string_view input) { + return set_host_or_hostname(input); +} + +bool url::set_username(const std::string_view input) { + if (cannot_have_credentials_or_port()) { + return false; + } + username = ada::unicode::percent_encode( + input, character_sets::USERINFO_PERCENT_ENCODE); + return true; +} + +bool url::set_password(const std::string_view input) { + if (cannot_have_credentials_or_port()) { + return false; + } + password = ada::unicode::percent_encode( + input, character_sets::USERINFO_PERCENT_ENCODE); + return true; +} + +bool url::set_port(const std::string_view input) { + if (cannot_have_credentials_or_port()) { + return false; + } + std::string trimmed(input); + helpers::remove_ascii_tab_or_newline(trimmed); + if (trimmed.empty()) { + port = std::nullopt; + return true; + } + // Input should not start with control characters. + if (ada::unicode::is_c0_control_or_space(trimmed.front())) { + return false; + } + // Input should contain at least one ascii digit. + if (input.find_first_of("0123456789") == std::string_view::npos) { + return false; + } + + // Revert changes if parse_port fails. + std::optional previous_port = port; + parse_port(trimmed); + if (is_valid) { + return true; + } + port = previous_port; + is_valid = true; + return false; +} + +void url::set_hash(const std::string_view input) { + if (input.empty()) { + hash = std::nullopt; + helpers::strip_trailing_spaces_from_opaque_path(*this); + return; + } + + std::string new_value; + new_value = input[0] == '#' ? input.substr(1) : input; + helpers::remove_ascii_tab_or_newline(new_value); + hash = unicode::percent_encode(new_value, + ada::character_sets::FRAGMENT_PERCENT_ENCODE); +} + +void url::set_search(const std::string_view input) { + if (input.empty()) { + query = std::nullopt; + helpers::strip_trailing_spaces_from_opaque_path(*this); + return; + } + + std::string new_value; + new_value = input[0] == '?' ? input.substr(1) : input; + helpers::remove_ascii_tab_or_newline(new_value); + + auto query_percent_encode_set = + is_special() ? ada::character_sets::SPECIAL_QUERY_PERCENT_ENCODE + : ada::character_sets::QUERY_PERCENT_ENCODE; + + query = ada::unicode::percent_encode(new_value, query_percent_encode_set); +} + +bool url::set_pathname(const std::string_view input) { + if (has_opaque_path) { + return false; + } + path = ""; + parse_path(input); + return true; +} + +bool url::set_protocol(const std::string_view input) { + std::string view(input); + helpers::remove_ascii_tab_or_newline(view); + if (view.empty()) { + return true; + } + + // Schemes should start with alpha values. + if (!checkers::is_alpha(view[0])) { + return false; + } + + view.append(":"); + + std::string::iterator pointer = + std::ranges::find_if_not(view, unicode::is_alnum_plus); + + if (pointer != view.end() && *pointer == ':') { + return parse_scheme( + std::string_view(view.data(), pointer - view.begin())); + } + return false; +} + +bool url::set_href(const std::string_view input) { + ada::result out = ada::parse(input); + + if (out) { + *this = *out; + } + + return out.has_value(); +} + } // namespace ada