srctree

Robin Linden parent de8db936 5f7bf716
uri: Remove recursion

This wasn't /really/ recursive as the second parse call in the 'parse -> complete_from_base_if_needed -> parse' chain would never get a base url, but this makes makes it more obvious that that was the case.
uri/uri.cpp added: 54, removed: 49, total 5
@@ -32,51 +32,7 @@ void normalize(Uri &uri) {
}
}
 
// NOLINTNEXTLINE(misc-no-recursion)
[[nodiscard]] bool complete_from_base_if_needed(Uri &uri, Uri const &base) {
if (uri.scheme.empty() && uri.authority.host.empty() && uri.path.starts_with('/')) {
// Origin-relative.
auto new_uri = Uri::parse(fmt::format("{}://{}{}", base.scheme, base.authority.host, uri.uri));
if (!new_uri) {
return false;
}
 
uri = *std::move(new_uri);
} else if (uri.scheme.empty() && uri.authority.host.empty() && !uri.path.empty()) {
// https://url.spec.whatwg.org/#path-relative-url-string
if (base.path == "/") {
auto new_uri = Uri::parse(fmt::format("{}/{}", base.uri, uri.uri));
if (!new_uri) {
return false;
}
 
uri = *std::move(new_uri);
} else {
auto end_of_last_path_segment = base.uri.find_last_of('/');
auto new_uri = Uri::parse(fmt::format("{}/{}", base.uri.substr(0, end_of_last_path_segment), uri.uri));
if (!new_uri) {
return false;
}
 
uri = *std::move(new_uri);
}
} else if (uri.scheme.empty() && !uri.authority.host.empty() && uri.uri.starts_with("//")) {
// Scheme-relative.
auto new_uri = Uri::parse(fmt::format("{}:{}", base.scheme, uri.uri));
if (!new_uri) {
return false;
}
 
uri = *std::move(new_uri);
}
 
return true;
}
 
} // namespace
 
// NOLINTNEXTLINE(misc-no-recursion)
std::optional<Uri> Uri::parse(std::string uristr, std::optional<std::reference_wrapper<Uri const>> base_uri) {
std::optional<Uri> parse_uri(std::string uristr) {
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86164
// Fuzz-testing w/ libstdc++13 still breaks the stack if 2048 characters are allowed.
if (uristr.size() > 1024) {
@@ -128,10 +84,59 @@ std::optional<Uri> Uri::parse(std::string uristr, std::optional<std::reference_w
 
normalize(uri);
 
if (base_uri) {
if (!complete_from_base_if_needed(uri, base_uri->get())) {
return std::nullopt;
return uri;
}
 
[[nodiscard]] bool complete_from_base_if_needed(Uri &uri, Uri const &base) {
if (uri.scheme.empty() && uri.authority.host.empty() && uri.path.starts_with('/')) {
// Origin-relative.
auto new_uri = parse_uri(fmt::format("{}://{}{}", base.scheme, base.authority.host, uri.uri));
if (!new_uri) {
return false;
}
 
uri = *std::move(new_uri);
} else if (uri.scheme.empty() && uri.authority.host.empty() && !uri.path.empty()) {
// https://url.spec.whatwg.org/#path-relative-url-string
if (base.path == "/") {
auto new_uri = parse_uri(fmt::format("{}/{}", base.uri, uri.uri));
if (!new_uri) {
return false;
}
 
uri = *std::move(new_uri);
} else {
auto end_of_last_path_segment = base.uri.find_last_of('/');
auto new_uri = parse_uri(fmt::format("{}/{}", base.uri.substr(0, end_of_last_path_segment), uri.uri));
if (!new_uri) {
return false;
}
 
uri = *std::move(new_uri);
}
} else if (uri.scheme.empty() && !uri.authority.host.empty() && uri.uri.starts_with("//")) {
// Scheme-relative.
auto new_uri = parse_uri(fmt::format("{}:{}", base.scheme, uri.uri));
if (!new_uri) {
return false;
}
 
uri = *std::move(new_uri);
}
 
return true;
}
 
} // namespace
 
std::optional<Uri> Uri::parse(std::string uristr, std::optional<std::reference_wrapper<Uri const>> base_uri) {
auto uri = parse_uri(std::move(uristr));
if (!uri) {
return uri;
}
 
if (base_uri && !complete_from_base_if_needed(*uri, base_uri->get())) {
return std::nullopt;
}
 
return uri;