improve the parsing of form data
This commit is contained in:
@@ -8,13 +8,14 @@
|
|||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
if (argc != 3) {
|
if (argc != 3) {
|
||||||
std::cout << "usage: file_downloader <url> <path>" << std::endl;
|
std::cout << "Usage: file_downloader <url> <path>" << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "examples:" << std::endl;
|
std::cout << "Examples:" << std::endl;
|
||||||
std::cout << " $ file_downloader http://httpbin.org/image/jpeg D:/test.jpg"
|
std::cout
|
||||||
|
<< " $ ./file_downloader http://httpbin.org/image/jpeg ~/test.jpg"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
std::cout << " $ file_downloader https://www.google.com/favicon.ico"
|
std::cout << " $ ./file_downloader https://www.google.com/favicon.ico"
|
||||||
<< " D:/test.ico" << std::endl;
|
<< " ~/test.ico" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-9
@@ -11,14 +11,19 @@ namespace bfs = boost::filesystem;
|
|||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
std::cout << "usage: form_client <upload_dir> [url]" << std::endl;
|
std::cout << "Usage: form_client <upload_dir> [url]" << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "default url: http://httpbin.org/post" << std::endl;
|
std::cout << "Default url: http://httpbin.org/post" << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "examples:" << std::endl;
|
std::cout << "Examples:" << std::endl;
|
||||||
std::cout << " $ form_client E:/github/webcc/data/upload" << std::endl;
|
std::cout << "(Post to httpbin.org)" << std::endl;
|
||||||
std::cout << " $ form_client E:/github/webcc/data/upload "
|
std::cout << " $ ./form_client path/to/webcc/data/upload" << std::endl;
|
||||||
|
std::cout << " $ ./form_client path/to/webcc/data/upload "
|
||||||
<< "http://httpbin.org/post" << std::endl;
|
<< "http://httpbin.org/post" << std::endl;
|
||||||
|
std::cout << "(Post the example 'form_server')" << std::endl;
|
||||||
|
std::cout << " $ ./form_client path/to/webcc/data/upload "
|
||||||
|
"http://localhost:8080/upload"
|
||||||
|
<< std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,10 +46,10 @@ int main(int argc, char* argv[]) {
|
|||||||
webcc::ClientSession session;
|
webcc::ClientSession session;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto r = session.Send(webcc::RequestBuilder{}.Post(url).
|
auto r = session.Send(webcc::RequestBuilder{}
|
||||||
FormFile("file", upload_dir / "remember.txt").
|
.Post(url)
|
||||||
FormData("json", "{}", "application/json")
|
.FormFile("file", upload_dir / "remember.txt")
|
||||||
());
|
.FormData("json", "{}", "application/json")());
|
||||||
|
|
||||||
std::cout << r->status() << std::endl;
|
std::cout << r->status() << std::endl;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// A server handling multipart form data.
|
// A server handling multipart form data.
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -25,7 +26,13 @@ private:
|
|||||||
|
|
||||||
for (auto& part : request->form_parts()) {
|
for (auto& part : request->form_parts()) {
|
||||||
std::cout << "name: " << part->name() << std::endl;
|
std::cout << "name: " << part->name() << std::endl;
|
||||||
std::cout << "data: " << std::endl << part->data() << std::endl;
|
|
||||||
|
if (part->file_name().empty()) {
|
||||||
|
std::cout << "data: " << part->data() << std::endl;
|
||||||
|
} else {
|
||||||
|
// Save part->data() as binary to file.
|
||||||
|
// ...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return webcc::ResponseBuilder{}.Created().Body("OK")();
|
return webcc::ResponseBuilder{}.Created().Body("OK")();
|
||||||
@@ -36,7 +43,7 @@ private:
|
|||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
std::cout << "usage: file_upload_server <port>" << std::endl;
|
std::cout << "usage: form_server <port>" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ protected:
|
|||||||
|
|
||||||
bool ParseHeaderLine(const std::string& line);
|
bool ParseHeaderLine(const std::string& line);
|
||||||
|
|
||||||
|
// Parse the given length of data.
|
||||||
virtual bool ParseContent(const char* data, std::size_t length);
|
virtual bool ParseContent(const char* data, std::size_t length);
|
||||||
|
|
||||||
bool ParseFixedContent(const char* data, std::size_t length);
|
bool ParseFixedContent(const char* data, std::size_t length);
|
||||||
|
|||||||
@@ -50,14 +50,10 @@ bool RequestParser::ParseStartLine(const std::string& line) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool RequestParser::ParseContent(const char* data, std::size_t length) {
|
bool RequestParser::ParseContent(const char* data, std::size_t length) {
|
||||||
if (chunked_) {
|
|
||||||
return ParseChunkedContent(data, length);
|
|
||||||
} else {
|
|
||||||
if (content_type_.multipart()) {
|
if (content_type_.multipart()) {
|
||||||
return ParseMultipartContent(data, length);
|
return ParseMultipartContent(data, length);
|
||||||
} else {
|
} else {
|
||||||
return ParseFixedContent(data, length);
|
return Parser::ParseContent(data, length);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,13 +111,18 @@ bool RequestParser::ParseMultipartContent(const char* data,
|
|||||||
std::size_t off = 0;
|
std::size_t off = 0;
|
||||||
std::size_t count = 0;
|
std::size_t count = 0;
|
||||||
bool ended = false;
|
bool ended = false;
|
||||||
|
|
||||||
// TODO: Remember last CRLF position.
|
// TODO: Remember last CRLF position.
|
||||||
if (!GetNextBoundaryLine(&off, &count, &ended)) {
|
|
||||||
// Wait until next boundary.
|
bool next_boundary_found = GetNextBoundaryLine(&off, &count, &ended);
|
||||||
|
|
||||||
|
if (!next_boundary_found) {
|
||||||
|
part_->AppendData(pending_data_);
|
||||||
|
pending_data_.clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("Next boundary found.");
|
// Next boundary found.
|
||||||
|
|
||||||
// This part has ended.
|
// This part has ended.
|
||||||
if (off > 2) {
|
if (off > 2) {
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ private:
|
|||||||
// asks for data streaming.
|
// asks for data streaming.
|
||||||
bool OnHeadersEnd() override;
|
bool OnHeadersEnd() override;
|
||||||
|
|
||||||
bool Stream() const;
|
|
||||||
|
|
||||||
bool ParseStartLine(const std::string& line) override;
|
bool ParseStartLine(const std::string& line) override;
|
||||||
|
|
||||||
// Override to handle multipart form data which is request only.
|
// Override to handle multipart form data which is request only.
|
||||||
@@ -51,13 +49,14 @@ private:
|
|||||||
// received. The parsing will stop and fail if no view can be matched.
|
// received. The parsing will stop and fail if no view can be matched.
|
||||||
ViewMatcher view_matcher_;
|
ViewMatcher view_matcher_;
|
||||||
|
|
||||||
// Form data parsing step.
|
// Form data parsing steps.
|
||||||
enum Step {
|
enum Step {
|
||||||
kStart,
|
kStart,
|
||||||
kBoundaryParsed,
|
kBoundaryParsed,
|
||||||
kHeadersParsed,
|
kHeadersParsed,
|
||||||
kEnded,
|
kEnded,
|
||||||
};
|
};
|
||||||
|
|
||||||
Step step_ = kStart;
|
Step step_ = kStart;
|
||||||
|
|
||||||
// The current form part being parsed.
|
// The current form part being parsed.
|
||||||
|
|||||||
Reference in New Issue
Block a user