Skip to content

Commit fd8da4d

Browse files
authored
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
1 parent 365cbe3 commit fd8da4d

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

httplib.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ struct MultipartFormData {
544544
std::string content;
545545
std::string filename;
546546
std::string content_type;
547+
Headers headers;
547548
};
548549
using MultipartFormDataItems = std::vector<MultipartFormData>;
549550
using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
@@ -5045,6 +5046,16 @@ class MultipartFormDataParser {
50455046
return false;
50465047
}
50475048

5049+
// parse and emplace space trimmed headers into a map
5050+
if (!parse_header(
5051+
header.data(), header.data() + header.size(),
5052+
[&](const std::string &key, const std::string &val) {
5053+
file_.headers.emplace(key, val);
5054+
})) {
5055+
is_valid_ = false;
5056+
return false;
5057+
}
5058+
50485059
constexpr const char header_content_type[] = "Content-Type:";
50495060

50505061
if (start_with_case_ignore(header, header_content_type)) {
@@ -5144,6 +5155,7 @@ class MultipartFormDataParser {
51445155
file_.name.clear();
51455156
file_.filename.clear();
51465157
file_.content_type.clear();
5158+
file_.headers.clear();
51475159
}
51485160

51495161
bool start_with_case_ignore(const std::string &a, const char *b) const {

test/test.cc

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8010,6 +8010,68 @@ TEST(MultipartFormDataTest, ContentLength) {
80108010
ASSERT_TRUE(send_request(1, req, &response));
80118011
ASSERT_EQ("200", response.substr(9, 3));
80128012
}
8013+
8014+
TEST(MultipartFormDataTest, AccessPartHeaders) {
8015+
auto handled = false;
8016+
8017+
Server svr;
8018+
svr.Post("/test", [&](const Request &req, Response &) {
8019+
ASSERT_EQ(2u, req.files.size());
8020+
8021+
auto it = req.files.begin();
8022+
ASSERT_EQ("text1", it->second.name);
8023+
ASSERT_EQ("text1", it->second.content);
8024+
ASSERT_EQ(1, it->second.headers.count("Content-Length"));
8025+
auto content_length = it->second.headers.find("CONTENT-length");
8026+
ASSERT_EQ("5", content_length->second);
8027+
ASSERT_EQ(3, it->second.headers.size());
8028+
8029+
++it;
8030+
ASSERT_EQ("text2", it->second.name);
8031+
ASSERT_EQ("text2", it->second.content);
8032+
auto &headers = it->second.headers;
8033+
ASSERT_EQ(3, headers.size());
8034+
auto custom_header = headers.find("x-whatever");
8035+
ASSERT_TRUE(custom_header != headers.end());
8036+
ASSERT_NE("customvalue", custom_header->second);
8037+
ASSERT_EQ("CustomValue", custom_header->second);
8038+
ASSERT_TRUE(headers.find("X-Test") == headers.end()); // text1 header
8039+
8040+
handled = true;
8041+
});
8042+
8043+
thread t = thread([&] { svr.listen(HOST, PORT); });
8044+
auto se = detail::scope_exit([&] {
8045+
svr.stop();
8046+
t.join();
8047+
ASSERT_FALSE(svr.is_running());
8048+
ASSERT_TRUE(handled);
8049+
});
8050+
8051+
svr.wait_until_ready();
8052+
8053+
auto req = "POST /test HTTP/1.1\r\n"
8054+
"Content-Type: multipart/form-data;boundary=--------\r\n"
8055+
"Content-Length: 232\r\n"
8056+
"\r\n----------\r\n"
8057+
"Content-Disposition: form-data; name=\"text1\"\r\n"
8058+
"Content-Length: 5\r\n"
8059+
"X-Test: 1\r\n"
8060+
"\r\n"
8061+
"text1"
8062+
"\r\n----------\r\n"
8063+
"Content-Disposition: form-data; name=\"text2\"\r\n"
8064+
"Content-Type: text/plain\r\n"
8065+
"X-Whatever: CustomValue\r\n"
8066+
"\r\n"
8067+
"text2"
8068+
"\r\n------------\r\n"
8069+
"That should be disregarded. Not even read";
8070+
8071+
std::string response;
8072+
ASSERT_TRUE(send_request(1, req, &response));
8073+
ASSERT_EQ("200", response.substr(9, 3));
8074+
}
80138075
#endif
80148076

80158077
TEST(TaskQueueTest, IncreaseAtomicInteger) {

0 commit comments

Comments
 (0)