Refine REST support; add demo for REST client and server.
This commit is contained in:
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user