Fix query parsing when value has = characters (#1822)

* Implement string divider to replace splitter

* Divide query string in half

* Add a test case for query values containing the '=' character

* Add test cases for string divider

* Fix warnings
This commit is contained in:
Jiwoo Park 2024-04-22 08:17:14 +09:00 committed by GitHub
parent f10720ed69
commit 3b6597bba9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 144 additions and 36 deletions

View file

@ -2178,6 +2178,16 @@ void read_file(const std::string &path, std::string &out);
std::string trim_copy(const std::string &s);
void divide(
const char *data, std::size_t size, char d,
std::function<void(const char *, std::size_t, const char *, std::size_t)>
fn);
void divide(
const std::string &str, char d,
std::function<void(const char *, std::size_t, const char *, std::size_t)>
fn);
void split(const char *b, const char *e, char d,
std::function<void(const char *, const char *)> fn);
@ -2201,6 +2211,8 @@ const char *get_header_value(const Headers &headers, const std::string &key,
std::string params_to_query_str(const Params &params);
void parse_query_text(const char *data, std::size_t size, Params &params);
void parse_query_text(const std::string &s, Params &params);
bool parse_multipart_boundary(const std::string &content_type,
@ -2669,6 +2681,27 @@ inline std::string trim_double_quotes_copy(const std::string &s) {
return s;
}
inline void
divide(const char *data, std::size_t size, char d,
std::function<void(const char *, std::size_t, const char *, std::size_t)>
fn) {
const auto it = std::find(data, data + size, d);
const auto found = static_cast<std::size_t>(it != data + size);
const auto lhs_data = data;
const auto lhs_size = static_cast<std::size_t>(it - data);
const auto rhs_data = it + found;
const auto rhs_size = size - lhs_size - found;
fn(lhs_data, lhs_size, rhs_data, rhs_size);
}
inline void
divide(const std::string &str, char d,
std::function<void(const char *, std::size_t, const char *, std::size_t)>
fn) {
divide(str.data(), str.size(), d, std::move(fn));
}
inline void split(const char *b, const char *e, char d,
std::function<void(const char *, const char *)> fn) {
return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
@ -4392,22 +4425,22 @@ inline std::string params_to_query_str(const Params &params) {
return query;
}
inline void parse_query_text(const std::string &s, Params &params) {
inline void parse_query_text(const char *data, std::size_t size,
Params &params) {
std::set<std::string> cache;
split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {
split(data, data + size, '&', [&](const char *b, const char *e) {
std::string kv(b, e);
if (cache.find(kv) != cache.end()) { return; }
cache.insert(kv);
cache.insert(std::move(kv));
std::string key;
std::string val;
split(b, e, '=', [&](const char *b2, const char *e2) {
if (key.empty()) {
key.assign(b2, e2);
} else {
val.assign(b2, e2);
}
});
divide(b, static_cast<std::size_t>(e - b), '=',
[&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data,
std::size_t rhs_size) {
key.assign(lhs_data, lhs_size);
val.assign(rhs_data, rhs_size);
});
if (!key.empty()) {
params.emplace(decode_url(key, true), decode_url(val, true));
@ -4415,6 +4448,10 @@ inline void parse_query_text(const std::string &s, Params &params) {
});
}
inline void parse_query_text(const std::string &s, Params &params) {
parse_query_text(s.data(), s.size(), params);
}
inline bool parse_multipart_boundary(const std::string &content_type,
std::string &boundary) {
auto boundary_keyword = "boundary=";
@ -6072,26 +6109,13 @@ inline bool Server::parse_request_line(const char *s, Request &req) const {
}
}
size_t count = 0;
detail::split(req.target.data(), req.target.data() + req.target.size(), '?',
2, [&](const char *b, const char *e) {
switch (count) {
case 0:
req.path = detail::decode_url(std::string(b, e), false);
break;
case 1: {
if (e - b > 0) {
detail::parse_query_text(std::string(b, e), req.params);
}
break;
}
default: break;
}
count++;
});
if (count > 2) { return false; }
detail::divide(req.target, '?',
[&](const char *lhs_data, std::size_t lhs_size,
const char *rhs_data, std::size_t rhs_size) {
req.path = detail::decode_url(
std::string(lhs_data, lhs_size), false);
detail::parse_query_text(rhs_data, rhs_size, req.params);
});
}
return true;