Skip to content

Commit 5324b3d

Browse files
committed
Improved multipart form data interface
1 parent 151ccba commit 5324b3d

File tree

3 files changed

+34
-40
lines changed

3 files changed

+34
-40
lines changed

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,14 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
119119
svr.Post("/content_receiver",
120120
[&](const Request &req, Response &res, const ContentReader &content_reader) {
121121
if (req.is_multipart_form_data()) {
122-
MultipartFiles files;
122+
MultipartFormDataItems files;
123123
content_reader(
124-
[&](const std::string &name, const MultipartFile &file) {
125-
files.emplace(name, file);
124+
[&](const MultipartFormData &file) {
125+
files.push_back(file);
126126
return true;
127127
},
128-
[&](const std::string &name, const char *data, size_t data_length) {
129-
auto &file = files.find(name)->second;
130-
file.content.append(data, data_length);
128+
[&](const char *data, size_t data_length) {
129+
files.back().content.append(data, data_length);
131130
return true;
132131
});
133132
} else {

httplib.h

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -224,20 +224,17 @@ using ContentReceiver =
224224
std::function<bool(const char *data, size_t data_length)>;
225225

226226
using MultipartContentHeader =
227-
std::function<bool(const std::string &name, const MultipartFormData &file)>;
228-
229-
using MultipartContentReceiver =
230-
std::function<bool(const std::string& name, const char *data, size_t data_length)>;
227+
std::function<bool(const MultipartFormData &file)>;
231228

232229
class ContentReader {
233230
public:
234231
using Reader = std::function<bool(ContentReceiver receiver)>;
235-
using MultipartReader = std::function<bool(MultipartContentHeader header, MultipartContentReceiver receiver)>;
232+
using MultipartReader = std::function<bool(MultipartContentHeader header, ContentReceiver receiver)>;
236233

237234
ContentReader(Reader reader, MultipartReader muitlpart_reader)
238235
: reader_(reader), muitlpart_reader_(muitlpart_reader) {}
239236

240-
bool operator()(MultipartContentHeader header, MultipartContentReceiver receiver) const {
237+
bool operator()(MultipartContentHeader header, ContentReceiver receiver) const {
241238
return muitlpart_reader_(header, receiver);
242239
}
243240

@@ -590,12 +587,12 @@ class Server {
590587
Request &req, Response &res,
591588
ContentReceiver receiver,
592589
MultipartContentHeader multipart_header,
593-
MultipartContentReceiver multipart_receiver);
590+
ContentReceiver multipart_receiver);
594591
bool read_content_core(Stream &strm, bool last_connection,
595592
Request &req, Response &res,
596593
ContentReceiver receiver,
597594
MultipartContentHeader mulitpart_header,
598-
MultipartContentReceiver multipart_receiver);
595+
ContentReceiver multipart_receiver);
599596

600597
virtual bool process_and_close_socket(socket_t sock);
601598

@@ -2011,7 +2008,7 @@ class MultipartFormDataParser {
20112008
while (pos != std::string::npos) {
20122009
// Empty line
20132010
if (pos == 0) {
2014-
if (!header_callback(name_, file_)) {
2011+
if (!header_callback(file_)) {
20152012
is_valid_ = false;
20162013
is_done_ = false;
20172014
return false;
@@ -2028,8 +2025,7 @@ class MultipartFormDataParser {
20282025
if (std::regex_match(header, m, re_content_type)) {
20292026
file_.content_type = m[1];
20302027
} else if (std::regex_match(header, m, re_content_disposition)) {
2031-
name_ = m[1];
2032-
file_.name = name_;
2028+
file_.name = m[1];
20332029
file_.filename = m[2];
20342030
}
20352031
}
@@ -2047,7 +2043,7 @@ class MultipartFormDataParser {
20472043
if (pos == std::string::npos) {
20482044
pos = buf_.size();
20492045
}
2050-
if (!content_callback(name_, buf_.data(), pos)) {
2046+
if (!content_callback(buf_.data(), pos)) {
20512047
is_valid_ = false;
20522048
is_done_ = false;
20532049
return false;
@@ -2063,7 +2059,7 @@ class MultipartFormDataParser {
20632059

20642060
auto pos = buf_.find(pattern);
20652061
if (pos != std::string::npos) {
2066-
if (!content_callback(name_, buf_.data(), pos)) {
2062+
if (!content_callback(buf_.data(), pos)) {
20672063
is_valid_ = false;
20682064
is_done_ = false;
20692065
return false;
@@ -2073,7 +2069,7 @@ class MultipartFormDataParser {
20732069
buf_.erase(0, pos + pattern.size());
20742070
state_ = 4;
20752071
} else {
2076-
if (!content_callback(name_, buf_.data(), pattern.size())) {
2072+
if (!content_callback(buf_.data(), pattern.size())) {
20772073
is_valid_ = false;
20782074
is_done_ = false;
20792075
return false;
@@ -2118,7 +2114,7 @@ class MultipartFormDataParser {
21182114

21192115
private:
21202116
void clear_file_info() {
2121-
name_.clear();
2117+
file_.name.clear();
21222118
file_.filename.clear();
21232119
file_.content_type.clear();
21242120
}
@@ -2132,7 +2128,6 @@ class MultipartFormDataParser {
21322128
size_t is_valid_ = false;
21332129
size_t is_done_ = false;
21342130
size_t off_ = 0;
2135-
std::string name_;
21362131
MultipartFormData file_;
21372132
};
21382133

@@ -2973,6 +2968,7 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
29732968

29742969
inline bool Server::read_content(Stream &strm, bool last_connection,
29752970
Request &req, Response &res) {
2971+
MultipartFormDataMap::iterator cur;
29762972
auto ret = read_content_core(strm, last_connection, req, res,
29772973
// Regular
29782974
[&](const char *buf, size_t n) {
@@ -2981,14 +2977,12 @@ inline bool Server::read_content(Stream &strm, bool last_connection,
29812977
return true;
29822978
},
29832979
// Multipart
2984-
[&](const std::string &name, const MultipartFormData &file) {
2985-
req.files.emplace(name, file);
2980+
[&](const MultipartFormData &file) {
2981+
cur = req.files.emplace(file.name, file);
29862982
return true;
29872983
},
2988-
[&](const std::string &name, const char *buf, size_t n) {
2989-
// TODO: handle elements with a same key
2990-
auto it = req.files.find(name);
2991-
auto &content = it->second.content;
2984+
[&](const char *buf, size_t n) {
2985+
auto &content = cur->second.content;
29922986
if (content.size() + n > content.max_size()) { return false; }
29932987
content.append(buf, n);
29942988
return true;
@@ -3008,7 +3002,7 @@ Server::read_content_with_content_receiver(Stream &strm, bool last_connection,
30083002
Request &req, Response &res,
30093003
ContentReceiver receiver,
30103004
MultipartContentHeader multipart_header,
3011-
MultipartContentReceiver multipart_receiver) {
3005+
ContentReceiver multipart_receiver) {
30123006
return read_content_core(strm, last_connection, req, res,
30133007
receiver, multipart_header, multipart_receiver);
30143008
}
@@ -3018,7 +3012,7 @@ Server::read_content_core(Stream &strm, bool last_connection,
30183012
Request &req, Response &res,
30193013
ContentReceiver receiver,
30203014
MultipartContentHeader mulitpart_header,
3021-
MultipartContentReceiver multipart_receiver) {
3015+
ContentReceiver multipart_receiver) {
30223016
detail::MultipartFormDataParser multipart_form_data_parser;
30233017
ContentReceiver out;
30243018

@@ -3181,7 +3175,7 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
31813175
return read_content_with_content_receiver(strm, last_connection, req, res,
31823176
receiver, nullptr, nullptr);
31833177
},
3184-
[&](MultipartContentHeader header, MultipartContentReceiver receiver) {
3178+
[&](MultipartContentHeader header, ContentReceiver receiver) {
31853179
return read_content_with_content_receiver(strm, last_connection, req, res,
31863180
nullptr, header, receiver);
31873181
}

test/test.cc

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ const std::string JSON_DATA = "{\"hello\":\"world\"}";
3030

3131
const string LARGE_DATA = string(1024 * 1024 * 100, '@'); // 100MB
3232

33-
MultipartFormData& get_file_value(MultipartFormDataMap &files, const char *key) {
34-
auto it = files.find(key);
35-
if (it != files.end()) { return it->second; }
33+
MultipartFormData& get_file_value(MultipartFormDataItems &files, const char *key) {
34+
auto it = std::find_if(files.begin(), files.end(), [&](const MultipartFormData &file) {
35+
return file.name == key;
36+
});
37+
if (it != files.end()) { return *it; }
3638
throw std::runtime_error("invalid mulitpart form data name error");
3739
}
3840

@@ -801,15 +803,14 @@ class ServerTest : public ::testing::Test {
801803
.Post("/content_receiver",
802804
[&](const Request & req, Response &res, const ContentReader &content_reader) {
803805
if (req.is_multipart_form_data()) {
804-
MultipartFormDataMap files;
806+
MultipartFormDataItems files;
805807
content_reader(
806-
[&](const std::string &name, const MultipartFormData &file) {
807-
files.emplace(name, file);
808+
[&](const MultipartFormData &file) {
809+
files.push_back(file);
808810
return true;
809811
},
810-
[&](const std::string &name, const char *data, size_t data_length) {
811-
auto &file = files.find(name)->second;
812-
file.content.append(data, data_length);
812+
[&](const char *data, size_t data_length) {
813+
files.back().content.append(data, data_length);
813814
return true;
814815
});
815816

0 commit comments

Comments
 (0)