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");