1
0
mirror of synced 2025-04-20 11:47:43 +03:00

Improved multipart form data interface

This commit is contained in:
yhirose 2019-12-12 22:44:54 -05:00
parent 151ccba57e
commit 5324b3d661
3 changed files with 34 additions and 40 deletions

View File

@ -119,15 +119,14 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
svr.Post("/content_receiver", svr.Post("/content_receiver",
[&](const Request &req, Response &res, const ContentReader &content_reader) { [&](const Request &req, Response &res, const ContentReader &content_reader) {
if (req.is_multipart_form_data()) { if (req.is_multipart_form_data()) {
MultipartFiles files; MultipartFormDataItems files;
content_reader( content_reader(
[&](const std::string &name, const MultipartFile &file) { [&](const MultipartFormData &file) {
files.emplace(name, file); files.push_back(file);
return true; return true;
}, },
[&](const std::string &name, const char *data, size_t data_length) { [&](const char *data, size_t data_length) {
auto &file = files.find(name)->second; files.back().content.append(data, data_length);
file.content.append(data, data_length);
return true; return true;
}); });
} else { } else {

View File

@ -224,20 +224,17 @@ using ContentReceiver =
std::function<bool(const char *data, size_t data_length)>; std::function<bool(const char *data, size_t data_length)>;
using MultipartContentHeader = using MultipartContentHeader =
std::function<bool(const std::string &name, const MultipartFormData &file)>; std::function<bool(const MultipartFormData &file)>;
using MultipartContentReceiver =
std::function<bool(const std::string& name, const char *data, size_t data_length)>;
class ContentReader { class ContentReader {
public: public:
using Reader = std::function<bool(ContentReceiver receiver)>; using Reader = std::function<bool(ContentReceiver receiver)>;
using MultipartReader = std::function<bool(MultipartContentHeader header, MultipartContentReceiver receiver)>; using MultipartReader = std::function<bool(MultipartContentHeader header, ContentReceiver receiver)>;
ContentReader(Reader reader, MultipartReader muitlpart_reader) ContentReader(Reader reader, MultipartReader muitlpart_reader)
: reader_(reader), muitlpart_reader_(muitlpart_reader) {} : reader_(reader), muitlpart_reader_(muitlpart_reader) {}
bool operator()(MultipartContentHeader header, MultipartContentReceiver receiver) const { bool operator()(MultipartContentHeader header, ContentReceiver receiver) const {
return muitlpart_reader_(header, receiver); return muitlpart_reader_(header, receiver);
} }
@ -590,12 +587,12 @@ private:
Request &req, Response &res, Request &req, Response &res,
ContentReceiver receiver, ContentReceiver receiver,
MultipartContentHeader multipart_header, MultipartContentHeader multipart_header,
MultipartContentReceiver multipart_receiver); ContentReceiver multipart_receiver);
bool read_content_core(Stream &strm, bool last_connection, bool read_content_core(Stream &strm, bool last_connection,
Request &req, Response &res, Request &req, Response &res,
ContentReceiver receiver, ContentReceiver receiver,
MultipartContentHeader mulitpart_header, MultipartContentHeader mulitpart_header,
MultipartContentReceiver multipart_receiver); ContentReceiver multipart_receiver);
virtual bool process_and_close_socket(socket_t sock); virtual bool process_and_close_socket(socket_t sock);
@ -2011,7 +2008,7 @@ public:
while (pos != std::string::npos) { while (pos != std::string::npos) {
// Empty line // Empty line
if (pos == 0) { if (pos == 0) {
if (!header_callback(name_, file_)) { if (!header_callback(file_)) {
is_valid_ = false; is_valid_ = false;
is_done_ = false; is_done_ = false;
return false; return false;
@ -2028,8 +2025,7 @@ public:
if (std::regex_match(header, m, re_content_type)) { if (std::regex_match(header, m, re_content_type)) {
file_.content_type = m[1]; file_.content_type = m[1];
} else if (std::regex_match(header, m, re_content_disposition)) { } else if (std::regex_match(header, m, re_content_disposition)) {
name_ = m[1]; file_.name = m[1];
file_.name = name_;
file_.filename = m[2]; file_.filename = m[2];
} }
} }
@ -2047,7 +2043,7 @@ public:
if (pos == std::string::npos) { if (pos == std::string::npos) {
pos = buf_.size(); pos = buf_.size();
} }
if (!content_callback(name_, buf_.data(), pos)) { if (!content_callback(buf_.data(), pos)) {
is_valid_ = false; is_valid_ = false;
is_done_ = false; is_done_ = false;
return false; return false;
@ -2063,7 +2059,7 @@ public:
auto pos = buf_.find(pattern); auto pos = buf_.find(pattern);
if (pos != std::string::npos) { if (pos != std::string::npos) {
if (!content_callback(name_, buf_.data(), pos)) { if (!content_callback(buf_.data(), pos)) {
is_valid_ = false; is_valid_ = false;
is_done_ = false; is_done_ = false;
return false; return false;
@ -2073,7 +2069,7 @@ public:
buf_.erase(0, pos + pattern.size()); buf_.erase(0, pos + pattern.size());
state_ = 4; state_ = 4;
} else { } else {
if (!content_callback(name_, buf_.data(), pattern.size())) { if (!content_callback(buf_.data(), pattern.size())) {
is_valid_ = false; is_valid_ = false;
is_done_ = false; is_done_ = false;
return false; return false;
@ -2118,7 +2114,7 @@ public:
private: private:
void clear_file_info() { void clear_file_info() {
name_.clear(); file_.name.clear();
file_.filename.clear(); file_.filename.clear();
file_.content_type.clear(); file_.content_type.clear();
} }
@ -2132,7 +2128,6 @@ private:
size_t is_valid_ = false; size_t is_valid_ = false;
size_t is_done_ = false; size_t is_done_ = false;
size_t off_ = 0; size_t off_ = 0;
std::string name_;
MultipartFormData file_; MultipartFormData file_;
}; };
@ -2973,6 +2968,7 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
inline bool Server::read_content(Stream &strm, bool last_connection, inline bool Server::read_content(Stream &strm, bool last_connection,
Request &req, Response &res) { Request &req, Response &res) {
MultipartFormDataMap::iterator cur;
auto ret = read_content_core(strm, last_connection, req, res, auto ret = read_content_core(strm, last_connection, req, res,
// Regular // Regular
[&](const char *buf, size_t n) { [&](const char *buf, size_t n) {
@ -2981,14 +2977,12 @@ inline bool Server::read_content(Stream &strm, bool last_connection,
return true; return true;
}, },
// Multipart // Multipart
[&](const std::string &name, const MultipartFormData &file) { [&](const MultipartFormData &file) {
req.files.emplace(name, file); cur = req.files.emplace(file.name, file);
return true; return true;
}, },
[&](const std::string &name, const char *buf, size_t n) { [&](const char *buf, size_t n) {
// TODO: handle elements with a same key auto &content = cur->second.content;
auto it = req.files.find(name);
auto &content = it->second.content;
if (content.size() + n > content.max_size()) { return false; } if (content.size() + n > content.max_size()) { return false; }
content.append(buf, n); content.append(buf, n);
return true; return true;
@ -3008,7 +3002,7 @@ Server::read_content_with_content_receiver(Stream &strm, bool last_connection,
Request &req, Response &res, Request &req, Response &res,
ContentReceiver receiver, ContentReceiver receiver,
MultipartContentHeader multipart_header, MultipartContentHeader multipart_header,
MultipartContentReceiver multipart_receiver) { ContentReceiver multipart_receiver) {
return read_content_core(strm, last_connection, req, res, return read_content_core(strm, last_connection, req, res,
receiver, multipart_header, multipart_receiver); receiver, multipart_header, multipart_receiver);
} }
@ -3018,7 +3012,7 @@ Server::read_content_core(Stream &strm, bool last_connection,
Request &req, Response &res, Request &req, Response &res,
ContentReceiver receiver, ContentReceiver receiver,
MultipartContentHeader mulitpart_header, MultipartContentHeader mulitpart_header,
MultipartContentReceiver multipart_receiver) { ContentReceiver multipart_receiver) {
detail::MultipartFormDataParser multipart_form_data_parser; detail::MultipartFormDataParser multipart_form_data_parser;
ContentReceiver out; ContentReceiver out;
@ -3181,7 +3175,7 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
return read_content_with_content_receiver(strm, last_connection, req, res, return read_content_with_content_receiver(strm, last_connection, req, res,
receiver, nullptr, nullptr); receiver, nullptr, nullptr);
}, },
[&](MultipartContentHeader header, MultipartContentReceiver receiver) { [&](MultipartContentHeader header, ContentReceiver receiver) {
return read_content_with_content_receiver(strm, last_connection, req, res, return read_content_with_content_receiver(strm, last_connection, req, res,
nullptr, header, receiver); nullptr, header, receiver);
} }

View File

@ -30,9 +30,11 @@ const std::string JSON_DATA = "{\"hello\":\"world\"}";
const string LARGE_DATA = string(1024 * 1024 * 100, '@'); // 100MB const string LARGE_DATA = string(1024 * 1024 * 100, '@'); // 100MB
MultipartFormData& get_file_value(MultipartFormDataMap &files, const char *key) { MultipartFormData& get_file_value(MultipartFormDataItems &files, const char *key) {
auto it = files.find(key); auto it = std::find_if(files.begin(), files.end(), [&](const MultipartFormData &file) {
if (it != files.end()) { return it->second; } return file.name == key;
});
if (it != files.end()) { return *it; }
throw std::runtime_error("invalid mulitpart form data name error"); throw std::runtime_error("invalid mulitpart form data name error");
} }
@ -801,15 +803,14 @@ protected:
.Post("/content_receiver", .Post("/content_receiver",
[&](const Request & req, Response &res, const ContentReader &content_reader) { [&](const Request & req, Response &res, const ContentReader &content_reader) {
if (req.is_multipart_form_data()) { if (req.is_multipart_form_data()) {
MultipartFormDataMap files; MultipartFormDataItems files;
content_reader( content_reader(
[&](const std::string &name, const MultipartFormData &file) { [&](const MultipartFormData &file) {
files.emplace(name, file); files.push_back(file);
return true; return true;
}, },
[&](const std::string &name, const char *data, size_t data_length) { [&](const char *data, size_t data_length) {
auto &file = files.find(name)->second; files.back().content.append(data, data_length);
file.content.append(data, data_length);
return true; return true;
}); });