Refine REST support; add demo for REST client and server.

This commit is contained in:
Adam Gu
2018-04-04 17:26:27 +08:00
parent e04565a468
commit c557430b8e
16 changed files with 368 additions and 26 deletions
+4 -5
View File
@@ -28,7 +28,7 @@ public:
start_line_ = start_line;
}
size_t content_length() const {
std::size_t content_length() const {
return content_length_;
}
@@ -43,7 +43,7 @@ public:
SetHeader(kContentType, content_type);
}
void SetContentLength(size_t content_length) {
void SetContentLength(std::size_t content_length) {
content_length_ = content_length;
SetHeader(kContentLength, std::to_string(content_length));
}
@@ -53,7 +53,7 @@ public:
content_ = std::move(content);
}
void AppendContent(const char* data, size_t count) {
void AppendContent(const char* data, std::size_t count) {
content_.append(data, count);
}
@@ -62,8 +62,7 @@ public:
}
bool IsContentFull() const {
assert(IsContentLengthValid());
return content_.length() >= content_length_;
return IsContentLengthValid() && content_.length() >= content_length_;
}
bool IsContentLengthValid() const {
+21 -11
View File
@@ -10,11 +10,12 @@ namespace webcc {
HttpParser::HttpParser(HttpMessage* message)
: message_(message)
, start_line_parsed_(false)
, content_length_parsed_(false)
, header_parsed_(false)
, finished_(false) {
}
Error HttpParser::Parse(const char* data, size_t len) {
Error HttpParser::Parse(const char* data, std::size_t len) {
if (header_parsed_) {
// Add the data to the content.
message_->AppendContent(data, len);
@@ -28,10 +29,10 @@ Error HttpParser::Parse(const char* data, size_t len) {
}
pending_data_.append(data, len);
size_t off = 0;
std::size_t off = 0;
while (true) {
size_t pos = pending_data_.find("\r\n", off);
std::size_t pos = pending_data_.find("\r\n", off);
if (pos == std::string::npos) {
break;
}
@@ -52,8 +53,8 @@ Error HttpParser::Parse(const char* data, size_t len) {
}
} else {
// Currently, only Content-Length is important to us.
// Other fields are ignored.
if (!message_->IsContentLengthValid()) {
// Other header fields are ignored.
if (!content_length_parsed_) {
ParseContentLength(line);
}
}
@@ -64,9 +65,16 @@ Error HttpParser::Parse(const char* data, size_t len) {
if (header_parsed_) {
// Headers just ended.
if (!message_->IsContentLengthValid()) {
// No Content-Length?
return kHttpContentLengthError;
if (!content_length_parsed_) {
// No Content-Length, no content.
message_->SetContentLength(0);
finished_ = true;
return kNoError;
} else {
// Invalid Content-Length in the request.
if (!message_->IsContentLengthValid()) {
return kHttpContentLengthError;
}
}
message_->AppendContent(pending_data_.substr(off));
@@ -84,7 +92,7 @@ Error HttpParser::Parse(const char* data, size_t len) {
}
void HttpParser::ParseContentLength(const std::string& line) {
size_t pos = line.find(':');
std::size_t pos = line.find(':');
if (pos == std::string::npos) {
return;
}
@@ -92,6 +100,8 @@ void HttpParser::ParseContentLength(const std::string& line) {
std::string name = line.substr(0, pos);
if (boost::iequals(name, kContentLength)) {
content_length_parsed_ = true;
++pos; // Skip ':'.
while (line[pos] == ' ') { // Skip spaces.
++pos;
@@ -100,9 +110,9 @@ void HttpParser::ParseContentLength(const std::string& line) {
std::string value = line.substr(pos);
try {
message_->SetContentLength(boost::lexical_cast<size_t>(value));
std::size_t length = boost::lexical_cast<std::size_t>(value);
message_->SetContentLength(length);
} catch (boost::bad_lexical_cast&) {
// TODO
}
}
}
+2 -1
View File
@@ -17,7 +17,7 @@ public:
return finished_;
}
Error Parse(const char* data, size_t len);
Error Parse(const char* data, std::size_t len);
protected:
// Parse HTTP start line.
@@ -36,6 +36,7 @@ protected:
// Parsing helper flags.
bool start_line_parsed_;
bool content_length_parsed_;
bool header_parsed_;
bool finished_;
};
+4 -3
View File
@@ -29,7 +29,7 @@ void HttpRequest::SetHost(const std::string& host, const std::string& port) {
}
}
void HttpRequest::MakeStartLine() {
void HttpRequest::Build() {
if (start_line_.empty()) {
start_line_ = method_;
start_line_ += " ";
@@ -48,7 +48,6 @@ const char CRLF[] = { '\r', '\n' };
// ATTENTION: The buffers don't hold the memory!
std::vector<boost::asio::const_buffer> HttpRequest::ToBuffers() const {
assert(!start_line_.empty());
assert(IsContentLengthValid());
std::vector<boost::asio::const_buffer> buffers;
@@ -63,7 +62,9 @@ std::vector<boost::asio::const_buffer> HttpRequest::ToBuffers() const {
buffers.push_back(boost::asio::buffer(misc_strings::CRLF));
buffers.push_back(boost::asio::buffer(content_));
if (content_length_ > 0) {
buffers.push_back(boost::asio::buffer(content_));
}
return buffers;
}
+3 -3
View File
@@ -51,13 +51,13 @@ public:
// \param port Numeric port number, "80" will be used if it's empty.
void SetHost(const std::string& host, const std::string& port);
// Compose start line from method, url, etc.
void MakeStartLine();
// Compose start line, etc.
// Must be called before ToBuffers()!
void Build();
// Convert the response into a vector of buffers. The buffers do not own the
// underlying memory blocks, therefore the request object must remain valid
// and not be changed until the write operation has completed.
// NOTE: Please call MakeStartLine() before.
std::vector<boost::asio::const_buffer> ToBuffers() const;
private:
+1
View File
@@ -87,6 +87,7 @@ HttpStatus::Enum RestRequestHandler::HandleSession(HttpSessionPtr session) {
// TODO: Error handling.
std::string content;
service->Handle(session->request().method(),
sub_matches,
session->request().content(),
&content);
+5 -3
View File
@@ -1,8 +1,9 @@
#ifndef WEBCC_REST_SERVICE_H_
#define WEBCC_REST_SERVICE_H_
#include <string>
#include <memory>
#include <string>
#include <vector>
#include "webcc/common.h"
@@ -18,8 +19,9 @@ public:
// Both the request and response parameters should be JSON.
// TODO: Query parameters.
virtual bool Handle(const std::string& http_method,
const std::string& request,
std::string* response) = 0;
const std::vector<std::string>& url_sub_matches,
const std::string& request_content,
std::string* response_content) = 0;
};
typedef std::shared_ptr<RestService> RestServicePtr;