mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-19 11:19:36 +00:00
Parse status lines without std::regex
std::regex_match recursed once per character of a status line's reason phrase, so a server that sent one as long as a whole line overflowed the client's stack. file_extension() and append_query_params() ran the same kind of regex over a request path and a caller's path. Parse all three by hand, with the same results, except that a path that starts with '?' now gets its parameters joined with '&'. Serving a static file no longer defers the content type lookup to keep a regex off the 304 path, so it looks the type up once.
This commit is contained in:
1 parent
b917676861
commit
2e278fd6e9
2 files changed
+142
-29
No files matched your search
@@ -5917,10 +5917,14 @@ inline std::string encode_path(const std::string &s) {
|
||||
}
|
||||
|
||||
inline std::string file_extension(const std::string &path) {
|
||||
std::smatch m;
|
||||
thread_local auto re = std::regex("\\.([a-zA-Z0-9]+)$");
|
||||
if (std::regex_search(path, m, re)) { return m[1].str(); }
|
||||
return std::string();
|
||||
auto i = path.size();
|
||||
while (i > 0 && is_ascii_alnum(path[i - 1])) {
|
||||
i--;
|
||||
}
|
||||
if (i == path.size() || i == 0 || path[i - 1] != '.') {
|
||||
return std::string();
|
||||
}
|
||||
return path.substr(i);
|
||||
}
|
||||
|
||||
inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
|
||||
@@ -8292,19 +8296,35 @@ inline bool read_headers(Stream &strm, Headers &headers) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parsed by hand: std::regex_match recurses once per character of the reason
|
||||
// phrase, which a server can make as long as a whole line.
|
||||
inline bool parse_status_line(const char *line, std::string &version,
|
||||
int &status, std::string &reason) {
|
||||
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
|
||||
thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
|
||||
#else
|
||||
thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
|
||||
#endif
|
||||
if (std::strncmp(line, "HTTP/1.", 7) != 0 ||
|
||||
(line[7] != '0' && line[7] != '1') || line[8] != ' ' ||
|
||||
!is_ascii_digit(line[9]) || !is_ascii_digit(line[10]) ||
|
||||
!is_ascii_digit(line[11])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cmatch m;
|
||||
if (!std::regex_match(line, m, re)) { return false; }
|
||||
version = std::string(m[1]);
|
||||
status = std::stoi(std::string(m[2]));
|
||||
reason = std::string(m[3]);
|
||||
auto p = line + 12;
|
||||
auto reason_begin = p;
|
||||
if (*p == ' ') {
|
||||
reason_begin = ++p;
|
||||
p += std::strcspn(p, "\r\n");
|
||||
}
|
||||
auto reason_end = p;
|
||||
|
||||
#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
|
||||
if (*p == '\r') { p++; }
|
||||
#else
|
||||
if (*p++ != '\r') { return false; }
|
||||
#endif
|
||||
if (*p++ != '\n' || *p != '\0') { return false; }
|
||||
|
||||
version.assign(line, 8);
|
||||
status = (line[9] - '0') * 100 + (line[10] - '0') * 10 + (line[11] - '0');
|
||||
reason.assign(reason_begin, reason_end);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -11636,9 +11656,8 @@ inline std::string sanitize_filename(const std::string &filename) {
|
||||
inline std::string append_query_params(const std::string &path,
|
||||
const Params ¶ms) {
|
||||
std::string path_with_query = path;
|
||||
thread_local const std::regex re("[^?]+\\?.*");
|
||||
auto delm = std::regex_match(path, re) ? '&' : '?';
|
||||
path_with_query += delm + detail::params_to_query_str(params);
|
||||
path_with_query += path.find('?') == std::string::npos ? '?' : '&';
|
||||
path_with_query += detail::params_to_query_str(params);
|
||||
return path_with_query;
|
||||
}
|
||||
|
||||
@@ -13937,18 +13956,10 @@ inline bool Server::handle_file_request(Request &req, Response &res) {
|
||||
res.set_header(kv.first, kv.second);
|
||||
}
|
||||
|
||||
auto content_type_of = [&]() {
|
||||
return detail::find_content_type(
|
||||
path, file_extension_and_mimetype_map_, default_file_mimetype_);
|
||||
};
|
||||
|
||||
// Only the ETag needs the content type this early, and only to name
|
||||
// the coding. Deciding it here would otherwise put a regex in front
|
||||
// of the 304 below, which serving a file never used to pay for.
|
||||
std::string content_type;
|
||||
auto content_type = detail::find_content_type(
|
||||
path, file_extension_and_mimetype_map_, default_file_mimetype_);
|
||||
auto encoding = detail::EncodingType::None;
|
||||
if (static_file_compression_) {
|
||||
content_type = content_type_of();
|
||||
encoding =
|
||||
static_file_encoding(req, res, content_type, stat.size());
|
||||
}
|
||||
@@ -13980,8 +13991,6 @@ inline bool Server::handle_file_request(Request &req, Response &res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!static_file_compression_) { content_type = content_type_of(); }
|
||||
|
||||
detail::set_file_content_provider(res, mm, content_type, encoding);
|
||||
|
||||
if (req.method != "HEAD" && file_request_handler_) {
|
||||
|
||||
Reference in new issue
Block a user