Compare commits

..

3 Commits

Author SHA1 Message Date
yhirose
5324b3d661 Improved multipart form data interface 2019-12-12 22:48:09 -05:00
yhirose
151ccba57e Code cleanup 2019-12-12 21:50:12 -05:00
yhirose
69a28d50f6 Fix #287 2019-12-12 12:50:45 -05:00
4 changed files with 69 additions and 51 deletions

View File

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

View File

@@ -211,13 +211,6 @@ using Progress = std::function<bool(uint64_t current, uint64_t total)>;
struct Response;
using ResponseHandler = std::function<bool(const Response &response)>;
struct MultipartFile {
std::string filename;
std::string content_type;
std::string content;
};
using MultipartFiles = std::multimap<std::string, MultipartFile>;
struct MultipartFormData {
std::string name;
std::string content;
@@ -225,25 +218,23 @@ struct MultipartFormData {
std::string content_type;
};
using MultipartFormDataItems = std::vector<MultipartFormData>;
using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
using ContentReceiver =
std::function<bool(const char *data, size_t data_length)>;
using MultipartContentHeader =
std::function<bool(const std::string &name, const MultipartFile &file)>;
using MultipartContentReceiver =
std::function<bool(const std::string& name, const char *data, size_t data_length)>;
std::function<bool(const MultipartFormData &file)>;
class ContentReader {
public:
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)
: 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);
}
@@ -268,7 +259,7 @@ struct Request {
std::string version;
std::string target;
Params params;
MultipartFiles files;
MultipartFormDataMap files;
Ranges ranges;
Match matches;
@@ -295,7 +286,7 @@ struct Request {
bool is_multipart_form_data() const;
bool has_file(const char *key) const;
MultipartFile get_file_value(const char *key) const;
MultipartFormData get_file_value(const char *key) const;
// private members...
size_t content_length;
@@ -596,12 +587,12 @@ private:
Request &req, Response &res,
ContentReceiver receiver,
MultipartContentHeader multipart_header,
MultipartContentReceiver multipart_receiver);
ContentReceiver multipart_receiver);
bool read_content_core(Stream &strm, bool last_connection,
Request &req, Response &res,
ContentReceiver receiver,
MultipartContentHeader mulitpart_header,
MultipartContentReceiver multipart_receiver);
ContentReceiver multipart_receiver);
virtual bool process_and_close_socket(socket_t sock);
@@ -912,6 +903,8 @@ private:
};
#endif
// ----------------------------------------------------------------------------
/*
* Implementation
*/
@@ -2013,8 +2006,9 @@ public:
case 2: { // Headers
auto pos = buf_.find(crlf_);
while (pos != std::string::npos) {
// Empty line
if (pos == 0) {
if (!header_callback(name_, file_)) {
if (!header_callback(file_)) {
is_valid_ = false;
is_done_ = false;
return false;
@@ -2031,7 +2025,7 @@ public:
if (std::regex_match(header, m, re_content_type)) {
file_.content_type = m[1];
} else if (std::regex_match(header, m, re_content_disposition)) {
name_ = m[1];
file_.name = m[1];
file_.filename = m[2];
}
}
@@ -2049,7 +2043,7 @@ public:
if (pos == std::string::npos) {
pos = buf_.size();
}
if (!content_callback(name_, buf_.data(), pos)) {
if (!content_callback(buf_.data(), pos)) {
is_valid_ = false;
is_done_ = false;
return false;
@@ -2065,7 +2059,7 @@ public:
auto pos = buf_.find(pattern);
if (pos != std::string::npos) {
if (!content_callback(name_, buf_.data(), pos)) {
if (!content_callback(buf_.data(), pos)) {
is_valid_ = false;
is_done_ = false;
return false;
@@ -2075,7 +2069,7 @@ public:
buf_.erase(0, pos + pattern.size());
state_ = 4;
} else {
if (!content_callback(name_, buf_.data(), pattern.size())) {
if (!content_callback(buf_.data(), pattern.size())) {
is_valid_ = false;
is_done_ = false;
return false;
@@ -2120,7 +2114,7 @@ public:
private:
void clear_file_info() {
name_.clear();
file_.name.clear();
file_.filename.clear();
file_.content_type.clear();
}
@@ -2134,8 +2128,7 @@ private:
size_t is_valid_ = false;
size_t is_done_ = false;
size_t off_ = 0;
std::string name_;
MultipartFile file_;
MultipartFormData file_;
};
inline std::string to_lower(const char *beg, const char *end) {
@@ -2510,10 +2503,10 @@ inline bool Request::has_file(const char *key) const {
return files.find(key) != files.end();
}
inline MultipartFile Request::get_file_value(const char *key) const {
inline MultipartFormData Request::get_file_value(const char *key) const {
auto it = files.find(key);
if (it != files.end()) { return it->second; }
return MultipartFile();
return MultipartFormData();
}
// Response implementation
@@ -2975,6 +2968,7 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
inline bool Server::read_content(Stream &strm, bool last_connection,
Request &req, Response &res) {
MultipartFormDataMap::iterator cur;
auto ret = read_content_core(strm, last_connection, req, res,
// Regular
[&](const char *buf, size_t n) {
@@ -2983,14 +2977,12 @@ inline bool Server::read_content(Stream &strm, bool last_connection,
return true;
},
// Multipart
[&](const std::string &name, const MultipartFile &file) {
req.files.emplace(name, file);
[&](const MultipartFormData &file) {
cur = req.files.emplace(file.name, file);
return true;
},
[&](const std::string &name, const char *buf, size_t n) {
// TODO: handle elements with a same key
auto it = req.files.find(name);
auto &content = it->second.content;
[&](const char *buf, size_t n) {
auto &content = cur->second.content;
if (content.size() + n > content.max_size()) { return false; }
content.append(buf, n);
return true;
@@ -3010,7 +3002,7 @@ Server::read_content_with_content_receiver(Stream &strm, bool last_connection,
Request &req, Response &res,
ContentReceiver receiver,
MultipartContentHeader multipart_header,
MultipartContentReceiver multipart_receiver) {
ContentReceiver multipart_receiver) {
return read_content_core(strm, last_connection, req, res,
receiver, multipart_header, multipart_receiver);
}
@@ -3020,7 +3012,7 @@ Server::read_content_core(Stream &strm, bool last_connection,
Request &req, Response &res,
ContentReceiver receiver,
MultipartContentHeader mulitpart_header,
MultipartContentReceiver multipart_receiver) {
ContentReceiver multipart_receiver) {
detail::MultipartFormDataParser multipart_form_data_parser;
ContentReceiver out;
@@ -3183,7 +3175,7 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
return read_content_with_content_receiver(strm, last_connection, req, res,
receiver, nullptr, nullptr);
},
[&](MultipartContentHeader header, MultipartContentReceiver receiver) {
[&](MultipartContentHeader header, ContentReceiver receiver) {
return read_content_with_content_receiver(strm, last_connection, req, res,
nullptr, header, receiver);
}
@@ -4435,6 +4427,8 @@ inline bool SSLClient::check_host_name(const char *pattern,
}
#endif
// ----------------------------------------------------------------------------
} // namespace httplib
#endif // CPPHTTPLIB_HTTPLIB_H

24
split.py Normal file
View File

@@ -0,0 +1,24 @@
import os
border = '// ----------------------------------------------------------------------------'
with open('httplib.h') as f:
lines = f.readlines()
inImplementation = False
os.makedirs('out', exist_ok=True)
with open('out/httplib.h', 'w') as fh:
with open('out/httplib.cc', 'w') as fc:
fc.write('#include "httplib.h"\n')
fc.write('namespace httplib {\n')
for line in lines:
isBorderLine = border in line
if isBorderLine:
inImplementation = not inImplementation
else:
if inImplementation:
fc.write(line.replace('inline ', ''))
pass
else:
fh.write(line)
pass
fc.write('} // namespace httplib\n')

View File

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