1
0
mirror of synced 2025-06-13 18:41:30 +03:00

Feature/multipart headers (#2152)

* Adds headers to multipart form data

Adds a `headers` field to the `MultipartFormData` struct.

Populates this field by parsing headers from the multipart form data.
This allows access to specific headers associated with each form data part.

* Adds multipart header access test

Verifies the correct retrieval of headers from multipart form data file parts.

Ensures that custom and content-related headers are accessible and parsed as expected.

* Enables automatic test discovery with GoogleTest

Uses `gtest_discover_tests` to automatically find and run tests, simplifying test maintenance and improving discoverability.

* Removes explicit GoogleTest include

* Refactors header parsing logic

Improves header parsing by using a dedicated parsing function,
resulting in cleaner and more robust code.

This change also adds error handling during header parsing,
returning an error and marking the request as invalid
if parsing fails.

* clang-format corrected

* Renames variable for better readability.

Renames the `customHeader` variable to `custom_header` for improved code readability and consistency.

* typo
This commit is contained in:
Marek Kwasecki
2025-06-09 21:59:25 +02:00
committed by GitHub
parent 365cbe37fa
commit fd8da4d8e4
2 changed files with 74 additions and 0 deletions

View File

@ -544,6 +544,7 @@ struct MultipartFormData {
std::string content;
std::string filename;
std::string content_type;
Headers headers;
};
using MultipartFormDataItems = std::vector<MultipartFormData>;
using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
@ -5045,6 +5046,16 @@ public:
return false;
}
// parse and emplace space trimmed headers into a map
if (!parse_header(
header.data(), header.data() + header.size(),
[&](const std::string &key, const std::string &val) {
file_.headers.emplace(key, val);
})) {
is_valid_ = false;
return false;
}
constexpr const char header_content_type[] = "Content-Type:";
if (start_with_case_ignore(header, header_content_type)) {
@ -5144,6 +5155,7 @@ private:
file_.name.clear();
file_.filename.clear();
file_.content_type.clear();
file_.headers.clear();
}
bool start_with_case_ignore(const std::string &a, const char *b) const {

View File

@ -8010,6 +8010,68 @@ TEST(MultipartFormDataTest, ContentLength) {
ASSERT_TRUE(send_request(1, req, &response));
ASSERT_EQ("200", response.substr(9, 3));
}
TEST(MultipartFormDataTest, AccessPartHeaders) {
auto handled = false;
Server svr;
svr.Post("/test", [&](const Request &req, Response &) {
ASSERT_EQ(2u, req.files.size());
auto it = req.files.begin();
ASSERT_EQ("text1", it->second.name);
ASSERT_EQ("text1", it->second.content);
ASSERT_EQ(1, it->second.headers.count("Content-Length"));
auto content_length = it->second.headers.find("CONTENT-length");
ASSERT_EQ("5", content_length->second);
ASSERT_EQ(3, it->second.headers.size());
++it;
ASSERT_EQ("text2", it->second.name);
ASSERT_EQ("text2", it->second.content);
auto &headers = it->second.headers;
ASSERT_EQ(3, headers.size());
auto custom_header = headers.find("x-whatever");
ASSERT_TRUE(custom_header != headers.end());
ASSERT_NE("customvalue", custom_header->second);
ASSERT_EQ("CustomValue", custom_header->second);
ASSERT_TRUE(headers.find("X-Test") == headers.end()); // text1 header
handled = true;
});
thread t = thread([&] { svr.listen(HOST, PORT); });
auto se = detail::scope_exit([&] {
svr.stop();
t.join();
ASSERT_FALSE(svr.is_running());
ASSERT_TRUE(handled);
});
svr.wait_until_ready();
auto req = "POST /test HTTP/1.1\r\n"
"Content-Type: multipart/form-data;boundary=--------\r\n"
"Content-Length: 232\r\n"
"\r\n----------\r\n"
"Content-Disposition: form-data; name=\"text1\"\r\n"
"Content-Length: 5\r\n"
"X-Test: 1\r\n"
"\r\n"
"text1"
"\r\n----------\r\n"
"Content-Disposition: form-data; name=\"text2\"\r\n"
"Content-Type: text/plain\r\n"
"X-Whatever: CustomValue\r\n"
"\r\n"
"text2"
"\r\n------------\r\n"
"That should be disregarded. Not even read";
std::string response;
ASSERT_TRUE(send_request(1, req, &response));
ASSERT_EQ("200", response.substr(9, 3));
}
#endif
TEST(TaskQueueTest, IncreaseAtomicInteger) {