Fix multipart form data parsing

This commit is contained in:
Chunting Gu
2019-04-18 16:20:45 +08:00
parent e621025cc3
commit 4721d50363
9 changed files with 142 additions and 63 deletions
+4 -25
View File
@@ -169,31 +169,8 @@ public:
FormPart(const std::string& name, std::string&& data,
const std::string& media_type = "");
#if WEBCC_DEFAULT_MOVE_COPY_ASSIGN
FormPart(FormPart&&) = default;
FormPart& operator=(FormPart&&) = default;
#else
FormPart(FormPart&& rhs)
: name_(std::move(rhs.name_)),
file_name_(std::move(rhs.file_name_)),
media_type_(std::move(rhs.media_type_)),
data_(std::move(rhs.data_)) {
}
FormPart& operator=(FormPart&& rhs) {
if (&rhs != this) {
name_ = std::move(rhs.name_);
file_name_ = std::move(rhs.file_name_);
media_type_ = std::move(rhs.media_type_);
data_ = std::move(rhs.data_);
}
return *this;
}
#endif // WEBCC_DEFAULT_MOVE_COPY_ASSIGN
FormPart(const FormPart&) = delete;
FormPart& operator=(const FormPart&) = delete;
// API: SERVER
const std::string& name() const {
@@ -266,6 +243,8 @@ private:
std::string data_;
};
using FormPartPtr = std::shared_ptr<FormPart>;
} // namespace webcc
#endif // WEBCC_COMMON_H_
+6 -1
View File
@@ -2,6 +2,7 @@
#include "webcc/logger.h"
#include "webcc/utility.h"
#include <iostream>
namespace webcc {
@@ -52,7 +53,7 @@ void Request::Prepare() {
data_payload.push_back(buffer(boundary_));
data_payload.push_back(buffer(misc_strings::CRLF));
part.Prepare(&data_payload);
part->Prepare(&data_payload);
}
// Boundary end
@@ -73,6 +74,10 @@ void Request::Prepare() {
// Append payload of content data.
payload_.insert(payload_.end(), data_payload.begin(), data_payload.end());
std::string str;
CopyPayload(&str);
std::cout << str;
}
void Request::CreateStartLine() {
+5 -5
View File
@@ -55,16 +55,16 @@ public:
return port().empty() ? default_port : port();
}
const std::vector<FormPart>& form_parts() const {
const std::vector<FormPartPtr>& form_parts() const {
return form_parts_;
}
void set_form_parts(std::vector<FormPart>&& form_parts) {
void set_form_parts(std::vector<FormPartPtr>&& form_parts) {
form_parts_ = std::move(form_parts);
}
void AddFormPart(FormPart&& form_part) {
form_parts_.push_back(std::move(form_part));
void AddFormPart(FormPartPtr form_part) {
form_parts_.push_back(form_part);
}
// Prepare payload.
@@ -79,7 +79,7 @@ private:
Url url_;
// Files to upload for a POST request.
std::vector<FormPart> form_parts_;
std::vector<FormPartPtr> form_parts_;
std::string boundary_;
};
+5 -3
View File
@@ -7,7 +7,7 @@
namespace webcc {
RequestPtr RequestBuilder::Build() {
RequestPtr RequestBuilder::operator()() {
assert(parameters_.size() % 2 == 0);
assert(headers_.size() % 2 == 0);
@@ -44,7 +44,8 @@ RequestBuilder& RequestBuilder::File(const std::string& name,
const Path& path,
const std::string& media_type) {
assert(!name.empty());
form_parts_.push_back(FormPart{ name, path, media_type });
auto part = std::make_shared<FormPart>(name, path, media_type);
form_parts_.push_back(part);
return *this;
}
@@ -52,7 +53,8 @@ RequestBuilder& RequestBuilder::Form(const std::string& name,
std::string&& data,
const std::string& media_type) {
assert(!name.empty());
form_parts_.push_back(FormPart{ name, std::move(data), media_type });
auto part = std::make_shared<FormPart>(name, std::move(data), media_type);
form_parts_.push_back(part);
return *this;
}
+5 -12
View File
@@ -10,21 +10,14 @@ namespace webcc {
class RequestBuilder {
public:
explicit RequestBuilder(const std::string& method = "")
: method_(method) {
}
RequestBuilder() = default;
~RequestBuilder() = default;
RequestBuilder(const RequestBuilder&) = delete;
RequestBuilder& operator=(const RequestBuilder&) = delete;
// Build the request.
RequestPtr Build();
RequestPtr operator()() {
return Build();
}
RequestPtr operator()();
RequestBuilder& Get() { return Method(methods::kGet); }
RequestBuilder& Head() { return Method(methods::kHead); }
@@ -72,8 +65,8 @@ public:
RequestBuilder& File(const std::string& name, const Path& path,
const std::string& media_type = "");
RequestBuilder& Form(FormPart&& part) {
form_parts_.push_back(std::move(part));
RequestBuilder& Form(FormPartPtr part) {
form_parts_.push_back(part);
return *this;
}
@@ -121,7 +114,7 @@ private:
bool json_ = false;
// Files to upload for a POST request.
std::vector<FormPart> form_parts_;
std::vector<FormPartPtr> form_parts_;
// Compress the request content.
// NOTE: Most servers don't support compressed requests.
+25 -13
View File
@@ -47,12 +47,12 @@ bool RequestParser::ParseContent(const char* data, std::size_t length) {
}
bool RequestParser::ParseMultipartContent(const char* data,
std::size_t length) {
std::size_t length) {
// Append the new data to the pending data.
// NOTE: It's more difficult to avoid this than normal fixed-length content.
pending_data_.append(data, length);
LOG_VERB("Parse multipart content (pending data size: %u).",
LOG_VERB("Parse multipart content (3pending data size: %u).",
pending_data_.size());
if (!content_length_parsed_ || content_length_ == kInvalidLength) {
@@ -82,6 +82,9 @@ bool RequestParser::ParseMultipartContent(const char* data,
}
if (step_ == Step::kBoundaryParsed) {
if (!part_) {
part_.reset(new FormPart{});
}
bool need_more_data = false;
if (ParsePartHeaders(&need_more_data)) {
// Go to next step.
@@ -103,9 +106,7 @@ bool RequestParser::ParseMultipartContent(const char* data,
std::size_t count = 0;
bool ended = false;
if (!GetNextBoundaryLine(&off, &count, &ended)) {
// All pending data belongs to this part.
part_.AppendData(pending_data_);
pending_data_.clear();
// Wait until next boundary.
break;
}
@@ -113,11 +114,22 @@ bool RequestParser::ParseMultipartContent(const char* data,
// This part has ended.
if (off > 2) {
// -2 for exluding the CRLF after the data.
part_.AppendData(pending_data_.data(), off - 2);
// -2 for excluding the CRLF after the data.
part_->AppendData(pending_data_.data(), off - 2);
// Erase the data of this part and the next boundary.
// +2 for including the CRLF after the boundary.
pending_data_.erase(0, off + count + 2);
} else {
LOG_ERRO("Invalid part data.");
return false;
}
request_->AddFormPart(std::move(part_));
// Add this part to request.
request_->AddFormPart(part_);
// Reset for next part.
part_.reset();
if (ended) {
// Go to the end step.
@@ -174,10 +186,10 @@ bool RequestParser::ParsePartHeaders(bool* need_more_data) {
header.second.c_str());
return false;
}
part_.set_name(content_disposition.name());
part_.set_file_name(content_disposition.file_name());
part_->set_name(content_disposition.name());
part_->set_file_name(content_disposition.file_name());
LOG_INFO("Content-Disposition (name=%s; filename=%s)",
part_.name().c_str(), part_.file_name().c_str());
part_->name().c_str(), part_->file_name().c_str());
}
// TODO: Parse other headers.
@@ -190,8 +202,8 @@ bool RequestParser::ParsePartHeaders(bool* need_more_data) {
}
bool RequestParser::GetNextBoundaryLine(std::size_t* b_off,
std::size_t* b_count,
bool* ended) {
std::size_t* b_count,
bool* ended) {
std::size_t off = 0;
while (true) {
+1 -1
View File
@@ -43,7 +43,7 @@ private:
};
Step step_ = kStart;
FormPart part_;
FormPartPtr part_;
};
} // namespace webcc