Platformize AsciiUrlDecode This function broke Chromium merge because of a different method signature for DecodeURLEscapeSequences. This CL allows us to override the method in Chromium. We've confirmed that this works in Chromium. This CL also fixes a bug where URI expansion was incorrectly leaving unknown variables instead of removing them. That issue was found by use the Chromium platform override in the corresponding merge CL. PiperOrigin-RevId: 406948688
diff --git a/common/platform/api/quiche_url_utils.h b/common/platform/api/quiche_url_utils.h index 126c17a..e6c9fc9 100644 --- a/common/platform/api/quiche_url_utils.h +++ b/common/platform/api/quiche_url_utils.h
@@ -9,6 +9,8 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "quiche_platform_impl/quiche_url_utils_impl.h" namespace quiche { @@ -25,6 +27,12 @@ return ExpandURITemplateImpl(uri_template, parameters, target, vars_found); } +// Decodes a URL-encoded string and converts it to ASCII. If the decoded input +// contains non-ASCII characters, decoding fails and absl::nullopt is returned. +inline absl::optional<std::string> AsciiUrlDecode(absl::string_view input) { + return AsciiUrlDecodeImpl(input); +} + } // namespace quiche #endif // QUICHE_COMMON_PLATFORM_API_QUICHE_URL_UTILS_H_
diff --git a/common/platform/api/quiche_url_utils_test.cc b/common/platform/api/quiche_url_utils_test.cc index e99fb9d..1a59d74 100644 --- a/common/platform/api/quiche_url_utils_test.cc +++ b/common/platform/api/quiche_url_utils_test.cc
@@ -9,6 +9,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "absl/types/optional.h" #include "common/platform/api/quiche_test.h" namespace quiche { @@ -34,12 +35,12 @@ TEST(QuicheUrlUtilsTest, ExtraParameter) { ValidateExpansion("/{foo}/{bar}/{baz}/", {{"foo", "123"}, {"bar", "456"}}, - "/123/456/{baz}/", {"foo", "bar"}); + "/123/456//", {"foo", "bar"}); } TEST(QuicheUrlUtilsTest, MissingParameter) { - ValidateExpansion("/{foo}/{baz}/", {{"foo", "123"}, {"bar", "456"}}, - "/123/{baz}/", {"foo"}); + ValidateExpansion("/{foo}/{baz}/", {{"foo", "123"}, {"bar", "456"}}, "/123//", + {"foo"}); } TEST(QuicheUrlUtilsTest, RepeatedParameter) { @@ -52,5 +53,28 @@ "/123/%3A/", {"foo", "bar"}); } +void ValidateUrlDecode(const std::string& input, + const absl::optional<std::string>& expected_output) { + absl::optional<std::string> decode_result = AsciiUrlDecode(input); + if (!expected_output.has_value()) { + EXPECT_FALSE(decode_result.has_value()); + return; + } + ASSERT_TRUE(decode_result.has_value()); + EXPECT_EQ(decode_result.value(), expected_output); +} + +TEST(QuicheUrlUtilsTest, DecodeNoChange) { + ValidateUrlDecode("foobar", "foobar"); +} + +TEST(QuicheUrlUtilsTest, DecodeReplace) { + ValidateUrlDecode("%7Bfoobar%7D", "{foobar}"); +} + +TEST(QuicheUrlUtilsTest, DecodeFail) { + ValidateUrlDecode("%FF", absl::nullopt); +} + } // namespace } // namespace quiche
diff --git a/common/platform/default/quiche_platform_impl/quiche_url_utils_impl.cc b/common/platform/default/quiche_platform_impl/quiche_url_utils_impl.cc index 801d95d..bcdfa9b 100644 --- a/common/platform/default/quiche_platform_impl/quiche_url_utils_impl.cc +++ b/common/platform/default/quiche_platform_impl/quiche_url_utils_impl.cc
@@ -4,10 +4,18 @@ #include "quiche_platform_impl/quiche_url_utils_impl.h" +#include <cstddef> +#include <cstdint> +#include <limits> +#include <string> + #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_replace.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "url/url_canon.h" #include "url/url_util.h" namespace quiche { @@ -31,6 +39,18 @@ found.insert(name); } } + // Remove any remaining variables that were not present in |parameters|. + while (true) { + size_t start = result.find('{'); + if (start == std::string::npos) { + break; + } + size_t end = result.find('}'); + if (end == std::string::npos || end <= start) { + return false; + } + result.erase(start, (end - start) + 1); + } if (vars_found != nullptr) { *vars_found = found; } @@ -38,4 +58,21 @@ return true; } +absl::optional<std::string> AsciiUrlDecodeImpl(absl::string_view input) { + std::string input_encoded = std::string(input); + url::RawCanonOutputW<1024> canon_output; + url::DecodeURLEscapeSequences(input_encoded.c_str(), input_encoded.length(), + &canon_output); + std::string output; + output.reserve(canon_output.length()); + for (int i = 0; i < canon_output.length(); i++) { + const uint16_t c = reinterpret_cast<uint16_t*>(canon_output.data())[i]; + if (c > std::numeric_limits<signed char>::max()) { + return absl::nullopt; + } + output += static_cast<char>(c); + } + return output; +} + } // namespace quiche
diff --git a/common/platform/default/quiche_platform_impl/quiche_url_utils_impl.h b/common/platform/default/quiche_platform_impl/quiche_url_utils_impl.h index 45f0e42..3bcf3b0 100644 --- a/common/platform/default/quiche_platform_impl/quiche_url_utils_impl.h +++ b/common/platform/default/quiche_platform_impl/quiche_url_utils_impl.h
@@ -9,6 +9,9 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "common/platform/api/quiche_export.h" namespace quiche { @@ -16,12 +19,17 @@ // Parameters are URL-encoded. Collects the names of any expanded variables in // |vars_found|. Supports level 1 templates as specified in RFC 6570. Returns // true if the template was parseable, false if it was malformed. -bool ExpandURITemplateImpl( +QUICHE_EXPORT_PRIVATE bool ExpandURITemplateImpl( const std::string& uri_template, const absl::flat_hash_map<std::string, std::string>& parameters, std::string* target, absl::flat_hash_set<std::string>* vars_found = nullptr); +// Decodes a URL-encoded string and converts it to ASCII. If the decoded input +// contains non-ASCII characters, decoding fails and absl::nullopt is returned. +QUICHE_EXPORT_PRIVATE absl::optional<std::string> AsciiUrlDecodeImpl( + absl::string_view input); + } // namespace quiche #endif // QUICHE_COMMON_PLATFORM_DEFAULT_QUICHE_PLATFORM_IMPL_QUICHE_URL_UTILS_IMPL_H_
diff --git a/quic/masque/masque_server_session.cc b/quic/masque/masque_server_session.cc index b3aa99e..6484d8a 100644 --- a/quic/masque/masque_server_session.cc +++ b/quic/masque/masque_server_session.cc
@@ -10,13 +10,14 @@ #include <limits> #include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" -#include "url/url_util.h" #include "quic/core/http/spdy_utils.h" #include "quic/core/quic_data_reader.h" #include "quic/core/quic_udp_socket.h" #include "quic/tools/quic_url.h" +#include "common/platform/api/quiche_url_utils.h" namespace quic { @@ -72,23 +73,6 @@ return response; } -absl::optional<std::string> AsciiUrlDecode(absl::string_view input) { - std::string input_encoded = std::string(input); - url::RawCanonOutputW<1024> canon_output; - DecodeURLEscapeSequences(input_encoded.c_str(), input_encoded.length(), - &canon_output); - std::string output; - output.reserve(canon_output.length()); - for (int i = 0; i < canon_output.length(); i++) { - const uint16_t c = reinterpret_cast<uint16_t*>(canon_output.data())[i]; - if (c > std::numeric_limits<signed char>::max()) { - return absl::nullopt; - } - output += static_cast<char>(c); - } - return output; -} - } // namespace MasqueServerSession::MasqueServerSession( @@ -251,12 +235,12 @@ QUIC_DLOG(ERROR) << "MASQUE request with bad path \"" << path << "\""; return CreateBackendErrorResponse("400", "Bad path"); } - absl::optional<std::string> host = AsciiUrlDecode(path_split[1]); + absl::optional<std::string> host = quiche::AsciiUrlDecode(path_split[1]); if (!host.has_value()) { QUIC_DLOG(ERROR) << "Failed to decode host \"" << path_split[1] << "\""; return CreateBackendErrorResponse("500", "Failed to decode host"); } - absl::optional<std::string> port = AsciiUrlDecode(path_split[2]); + absl::optional<std::string> port = quiche::AsciiUrlDecode(path_split[2]); if (!port.has_value()) { QUIC_DLOG(ERROR) << "Failed to decode port \"" << path_split[2] << "\""; return CreateBackendErrorResponse("500", "Failed to decode port");