Refine HTTP request and response.
This commit is contained in:
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
#include "boost/asio/io_context.hpp"
|
#include "boost/asio/io_context.hpp"
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
#include "webcc/async_http_client.h"
|
#include "webcc/async_http_client.h"
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
// In order to test this client, create a file index.html whose content is
|
// In order to test this client, create a file index.html whose content is
|
||||||
// simply "Hello, World!", then start a HTTP server with Python 3:
|
// simply "Hello, World!", then start a HTTP server with Python 3:
|
||||||
@@ -11,13 +11,12 @@
|
|||||||
// The default port number should be 8000.
|
// The default port number should be 8000.
|
||||||
|
|
||||||
void Test(boost::asio::io_context& ioc) {
|
void Test(boost::asio::io_context& ioc) {
|
||||||
std::shared_ptr<webcc::HttpRequest> request(new webcc::HttpRequest());
|
webcc::HttpRequestPtr request(new webcc::HttpRequest());
|
||||||
|
|
||||||
request->set_method(webcc::kHttpGet);
|
request->set_method(webcc::kHttpGet);
|
||||||
request->set_url("/index.html");
|
request->set_url("/index.html");
|
||||||
request->SetHost("localhost", "8000");
|
request->SetHost("localhost", "8000");
|
||||||
|
request->UpdateStartLine();
|
||||||
request->Build();
|
|
||||||
|
|
||||||
webcc::HttpAsyncClientPtr client(new webcc::AsyncHttpClient(ioc));
|
webcc::HttpAsyncClientPtr client(new webcc::AsyncHttpClient(ioc));
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
#include "webcc/http_client.h"
|
#include "webcc/http_client.h"
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
// In order to test this client, create a file index.html whose content is
|
// In order to test this client, create a file index.html whose content is
|
||||||
// simply "Hello, World!", then start a HTTP server with Python 3:
|
// simply "Hello, World!", then start a HTTP server with Python 3:
|
||||||
@@ -10,21 +10,21 @@
|
|||||||
|
|
||||||
void Test() {
|
void Test() {
|
||||||
webcc::HttpRequest request;
|
webcc::HttpRequest request;
|
||||||
|
|
||||||
request.set_method(webcc::kHttpGet);
|
request.set_method(webcc::kHttpGet);
|
||||||
request.set_url("/index.html");
|
request.set_url("/index.html");
|
||||||
request.SetHost("localhost", "8000");
|
request.SetHost("localhost", "8000");
|
||||||
|
request.UpdateStartLine();
|
||||||
request.Build();
|
|
||||||
|
|
||||||
webcc::HttpResponse response;
|
|
||||||
|
|
||||||
webcc::HttpClient client;
|
webcc::HttpClient client;
|
||||||
if (!client.Request(request)) {
|
if (client.Request(request)) {
|
||||||
return;
|
std::cout << client.response()->content() << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << webcc::DescribeError(client.error());
|
||||||
|
if (client.timed_out()) {
|
||||||
|
std::cout << " (timed out)";
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << response.content() << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
#include "json/json.h"
|
#include "json/json.h"
|
||||||
|
|
||||||
#include "webcc/logger.h"
|
|
||||||
#include "webcc/async_rest_client.h"
|
#include "webcc/async_rest_client.h"
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -118,7 +118,11 @@ int main(int argc, char* argv[]) {
|
|||||||
if (error == webcc::kNoError) {
|
if (error == webcc::kNoError) {
|
||||||
std::cout << response->content() << std::endl;
|
std::cout << response->content() << std::endl;
|
||||||
} else {
|
} else {
|
||||||
std::cout << webcc::DescribeError(error) << std::endl;
|
std::cout << webcc::DescribeError(error);
|
||||||
|
if (timed_out) {
|
||||||
|
std::cout << " (timed out)";
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ void AsyncRestClient::Request(const std::string& method,
|
|||||||
request->SetContent(content);
|
request->SetContent(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
request->Build();
|
request->UpdateStartLine();
|
||||||
|
|
||||||
HttpAsyncClientPtr http_client(new AsyncHttpClient(io_context_));
|
HttpAsyncClientPtr http_client(new AsyncHttpClient(io_context_));
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ namespace webcc {
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Constants
|
// Constants
|
||||||
|
|
||||||
|
const char* const CRLF = "\r\n";
|
||||||
|
|
||||||
// Default buffer size for socket reading.
|
// Default buffer size for socket reading.
|
||||||
const std::size_t kBufferSize = 1024;
|
const std::size_t kBufferSize = 1024;
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ void HttpConnection::Start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnection::Close() {
|
void HttpConnection::Close() {
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ignored_ec;
|
||||||
socket_.close(ec);
|
socket_.close(ignored_ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnection::SetResponseContent(std::string&& content,
|
void HttpConnection::SetResponseContent(std::string&& content,
|
||||||
@@ -36,6 +36,7 @@ void HttpConnection::SetResponseContent(std::string&& content,
|
|||||||
|
|
||||||
void HttpConnection::SendResponse(HttpStatus::Enum status) {
|
void HttpConnection::SendResponse(HttpStatus::Enum status) {
|
||||||
response_.set_status(status);
|
response_.set_status(status);
|
||||||
|
response_.UpdateStartLine();
|
||||||
AsyncWrite();
|
AsyncWrite();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ void HttpConnection::ReadHandler(boost::system::error_code ec,
|
|||||||
|
|
||||||
if (!request_parser_.Parse(buffer_.data(), length)) {
|
if (!request_parser_.Parse(buffer_.data(), length)) {
|
||||||
// Bad request.
|
// Bad request.
|
||||||
|
LOG_ERRO("Failed to parse HTTP request.");
|
||||||
response_ = HttpResponse::Fault(HttpStatus::kBadRequest);
|
response_ = HttpResponse::Fault(HttpStatus::kBadRequest);
|
||||||
AsyncWrite();
|
AsyncWrite();
|
||||||
return;
|
return;
|
||||||
@@ -69,13 +71,16 @@ void HttpConnection::ReadHandler(boost::system::error_code ec,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_VERB("HTTP request:\n%s", request_.Dump(4, "> ").c_str());
|
||||||
|
|
||||||
// Enqueue this connection.
|
// Enqueue this connection.
|
||||||
// Some worker thread will handle it later.
|
// Some worker thread will handle it later.
|
||||||
// And DoWrite() will be called in the worker thread.
|
|
||||||
request_handler_->Enqueue(shared_from_this());
|
request_handler_->Enqueue(shared_from_this());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnection::AsyncWrite() {
|
void HttpConnection::AsyncWrite() {
|
||||||
|
LOG_VERB("HTTP response:\n%s", response_.Dump(4, "> ").c_str());
|
||||||
|
|
||||||
boost::asio::async_write(socket_,
|
boost::asio::async_write(socket_,
|
||||||
response_.ToBuffers(),
|
response_.ToBuffers(),
|
||||||
std::bind(&HttpConnection::WriteHandler,
|
std::bind(&HttpConnection::WriteHandler,
|
||||||
@@ -86,8 +91,8 @@ void HttpConnection::AsyncWrite() {
|
|||||||
|
|
||||||
// NOTE:
|
// NOTE:
|
||||||
// This write handler will be called from main thread (the thread calling
|
// This write handler will be called from main thread (the thread calling
|
||||||
// io_context.run), even though DoWrite() is invoked by worker threads. This is
|
// io_context.run), even though AsyncWrite() is invoked by worker threads.
|
||||||
// ensured by Asio.
|
// This is ensured by Asio.
|
||||||
void HttpConnection::WriteHandler(boost::system::error_code ec,
|
void HttpConnection::WriteHandler(boost::system::error_code ec,
|
||||||
std::size_t length) {
|
std::size_t length) {
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
|
|||||||
+36
-1
@@ -6,6 +6,17 @@
|
|||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace misc_strings {
|
||||||
|
|
||||||
|
const char NAME_VALUE_SEPARATOR[] = { ':', ' ' };
|
||||||
|
const char CRLF[] = { '\r', '\n' };
|
||||||
|
|
||||||
|
} // misc_strings
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
void HttpMessage::SetHeader(const std::string& name, const std::string& value) {
|
void HttpMessage::SetHeader(const std::string& name, const std::string& value) {
|
||||||
for (HttpHeader& h : headers_) {
|
for (HttpHeader& h : headers_) {
|
||||||
if (h.name == name) {
|
if (h.name == name) {
|
||||||
@@ -17,6 +28,30 @@ void HttpMessage::SetHeader(const std::string& name, const std::string& value) {
|
|||||||
headers_.push_back({ name, value });
|
headers_.push_back({ name, value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ATTENTION: The buffers don't hold the memory!
|
||||||
|
std::vector<boost::asio::const_buffer> HttpMessage::ToBuffers() const {
|
||||||
|
assert(!start_line_.empty());
|
||||||
|
|
||||||
|
std::vector<boost::asio::const_buffer> buffers;
|
||||||
|
|
||||||
|
buffers.push_back(boost::asio::buffer(start_line_));
|
||||||
|
|
||||||
|
for (const HttpHeader& h : headers_) {
|
||||||
|
buffers.push_back(boost::asio::buffer(h.name));
|
||||||
|
buffers.push_back(boost::asio::buffer(misc_strings::NAME_VALUE_SEPARATOR));
|
||||||
|
buffers.push_back(boost::asio::buffer(h.value));
|
||||||
|
buffers.push_back(boost::asio::buffer(misc_strings::CRLF));
|
||||||
|
}
|
||||||
|
|
||||||
|
buffers.push_back(boost::asio::buffer(misc_strings::CRLF));
|
||||||
|
|
||||||
|
if (content_length_ > 0) {
|
||||||
|
buffers.push_back(boost::asio::buffer(content_));
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffers;
|
||||||
|
}
|
||||||
|
|
||||||
void HttpMessage::Dump(std::ostream& os, std::size_t indent,
|
void HttpMessage::Dump(std::ostream& os, std::size_t indent,
|
||||||
const std::string& prefix) const {
|
const std::string& prefix) const {
|
||||||
std::string indent_str;
|
std::string indent_str;
|
||||||
@@ -38,7 +73,7 @@ void HttpMessage::Dump(std::ostream& os, std::size_t indent,
|
|||||||
os << content_ << std::endl;
|
os << content_ << std::endl;
|
||||||
} else {
|
} else {
|
||||||
std::vector<std::string> splitted;
|
std::vector<std::string> splitted;
|
||||||
boost::split(splitted, content_, boost::is_any_of("\r\n"));
|
boost::split(splitted, content_, boost::is_any_of(CRLF));
|
||||||
for (const std::string& line : splitted) {
|
for (const std::string& line : splitted) {
|
||||||
os << indent_str << line << std::endl;
|
os << indent_str << line << std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-2
@@ -6,6 +6,8 @@
|
|||||||
#include <utility> // for move()
|
#include <utility> // for move()
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "boost/asio/buffer.hpp" // for const_buffer
|
||||||
|
|
||||||
#include "webcc/globals.h"
|
#include "webcc/globals.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
@@ -18,6 +20,8 @@ struct HttpHeader {
|
|||||||
// Base class for HTTP request and response messages.
|
// Base class for HTTP request and response messages.
|
||||||
class HttpMessage {
|
class HttpMessage {
|
||||||
public:
|
public:
|
||||||
|
HttpMessage() : content_length_(kInvalidLength) {}
|
||||||
|
|
||||||
virtual ~HttpMessage() = default;
|
virtual ~HttpMessage() = default;
|
||||||
|
|
||||||
const std::string& start_line() const { return start_line_; }
|
const std::string& start_line() const { return start_line_; }
|
||||||
@@ -47,6 +51,15 @@ class HttpMessage {
|
|||||||
SetContentLength(content_.size());
|
SetContentLength(content_.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set start line according to other informations.
|
||||||
|
// Must be called before ToBuffers()!
|
||||||
|
virtual void UpdateStartLine() = 0;
|
||||||
|
|
||||||
|
// Convert the message into a vector of buffers. The buffers do not own the
|
||||||
|
// underlying memory blocks, therefore the message object must remain valid
|
||||||
|
// and not be changed until the write operation has completed.
|
||||||
|
std::vector<boost::asio::const_buffer> ToBuffers() const;
|
||||||
|
|
||||||
// Dump to output stream.
|
// Dump to output stream.
|
||||||
void Dump(std::ostream& os, std::size_t indent = 0,
|
void Dump(std::ostream& os, std::size_t indent = 0,
|
||||||
const std::string& prefix = "") const;
|
const std::string& prefix = "") const;
|
||||||
@@ -61,10 +74,10 @@ class HttpMessage {
|
|||||||
SetHeader(kContentLength, std::to_string(content_length));
|
SetHeader(kContentLength, std::to_string(content_length));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start line with trailing "\r\n".
|
// Start line with trailing CRLF.
|
||||||
std::string start_line_;
|
std::string start_line_;
|
||||||
|
|
||||||
std::size_t content_length_ = kInvalidLength;
|
std::size_t content_length_;
|
||||||
|
|
||||||
std::vector<HttpHeader> headers_;
|
std::vector<HttpHeader> headers_;
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ bool HttpParser::Parse(const char* data, std::size_t length) {
|
|||||||
std::size_t off = 0;
|
std::size_t off = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
std::size_t pos = pending_data_.find("\r\n", off);
|
std::size_t pos = pending_data_.find(CRLF, off);
|
||||||
if (pos == std::string::npos) {
|
if (pos == std::string::npos) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -48,6 +48,7 @@ bool HttpParser::Parse(const char* data, std::size_t length) {
|
|||||||
|
|
||||||
if (!start_line_parsed_) {
|
if (!start_line_parsed_) {
|
||||||
start_line_parsed_ = true;
|
start_line_parsed_ = true;
|
||||||
|
message_->set_start_line(line + CRLF);
|
||||||
if (!ParseStartLine(line)) {
|
if (!ParseStartLine(line)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -111,7 +112,7 @@ void HttpParser::ParseContentLength(const std::string& line) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
content_length_ = static_cast<std::size_t>(std::stoul(value));
|
content_length_ = static_cast<std::size_t>(std::stoul(value));
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception&) {
|
||||||
LOG_ERRO("Invalid content length: %s.", value.c_str());
|
LOG_ERRO("Invalid content length: %s.", value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-39
@@ -2,17 +2,6 @@
|
|||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace misc_strings {
|
|
||||||
|
|
||||||
const char NAME_VALUE_SEPARATOR[] = { ':', ' ' };
|
|
||||||
const char CRLF[] = { '\r', '\n' };
|
|
||||||
|
|
||||||
} // misc_strings
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void HttpRequest::SetHost(const std::string& host, const std::string& port) {
|
void HttpRequest::SetHost(const std::string& host, const std::string& port) {
|
||||||
host_ = host;
|
host_ = host;
|
||||||
port_ = port;
|
port_ = port;
|
||||||
@@ -24,37 +13,12 @@ void HttpRequest::SetHost(const std::string& host, const std::string& port) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpRequest::Build() {
|
void HttpRequest::UpdateStartLine() {
|
||||||
if (start_line_.empty()) {
|
|
||||||
start_line_ = method_;
|
start_line_ = method_;
|
||||||
start_line_ += " ";
|
start_line_ += " ";
|
||||||
start_line_ += url_;
|
start_line_ += url_;
|
||||||
start_line_ += " HTTP/1.1\r\n";
|
start_line_ += " HTTP/1.1";
|
||||||
}
|
start_line_ += CRLF;
|
||||||
}
|
|
||||||
|
|
||||||
// ATTENTION: The buffers don't hold the memory!
|
|
||||||
std::vector<boost::asio::const_buffer> HttpRequest::ToBuffers() const {
|
|
||||||
assert(!start_line_.empty());
|
|
||||||
|
|
||||||
std::vector<boost::asio::const_buffer> buffers;
|
|
||||||
|
|
||||||
buffers.push_back(boost::asio::buffer(start_line_));
|
|
||||||
|
|
||||||
for (const HttpHeader& h : headers_) {
|
|
||||||
buffers.push_back(boost::asio::buffer(h.name));
|
|
||||||
buffers.push_back(boost::asio::buffer(misc_strings::NAME_VALUE_SEPARATOR));
|
|
||||||
buffers.push_back(boost::asio::buffer(h.value));
|
|
||||||
buffers.push_back(boost::asio::buffer(misc_strings::CRLF));
|
|
||||||
}
|
|
||||||
|
|
||||||
buffers.push_back(boost::asio::buffer(misc_strings::CRLF));
|
|
||||||
|
|
||||||
if (content_length_ > 0) {
|
|
||||||
buffers.push_back(boost::asio::buffer(content_));
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
+2
-14
@@ -3,9 +3,6 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "boost/asio/buffer.hpp" // for const_buffer
|
|
||||||
|
|
||||||
#include "webcc/http_message.h"
|
#include "webcc/http_message.h"
|
||||||
|
|
||||||
@@ -14,9 +11,6 @@ namespace webcc {
|
|||||||
class HttpRequest : public HttpMessage {
|
class HttpRequest : public HttpMessage {
|
||||||
public:
|
public:
|
||||||
HttpRequest() = default;
|
HttpRequest() = default;
|
||||||
HttpRequest(const HttpRequest&) = default;
|
|
||||||
HttpRequest& operator=(const HttpRequest&) = default;
|
|
||||||
|
|
||||||
virtual ~HttpRequest() = default;
|
virtual ~HttpRequest() = default;
|
||||||
|
|
||||||
const std::string& method() const { return method_; }
|
const std::string& method() const { return method_; }
|
||||||
@@ -33,14 +27,8 @@ class HttpRequest : public HttpMessage {
|
|||||||
// a numeric number (e.g., "9000") and "80" will be used if it's empty.
|
// a numeric number (e.g., "9000") and "80" will be used if it's empty.
|
||||||
void SetHost(const std::string& host, const std::string& port);
|
void SetHost(const std::string& host, const std::string& port);
|
||||||
|
|
||||||
// Compose start line, etc.
|
// Set start line according to HTTP method, URL, etc.
|
||||||
// Must be called before ToBuffers()!
|
void UpdateStartLine() override;
|
||||||
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.
|
|
||||||
std::vector<boost::asio::const_buffer> ToBuffers() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// HTTP method.
|
// HTTP method.
|
||||||
|
|||||||
+15
-42
@@ -16,75 +16,47 @@ const std::string INTERNAL_SERVER_ERROR =
|
|||||||
const std::string NOT_IMPLEMENTED = "HTTP/1.1 501 Not Implemented\r\n";
|
const std::string NOT_IMPLEMENTED = "HTTP/1.1 501 Not Implemented\r\n";
|
||||||
const std::string SERVICE_UNAVAILABLE = "HTTP/1.1 503 Service Unavailable\r\n";
|
const std::string SERVICE_UNAVAILABLE = "HTTP/1.1 503 Service Unavailable\r\n";
|
||||||
|
|
||||||
boost::asio::const_buffer ToBuffer(int status) {
|
const std::string& ToString(int status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case HttpStatus::kOK:
|
case HttpStatus::kOK:
|
||||||
return boost::asio::buffer(OK);
|
return OK;
|
||||||
|
|
||||||
case HttpStatus::kCreated:
|
case HttpStatus::kCreated:
|
||||||
return boost::asio::buffer(CREATED);
|
return CREATED;
|
||||||
|
|
||||||
case HttpStatus::kAccepted:
|
case HttpStatus::kAccepted:
|
||||||
return boost::asio::buffer(ACCEPTED);
|
return ACCEPTED;
|
||||||
|
|
||||||
case HttpStatus::kNoContent:
|
case HttpStatus::kNoContent:
|
||||||
return boost::asio::buffer(NO_CONTENT);
|
return NO_CONTENT;
|
||||||
|
|
||||||
case HttpStatus::kNotModified:
|
case HttpStatus::kNotModified:
|
||||||
return boost::asio::buffer(NOT_MODIFIED);
|
return NOT_MODIFIED;
|
||||||
|
|
||||||
case HttpStatus::kBadRequest:
|
case HttpStatus::kBadRequest:
|
||||||
return boost::asio::buffer(BAD_REQUEST);
|
return BAD_REQUEST;
|
||||||
|
|
||||||
case HttpStatus::kNotFound:
|
case HttpStatus::kNotFound:
|
||||||
return boost::asio::buffer(NOT_FOUND);
|
return NOT_FOUND;
|
||||||
|
|
||||||
case HttpStatus::InternalServerError:
|
case HttpStatus::InternalServerError:
|
||||||
return boost::asio::buffer(INTERNAL_SERVER_ERROR);
|
return INTERNAL_SERVER_ERROR;
|
||||||
|
|
||||||
case HttpStatus::kNotImplemented:
|
case HttpStatus::kNotImplemented:
|
||||||
return boost::asio::buffer(NOT_IMPLEMENTED);
|
return NOT_IMPLEMENTED;
|
||||||
|
|
||||||
case HttpStatus::kServiceUnavailable:
|
case HttpStatus::kServiceUnavailable:
|
||||||
return boost::asio::buffer(SERVICE_UNAVAILABLE);
|
return SERVICE_UNAVAILABLE;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return boost::asio::buffer(NOT_IMPLEMENTED);
|
return NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace status_strings
|
} // namespace status_strings
|
||||||
|
|
||||||
namespace misc_strings {
|
void HttpResponse::UpdateStartLine() {
|
||||||
|
start_line_ = status_strings::ToString(status_);
|
||||||
const char NAME_VALUE_SEPARATOR[] = { ':', ' ' };
|
|
||||||
const char CRLF[] = { '\r', '\n' };
|
|
||||||
|
|
||||||
} // misc_strings
|
|
||||||
|
|
||||||
// ATTENTION: The buffers don't hold the memory!
|
|
||||||
std::vector<boost::asio::const_buffer> HttpResponse::ToBuffers() const {
|
|
||||||
std::vector<boost::asio::const_buffer> buffers;
|
|
||||||
|
|
||||||
// Status line
|
|
||||||
buffers.push_back(status_strings::ToBuffer(status_));
|
|
||||||
|
|
||||||
// Header fields (optional)
|
|
||||||
for (const HttpHeader& h : headers_) {
|
|
||||||
buffers.push_back(boost::asio::buffer(h.name));
|
|
||||||
buffers.push_back(boost::asio::buffer(misc_strings::NAME_VALUE_SEPARATOR));
|
|
||||||
buffers.push_back(boost::asio::buffer(h.value));
|
|
||||||
buffers.push_back(boost::asio::buffer(misc_strings::CRLF));
|
|
||||||
}
|
|
||||||
|
|
||||||
buffers.push_back(boost::asio::buffer(misc_strings::CRLF));
|
|
||||||
|
|
||||||
// Content (optional)
|
|
||||||
if (!content_.empty()) {
|
|
||||||
buffers.push_back(boost::asio::buffer(content_));
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse HttpResponse::Fault(HttpStatus::Enum status) {
|
HttpResponse HttpResponse::Fault(HttpStatus::Enum status) {
|
||||||
@@ -92,6 +64,7 @@ HttpResponse HttpResponse::Fault(HttpStatus::Enum status) {
|
|||||||
|
|
||||||
HttpResponse response;
|
HttpResponse response;
|
||||||
response.set_status(status);
|
response.set_status(status);
|
||||||
|
response.UpdateStartLine();
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "boost/asio/buffer.hpp" // for const_buffer
|
|
||||||
|
|
||||||
#include "webcc/http_message.h"
|
#include "webcc/http_message.h"
|
||||||
|
|
||||||
@@ -13,20 +10,19 @@ namespace webcc {
|
|||||||
|
|
||||||
class HttpResponse : public HttpMessage {
|
class HttpResponse : public HttpMessage {
|
||||||
public:
|
public:
|
||||||
HttpResponse() : status_(HttpStatus::kOK) {
|
HttpResponse() : status_(HttpStatus::kOK) {}
|
||||||
}
|
|
||||||
|
|
||||||
~HttpResponse() override = default;
|
~HttpResponse() override = default;
|
||||||
|
|
||||||
int status() const { return status_; }
|
int status() const { return status_; }
|
||||||
|
|
||||||
void set_status(int status) { status_ = status; }
|
void set_status(int status) { status_ = status; }
|
||||||
|
|
||||||
// Convert the response into a vector of buffers. The buffers do not own the
|
// Set start line according to status code.
|
||||||
// underlying memory blocks, therefore the response object must remain valid
|
void UpdateStartLine() override;
|
||||||
// and not be changed until the write operation has completed.
|
|
||||||
std::vector<boost::asio::const_buffer> ToBuffers() const;
|
|
||||||
|
|
||||||
// Get a fault response when HTTP status is not OK.
|
// Get a fault response when HTTP status is not OK.
|
||||||
|
// TODO: Avoid copy.
|
||||||
static HttpResponse Fault(HttpStatus::Enum status);
|
static HttpResponse Fault(HttpStatus::Enum status);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ HttpResponseParser::HttpResponseParser(HttpResponse* response)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool HttpResponseParser::ParseStartLine(const std::string& line) {
|
bool HttpResponseParser::ParseStartLine(const std::string& line) {
|
||||||
response_->set_start_line(line + "\r\n");
|
|
||||||
|
|
||||||
std::size_t off = 0;
|
std::size_t off = 0;
|
||||||
|
|
||||||
std::size_t pos = line.find(' ');
|
std::size_t pos = line.find(' ');
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ bool RestClient::Request(const std::string& method,
|
|||||||
request.SetContent(content);
|
request.SetContent(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
request.Build();
|
request.UpdateStartLine();
|
||||||
|
|
||||||
HttpClient http_client;
|
HttpClient http_client;
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ Error SoapClient::Call(const std::string& operation,
|
|||||||
http_request.SetContent(std::move(http_content));
|
http_request.SetContent(std::move(http_content));
|
||||||
http_request.SetHost(host_, port_);
|
http_request.SetHost(host_, port_);
|
||||||
http_request.SetHeader(kSoapAction, operation);
|
http_request.SetHeader(kSoapAction, operation);
|
||||||
http_request.Build();
|
http_request.UpdateStartLine();
|
||||||
|
|
||||||
HttpResponse http_response;
|
HttpResponse http_response;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user