Performance improvement for multipart form data file upload.

This commit is contained in:
yhirose 2021-12-17 23:36:26 -05:00
parent bc3e098964
commit 24a3ef949b

149
httplib.h
View File

@ -2874,7 +2874,8 @@ inline const char *status_message(int status) {
} }
inline bool can_compress_content_type(const std::string &content_type) { inline bool can_compress_content_type(const std::string &content_type) {
return (!content_type.find("text/") && content_type != "text/event-stream") || return (!content_type.rfind("text/", 0) &&
content_type != "text/event-stream") ||
content_type == "image/svg+xml" || content_type == "image/svg+xml" ||
content_type == "application/javascript" || content_type == "application/javascript" ||
content_type == "application/json" || content_type == "application/json" ||
@ -3681,17 +3682,15 @@ public:
static const std::string dash_ = "--"; static const std::string dash_ = "--";
static const std::string crlf_ = "\r\n"; static const std::string crlf_ = "\r\n";
buf_.append(buf, n); // TODO: performance improvement buf_append(buf, n);
while (!buf_.empty()) { while (buf_size() > 0) {
switch (state_) { switch (state_) {
case 0: { // Initial boundary case 0: { // Initial boundary
auto pattern = dash_ + boundary_ + crlf_; auto pattern = dash_ + boundary_ + crlf_;
if (pattern.size() > buf_.size()) { return true; } if (pattern.size() > buf_size()) { return true; }
auto pos = buf_.find(pattern); if (!buf_start_with(pattern)) { return false; }
if (pos != 0) { return false; } buf_erase(pattern.size());
buf_.erase(0, pattern.size());
off_ += pattern.size();
state_ = 1; state_ = 1;
break; break;
} }
@ -3701,22 +3700,21 @@ public:
break; break;
} }
case 2: { // Headers case 2: { // Headers
auto pos = buf_.find(crlf_); auto pos = buf_find(crlf_);
while (pos != std::string::npos) { while (pos < buf_size()) {
// Empty line // Empty line
if (pos == 0) { if (pos == 0) {
if (!header_callback(file_)) { if (!header_callback(file_)) {
is_valid_ = false; is_valid_ = false;
return false; return false;
} }
buf_.erase(0, crlf_.size()); buf_erase(crlf_.size());
off_ += crlf_.size();
state_ = 3; state_ = 3;
break; break;
} }
static const std::string header_name = "content-type:"; static const std::string header_name = "content-type:";
const auto header = buf_.substr(0, pos); const auto header = buf_head(pos);
if (start_with_case_ignore(header, header_name)) { if (start_with_case_ignore(header, header_name)) {
file_.content_type = trim_copy(header.substr(header_name.size())); file_.content_type = trim_copy(header.substr(header_name.size()));
} else { } else {
@ -3727,9 +3725,8 @@ public:
} }
} }
buf_.erase(0, pos + crlf_.size()); buf_erase(pos + crlf_.size());
off_ += pos + crlf_.size(); pos = buf_find(crlf_);
pos = buf_.find(crlf_);
} }
if (state_ != 3) { return true; } if (state_ != 3) { return true; }
break; break;
@ -3737,56 +3734,51 @@ public:
case 3: { // Body case 3: { // Body
{ {
auto pattern = crlf_ + dash_; auto pattern = crlf_ + dash_;
if (pattern.size() > buf_.size()) { return true; } if (pattern.size() > buf_size()) { return true; }
auto pos = find_string(buf_, pattern); auto pos = buf_find(pattern);
if (!content_callback(buf_.data(), pos)) { if (!content_callback(buf_data(), pos)) {
is_valid_ = false; is_valid_ = false;
return false; return false;
} }
off_ += pos; buf_erase(pos);
buf_.erase(0, pos);
} }
{ {
auto pattern = crlf_ + dash_ + boundary_; auto pattern = crlf_ + dash_ + boundary_;
if (pattern.size() > buf_.size()) { return true; } if (pattern.size() > buf_size()) { return true; }
auto pos = buf_.find(pattern); auto pos = buf_find(pattern);
if (pos != std::string::npos) { if (pos < buf_size()) {
if (!content_callback(buf_.data(), pos)) { if (!content_callback(buf_data(), pos)) {
is_valid_ = false; is_valid_ = false;
return false; return false;
} }
off_ += pos + pattern.size(); buf_erase(pos + pattern.size());
buf_.erase(0, pos + pattern.size());
state_ = 4; state_ = 4;
} else { } else {
if (!content_callback(buf_.data(), pattern.size())) { if (!content_callback(buf_data(), pattern.size())) {
is_valid_ = false; is_valid_ = false;
return false; return false;
} }
off_ += pattern.size(); buf_erase(pattern.size());
buf_.erase(0, pattern.size());
} }
} }
break; break;
} }
case 4: { // Boundary case 4: { // Boundary
if (crlf_.size() > buf_.size()) { return true; } if (crlf_.size() > buf_size()) { return true; }
if (buf_.compare(0, crlf_.size(), crlf_) == 0) { if (buf_start_with(crlf_)) {
buf_.erase(0, crlf_.size()); buf_erase(crlf_.size());
off_ += crlf_.size();
state_ = 1; state_ = 1;
} else { } else {
auto pattern = dash_ + crlf_; auto pattern = dash_ + crlf_;
if (pattern.size() > buf_.size()) { return true; } if (pattern.size() > buf_size()) { return true; }
if (buf_.compare(0, pattern.size(), pattern) == 0) { if (buf_start_with(pattern)) {
buf_.erase(0, pattern.size()); buf_erase(pattern.size());
off_ += pattern.size();
is_valid_ = true; is_valid_ = true;
state_ = 5; state_ = 5;
} else { } else {
@ -3821,41 +3813,80 @@ private:
return true; return true;
} }
bool start_with(const std::string &a, size_t off, std::string boundary_;
size_t state_ = 0;
bool is_valid_ = false;
MultipartFormData file_;
// Buffer
bool start_with(const std::string &a, size_t spos, size_t epos,
const std::string &b) const { const std::string &b) const {
if (a.size() - off < b.size()) { return false; } if (epos - spos < b.size()) { return false; }
for (size_t i = 0; i < b.size(); i++) { for (size_t i = 0; i < b.size(); i++) {
if (a[i + off] != b[i]) { return false; } if (a[i + spos] != b[i]) { return false; }
} }
return true; return true;
} }
size_t find_string(const std::string &s, const std::string &pattern) const { size_t buf_size() const { return buf_epos_ - buf_spos_; }
auto c = pattern.front();
size_t off = 0; const char *buf_data() const { return &buf_[buf_spos_]; }
while (off < s.size()) {
auto pos = s.find(c, off);
if (pos == std::string::npos) { return s.size(); }
auto rem = s.size() - pos; std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }
if (pattern.size() > rem) { return pos; }
if (start_with(s, pos, pattern)) { return pos; } bool buf_start_with(const std::string &s) const {
return start_with(buf_, buf_spos_, buf_epos_, s);
}
size_t buf_find(const std::string &s) const {
auto c = s.front();
size_t off = buf_spos_;
while (off < buf_epos_) {
auto pos = off;
while (true) {
if (pos == buf_epos_) { return buf_size(); }
if (buf_[pos] == c) { break; }
pos++;
}
auto remaining_size = buf_epos_ - pos;
if (s.size() > remaining_size) { return buf_size(); }
if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }
off = pos + 1; off = pos + 1;
} }
return s.size(); return buf_size();
} }
std::string boundary_; void buf_append(const char *data, size_t n) {
auto remaining_size = buf_size();
if (remaining_size > 0) {
for (size_t i = 0; i < remaining_size; i++) {
buf_[i] = buf_[buf_spos_ + i];
}
}
buf_spos_ = 0;
buf_epos_ = remaining_size;
if (remaining_size + n > buf_.size()) {
buf_.resize(remaining_size + n);
}
for (size_t i = 0; i < n; i++) {
buf_[buf_epos_ + i] = data[i];
}
buf_epos_ += n;
}
void buf_erase(size_t size) { buf_spos_ += size; }
std::string buf_; std::string buf_;
size_t state_ = 0; size_t buf_spos_ = 0;
bool is_valid_ = false; size_t buf_epos_ = 0;
size_t off_ = 0;
MultipartFormData file_;
}; };
inline std::string to_lower(const char *beg, const char *end) { inline std::string to_lower(const char *beg, const char *end) {
@ -4318,7 +4349,7 @@ inline size_t Request::get_param_value_count(const char *key) const {
inline bool Request::is_multipart_form_data() const { inline bool Request::is_multipart_form_data() const {
const auto &content_type = get_header_value("Content-Type"); const auto &content_type = get_header_value("Content-Type");
return !content_type.find("multipart/form-data"); return !content_type.rfind("multipart/form-data", 0);
} }
inline bool Request::has_file(const char *key) const { inline bool Request::has_file(const char *key) const {
@ -5122,9 +5153,7 @@ Server::create_server_socket(const char *host, int port, int socket_flags,
if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) { if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
return false; return false;
} }
if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { return false; }
return false;
}
return true; return true;
}); });
} }