mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-16 10:49:36 +00:00
Fix #2379
This commit is contained in:
1 parent
c99d7472b5
commit
0853ce7753
2 files changed
+69
-15
No files matched your search
@@ -6380,10 +6380,46 @@ find_content_type(const std::string &path,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string
|
||||||
|
extract_media_type(const std::string &content_type,
|
||||||
|
std::map<std::string, std::string> *params = nullptr) {
|
||||||
|
// Extract type/subtype from Content-Type value (RFC 2045)
|
||||||
|
// e.g. "application/json; charset=utf-8" -> "application/json"
|
||||||
|
auto media_type = content_type;
|
||||||
|
auto semicolon_pos = media_type.find(';');
|
||||||
|
if (semicolon_pos != std::string::npos) {
|
||||||
|
auto param_str = media_type.substr(semicolon_pos + 1);
|
||||||
|
media_type = media_type.substr(0, semicolon_pos);
|
||||||
|
|
||||||
|
if (params) {
|
||||||
|
// Parse parameters: key=value pairs separated by ';'
|
||||||
|
split(param_str.data(), param_str.data() + param_str.size(), ';',
|
||||||
|
[&](const char *b, const char *e) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!key.empty()) {
|
||||||
|
params->emplace(trim_copy(key), trim_double_quotes_copy(val));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim whitespace from media type
|
||||||
|
return trim_copy(media_type);
|
||||||
|
}
|
||||||
|
|
||||||
inline bool can_compress_content_type(const std::string &content_type) {
|
inline bool can_compress_content_type(const std::string &content_type) {
|
||||||
using udl::operator""_t;
|
using udl::operator""_t;
|
||||||
|
|
||||||
auto tag = str2tag(content_type);
|
auto mime_type = extract_media_type(content_type);
|
||||||
|
auto tag = str2tag(mime_type);
|
||||||
|
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case "image/svg+xml"_t:
|
case "image/svg+xml"_t:
|
||||||
@@ -6395,7 +6431,7 @@ inline bool can_compress_content_type(const std::string &content_type) {
|
|||||||
|
|
||||||
case "text/event-stream"_t: return false;
|
case "text/event-stream"_t: return false;
|
||||||
|
|
||||||
default: return !content_type.rfind("text/", 0);
|
default: return !mime_type.rfind("text/", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7426,12 +7462,11 @@ inline std::string normalize_query_string(const std::string &query) {
|
|||||||
|
|
||||||
inline bool parse_multipart_boundary(const std::string &content_type,
|
inline bool parse_multipart_boundary(const std::string &content_type,
|
||||||
std::string &boundary) {
|
std::string &boundary) {
|
||||||
auto boundary_keyword = "boundary=";
|
std::map<std::string, std::string> params;
|
||||||
auto pos = content_type.find(boundary_keyword);
|
extract_media_type(content_type, ¶ms);
|
||||||
if (pos == std::string::npos) { return false; }
|
auto it = params.find("boundary");
|
||||||
auto end = content_type.find(';', pos);
|
if (it == params.end()) { return false; }
|
||||||
auto beg = pos + strlen(boundary_keyword);
|
boundary = it->second;
|
||||||
boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));
|
|
||||||
return !boundary.empty();
|
return !boundary.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7599,11 +7634,7 @@ inline bool parse_accept_header(const std::string &s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove additional parameters from media type
|
// Remove additional parameters from media type
|
||||||
auto param_pos = accept_entry.media_type.find(';');
|
accept_entry.media_type = extract_media_type(accept_entry.media_type);
|
||||||
if (param_pos != std::string::npos) {
|
|
||||||
accept_entry.media_type =
|
|
||||||
trim_copy(accept_entry.media_type.substr(0, param_pos));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basic validation of media type format
|
// Basic validation of media type format
|
||||||
if (accept_entry.media_type.empty()) {
|
if (accept_entry.media_type.empty()) {
|
||||||
@@ -9433,7 +9464,7 @@ inline size_t Request::get_param_value_count(const std::string &key) const {
|
|||||||
|
|
||||||
inline bool Request::is_multipart_form_data() const {
|
inline bool Request::is_multipart_form_data() const {
|
||||||
const auto &content_type = get_header_value("Content-Type");
|
const auto &content_type = get_header_value("Content-Type");
|
||||||
return !content_type.rfind("multipart/form-data", 0);
|
return detail::extract_media_type(content_type) == "multipart/form-data";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multipart FormData implementation
|
// Multipart FormData implementation
|
||||||
@@ -10915,7 +10946,8 @@ inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
|
|||||||
return true;
|
return true;
|
||||||
})) {
|
})) {
|
||||||
const auto &content_type = req.get_header_value("Content-Type");
|
const auto &content_type = req.get_header_value("Content-Type");
|
||||||
if (!content_type.find("application/x-www-form-urlencoded")) {
|
if (detail::extract_media_type(content_type) ==
|
||||||
|
"application/x-www-form-urlencoded") {
|
||||||
if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
|
if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {
|
||||||
res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
|
res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?
|
||||||
output_error_log(Error::ExceedMaxPayloadSize, &req);
|
output_error_log(Error::ExceedMaxPayloadSize, &req);
|
||||||
|
|||||||
@@ -3887,6 +3887,13 @@ protected:
|
|||||||
"01234567890123456789012345678901234567890",
|
"01234567890123456789012345678901234567890",
|
||||||
"text/plain");
|
"text/plain");
|
||||||
})
|
})
|
||||||
|
.Get("/compress-with-charset",
|
||||||
|
[&](const Request & /*req*/, Response &res) {
|
||||||
|
res.set_content(
|
||||||
|
"12345678901234567890123456789012345678901234567890123456789"
|
||||||
|
"01234567890123456789012345678901234567890",
|
||||||
|
"application/json; charset=utf-8");
|
||||||
|
})
|
||||||
.Get("/nocompress",
|
.Get("/nocompress",
|
||||||
[&](const Request & /*req*/, Response &res) {
|
[&](const Request & /*req*/, Response &res) {
|
||||||
res.set_content(
|
res.set_content(
|
||||||
@@ -6247,6 +6254,21 @@ TEST_F(ServerTest, Gzip) {
|
|||||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ServerTest, GzipWithContentTypeParameters) {
|
||||||
|
Headers headers;
|
||||||
|
headers.emplace("Accept-Encoding", "gzip, deflate");
|
||||||
|
auto res = cli_.Get("/compress-with-charset", headers);
|
||||||
|
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
EXPECT_EQ("gzip", res->get_header_value("Content-Encoding"));
|
||||||
|
EXPECT_EQ("application/json; charset=utf-8",
|
||||||
|
res->get_header_value("Content-Type"));
|
||||||
|
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
|
||||||
|
"7890123456789012345678901234567890",
|
||||||
|
res->body);
|
||||||
|
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, GzipWithoutAcceptEncoding) {
|
TEST_F(ServerTest, GzipWithoutAcceptEncoding) {
|
||||||
Headers headers;
|
Headers headers;
|
||||||
headers.emplace("Accept-Encoding", "");
|
headers.emplace("Accept-Encoding", "");
|
||||||
|
|||||||
Reference in new issue
Block a user