Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions source/common/http/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -264,15 +264,18 @@ std::string Utility::createSslRedirectPath(const RequestHeaderMap& headers) {
return fmt::format("https://{}{}", headers.getHostValue(), headers.getPathValue());
}

Utility::QueryParams Utility::parseQueryString(absl::string_view url) {
Utility::QueryParams Utility::parseQueryString(absl::string_view url, bool do_percent_decoding) {
size_t start = url.find('?');
if (start == std::string::npos) {
QueryParams params;
return params;
}

start++;
return parseParameters(url, start);
return do_percent_decoding
? parseParameters(PercentEncoding::decode(StringUtil::subspan(url, start, url.size())),
0)
: parseParameters(url, start);
}

Utility::QueryParams Utility::parseFromBody(absl::string_view body) {
Expand Down
4 changes: 3 additions & 1 deletion source/common/http/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,11 @@ std::string createSslRedirectPath(const RequestHeaderMap& headers);
/**
* Parse a URL into query parameters.
* @param url supplies the url to parse.
* @param do_percent_decoding supplies the flag to do percent decoding to the query parameters.
* It defaults to false.
* @return QueryParams the parsed parameters, if any.
*/
QueryParams parseQueryString(absl::string_view url);
QueryParams parseQueryString(absl::string_view url, bool do_percent_decoding = false);

/**
* Parse a a request body into query parameters.
Expand Down
3 changes: 2 additions & 1 deletion source/server/admin/stats_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ Http::Code StatsHandler::handlerStats(absl::string_view url,
Http::Code StatsHandler::handlerPrometheusStats(absl::string_view path_and_query,
Http::ResponseHeaderMap&,
Buffer::Instance& response, AdminStream&) {
const Http::Utility::QueryParams params = Http::Utility::parseQueryString(path_and_query);
const Http::Utility::QueryParams params =
Http::Utility::parseQueryString(path_and_query, /*do_percent_decoding=*/true);
Comment thread
dio marked this conversation as resolved.
Outdated
const bool used_only = params.find("usedonly") != params.end();
absl::optional<std::regex> regex;
if (!Utility::filterParam(params, response, regex)) {
Expand Down
23 changes: 22 additions & 1 deletion test/common/http/utility_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ TEST(HttpUtility, parseQueryString) {
Utility::parseQueryString("/hello?hello=&hello2=world2"));
EXPECT_EQ(Utility::QueryParams({{"name", "admin"}, {"level", "trace"}}),
Utility::parseQueryString("/logging?name=admin&level=trace"));
// A sample of an encoded query string of a request by prometheus:
// https://github.com/envoyproxy/envoy/issues/10926#issuecomment-651085261.
EXPECT_EQ(
Utility::QueryParams(
{{"filter", "(cluster.upstream_(rq_total|rq_time_sum|rq_time_count|rq_time_bucket|rq_xx|"
"rq_complete|rq_active|cx_active))|(server.version)"}}),
Utility::parseQueryString(
"/stats?filter=%28cluster.upstream_%28rq_total%7Crq_time_sum%7Crq_time_count%7Crq_time_"
"bucket%7Crq_xx%7Crq_complete%7Crq_active%7Ccx_active%29%29%7C%28server.version%29",
/*do_percent_decoding=*/true));
}

TEST(HttpUtility, getResponseStatus) {
Expand Down Expand Up @@ -1188,7 +1198,18 @@ TEST(PercentEncoding, EncodeDecode) {
validatePercentEncodingEncodeDecode("_-ok-_", "_-ok-_");
}

TEST(PercentEncoding, Trailing) {
TEST(PercentEncoding, Decoding) {
EXPECT_EQ(Utility::PercentEncoding::decode("hello%20world"), "hello world");
EXPECT_EQ(Utility::PercentEncoding::decode("upstream%7Cdownstream"), "upstream|downstream");
EXPECT_EQ(
Utility::PercentEncoding::decode(
"filter=%28cluster.upstream_%28rq_total%7Crq_time_sum%7Crq_time_count%7Crq_time_bucket%"
"7Crq_xx%7Crq_complete%7Crq_active%7Ccx_active%29%29%7C%28server.version%29"),
"filter=(cluster.upstream_(rq_total|rq_time_sum|rq_time_count|rq_time_bucket|rq_xx|rq_"
"complete|rq_active|cx_active))|(server.version)");
}

TEST(PercentEncoding, DecodingWithTrailingInput) {
EXPECT_EQ(Utility::PercentEncoding::decode("too%20lar%20"), "too lar ");
EXPECT_EQ(Utility::PercentEncoding::decode("too%20larg%e"), "too larg%e");
EXPECT_EQ(Utility::PercentEncoding::decode("too%20large%"), "too large%");
Expand Down