Remove http prefix

master
Chunting Gu 6 years ago
parent 2ea04b9705
commit e621025cc3

@ -96,7 +96,7 @@ if(WEBCC_ENABLE_SSL)
endif()
include_directories(
# For including its own headers as "webcc/http_client.h".
# For including its own headers as "webcc/client.h".
${PROJECT_SOURCE_DIR}
# For including config.h as "webcc/config.h".
${PROJECT_BINARY_DIR}
@ -109,8 +109,6 @@ if(WIN32)
link_directories(${THIRD_PARTY_DIR}/win32/lib)
endif()
# For including pugixml as "pugixml/pugixml.hpp" or gtest as
# "gtest/gtest.h".
include_directories(${THIRD_PARTY_DIR}/src)
# For using zlib on Windows.

@ -12,7 +12,7 @@ A complete client example:
```cpp
#include <iostream>
#include "webcc/http_client_session.h"
#include "webcc/client_session.h"
#include "webcc/logger.h"
int main() {
@ -21,7 +21,7 @@ int main() {
// Session provides convenient request APIs, stores request configurations
// and manages persistent connenction.
webcc::HttpClientSession session;
webcc::ClientSession session;
// Catch exceptions for error handling.
try {
@ -41,11 +41,11 @@ int main() {
The `Get()` method is nothing but a shortcut of `Request()`. Using `Request()` directly is more complicated:
```cpp
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
auto r = session.Request(webcc::RequestBuilder{}.Get().
Url("http://httpbin.org/get")
());
```
As you can see, a helper class named `HttpRequestBuilder` is used to chain the parameters and finally build (don't miss the `()` operator) a request object.
As you can see, a helper class named `RequestBuilder` is used to chain the parameters and finally build (don't miss the `()` operator) a request object.
Both the shortcut and `Request()` accept URL query parameters:
@ -53,7 +53,7 @@ Both the shortcut and `Request()` accept URL query parameters:
// Query parameters are passed using a std::vector.
session.Get("http://httpbin.org/get", { "key1", "value1", "key2", "value2" });
session.Request(webcc::HttpRequestBuilder{}.Get().
session.Request(webcc::RequestBuilder{}.Get().
Url("http://httpbin.org/get").
Query("key1", "value1").
Query("key2", "value2")
@ -66,7 +66,7 @@ session.Get("http://httpbin.org/get",
{"key1", "value1", "key2", "value2"},
{"Accept", "application/json"}); // Also a std::vector
session.Request(webcc::HttpRequestBuilder{}.Get().
session.Request(webcc::RequestBuilder{}.Get().
Url("http://httpbin.org/get").
Query("key1", "value1").
Query("key2", "value2").

@ -1,7 +1,7 @@
# Auto test
set(AT_SRCS
http_client_autotest.cc
client_autotest.cc
)
set(AT_TARGET_NAME webcc_autotest)

@ -3,7 +3,7 @@
#include "boost/algorithm/string.hpp"
#include "json/json.h"
#include "webcc/http_client_session.h"
#include "webcc/client_session.h"
#include "webcc/logger.h"
// -----------------------------------------------------------------------------
@ -36,7 +36,7 @@ static Json::Value StringToJson(const std::string& str) {
// -----------------------------------------------------------------------------
static void AssertGet(webcc::HttpResponsePtr r) {
static void AssertGet(webcc::ResponsePtr r) {
Json::Value json = StringToJson(r->content());
Json::Value args = json["args"];
@ -52,11 +52,11 @@ static void AssertGet(webcc::HttpResponsePtr r) {
EXPECT_EQ("httpbin.org", headers["Host"].asString());
}
TEST(HttpClientTest, Get_RequestFunc) {
webcc::HttpClientSession session;
TEST(ClientTest, Get_RequestFunc) {
webcc::ClientSession session;
try {
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
auto r = session.Request(webcc::RequestBuilder{}.Get().
Url("http://httpbin.org/get").
Query("key1", "value1").
Query("key2", "value2").
@ -70,8 +70,8 @@ TEST(HttpClientTest, Get_RequestFunc) {
}
}
TEST(HttpClientTest, Get_Shortcut) {
webcc::HttpClientSession session;
TEST(ClientTest, Get_Shortcut) {
webcc::ClientSession session;
try {
auto r = session.Get("http://httpbin.org/get",
@ -86,14 +86,14 @@ TEST(HttpClientTest, Get_Shortcut) {
}
#if WEBCC_ENABLE_SSL
TEST(HttpClientTest, Get_SSL) {
webcc::HttpClientSession session;
TEST(ClientTest, Get_SSL) {
webcc::ClientSession session;
session.set_ssl_verify(kSslVerify);
try {
// HTTPS is auto-detected from the URL scheme.
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
auto r = session.Request(webcc::RequestBuilder{}.Get().
Url("https://httpbin.org/get").
Query("key1", "value1").
Query("key2", "value2").
@ -111,8 +111,8 @@ TEST(HttpClientTest, Get_SSL) {
// -----------------------------------------------------------------------------
// Test Gzip compressed response.
TEST(HttpClientTest, Compression_Gzip) {
webcc::HttpClientSession session;
TEST(ClientTest, Compression_Gzip) {
webcc::ClientSession session;
try {
auto r = session.Get("http://httpbin.org/gzip");
@ -127,8 +127,8 @@ TEST(HttpClientTest, Compression_Gzip) {
}
// Test Deflate compressed response.
TEST(HttpClientTest, Compression_Deflate) {
webcc::HttpClientSession session;
TEST(ClientTest, Compression_Deflate) {
webcc::ClientSession session;
try {
auto r = session.Get("http://httpbin.org/deflate");
@ -157,8 +157,8 @@ TEST(HttpClientTest, Compression_Deflate) {
// "https://www.google.com";
// "https://api.github.com/events";
//
TEST(HttpClientTest, KeepAlive) {
webcc::HttpClientSession session;
TEST(ClientTest, KeepAlive) {
webcc::ClientSession session;
std::string url = "http://httpbin.org/get";
try {
@ -176,14 +176,14 @@ TEST(HttpClientTest, KeepAlive) {
EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Close"));
// Close by using request builder.
r = session.Request(webcc::HttpRequestBuilder{}.Get().
r = session.Request(webcc::RequestBuilder{}.Get().
Url(url).KeepAlive(false)
());
EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Close"));
// Keep-Alive explicitly by using request builder.
r = session.Request(webcc::HttpRequestBuilder{}.Get().
r = session.Request(webcc::RequestBuilder{}.Get().
Url(url).KeepAlive(true)
());
@ -197,8 +197,8 @@ TEST(HttpClientTest, KeepAlive) {
// -----------------------------------------------------------------------------
// Get a JPEG image.
TEST(HttpClientTest, GetImageJpeg) {
webcc::HttpClientSession session;
TEST(ClientTest, GetImageJpeg) {
webcc::ClientSession session;
std::string url = "http://httpbin.org/get";
try {

@ -1,6 +1,6 @@
#include <iostream>
#include "webcc/http_client_session.h"
#include "webcc/client_session.h"
#include "webcc/logger.h"
#if (defined(WIN32) || defined(_WIN64))
@ -19,7 +19,7 @@ static void PrintSeparator() {
int main() {
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
webcc::HttpClientSession session;
webcc::ClientSession session;
session.set_ssl_verify(kSslVerify);
@ -27,7 +27,7 @@ int main() {
PrintSeparator();
// Using request builder:
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
auto r = session.Request(webcc::RequestBuilder{}.Get().
Url("http://httpbin.org/get").
Query("key1", "value1").
Query("key2", "value2").

@ -2,7 +2,7 @@
#include "boost/filesystem.hpp"
#include "webcc/http_client_session.h"
#include "webcc/client_session.h"
#include "webcc/logger.h"
#if (defined(WIN32) || defined(_WIN64))
@ -47,12 +47,12 @@ int main(int argc, char* argv[]) {
return 1;
}
webcc::HttpClientSession session;
webcc::ClientSession session;
try {
//auto r = session.PostFile(url, "file", upload_dir / "remember.txt");
auto r = session.Request(webcc::HttpRequestBuilder{}.Post().
auto r = session.Request(webcc::RequestBuilder{}.Post().
Url(url).
File("file", upload_dir / "remember.txt").
Form("json", "{}", "application/json")

@ -20,9 +20,9 @@ public:
}
response->content = "OK";
response->media_type = webcc::http::media_types::kTextPlain;
response->media_type = webcc::media_types::kTextPlain;
response->charset = "utf-8";
response->status = webcc::http::Status::kCreated;
response->status = webcc::Status::kCreated;
}
}
};

@ -2,7 +2,7 @@
#include "json/json.h"
#include "webcc/http_client_session.h"
#include "webcc/client_session.h"
#include "webcc/logger.h"
// -----------------------------------------------------------------------------
@ -61,7 +61,7 @@ void PrettyPrintJsonString(const std::string& str) {
// -----------------------------------------------------------------------------
// List public events.
void ListEvents(webcc::HttpClientSession& session) {
void ListEvents(webcc::ClientSession& session) {
try {
auto r = session.Get(kUrlRoot + "/events");
@ -75,8 +75,7 @@ void ListEvents(webcc::HttpClientSession& session) {
// List the followers of the given user.
// Example:
// ListUserFollowers(session, "<login>")
void ListUserFollowers(webcc::HttpClientSession& session,
const std::string& user) {
void ListUserFollowers(webcc::ClientSession& session, const std::string& user) {
try {
auto r = session.Get(kUrlRoot + "/users/" + user + "/followers");
@ -90,11 +89,11 @@ void ListUserFollowers(webcc::HttpClientSession& session,
// List the followers of the current authorized user using Basic authorization.
// E.g., list my own followers:
// ListAuthUserFollowers(session, "sprinfall@gmail.com", "<MyPassword>");
void ListAuthUserFollowers(webcc::HttpClientSession& session,
void ListAuthUserFollowers(webcc::ClientSession& session,
const std::string& login,
const std::string& password) {
try {
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
auto r = session.Request(webcc::RequestBuilder{}.Get().
Url(kUrlRoot + "/user/followers").
AuthBasic(login, password)
());
@ -106,8 +105,9 @@ void ListAuthUserFollowers(webcc::HttpClientSession& session,
}
}
void CreateAuthorization(webcc::HttpClientSession& session,
const std::string& auth) {
void CreateAuthorization(webcc::ClientSession& session,
const std::string& login,
const std::string& password) {
try {
std::string data =
"{\n"
@ -115,8 +115,12 @@ void CreateAuthorization(webcc::HttpClientSession& session,
" 'scopes': ['public_repo', 'repo', 'repo:status', 'user']\n"
"}";
auto r = session.Post(kUrlRoot + "/authorizations", std::move(data), true,
{"Authorization", auth});
auto r = session.Request(webcc::RequestBuilder{}.Post().
Url(kUrlRoot + "/authorizations").
Data(std::move(data)).
Json(true).
AuthBasic(login, password)
());
std::cout << r->content() << std::endl;
@ -130,7 +134,7 @@ void CreateAuthorization(webcc::HttpClientSession& session,
int main() {
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
webcc::HttpClientSession session;
webcc::ClientSession session;
session.set_ssl_verify(kSslVerify);

@ -3,7 +3,7 @@
#include "json/json.h"
#include "webcc/http_client_session.h"
#include "webcc/client_session.h"
#include "webcc/logger.h"
#include "examples/common/book.h"
@ -21,7 +21,7 @@
class BookClientBase {
public:
BookClientBase(webcc::HttpClientSession& session, const std::string& url)
BookClientBase(webcc::ClientSession& session, const std::string& url)
: session_(session), url_(url) {
}
@ -29,7 +29,7 @@ public:
protected:
// Check HTTP response status.
bool CheckStatus(webcc::HttpResponsePtr response, int expected_status) {
bool CheckStatus(webcc::ResponsePtr response, int expected_status) {
int status = response->status();
if (status != expected_status) {
LOG_ERRO("HTTP status error (actual: %d, expected: %d).",
@ -42,14 +42,14 @@ protected:
protected:
std::string url_;
webcc::HttpClientSession& session_;
webcc::ClientSession& session_;
};
// -----------------------------------------------------------------------------
class BookListClient : public BookClientBase {
public:
BookListClient(webcc::HttpClientSession& session, const std::string& url)
BookListClient(webcc::ClientSession& session, const std::string& url)
: BookClientBase(session, url) {
}
@ -57,7 +57,7 @@ public:
try {
auto r = session_.Get(url_ + "/books");
if (!CheckStatus(r, webcc::http::Status::kOK)) {
if (!CheckStatus(r, webcc::Status::kOK)) {
// Response HTTP status error.
return false;
}
@ -88,7 +88,7 @@ public:
try {
auto r = session_.Post(url_ + "/books", JsonToString(req_json), true);
if (!CheckStatus(r, webcc::http::Status::kCreated)) {
if (!CheckStatus(r, webcc::Status::kCreated)) {
return false;
}
@ -108,7 +108,7 @@ public:
class BookDetailClient : public BookClientBase {
public:
BookDetailClient(webcc::HttpClientSession& session, const std::string& url)
BookDetailClient(webcc::ClientSession& session, const std::string& url)
: BookClientBase(session, url) {
}
@ -116,7 +116,7 @@ public:
try {
auto r = session_.Get(url_ + "/books/" + id);
if (!CheckStatus(r, webcc::http::Status::kOK)) {
if (!CheckStatus(r, webcc::Status::kOK)) {
return false;
}
@ -137,7 +137,7 @@ public:
try {
auto r = session_.Put(url_ + "/books/" + id, JsonToString(json), true);
if (!CheckStatus(r, webcc::http::Status::kOK)) {
if (!CheckStatus(r, webcc::Status::kOK)) {
return false;
}
@ -153,7 +153,7 @@ public:
try {
auto r = session_.Delete(url_ + "/books/" + id);
if (!CheckStatus(r, webcc::http::Status::kOK)) {
if (!CheckStatus(r, webcc::Status::kOK)) {
return false;
}
@ -209,7 +209,7 @@ int main(int argc, char* argv[]) {
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE_FILE_OVERWRITE);
// Share the same session.
webcc::HttpClientSession session;
webcc::ClientSession session;
// Session-level settings.
session.set_timeout(timeout);

@ -100,9 +100,9 @@ void BookListService::Get(const webcc::UrlQuery& /*query*/,
// TODO: Simplify
response->content = JsonToString(json);
response->media_type = webcc::http::media_types::kApplicationJson;
response->media_type = webcc::media_types::kApplicationJson;
response->charset = "utf-8";
response->status = webcc::http::Status::kOK;
response->status = webcc::Status::kOK;
}
void BookListService::Post(const std::string& request_content,
@ -117,12 +117,12 @@ void BookListService::Post(const std::string& request_content,
json["id"] = id;
response->content = JsonToString(json);
response->media_type = webcc::http::media_types::kApplicationJson;
response->media_type = webcc::media_types::kApplicationJson;
response->charset = "utf-8";
response->status = webcc::http::Status::kCreated;
response->status = webcc::Status::kCreated;
} else {
// Invalid JSON
response->status = webcc::http::Status::kBadRequest;
response->status = webcc::Status::kBadRequest;
}
}
@ -136,7 +136,7 @@ void BookDetailService::Get(const webcc::UrlMatches& url_matches,
if (url_matches.size() != 1) {
// Using kNotFound means the resource specified by the URL cannot be found.
// kBadRequest could be another choice.
response->status = webcc::http::Status::kNotFound;
response->status = webcc::Status::kNotFound;
return;
}
@ -144,14 +144,14 @@ void BookDetailService::Get(const webcc::UrlMatches& url_matches,
const Book& book = g_book_store.GetBook(book_id);
if (book.IsNull()) {
response->status = webcc::http::Status::kNotFound;
response->status = webcc::Status::kNotFound;
return;
}
response->content = BookToJsonString(book);
response->media_type = webcc::http::media_types::kApplicationJson;
response->media_type = webcc::media_types::kApplicationJson;
response->charset = "utf-8";
response->status = webcc::http::Status::kOK;
response->status = webcc::Status::kOK;
}
void BookDetailService::Put(const webcc::UrlMatches& url_matches,
@ -160,7 +160,7 @@ void BookDetailService::Put(const webcc::UrlMatches& url_matches,
Sleep(sleep_seconds_);
if (url_matches.size() != 1) {
response->status = webcc::http::Status::kNotFound;
response->status = webcc::Status::kNotFound;
return;
}
@ -168,14 +168,14 @@ void BookDetailService::Put(const webcc::UrlMatches& url_matches,
Book book;
if (!JsonStringToBook(request_content, &book)) {
response->status = webcc::http::Status::kBadRequest;
response->status = webcc::Status::kBadRequest;
return;
}
book.id = book_id;
g_book_store.UpdateBook(book);
response->status = webcc::http::Status::kOK;
response->status = webcc::Status::kOK;
}
void BookDetailService::Delete(const webcc::UrlMatches& url_matches,
@ -183,18 +183,18 @@ void BookDetailService::Delete(const webcc::UrlMatches& url_matches,
Sleep(sleep_seconds_);
if (url_matches.size() != 1) {
response->status = webcc::http::Status::kNotFound;
response->status = webcc::Status::kNotFound;
return;
}
const std::string& book_id = url_matches[0];
if (!g_book_store.DeleteBook(book_id)) {
response->status = webcc::http::Status::kNotFound;
response->status = webcc::Status::kNotFound;
return;
}
response->status = webcc::http::Status::kOK;
response->status = webcc::Status::kOK;
}
// -----------------------------------------------------------------------------

@ -2,7 +2,7 @@
set(UT_SRCS
base64_unittest.cc
http_parser_unittest.cc
parser_unittest.cc
rest_service_manager_unittest.cc
url_unittest.cc
)

@ -1,9 +1,9 @@
#include "gtest/gtest.h"
#include "webcc/http_request.h"
#include "webcc/http_request_parser.h"
#include "webcc/http_response.h"
#include "webcc/http_response_parser.h"
#include "webcc/request.h"
#include "webcc/request_parser.h"
#include "webcc/response.h"
#include "webcc/response_parser.h"
#include <iostream>
@ -35,8 +35,8 @@ protected:
std::string payload_;
webcc::HttpRequest request_;
webcc::HttpRequestParser parser_;
webcc::Request request_;
webcc::RequestParser parser_;
};
TEST_F(GetRequestParserTest, ParseFullDataOnce) {
@ -121,8 +121,8 @@ protected:
std::string payload_;
std::string data_;
webcc::HttpRequest request_;
webcc::HttpRequestParser parser_;
webcc::Request request_;
webcc::RequestParser parser_;
};
TEST_F(PostRequestParserTest, ParseFullDataOnce) {

@ -8,7 +8,7 @@ class MyRestService : public webcc::RestService {
public:
void Handle(const webcc::RestRequest& request,
webcc::RestResponse* response) final {
response->status = webcc::http::Status::kOK;
response->status = webcc::Status::kOK;
}
};

@ -116,7 +116,7 @@ std::size_t Encode(const void* src, std::size_t len, void* dst) {
return out - static_cast<char*>(dst);
}
typedef std::pair<std::size_t, std::size_t> SizePair;
using SizePair = std::pair<std::size_t, std::size_t>;
SizePair Decode(const char* src, std::size_t len, void* dst) {
auto in = reinterpret_cast<const unsigned char*>(src);
@ -161,21 +161,22 @@ SizePair Decode(const char* src, std::size_t len, void* dst) {
} // namespace base64
std::string Base64Encode(const std::uint8_t* data, std::size_t len) {
std::string Base64Encode(const std::uint8_t* data, std::size_t length) {
std::string dst;
dst.resize(base64::EncodedSize(len));
dst.resize(base64::Encode(data, len, &dst[0]));
dst.resize(base64::EncodedSize(length));
dst.resize(base64::Encode(data, length, &dst[0]));
return dst;
}
std::string Base64Encode(const std::string& s) {
return Base64Encode(reinterpret_cast<const std::uint8_t*>(s.data()), s.size());
std::string Base64Encode(const std::string& input) {
return Base64Encode(reinterpret_cast<const std::uint8_t*>(input.data()),
input.size());
}
std::string Base64Decode(const std::string& data) {
std::string Base64Decode(const std::string& input) {
std::string dst;
dst.resize(base64::DecodedSize(data.size()));
auto result = base64::Decode(data.data(), data.size(), &dst[0]);
dst.resize(base64::DecodedSize(input.size()));
auto result = base64::Decode(input.data(), input.size(), &dst[0]);
dst.resize(result.first);
return dst;
}

@ -6,11 +6,11 @@
namespace webcc {
std::string Base64Encode(const std::uint8_t* data, std::size_t len);
std::string Base64Encode(const std::uint8_t* data, std::size_t length);
std::string Base64Encode(const std::string& input);
std::string Base64Decode(const std::string& data);
std::string Base64Decode(const std::string& input);
} // namespace webcc

@ -1,4 +1,4 @@
#include "webcc/http_client.h"
#include "webcc/client.h"
#include "boost/date_time/posix_time/posix_time.hpp"
@ -9,7 +9,7 @@ using boost::asio::ip::tcp;
namespace webcc {
HttpClient::HttpClient()
Client::Client()
: timer_(io_context_),
ssl_verify_(true),
buffer_size_(kBufferSize),
@ -20,10 +20,10 @@ HttpClient::HttpClient()
error_(kNoError) {
}
bool HttpClient::Request(HttpRequestPtr request, bool connect) {
bool Client::Request(RequestPtr request, bool connect) {
io_context_.restart();
response_.reset(new HttpResponse{});
response_.reset(new Response{});
response_parser_.Init(response_.get());
closed_ = false;
@ -54,7 +54,7 @@ bool HttpClient::Request(HttpRequestPtr request, bool connect) {
return true;
}
void HttpClient::Close() {
void Client::Close() {
if (closed_) {
return;
}
@ -71,23 +71,22 @@ void HttpClient::Close() {
}
}
Error HttpClient::Connect(HttpRequestPtr request) {
Error Client::Connect(RequestPtr request) {
if (request->url().scheme() == "https") {
#if WEBCC_ENABLE_SSL
socket_.reset(new HttpSslSocket{ io_context_, ssl_verify_ });
socket_.reset(new SslSocket{ io_context_, ssl_verify_ });
return DoConnect(request, kPort443);
#else
LOG_ERRO("SSL/HTTPS support is not enabled.");
return kSchemaError;
#endif // WEBCC_ENABLE_SSL
} else {
socket_.reset(new HttpSocket{ io_context_ });
socket_.reset(new Socket{ io_context_ });
return DoConnect(request, kPort80);
}
}
Error HttpClient::DoConnect(HttpRequestPtr request,
const std::string& default_port) {
Error Client::DoConnect(RequestPtr request, const std::string& default_port) {
tcp::resolver resolver(io_context_);
std::string port = request->port(default_port);
@ -118,7 +117,7 @@ Error HttpClient::DoConnect(HttpRequestPtr request,
return kNoError;
}
Error HttpClient::WriteReqeust(HttpRequestPtr request) {
Error Client::WriteReqeust(RequestPtr request) {
LOG_VERB("HTTP request:\n%s", request->Dump(4, "> ").c_str());
// NOTE:
@ -142,7 +141,7 @@ Error HttpClient::WriteReqeust(HttpRequestPtr request) {
return kNoError;
}
Error HttpClient::ReadResponse() {
Error Client::ReadResponse() {
LOG_VERB("Read response (timeout: %ds)...", timeout_);
timer_.expires_from_now(boost::posix_time::seconds(timeout_));
@ -158,7 +157,7 @@ Error HttpClient::ReadResponse() {
return error;
}
void HttpClient::DoReadResponse(Error* error) {
void Client::DoReadResponse(Error* error) {
boost::system::error_code ec = boost::asio::error::would_block;
auto handler = [this, &ec, error](boost::system::error_code inner_ec,
@ -218,13 +217,12 @@ void HttpClient::DoReadResponse(Error* error) {
} while (ec == boost::asio::error::would_block);
}
void HttpClient::DoWaitTimer() {
void Client::DoWaitTimer() {
LOG_VERB("Wait timer asynchronously.");
timer_.async_wait(
std::bind(&HttpClient::OnTimer, this, std::placeholders::_1));
timer_.async_wait(std::bind(&Client::OnTimer, this, std::placeholders::_1));
}
void HttpClient::OnTimer(boost::system::error_code ec) {
void Client::OnTimer(boost::system::error_code ec) {
LOG_VERB("On deadline timer.");
// timer_.cancel() was called.
@ -252,7 +250,7 @@ void HttpClient::OnTimer(boost::system::error_code ec) {
DoWaitTimer();
}
void HttpClient::CancelTimer() {
void Client::CancelTimer() {
if (timer_canceled_) {
return;
}

@ -1,5 +1,5 @@
#ifndef WEBCC_HTTP_CLIENT_H_
#define WEBCC_HTTP_CLIENT_H_
#ifndef WEBCC_CLIENT_H_
#define WEBCC_CLIENT_H_
#include <cassert>
#include <memory>
@ -11,28 +11,28 @@
#include "boost/asio/ip/tcp.hpp"
#include "webcc/globals.h"
#include "webcc/http_request.h"
#include "webcc/http_response.h"
#include "webcc/http_response_parser.h"
#include "webcc/http_socket.h"
#include "webcc/request.h"
#include "webcc/response.h"
#include "webcc/response_parser.h"
#include "webcc/socket.h"
namespace webcc {
class HttpClient;
typedef std::shared_ptr<HttpClient> HttpClientPtr;
class Client;
using ClientPtr = std::shared_ptr<Client>;
// Synchronous HTTP & HTTPS client.
// In synchronous mode, a request won't return until the response is received
// or timeout occurs.
// Please don't use the same client object in multiple threads.
class HttpClient {
class Client {
public:
HttpClient();
Client();
virtual ~HttpClient() = default;
virtual ~Client() = default;
HttpClient(const HttpClient&) = delete;
HttpClient& operator=(const HttpClient&) = delete;
Client(const Client&) = delete;
Client& operator=(const Client&) = delete;
void set_ssl_verify(bool ssl_verify) {
ssl_verify_ = ssl_verify;
@ -52,12 +52,12 @@ public:
}
// Connect to server, send request, wait until response is received.
bool Request(HttpRequestPtr request, bool connect = true);
bool Request(RequestPtr request, bool connect = true);
// Close the socket.
void Close();
HttpResponsePtr response() const { return response_; }
ResponsePtr response() const { return response_; }
bool closed() const { return closed_; }
@ -66,11 +66,11 @@ public:
Error error() const { return error_; }
private:
Error Connect(HttpRequestPtr request);
Error Connect(RequestPtr request);
Error DoConnect(HttpRequestPtr request, const std::string& default_port);
Error DoConnect(RequestPtr request, const std::string& default_port);
Error WriteReqeust(HttpRequestPtr request);
Error WriteReqeust(RequestPtr request);
Error ReadResponse();
@ -86,10 +86,10 @@ private:
boost::asio::io_context io_context_;
// Socket connection.
std::unique_ptr<HttpSocketBase> socket_;
std::unique_ptr<SocketBase> socket_;
HttpResponsePtr response_;
HttpResponseParser response_parser_;
ResponsePtr response_;
ResponseParser response_parser_;
// Timer for the timeout control.
boost::asio::deadline_timer timer_;
@ -122,4 +122,4 @@ private:
} // namespace webcc
#endif // WEBCC_HTTP_CLIENT_H_
#endif // WEBCC_CLIENT_H_

@ -1,10 +1,10 @@
#include "webcc/http_client_pool.h"
#include "webcc/client_pool.h"
#include "webcc/logger.h"
namespace webcc {
HttpClientPool::~HttpClientPool() {
ClientPool::~ClientPool() {
if (!clients_.empty()) {
LOG_INFO("Close socket for all (%u) connections in the pool.",
clients_.size());
@ -15,24 +15,24 @@ HttpClientPool::~HttpClientPool() {
}
}
HttpClientPtr HttpClientPool::Get(const Key& key) const {
ClientPtr ClientPool::Get(const Key& key) const {
auto it = clients_.find(key);
if (it != clients_.end()) {
return it->second;
} else {
return HttpClientPtr{};
return ClientPtr{};
}
}
void HttpClientPool::Add(const Key& key, HttpClientPtr client) {
void ClientPool::Add(const Key& key, ClientPtr client) {
clients_[key] = client;
LOG_INFO("Added connection to pool (%s, %s, %s).",
key.scheme.c_str(), key.host.c_str(), key.port.c_str());
}
void HttpClientPool::Remove(const Key& key) {
void ClientPool::Remove(const Key& key) {
clients_.erase(key);
LOG_INFO("Removed connection from pool (%s, %s, %s).",

@ -1,16 +1,16 @@
#ifndef WEBCC_HTTP_CLIENT_POOL_H_
#define WEBCC_HTTP_CLIENT_POOL_H_
#ifndef WEBCC_CLIENT_POOL_H_
#define WEBCC_CLIENT_POOL_H_
#include <map>
#include <string>
#include "webcc/http_client.h"
#include "webcc/client.h"
#include "webcc/url.h"
namespace webcc {
// Connection pool for keep-alive connections.
class HttpClientPool {
class ClientPool {
public:
struct Key {
std::string scheme;
@ -42,20 +42,20 @@ public:
};
public:
HttpClientPool() = default;
ClientPool() = default;
~HttpClientPool();
~ClientPool();
HttpClientPtr Get(const Key& key) const;
ClientPtr Get(const Key& key) const;
void Add(const Key& key, HttpClientPtr client);
void Add(const Key& key, ClientPtr client);
void Remove(const Key& key);
private:
std::map<Key, HttpClientPtr> clients_;
std::map<Key, ClientPtr> clients_;
};
} // namespace webcc
#endif // WEBCC_HTTP_CLIENT_POOL_H_
#endif // WEBCC_CLIENT_POOL_H_

@ -1,4 +1,4 @@
#include "webcc/http_client_session.h"
#include "webcc/client_session.h"
#include "webcc/base64.h"
#include "webcc/logger.h"
@ -6,26 +6,26 @@
namespace webcc {
HttpClientSession::HttpClientSession() {
ClientSession::ClientSession() {
InitHeaders();
}
void HttpClientSession::Auth(const std::string& type,
const std::string& credentials) {
headers_.Set(http::headers::kAuthorization, type + " " + credentials);
void ClientSession::Auth(const std::string& type,
const std::string& credentials) {
headers_.Set(headers::kAuthorization, type + " " + credentials);
}
void HttpClientSession::AuthBasic(const std::string& login,
const std::string& password) {
void ClientSession::AuthBasic(const std::string& login,
const std::string& password) {
auto credentials = Base64Encode(login + ":" + password);
return Auth("Basic", credentials);
}
void HttpClientSession::AuthToken(const std::string& token) {
void ClientSession::AuthToken(const std::string& token) {
return Auth("Token", token);
}
HttpResponsePtr HttpClientSession::Request(HttpRequestPtr request) {
ResponsePtr ClientSession::Request(RequestPtr request) {
assert(request);
for (const auto& h : headers_.data()) {
@ -35,7 +35,7 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestPtr request) {
}
if (!content_type_.empty() &&
!request->HasHeader(http::headers::kContentType)) {
!request->HasHeader(headers::kContentType)) {
request->SetContentType(content_type_, charset_);
}
@ -45,7 +45,7 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestPtr request) {
}
static void SetHeaders(const std::vector<std::string>& headers,
HttpRequestBuilder* builder) {
RequestBuilder* builder) {
assert(headers.size() % 2 == 0);
for (std::size_t i = 1; i < headers.size(); i += 2) {
@ -53,10 +53,10 @@ static void SetHeaders(const std::vector<std::string>& headers,
}
}
HttpResponsePtr HttpClientSession::Get(
const std::string& url, const std::vector<std::string>& parameters,
const std::vector<std::string>& headers) {
HttpRequestBuilder builder;
ResponsePtr ClientSession::Get(const std::string& url,
const std::vector<std::string>& parameters,
const std::vector<std::string>& headers) {
RequestBuilder builder;
builder.Get().Url(url);
assert(parameters.size() % 2 == 0);
@ -69,10 +69,10 @@ HttpResponsePtr HttpClientSession::Get(
return Request(builder());
}
HttpResponsePtr HttpClientSession::Post(
const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers) {
HttpRequestBuilder builder;
ResponsePtr ClientSession::Post(const std::string& url, std::string&& data,
bool json,
const std::vector<std::string>& headers) {
RequestBuilder builder;
builder.Post().Url(url);
SetHeaders(headers, &builder);
@ -83,10 +83,10 @@ HttpResponsePtr HttpClientSession::Post(
return Request(builder());
}
HttpResponsePtr HttpClientSession::Put(
const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers) {
HttpRequestBuilder builder;
ResponsePtr ClientSession::Put(const std::string& url, std::string&& data,
bool json,
const std::vector<std::string>& headers) {
RequestBuilder builder;
builder.Put().Url(url);
SetHeaders(headers, &builder);
@ -97,9 +97,9 @@ HttpResponsePtr HttpClientSession::Put(
return Request(builder());
}
HttpResponsePtr HttpClientSession::Delete(
const std::string& url, const std::vector<std::string>& headers) {
HttpRequestBuilder builder;
ResponsePtr ClientSession::Delete(const std::string& url,
const std::vector<std::string>& headers) {
RequestBuilder builder;
builder.Delete().Url(url);
SetHeaders(headers, &builder);
@ -107,10 +107,10 @@ HttpResponsePtr HttpClientSession::Delete(
return Request(builder());
}
HttpResponsePtr HttpClientSession::Patch(
const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers) {
HttpRequestBuilder builder;
ResponsePtr ClientSession::Patch(const std::string& url, std::string&& data,
bool json,
const std::vector<std::string>& headers) {
RequestBuilder builder;
builder.Patch().Url(url);
SetHeaders(headers, &builder);
@ -121,10 +121,10 @@ HttpResponsePtr HttpClientSession::Patch(
return Request(builder());
}
HttpResponsePtr HttpClientSession::PostFile(
const std::string& url, const std::string& name, const Path& path,
const std::vector<std::string>& headers) {
HttpRequestBuilder builder;
ResponsePtr ClientSession::PostFile(const std::string& url,
const std::string& name, const Path& path,
const std::vector<std::string>& headers) {
RequestBuilder builder;
builder.Post().Url(url);
SetHeaders(headers, &builder);
@ -134,12 +134,12 @@ HttpResponsePtr HttpClientSession::PostFile(
return Request(builder());
}
HttpResponsePtr HttpClientSession::PostFiles(
const std::string& url, const std::map<std::string, Path>& paths,
const std::vector<std::string>& headers) {
ResponsePtr ClientSession::PostFiles(const std::string& url,
const std::map<std::string, Path>& paths,
const std::vector<std::string>& headers) {
assert(!paths.empty());
HttpRequestBuilder builder;
RequestBuilder builder;
builder.Post().Url(url);
SetHeaders(headers, &builder);
@ -151,10 +151,10 @@ HttpResponsePtr HttpClientSession::PostFiles(
return Request(builder());
}
void HttpClientSession::InitHeaders() {
using namespace http::headers;
void ClientSession::InitHeaders() {
using namespace headers;
headers_.Set(kUserAgent, http::UserAgent());
headers_.Set(kUserAgent, UserAgent());
// Content-Encoding Tokens:
// (https://en.wikipedia.org/wiki/HTTP_compression)
@ -184,15 +184,15 @@ void HttpClientSession::InitHeaders() {
headers_.Set(kConnection, "Keep-Alive");
}
HttpResponsePtr HttpClientSession::Send(HttpRequestPtr request) {
const HttpClientPool::Key key{request->url()};
ResponsePtr ClientSession::Send(RequestPtr request) {
const ClientPool::Key key{request->url()};
// Reuse a pooled connection.
bool reuse = false;
HttpClientPtr client = pool_.Get(key);
ClientPtr client = pool_.Get(key);
if (!client) {
client.reset(new HttpClient{});
client.reset(new Client{});
reuse = false;
} else {
LOG_VERB("Reuse an existing connection.");

@ -1,24 +1,23 @@
#ifndef WEBCC_HTTP_CLIENT_SESSION_H_
#define WEBCC_HTTP_CLIENT_SESSION_H_
#ifndef WEBCC_CLIENT_SESSION_H_
#define WEBCC_CLIENT_SESSION_H_
#include <string>
#include <vector>
#include "webcc/http_client_pool.h"
#include "webcc/http_request_builder.h"
#include "webcc/http_response.h"
#include "webcc/client_pool.h"
#include "webcc/request_builder.h"
#include "webcc/response.h"
namespace webcc {
// HTTP requests session providing connection-pooling, configuration and more.
// NOTE:
// A session shouldn't be shared by multiple threads. Please create a new
// session for each thread instead.
class HttpClientSession {
class ClientSession {
public:
HttpClientSession();
ClientSession();
~HttpClientSession() = default;
~ClientSession() = default;
void set_ssl_verify(bool ssl_verify) {
ssl_verify_ = ssl_verify;
@ -56,44 +55,44 @@ public:
void AuthToken(const std::string& token);
// Send a request.
// Please use HttpRequestBuilder to build the request.
HttpResponsePtr Request(HttpRequestPtr request);
// Please use RequestBuilder to build the request.
ResponsePtr Request(RequestPtr request);
// Shortcut for GET request.
HttpResponsePtr Get(const std::string& url,
const std::vector<std::string>& parameters = {},
const std::vector<std::string>& headers = {});
ResponsePtr Get(const std::string& url,
const std::vector<std::string>& parameters = {},
const std::vector<std::string>& headers = {});
// Shortcut for POST request.
HttpResponsePtr Post(const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers = {});
ResponsePtr Post(const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers = {});
// Shortcut for PUT request.
HttpResponsePtr Put(const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers = {});
ResponsePtr Put(const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers = {});
// Shortcut for DELETE request.
HttpResponsePtr Delete(const std::string& url,
const std::vector<std::string>& headers = {});
ResponsePtr Delete(const std::string& url,
const std::vector<std::string>& headers = {});
// Shortcut for PATCH request.
HttpResponsePtr Patch(const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers = {});
ResponsePtr Patch(const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers = {});
// Post a file.
HttpResponsePtr PostFile(const std::string& url, const std::string& name,
const Path& path,
const std::vector<std::string>& headers = {});
ResponsePtr PostFile(const std::string& url, const std::string& name,
const Path& path,
const std::vector<std::string>& headers = {});
// Post multiple files.
HttpResponsePtr PostFiles(const std::string& url,
const std::map<std::string, Path>& paths,
const std::vector<std::string>& headers = {});
ResponsePtr PostFiles(const std::string& url,
const std::map<std::string, Path>& paths,
const std::vector<std::string>& headers = {});
private:
void InitHeaders();
HttpResponsePtr Send(HttpRequestPtr request);
ResponsePtr Send(RequestPtr request);
private:
// E.g., "application/json".
@ -103,7 +102,7 @@ private:
std::string charset_;
// Additional headers for each request.
HttpHeaders headers_;
Headers headers_;
// Verify the certificate of the peer or not.
bool ssl_verify_ = true;
@ -116,9 +115,9 @@ private:
int timeout_ = 0;
// Connection pool for keep-alive.
HttpClientPool pool_;
ClientPool pool_;
};
} // namespace webcc
#endif // WEBCC_HTTP_CLIENT_SESSION_H_
#endif // WEBCC_CLIENT_SESSION_H_

@ -56,7 +56,7 @@ bool ReadFile(const Path& path, std::string* output) {
// -----------------------------------------------------------------------------
void HttpHeaders::Set(const std::string& key, const std::string& value) {
void Headers::Set(const std::string& key, const std::string& value) {
auto it = Find(key);
if (it != headers_.end()) {
it->second = value;
@ -65,7 +65,7 @@ void HttpHeaders::Set(const std::string& key, const std::string& value) {
}
}
void HttpHeaders::Set(std::string&& key, std::string&& value) {
void Headers::Set(std::string&& key, std::string&& value) {
auto it = Find(key);
if (it != headers_.end()) {
it->second = std::move(value);
@ -74,13 +74,13 @@ void HttpHeaders::Set(std::string&& key, std::string&& value) {
}
}
bool HttpHeaders::Has(const std::string& key) const {
return const_cast<HttpHeaders*>(this)->Find(key) != headers_.end();
bool Headers::Has(const std::string& key) const {
return const_cast<Headers*>(this)->Find(key) != headers_.end();
}
const std::string& HttpHeaders::Get(const std::string& key,
const std::string& Headers::Get(const std::string& key,
bool* existed) const {
auto it = const_cast<HttpHeaders*>(this)->Find(key);
auto it = const_cast<Headers*>(this)->Find(key);
if (existed != nullptr) {
*existed = (it != headers_.end());
@ -94,7 +94,7 @@ const std::string& HttpHeaders::Get(const std::string& key,
return s_no_value;
}
std::vector<HttpHeader>::iterator HttpHeaders::Find(const std::string& key) {
std::vector<Header>::iterator Headers::Find(const std::string& key) {
auto it = headers_.begin();
for (; it != headers_.end(); ++it) {
if (boost::iequals(it->first, key)) {
@ -225,7 +225,7 @@ FormPart::FormPart(const std::string& name, const Path& path,
// Determine media type from file extension.
if (media_type_.empty()) {
std::string extension = path.extension().string();
media_type_ = http::media_types::FromExtension(extension, false);
media_type_ = media_types::FromExtension(extension, false);
}
}
@ -244,7 +244,7 @@ void FormPart::Prepare(Payload* payload) {
using boost::asio::buffer;
for (const HttpHeader& h : headers_.data()) {
for (const Header& h : headers_.data()) {
payload->push_back(buffer(h.first));
payload->push_back(buffer(misc_strings::HEADER_SEPARATOR));
payload->push_back(buffer(h.second));
@ -270,12 +270,12 @@ void FormPart::SetHeaders() {
if (!file_name_.empty()) {
content_disposition.append("; filename=\"" + file_name_ + "\"");
}
headers_.Set(http::headers::kContentDisposition, content_disposition);
headers_.Set(headers::kContentDisposition, content_disposition);
// Header: Content-Type
if (!media_type_.empty()) {
headers_.Set(http::headers::kContentType, media_type_);
headers_.Set(headers::kContentType, media_type_);
}
}

@ -30,9 +30,9 @@ bool ReadFile(const Path& path, std::string* output);
// -----------------------------------------------------------------------------
typedef std::pair<std::string, std::string> HttpHeader;
using Header = std::pair<std::string, std::string>;
class HttpHeaders {
class Headers {
public:
std::size_t size() const {
return headers_.size();
@ -42,7 +42,7 @@ public:
return headers_.empty();
}
const std::vector<HttpHeader>& data() const {
const std::vector<Header>& data() const {
return headers_;
}
@ -53,7 +53,7 @@ public:
bool Has(const std::string& key) const;
// Get header by index.
const HttpHeader& Get(std::size_t index) const {
const Header& Get(std::size_t index) const {
assert(index < size());
return headers_[index];
}
@ -68,9 +68,9 @@ public:
}
private:
std::vector<HttpHeader>::iterator Find(const std::string& key);
std::vector<Header>::iterator Find(const std::string& key);
std::vector<HttpHeader> headers_;
std::vector<Header> headers_;
};
// -----------------------------------------------------------------------------
@ -261,7 +261,7 @@ private:
// Headers generated from the above properties.
// Only Used to prepare payload.
HttpHeaders headers_;
Headers headers_;
std::string data_;
};

@ -1,32 +1,32 @@
#include "webcc/http_connection.h"
#include "webcc/connection.h"
#include <utility> // for move()
#include "boost/asio/write.hpp"
#include "webcc/http_connection_pool.h"
#include "webcc/http_request_handler.h"
#include "webcc/connection_pool.h"
#include "webcc/logger.h"
#include "webcc/request_handler.h"
using boost::asio::ip::tcp;
namespace webcc {
HttpConnection::HttpConnection(tcp::socket socket, HttpConnectionPool* pool,
HttpRequestHandler* handler)
Connection::Connection(tcp::socket socket, ConnectionPool* pool,
RequestHandler* handler)
: socket_(std::move(socket)),
pool_(pool),
buffer_(kBufferSize),
request_handler_(handler) {
}
void HttpConnection::Start() {
request_.reset(new HttpRequest{});
void Connection::Start() {
request_.reset(new Request{});
request_parser_.Init(request_.get());
DoRead();
}
void HttpConnection::Close() {
void Connection::Close() {
LOG_INFO("Close socket...");
boost::system::error_code ec;
@ -36,15 +36,15 @@ void HttpConnection::Close() {
}
}
void HttpConnection::SendResponse(HttpResponsePtr response) {
void Connection::SendResponse(ResponsePtr response) {
assert(response);
response_ = response;
if (request_->IsConnectionKeepAlive()) {
response_->SetHeader(http::headers::kConnection, "Keep-Alive");
response_->SetHeader(headers::kConnection, "Keep-Alive");
} else {
response_->SetHeader(http::headers::kConnection, "Close");
response_->SetHeader(headers::kConnection, "Close");
}
response_->Prepare();
@ -52,18 +52,18 @@ void HttpConnection::SendResponse(HttpResponsePtr response) {
DoWrite();
}
void HttpConnection::SendResponse(http::Status status) {
SendResponse(std::make_shared<HttpResponse>(status));
void Connection::SendResponse(Status status) {
SendResponse(std::make_shared<Response>(status));
}
void HttpConnection::DoRead() {
void Connection::DoRead() {
socket_.async_read_some(boost::asio::buffer(buffer_),
std::bind(&HttpConnection::OnRead, shared_from_this(),
std::bind(&Connection::OnRead, shared_from_this(),
std::placeholders::_1,
std::placeholders::_2));
}
void HttpConnection::OnRead(boost::system::error_code ec, std::size_t length) {
void Connection::OnRead(boost::system::error_code ec, std::size_t length) {
if (ec) {
LOG_ERRO("Socket read error (%s).", ec.message().c_str());
if (ec != boost::asio::error::operation_aborted) {
@ -75,7 +75,7 @@ void HttpConnection::OnRead(boost::system::error_code ec, std::size_t length) {
if (!request_parser_.Parse(buffer_.data(), length)) {
// Bad request.
LOG_ERRO("Failed to parse HTTP request.");
SendResponse(http::Status::kBadRequest);
SendResponse(Status::kBadRequest);
return;
}
@ -92,11 +92,11 @@ void HttpConnection::OnRead(boost::system::error_code ec, std::size_t length) {
request_handler_->Enqueue(shared_from_this());
}
void HttpConnection::DoWrite() {
void Connection::DoWrite() {
LOG_VERB("HTTP response:\n%s", response_->Dump(4, "> ").c_str());
boost::asio::async_write(socket_, response_->payload(),
std::bind(&HttpConnection::OnWrite, shared_from_this(),
std::bind(&Connection::OnWrite, shared_from_this(),
std::placeholders::_1,
std::placeholders::_2));
}
@ -105,7 +105,7 @@ void HttpConnection::DoWrite() {
// This write handler will be called from main thread (the thread calling
// io_context.run), even though AsyncWrite() is invoked by worker threads.
// This is ensured by Asio.
void HttpConnection::OnWrite(boost::system::error_code ec, std::size_t length) {
void Connection::OnWrite(boost::system::error_code ec, std::size_t length) {
if (ec) {
LOG_ERRO("Socket write error (%s).", ec.message().c_str());
@ -128,7 +128,7 @@ void HttpConnection::OnWrite(boost::system::error_code ec, std::size_t length) {
// Socket close VS. shutdown:
// https://stackoverflow.com/questions/4160347/close-vs-shutdown-socket
void HttpConnection::Shutdown() {
void Connection::Shutdown() {
LOG_INFO("Shutdown socket...");
// Initiate graceful connection closure.

@ -0,0 +1,82 @@
#ifndef WEBCC_CONNECTION_H_
#define WEBCC_CONNECTION_H_
#include <memory>
#include <string>
#include <vector>
#include "boost/asio/ip/tcp.hpp" // for ip::tcp::socket
#include "webcc/globals.h"
#include "webcc/request.h"
#include "webcc/request_parser.h"
#include "webcc/response.h"
namespace webcc {
class Connection;
class ConnectionPool;
class RequestHandler;
using ConnectionPtr = std::shared_ptr<Connection>;
class Connection : public std::enable_shared_from_this<Connection> {
public:
Connection(boost::asio::ip::tcp::socket socket, ConnectionPool* pool,
RequestHandler* handler);
~Connection() = default;
Connection(const Connection&) = delete;
Connection& operator=(const Connection&) = delete;
RequestPtr request() const {
return request_;
}
// Start to read and process the client request.
void Start();
// Close the socket.
void Close();
// Send response to client.
void SendResponse(ResponsePtr response);
void SendResponse(Status status);
private:
void DoRead();
void OnRead(boost::system::error_code ec, std::size_t length);
void DoWrite();
void OnWrite(boost::system::error_code ec, std::size_t length);
// Shutdown the socket.
void Shutdown();
// Socket for the connection.
boost::asio::ip::tcp::socket socket_;
// The pool for this connection.
ConnectionPool* pool_;
// Buffer for incoming data.
std::vector<char> buffer_;
// The handler used to process the incoming request.
RequestHandler* request_handler_;
// The incoming request.
RequestPtr request_;
// The parser for the incoming request.
RequestParser request_parser_;
// The response to be sent back to the client.
ResponsePtr response_;
};
} // namespace webcc
#endif // WEBCC_CONNECTION_H_

@ -1,25 +1,25 @@
#include "webcc/http_connection_pool.h"
#include "webcc/connection_pool.h"
#include "webcc/logger.h"
namespace webcc {
HttpConnectionPool::HttpConnectionPool() {
ConnectionPool::ConnectionPool() {
}
void HttpConnectionPool::Start(HttpConnectionPtr c) {
void ConnectionPool::Start(ConnectionPtr c) {
LOG_VERB("Starting connection...");
connections_.insert(c);
c->Start();
}
void HttpConnectionPool::Close(HttpConnectionPtr c) {
void ConnectionPool::Close(ConnectionPtr c) {
LOG_VERB("Closing connection...");
connections_.erase(c);
c->Close();
}
void HttpConnectionPool::CloseAll() {
void ConnectionPool::CloseAll() {
LOG_VERB("Closing all (%u) connections...", connections_.size());
for (auto& c : connections_) {
c->Close();

@ -0,0 +1,33 @@
#ifndef WEBCC_CONNECTION_POOL_H_
#define WEBCC_CONNECTION_POOL_H_
#include <set>
#include "webcc/connection.h"
namespace webcc {
class ConnectionPool {
public:
ConnectionPool(const ConnectionPool&) = delete;
ConnectionPool& operator=(const ConnectionPool&) = delete;
ConnectionPool();
// Add a connection to the pool and start it.
void Start(ConnectionPtr c);
// Close a connection.
void Close(ConnectionPtr c);
// Close all connections.
void CloseAll();
private:
/// The managed connections.
std::set<ConnectionPtr> connections_;
};
} // namespace webcc
#endif // WEBCC_CONNECTION_POOL_H_

@ -8,8 +8,6 @@ namespace webcc {
// -----------------------------------------------------------------------------
namespace http {
const std::string& UserAgent() {
static std::string s_user_agent = std::string("Webcc/") + WEBCC_VERSION;
return s_user_agent;
@ -52,8 +50,6 @@ std::string FromExtension(const std::string& extension,
} // namespace media_types
} // namespace http
// -----------------------------------------------------------------------------
const char* DescribeError(Error error) {

@ -68,8 +68,6 @@ const std::size_t kGzipThreshold = 1400;
// -----------------------------------------------------------------------------
namespace http {
namespace methods {
// HTTP methods (verbs) in string.
@ -156,8 +154,6 @@ enum class ContentEncoding {
// Return default user agent for HTTP headers.
const std::string& UserAgent();
} // namespace http
// -----------------------------------------------------------------------------
// Client side error codes.

@ -1,82 +0,0 @@
#ifndef WEBCC_HTTP_CONNECTION_H_
#define WEBCC_HTTP_CONNECTION_H_
#include <memory>
#include <string>
#include <vector>
#include "boost/asio/ip/tcp.hpp" // for ip::tcp::socket
#include "webcc/globals.h"
#include "webcc/http_request.h"
#include "webcc/http_request_parser.h"
#include "webcc/http_response.h"
namespace webcc {
class HttpConnection;
class HttpConnectionPool;
class HttpRequestHandler;
typedef std::shared_ptr<HttpConnection> HttpConnectionPtr;
class HttpConnection : public std::enable_shared_from_this<HttpConnection> {
public:
HttpConnection(boost::asio::ip::tcp::socket socket, HttpConnectionPool* pool,
HttpRequestHandler* handler);
~HttpConnection() = default;
HttpConnection(const HttpConnection&) = delete;
HttpConnection& operator=(const HttpConnection&) = delete;
HttpRequestPtr request() const {
return request_;
}
// Start to read and process the client request.
void Start();
// Close the socket.
void Close();
// Send response to client.
void SendResponse(HttpResponsePtr response);
void SendResponse(http::Status status);
private:
void DoRead();
void OnRead(boost::system::error_code ec, std::size_t length);
void DoWrite();
void OnWrite(boost::system::error_code ec, std::size_t length);
// Shutdown the socket.
void Shutdown();
// Socket for the connection.
boost::asio::ip::tcp::socket socket_;
// The pool for this connection.
HttpConnectionPool* pool_;
// Buffer for incoming data.
std::vector<char> buffer_;
// The handler used to process the incoming request.
HttpRequestHandler* request_handler_;
// The incoming request.
HttpRequestPtr request_;
// The parser for the incoming request.
HttpRequestParser request_parser_;
// The response to be sent back to the client.
HttpResponsePtr response_;
};
} // namespace webcc
#endif // WEBCC_HTTP_CONNECTION_H_

@ -1,33 +0,0 @@
#ifndef WEBCC_HTTP_CONNECTION_POOL_H_
#define WEBCC_HTTP_CONNECTION_POOL_H_
#include <set>
#include "webcc/http_connection.h"
namespace webcc {
class HttpConnectionPool {
public:
HttpConnectionPool(const HttpConnectionPool&) = delete;
HttpConnectionPool& operator=(const HttpConnectionPool&) = delete;
HttpConnectionPool();
// Add a connection to the pool and start it.
void Start(HttpConnectionPtr c);
// Close a connection.
void Close(HttpConnectionPtr c);
// Close all connections.
void CloseAll();
private:
/// The managed connections.
std::set<HttpConnectionPtr> connections_;
};
} // namespace webcc
#endif // WEBCC_HTTP_CONNECTION_POOL_H_

@ -1,92 +0,0 @@
#include "webcc/http_request_builder.h"
#include "webcc/base64.h"
#include "webcc/logger.h"
#include "webcc/utility.h"
#include "webcc/zlib_wrapper.h"
namespace webcc {
HttpRequestPtr HttpRequestBuilder::Build() {
assert(parameters_.size() % 2 == 0);
assert(headers_.size() % 2 == 0);
auto request = std::make_shared<HttpRequest>(method_, url_);
for (std::size_t i = 1; i < parameters_.size(); i += 2) {
request->AddQuery(parameters_[i - 1], parameters_[i]);
}
for (std::size_t i = 1; i < headers_.size(); i += 2) {
request->SetHeader(std::move(headers_[i - 1]), std::move(headers_[i]));
}
// No keep-alive?
if (!keep_alive_) {
request->SetHeader(http::headers::kConnection, "Close");
}
if (!data_.empty()) {
SetContent(request, std::move(data_));
// TODO: Request-level charset.
if (json_) {
request->SetContentType(http::media_types::kApplicationJson, "");
}
} else if (!form_parts_.empty()) {
request->set_form_parts(std::move(form_parts_));
}
return request;
}
HttpRequestBuilder& HttpRequestBuilder::File(const std::string& name,
const Path& path,
const std::string& mime_type) {
assert(!name.empty());
form_parts_.push_back(FormPart{ name, path, mime_type });
return *this;
}
HttpRequestBuilder& HttpRequestBuilder::Form(const std::string& name,
std::string&& data,
const std::string& mime_type) {
assert(!name.empty());
form_parts_.push_back(FormPart{ name, std::move(data), mime_type });
return *this;
}
HttpRequestBuilder& HttpRequestBuilder::Auth(const std::string& type,
const std::string& credentials) {
headers_.push_back(http::headers::kAuthorization);
headers_.push_back(type + " " + credentials);
return *this;
}
HttpRequestBuilder& HttpRequestBuilder::AuthBasic(const std::string& login,
const std::string& password) {
auto credentials = Base64Encode(login + ":" + password);
return Auth("Basic", credentials);
}
HttpRequestBuilder& HttpRequestBuilder::AuthToken(const std::string& token) {
return Auth("Token", token);
}
void HttpRequestBuilder::SetContent(HttpRequestPtr request,
std::string&& data) {
if (gzip_ && data.size() > kGzipThreshold) {
std::string compressed;
if (Compress(data, &compressed)) {
request->SetContent(std::move(compressed), true);
request->SetHeader(http::headers::kContentEncoding, "gzip");
return;
}
LOG_WARN("Cannot compress the content data!");
}
request->SetContent(std::move(data), true);
}
} // namespace webcc

@ -1,144 +0,0 @@
#ifndef WEBCC_HTTP_REQUEST_BUILDER_H_
#define WEBCC_HTTP_REQUEST_BUILDER_H_
#include <string>
#include <vector>
#include "webcc/http_request.h"
namespace webcc {
class HttpRequestBuilder {
public:
explicit HttpRequestBuilder(const std::string& method = "")
: method_(method) {
}
~HttpRequestBuilder() = default;
HttpRequestBuilder(const HttpRequestBuilder&) = delete;
HttpRequestBuilder& operator=(const HttpRequestBuilder&) = delete;
// Build the request.
HttpRequestPtr Build();
HttpRequestPtr operator()() {
return Build();
}
HttpRequestBuilder& Get() { return Method(http::methods::kGet); }
HttpRequestBuilder& Head() { return Method(http::methods::kHead); }
HttpRequestBuilder& Post() { return Method(http::methods::kPost); }
HttpRequestBuilder& Put() { return Method(http::methods::kPut); }
HttpRequestBuilder& Delete() { return Method(http::methods::kDelete); }
HttpRequestBuilder& Patch() { return Method(http::methods::kPatch); }
// NOTE:
// The naming convention doesn't follow Google C++ Style for
// consistency and simplicity.
HttpRequestBuilder& Method(const std::string& method) {
method_ = method;
return *this;
}
HttpRequestBuilder& Url(const std::string& url) {
url_ = url;
return *this;
}
HttpRequestBuilder& Query(const std::string& key,
const std::string& value) {
parameters_.push_back(key);
parameters_.push_back(value);
return *this;
}
HttpRequestBuilder& Data(const std::string& data) {
data_ = data;
return *this;
}
HttpRequestBuilder& Data(std::string&& data) {
data_ = std::move(data);
return *this;
}
HttpRequestBuilder& Json(bool json = true) {
json_ = json;
return *this;
}
// Upload a file.
HttpRequestBuilder& File(const std::string& name, const Path& path,
const std::string& mime_type = "");
HttpRequestBuilder& Form(FormPart&& part) {
form_parts_.push_back(std::move(part));
return *this;
}
HttpRequestBuilder& Form(const std::string& name, std::string&& data,
const std::string& mime_type = "");
HttpRequestBuilder& Gzip(bool gzip = true) {
gzip_ = gzip;
return *this;
}
HttpRequestBuilder& Header(const std::string& key,
const std::string& value) {
headers_.push_back(key);
headers_.push_back(value);
return *this;
}
HttpRequestBuilder& KeepAlive(bool keep_alive = true) {
keep_alive_ = keep_alive;
return *this;
}
HttpRequestBuilder& Auth(const std::string& type,
const std::string& credentials);
HttpRequestBuilder& AuthBasic(const std::string& login,
const std::string& password);
HttpRequestBuilder& AuthToken(const std::string& token);
private:
void SetContent(HttpRequestPtr request, std::string&& data);
private:
std::string method_;
std::string url_;
// URL query parameters.
std::vector<std::string> parameters_;
// Data to send in the body of the request.
std::string data_;
// Is the data to send a JSON string?
bool json_ = false;
// Files to upload for a POST request.
std::vector<FormPart> form_parts_;
// Compress the request content.
// NOTE: Most servers don't support compressed requests.
// Even the requests module from Python doesn't have a built-in support.
// See: https://github.com/kennethreitz/requests/issues/1753
bool gzip_ = false;
// Additional request headers.
std::vector<std::string> headers_;
// Persistent connection.
bool keep_alive_ = true;
};
} // namespace webcc
#endif // WEBCC_HTTP_REQUEST_BUILDER_H_

@ -1,48 +0,0 @@
#ifndef WEBCC_HTTP_REQUEST_HANDLER_H_
#define WEBCC_HTTP_REQUEST_HANDLER_H_
#include <list>
#include <thread>
#include <vector>
#include "webcc/http_connection.h"
#include "webcc/queue.h"
namespace webcc {
class HttpRequest;
class HttpResponse;
// The common handler for all incoming requests.
class HttpRequestHandler {
public:
HttpRequestHandler() = default;
virtual ~HttpRequestHandler() = default;
HttpRequestHandler(const HttpRequestHandler&) = delete;
HttpRequestHandler& operator=(const HttpRequestHandler&) = delete;
// Put the connection into the queue.
void Enqueue(HttpConnectionPtr connection);
// Start worker threads.
void Start(std::size_t count);
// Clear pending connections from the queue and stop worker threads.
void Stop();
private:
void WorkerRoutine();
// Called by the worker routine.
virtual void HandleConnection(HttpConnectionPtr connection) = 0;
private:
Queue<HttpConnectionPtr> queue_;
std::vector<std::thread> workers_;
};
} // namespace webcc
#endif // WEBCC_HTTP_REQUEST_HANDLER_H_

@ -1,39 +0,0 @@
#ifndef WEBCC_HTTP_RESPONSE_H_
#define WEBCC_HTTP_RESPONSE_H_
#include <memory>
#include <string>
#include "webcc/http_message.h"
namespace webcc {
class HttpResponse;
typedef std::shared_ptr<HttpResponse> HttpResponsePtr;
class HttpResponse : public HttpMessage {
public:
explicit HttpResponse(http::Status status = http::Status::kOK)
: status_(status) {
}
~HttpResponse() override = default;
int status() const {
return status_;
}
void set_status(int status) {
status_ = status;
}
// Set start line according to status code.
void Prepare() final;
private:
int status_;
};
} // namespace webcc
#endif // WEBCC_HTTP_RESPONSE_H_

@ -1,30 +0,0 @@
#ifndef WEBCC_HTTP_RESPONSE_PARSER_H_
#define WEBCC_HTTP_RESPONSE_PARSER_H_
#include <string>
#include "webcc/http_parser.h"
namespace webcc {
class HttpResponse;
class HttpResponseParser : public HttpParser {
public:
explicit HttpResponseParser(HttpResponse* response = nullptr);
~HttpResponseParser() override = default;
void Init(HttpResponse* response);
private:
// Parse HTTP start line; E.g., "HTTP/1.1 200 OK".
bool ParseStartLine(const std::string& line) final;
// The result response message.
HttpResponse* response_;
};
} // namespace webcc
#endif // WEBCC_HTTP_RESPONSE_PARSER_H_

@ -214,15 +214,17 @@ void LogInit(const std::string& dir, int modes) {
}
static std::string GetTimestamp() {
auto now = std::chrono::system_clock::now();
std::time_t t = std::chrono::system_clock::to_time_t(now);
using system_clock = std::chrono::system_clock;
using milliseconds = std::chrono::milliseconds;
auto now = system_clock::now();
std::time_t t = system_clock::to_time_t(now);
std::stringstream ss;
ss << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S");
std::chrono::milliseconds milli_seconds =
std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch());
milliseconds milli_seconds =
std::chrono::duration_cast<milliseconds>(now.time_since_epoch());
std::string micro_seconds_str = std::to_string(milli_seconds.count() % 1000);
while (micro_seconds_str.size() < 3) {
micro_seconds_str = "0" + micro_seconds_str;
@ -233,7 +235,7 @@ static std::string GetTimestamp() {
return ss.str();
}
void LogWrite(int level, const char* file, int line, const char* format, ...) {
void Log(int level, const char* file, int line, const char* format, ...) {
assert(format != nullptr);
std::string timestamp = GetTimestamp();

@ -41,7 +41,7 @@ const int LOG_FILE_OVERWRITE = LOG_FILE | LOG_OVERWRITE;
// If |dir| is empty, log file will be generated in current directory.
void LogInit(const std::string& dir, int modes);
void LogWrite(int level, const char* file, int line, const char* format, ...);
void Log(int level, const char* file, int line, const char* format, ...);
} // namespace webcc
@ -58,35 +58,35 @@ void LogWrite(int level, const char* file, int line, const char* format, ...);
#if WEBCC_LOG_LEVEL <= WEBCC_VERB
#define LOG_VERB(format, ...) \
webcc::LogWrite(WEBCC_VERB, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
webcc::Log(WEBCC_VERB, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
#else
#define LOG_VERB(format, ...)
#endif
#if WEBCC_LOG_LEVEL <= WEBCC_INFO
#define LOG_INFO(format, ...) \
webcc::LogWrite(WEBCC_INFO, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
webcc::Log(WEBCC_INFO, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
#else
#define LOG_INFO(format, ...)
#endif
#if WEBCC_LOG_LEVEL <= WEBCC_WARN
#define LOG_WARN(format, ...) \
webcc::LogWrite(WEBCC_WARN, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
webcc::Log(WEBCC_WARN, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
#else
#define LOG_WARN(format, ...)
#endif
#if WEBCC_LOG_LEVEL <= WEBCC_ERRO
#define LOG_ERRO(format, ...) \
webcc::LogWrite(WEBCC_ERRO, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
webcc::Log(WEBCC_ERRO, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
#else
#define LOG_ERRO(format, ...)
#endif
#if WEBCC_LOG_LEVEL <= WEBCC_FATA
#define LOG_FATA(format, ...) \
webcc::LogWrite(WEBCC_FATA, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
webcc::Log(WEBCC_FATA, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
#else
#define LOG_FATA(format, ...)
#endif
@ -98,35 +98,35 @@ void LogWrite(int level, const char* file, int line, const char* format, ...);
#if WEBCC_LOG_LEVEL <= WEBCC_VERB
#define LOG_VERB(format, args...) \
webcc::LogWrite(WEBCC_VERB, __FILENAME__, __LINE__, format, ##args);
webcc::Log(WEBCC_VERB, __FILENAME__, __LINE__, format, ##args);
#else
#define LOG_VERB(format, args...)
#endif
#if WEBCC_LOG_LEVEL <= WEBCC_INFO
#define LOG_INFO(format, args...) \
webcc::LogWrite(WEBCC_INFO, __FILENAME__, __LINE__, format, ##args);
webcc::Log(WEBCC_INFO, __FILENAME__, __LINE__, format, ##args);
#else
#define LOG_INFO(format, args...)
#endif
#if WEBCC_LOG_LEVEL <= WEBCC_WARN
#define LOG_WARN(format, args...) \
webcc::LogWrite(WEBCC_WARN, __FILENAME__, __LINE__, format, ##args);
webcc::Log(WEBCC_WARN, __FILENAME__, __LINE__, format, ##args);
#else
#define LOG_WARN(format, args...)
#endif
#if WEBCC_LOG_LEVEL <= WEBCC_ERRO
#define LOG_ERRO(format, args...) \
webcc::LogWrite(WEBCC_ERRO, __FILENAME__, __LINE__, format, ##args);
webcc::Log(WEBCC_ERRO, __FILENAME__, __LINE__, format, ##args);
#else
#define LOG_ERRO(format, args...)
#endif
#if WEBCC_LOG_LEVEL <= WEBCC_FATA
#define LOG_FATA(format, args...) \
webcc::LogWrite(WEBCC_FATA, __FILENAME__, __LINE__, format, ##args);
webcc::Log(WEBCC_FATA, __FILENAME__, __LINE__, format, ##args);
#else
#define LOG_FATA(format, args...)
#endif

@ -1,5 +1,7 @@
#include "webcc/http_message.h"
#include "webcc/message.h"
#include <ctime>
#include <iomanip> // for put_time
#include <sstream>
#include "boost/algorithm/string.hpp"
@ -22,15 +24,15 @@ const char CRLF[] = { '\r', '\n' };
// -----------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& os, const HttpMessage& message) {
std::ostream& operator<<(std::ostream& os, const Message& message) {
message.Dump(os);
return os;
}
// -----------------------------------------------------------------------------
bool HttpMessage::IsConnectionKeepAlive() const {
using http::headers::kConnection;
bool Message::IsConnectionKeepAlive() const {
using headers::kConnection;
bool existed = false;
const std::string& connection = GetHeader(kConnection, &existed);
@ -47,29 +49,29 @@ bool HttpMessage::IsConnectionKeepAlive() const {
return false;
}
http::ContentEncoding HttpMessage::GetContentEncoding() const {
using http::headers::kContentEncoding;
ContentEncoding Message::GetContentEncoding() const {
using headers::kContentEncoding;
const std::string& encoding = GetHeader(kContentEncoding);
if (encoding == "gzip") {
return http::ContentEncoding::kGzip;
return ContentEncoding::kGzip;
}
if (encoding == "deflate") {
return http::ContentEncoding::kDeflate;
return ContentEncoding::kDeflate;
}
return http::ContentEncoding::kUnknown;
return ContentEncoding::kUnknown;
}
bool HttpMessage::AcceptEncodingGzip() const {
using http::headers::kAcceptEncoding;
bool Message::AcceptEncodingGzip() const {
using headers::kAcceptEncoding;
return GetHeader(kAcceptEncoding).find("gzip") != std::string::npos;
}
// See: https://tools.ietf.org/html/rfc7231#section-3.1.1.1
void HttpMessage::SetContentType(const std::string& media_type,
const std::string& charset) {
using http::headers::kContentType;
void Message::SetContentType(const std::string& media_type,
const std::string& charset) {
using headers::kContentType;
if (charset.empty()) {
SetHeader(kContentType, media_type);
@ -78,14 +80,14 @@ void HttpMessage::SetContentType(const std::string& media_type,
}
}
void HttpMessage::SetContent(std::string&& content, bool set_length) {
void Message::SetContent(std::string&& content, bool set_length) {
content_ = std::move(content);
if (set_length) {
SetContentLength(content_.size());
}
}
void HttpMessage::Prepare() {
void Message::Prepare() {
assert(!start_line_.empty());
using boost::asio::buffer;
@ -95,7 +97,7 @@ void HttpMessage::Prepare() {
payload_.push_back(buffer(start_line_));
payload_.push_back(buffer(misc_strings::CRLF));
for (const HttpHeader& h : headers_.data()) {
for (const Header& h : headers_.data()) {
payload_.push_back(buffer(h.first));
payload_.push_back(buffer(misc_strings::HEADER_SEPARATOR));
payload_.push_back(buffer(h.second));
@ -109,20 +111,20 @@ void HttpMessage::Prepare() {
}
}
void HttpMessage::CopyPayload(std::ostream& os) const {
void Message::CopyPayload(std::ostream& os) const {
for (const boost::asio::const_buffer& b : payload_) {
os.write(static_cast<const char*>(b.data()), b.size());
}
}
void HttpMessage::CopyPayload(std::string* str) const {
void Message::CopyPayload(std::string* str) const {
std::stringstream ss;
CopyPayload(ss);
*str = ss.str();
}
void HttpMessage::Dump(std::ostream& os, std::size_t indent,
const std::string& prefix) const {
void Message::Dump(std::ostream& os, std::size_t indent,
const std::string& prefix) const {
std::string indent_str;
if (indent > 0) {
indent_str.append(indent, ' ');
@ -131,7 +133,7 @@ void HttpMessage::Dump(std::ostream& os, std::size_t indent,
os << indent_str << start_line_ << std::endl;
for (const HttpHeader& h : headers_.data()) {
for (const Header& h : headers_.data()) {
os << indent_str << h.first << ": " << h.second << std::endl;
}
@ -172,11 +174,18 @@ void HttpMessage::Dump(std::ostream& os, std::size_t indent,
}
}
std::string HttpMessage::Dump(std::size_t indent,
const std::string& prefix) const {
std::string Message::Dump(std::size_t indent,
const std::string& prefix) const {
std::stringstream ss;
Dump(ss, indent, prefix);
return ss.str();
}
std::string Message::GetTimestamp() {
std::time_t t = std::time(nullptr);
std::stringstream ss;
ss << std::put_time(std::gmtime(&t), "%a, %d %b %Y %H:%M:%S") << " GMT";
return ss.str();
}
} // namespace webcc

@ -1,5 +1,5 @@
#ifndef WEBCC_HTTP_MESSAGE_H_
#define WEBCC_HTTP_MESSAGE_H_
#ifndef WEBCC_MESSAGE_H_
#define WEBCC_MESSAGE_H_
#include <cassert>
#include <string>
@ -11,16 +11,16 @@
namespace webcc {
class HttpMessage;
std::ostream& operator<<(std::ostream& os, const HttpMessage& message);
class Message;
std::ostream& operator<<(std::ostream& os, const Message& message);
// Base class for HTTP request and response messages.
class HttpMessage {
class Message {
public:
HttpMessage() : content_length_(kInvalidLength) {
Message() : content_length_(kInvalidLength) {
}
virtual ~HttpMessage() = default;
virtual ~Message() = default;
const std::string& start_line() const {
return start_line_;
@ -40,7 +40,7 @@ public:
bool IsConnectionKeepAlive() const;
void SetHeader(HttpHeader&& header) {
void SetHeader(Header&& header) {
headers_.Set(std::move(header.first), std::move(header.second));
}
@ -57,7 +57,7 @@ public:
return headers_.Has(key);
}
http::ContentEncoding GetContentEncoding() const;
ContentEncoding GetContentEncoding() const;
// Return true if header Accept-Encoding contains "gzip".
bool AcceptEncodingGzip() const;
@ -72,7 +72,7 @@ public:
}
void SetContentType(const std::string& content_type) {
SetHeader(http::headers::kContentType, content_type);
SetHeader(headers::kContentType, content_type);
}
// Example: SetContentType("application/json", "utf-8")
@ -105,9 +105,14 @@ public:
protected:
void SetContentLength(std::size_t content_length) {
content_length_ = content_length;
SetHeader(http::headers::kContentLength, std::to_string(content_length));
SetHeader(headers::kContentLength, std::to_string(content_length));
}
// Get the timestamp for HTTP Date header field.
// E.g., Wed, 21 Oct 2015 07:28:00 GMT
// See: https://tools.ietf.org/html/rfc7231#section-7.1.1.2
std::string GetTimestamp();
protected:
std::string start_line_;
@ -117,7 +122,7 @@ protected:
std::size_t content_length_;
HttpHeaders headers_;
Headers headers_;
// NOTE: The payload itself doesn't hold the memory!
Payload payload_;
@ -125,4 +130,4 @@ protected:
} // namespace webcc
#endif // WEBCC_HTTP_MESSAGE_H_
#endif // WEBCC_MESSAGE_H_

@ -1,9 +1,9 @@
#include "webcc/http_parser.h"
#include "webcc/parser.h"
#include "boost/algorithm/string.hpp"
#include "webcc/http_message.h"
#include "webcc/logger.h"
#include "webcc/message.h"
#include "webcc/utility.h"
#include "webcc/zlib_wrapper.h"
@ -26,7 +26,7 @@ bool StringToSizeT(const std::string& str, int base, std::size_t* output) {
// -----------------------------------------------------------------------------
HttpParser::HttpParser(HttpMessage* message)
Parser::Parser(Message* message)
: message_(message),
content_length_(kInvalidLength),
start_line_parsed_(false),
@ -37,12 +37,12 @@ HttpParser::HttpParser(HttpMessage* message)
finished_(false) {
}
void HttpParser::Init(HttpMessage* message) {
void Parser::Init(Message* message) {
Reset();
message_ = message;
}
bool HttpParser::Parse(const char* data, std::size_t length) {
bool Parser::Parse(const char* data, std::size_t length) {
if (header_ended_) {
return ParseContent(data, length);
}
@ -64,7 +64,7 @@ bool HttpParser::Parse(const char* data, std::size_t length) {
}
}
void HttpParser::Reset() {
void Parser::Reset() {
pending_data_.clear();
content_.clear();
@ -77,7 +77,7 @@ void HttpParser::Reset() {
finished_ = false;
}
bool HttpParser::ParseHeaders() {
bool Parser::ParseHeaders() {
std::size_t off = 0;
while (true) {
@ -113,8 +113,7 @@ bool HttpParser::ParseHeaders() {
return true;
}
bool HttpParser::GetNextLine(std::size_t off, std::string* line,
bool erase) {
bool Parser::GetNextLine(std::size_t off, std::string* line, bool erase) {
std::size_t pos = pending_data_.find(kCRLF, off);
if (pos == std::string::npos) {
@ -134,15 +133,15 @@ bool HttpParser::GetNextLine(std::size_t off, std::string* line,
return true;
}
bool HttpParser::ParseHeaderLine(const std::string& line) {
HttpHeader header;
bool Parser::ParseHeaderLine(const std::string& line) {
Header header;
if (!Split2(line, ':', &header.first, &header.second)) {
return false;
}
do {
if (!chunked_ && !content_length_parsed_) {
if (boost::iequals(header.first, http::headers::kContentLength)) {
if (boost::iequals(header.first, headers::kContentLength)) {
content_length_parsed_ = true;
if (!StringToSizeT(header.second, 10, &content_length_)) {
@ -166,7 +165,7 @@ bool HttpParser::ParseHeaderLine(const std::string& line) {
// TODO: Replace `!chunked_` with <TransferEncodingParsed>.
if (!chunked_ && !content_length_parsed_) {
if (boost::iequals(header.first, http::headers::kTransferEncoding)) {
if (boost::iequals(header.first, headers::kTransferEncoding)) {
if (header.second == "chunked") {
// The content is chunked.
chunked_ = true;
@ -178,7 +177,7 @@ bool HttpParser::ParseHeaderLine(const std::string& line) {
} while (false);
// Parse Content-Type.
if (boost::iequals(header.first, http::headers::kContentType)) {
if (boost::iequals(header.first, headers::kContentType)) {
ContentType content_type(header.second);
if (!content_type.Valid()) {
LOG_ERRO("Invalid content-type header: %s", header.second.c_str());
@ -192,7 +191,7 @@ bool HttpParser::ParseHeaderLine(const std::string& line) {
return true;
}
bool HttpParser::ParseContent(const char* data, std::size_t length) {
bool Parser::ParseContent(const char* data, std::size_t length) {
if (chunked_) {
return ParseChunkedContent(data, length);
} else {
@ -200,7 +199,7 @@ bool HttpParser::ParseContent(const char* data, std::size_t length) {
}
}
bool HttpParser::ParseFixedContent(const char* data, std::size_t length) {
bool Parser::ParseFixedContent(const char* data, std::size_t length) {
if (!content_length_parsed_) {
// No Content-Length, no content.
Finish();
@ -229,7 +228,7 @@ bool HttpParser::ParseFixedContent(const char* data, std::size_t length) {
return true;
}
bool HttpParser::ParseChunkedContent(const char* data, std::size_t length) {
bool Parser::ParseChunkedContent(const char* data, std::size_t length) {
// Append the new data to the pending data.
// NOTE: It's more difficult to avoid this than fixed-length content.
pending_data_.append(data, length);
@ -285,7 +284,7 @@ bool HttpParser::ParseChunkedContent(const char* data, std::size_t length) {
return true;
}
bool HttpParser::ParseChunkSize() {
bool Parser::ParseChunkSize() {
LOG_VERB("Parse chunk size.");
std::string line;
@ -312,7 +311,7 @@ bool HttpParser::ParseChunkSize() {
return true;
}
bool HttpParser::Finish() {
bool Parser::Finish() {
finished_ = true;
if (content_.empty()) {
@ -336,21 +335,21 @@ bool HttpParser::Finish() {
return true;
}
void HttpParser::AppendContent(const char* data, std::size_t count) {
void Parser::AppendContent(const char* data, std::size_t count) {
content_.append(data, count);
}
void HttpParser::AppendContent(const std::string& data) {
void Parser::AppendContent(const std::string& data) {
content_.append(data);
}
bool HttpParser::IsContentFull() const {
bool Parser::IsContentFull() const {
return content_length_ != kInvalidLength &&
content_length_ <= content_.length();
}
bool HttpParser::IsContentCompressed() const {
return message_->GetContentEncoding() != http::ContentEncoding::kUnknown;
bool Parser::IsContentCompressed() const {
return message_->GetContentEncoding() != ContentEncoding::kUnknown;
}
} // namespace webcc

@ -1,5 +1,5 @@
#ifndef WEBCC_HTTP_PARSER_H_
#define WEBCC_HTTP_PARSER_H_
#ifndef WEBCC_PARSER_H_
#define WEBCC_PARSER_H_
#include <string>
@ -8,19 +8,19 @@
namespace webcc {
class HttpMessage;
class Message;
// HTTP request and response parser.
class HttpParser {
class Parser {
public:
explicit HttpParser(HttpMessage* message);
explicit Parser(Message* message);
virtual ~HttpParser() = default;
virtual ~Parser() = default;
HttpParser(const HttpParser&) = delete;
HttpParser& operator=(const HttpParser&) = delete;
Parser(const Parser&) = delete;
Parser& operator=(const Parser&) = delete;
void Init(HttpMessage* message);
void Init(Message* message);
bool finished() const { return finished_; }
@ -66,7 +66,7 @@ protected:
protected:
// The result HTTP message.
HttpMessage* message_;
Message* message_;
// Data waiting to be parsed.
std::string pending_data_;
@ -84,4 +84,4 @@ protected:
} // namespace webcc
#endif // WEBCC_HTTP_PARSER_H_
#endif // WEBCC_PARSER_H_

@ -1,4 +1,4 @@
#include "webcc/http_request.h"
#include "webcc/request.h"
#include "webcc/logger.h"
#include "webcc/utility.h"
@ -18,17 +18,17 @@ const char DOUBLE_DASHES[] = { '-', '-' };
// -----------------------------------------------------------------------------
void HttpRequest::Prepare() {
void Request::Prepare() {
CreateStartLine();
if (url_.port().empty()) {
SetHeader(http::headers::kHost, url_.host());
SetHeader(headers::kHost, url_.host());
} else {
SetHeader(http::headers::kHost, url_.host() + ":" + url_.port());
SetHeader(headers::kHost, url_.host() + ":" + url_.port());
}
if (form_parts_.empty()) {
HttpMessage::Prepare();
Message::Prepare();
return;
}
@ -69,13 +69,13 @@ void HttpRequest::Prepare() {
SetContentLength(content_length);
// Prepare start line and headers.
HttpMessage::Prepare();
Message::Prepare();
// Append payload of content data.
payload_.insert(payload_.end(), data_payload.begin(), data_payload.end());
}
void HttpRequest::CreateStartLine() {
void Request::CreateStartLine() {
if (!start_line_.empty()) {
return;
}

@ -1,27 +1,27 @@
#ifndef WEBCC_HTTP_REQUEST_H_
#define WEBCC_HTTP_REQUEST_H_
#ifndef WEBCC_REQUEST_H_
#define WEBCC_REQUEST_H_
#include <memory>
#include <string>
#include <vector>
#include "webcc/http_message.h"
#include "webcc/message.h"
#include "webcc/url.h"
namespace webcc {
class HttpRequest;
typedef std::shared_ptr<HttpRequest> HttpRequestPtr;
class Request;
using RequestPtr = std::shared_ptr<Request>;
class HttpRequest : public HttpMessage {
class Request : public Message {
public:
HttpRequest() = default;
Request() = default;
HttpRequest(const std::string& method, const std::string& url)
Request(const std::string& method, const std::string& url)
: method_(method), url_(url) {
}
~HttpRequest() override = default;
~Request() override = default;
const std::string& method() const {
return method_;
@ -86,4 +86,4 @@ private:
} // namespace webcc
#endif // WEBCC_HTTP_REQUEST_H_
#endif // WEBCC_REQUEST_H_

@ -0,0 +1,91 @@
#include "webcc/request_builder.h"
#include "webcc/base64.h"
#include "webcc/logger.h"
#include "webcc/utility.h"
#include "webcc/zlib_wrapper.h"
namespace webcc {
RequestPtr RequestBuilder::Build() {
assert(parameters_.size() % 2 == 0);
assert(headers_.size() % 2 == 0);
auto request = std::make_shared<Request>(method_, url_);
for (std::size_t i = 1; i < parameters_.size(); i += 2) {
request->AddQuery(parameters_[i - 1], parameters_[i]);
}
for (std::size_t i = 1; i < headers_.size(); i += 2) {
request->SetHeader(std::move(headers_[i - 1]), std::move(headers_[i]));
}
// No keep-alive?
if (!keep_alive_) {
request->SetHeader(headers::kConnection, "Close");
}
if (!data_.empty()) {
SetContent(request, std::move(data_));
// TODO: Request-level charset.
if (json_) {
request->SetContentType(media_types::kApplicationJson, "");
}
} else if (!form_parts_.empty()) {
request->set_form_parts(std::move(form_parts_));
}
return request;
}
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 });
return *this;
}
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 });
return *this;
}
RequestBuilder& RequestBuilder::Auth(const std::string& type,
const std::string& credentials) {
headers_.push_back(headers::kAuthorization);
headers_.push_back(type + " " + credentials);
return *this;
}
RequestBuilder& RequestBuilder::AuthBasic(const std::string& login,
const std::string& password) {
auto credentials = Base64Encode(login + ":" + password);
return Auth("Basic", credentials);
}
RequestBuilder& RequestBuilder::AuthToken(const std::string& token) {
return Auth("Token", token);
}
void RequestBuilder::SetContent(RequestPtr request, std::string&& data) {
if (gzip_ && data.size() > kGzipThreshold) {
std::string compressed;
if (Compress(data, &compressed)) {
request->SetContent(std::move(compressed), true);
request->SetHeader(headers::kContentEncoding, "gzip");
return;
}
LOG_WARN("Cannot compress the content data!");
}
request->SetContent(std::move(data), true);
}
} // namespace webcc

@ -0,0 +1,141 @@
#ifndef WEBCC_REQUEST_BUILDER_H_
#define WEBCC_REQUEST_BUILDER_H_
#include <string>
#include <vector>
#include "webcc/request.h"
namespace webcc {
class RequestBuilder {
public:
explicit RequestBuilder(const std::string& method = "")
: method_(method) {
}
~RequestBuilder() = default;
RequestBuilder(const RequestBuilder&) = delete;
RequestBuilder& operator=(const RequestBuilder&) = delete;
// Build the request.
RequestPtr Build();
RequestPtr operator()() {
return Build();
}
RequestBuilder& Get() { return Method(methods::kGet); }
RequestBuilder& Head() { return Method(methods::kHead); }
RequestBuilder& Post() { return Method(methods::kPost); }
RequestBuilder& Put() { return Method(methods::kPut); }
RequestBuilder& Delete() { return Method(methods::kDelete); }
RequestBuilder& Patch() { return Method(methods::kPatch); }
// NOTE:
// The naming convention doesn't follow Google C++ Style for
// consistency and simplicity.
RequestBuilder& Method(const std::string& method) {
method_ = method;
return *this;
}
RequestBuilder& Url(const std::string& url) {
url_ = url;
return *this;
}
RequestBuilder& Query(const std::string& key, const std::string& value) {
parameters_.push_back(key);
parameters_.push_back(value);
return *this;
}
RequestBuilder& Data(const std::string& data) {
data_ = data;
return *this;
}
RequestBuilder& Data(std::string&& data) {
data_ = std::move(data);
return *this;
}
RequestBuilder& Json(bool json = true) {
json_ = json;
return *this;
}
// Upload a file.
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));
return *this;
}
RequestBuilder& Form(const std::string& name, std::string&& data,
const std::string& media_type = "");
RequestBuilder& Gzip(bool gzip = true) {
gzip_ = gzip;
return *this;
}
RequestBuilder& Header(const std::string& key, const std::string& value) {
headers_.push_back(key);
headers_.push_back(value);
return *this;
}
RequestBuilder& KeepAlive(bool keep_alive = true) {
keep_alive_ = keep_alive;
return *this;
}
RequestBuilder& Auth(const std::string& type, const std::string& credentials);
RequestBuilder& AuthBasic(const std::string& login,
const std::string& password);
RequestBuilder& AuthToken(const std::string& token);
private:
void SetContent(RequestPtr request, std::string&& data);
private:
std::string method_;
std::string url_;
// URL query parameters.
std::vector<std::string> parameters_;
// Data to send in the body of the request.
std::string data_;
// Is the data to send a JSON string?
bool json_ = false;
// Files to upload for a POST request.
std::vector<FormPart> form_parts_;
// Compress the request content.
// NOTE: Most servers don't support compressed requests.
// Even the requests module from Python doesn't have a built-in support.
// See: https://github.com/kennethreitz/requests/issues/1753
bool gzip_ = false;
// Additional request headers.
std::vector<std::string> headers_;
// Persistent connection.
bool keep_alive_ = true;
};
} // namespace webcc
#endif // WEBCC_REQUEST_BUILDER_H_

@ -1,36 +1,36 @@
#include "webcc/http_request_handler.h"
#include "webcc/request_handler.h"
#include <sstream>
#include "webcc/globals.h"
#include "webcc/http_request.h"
#include "webcc/http_response.h"
#include "webcc/request.h"
#include "webcc/response.h"
#include "webcc/logger.h"
namespace webcc {
void HttpRequestHandler::Enqueue(HttpConnectionPtr connection) {
void RequestHandler::Enqueue(ConnectionPtr connection) {
queue_.Push(connection);
}
void HttpRequestHandler::Start(std::size_t count) {
void RequestHandler::Start(std::size_t count) {
assert(count > 0 && workers_.size() == 0);
for (std::size_t i = 0; i < count; ++i) {
workers_.emplace_back(std::bind(&HttpRequestHandler::WorkerRoutine, this));
workers_.emplace_back(std::bind(&RequestHandler::WorkerRoutine, this));
}
}
void HttpRequestHandler::Stop() {
void RequestHandler::Stop() {
LOG_INFO("Stopping workers...");
// Clear pending connections.
// The connections will be closed later (see HttpServer::DoAwaitStop).
// The connections will be closed later (see Server::DoAwaitStop).
LOG_INFO("Clear pending connections...");
queue_.Clear();
// Enqueue a null connection to trigger the first worker to stop.
queue_.Push(HttpConnectionPtr());
queue_.Push(ConnectionPtr());
for (auto& worker : workers_) {
if (worker.joinable()) {
@ -41,17 +41,17 @@ void HttpRequestHandler::Stop() {
LOG_INFO("All workers have been stopped.");
}
void HttpRequestHandler::WorkerRoutine() {
void RequestHandler::WorkerRoutine() {
LOG_INFO("Worker is running.");
for (;;) {
HttpConnectionPtr connection = queue_.PopOrWait();
ConnectionPtr connection = queue_.PopOrWait();
if (!connection) {
LOG_INFO("Worker is going to stop.");
// For stopping next worker.
queue_.Push(HttpConnectionPtr());
queue_.Push(ConnectionPtr());
// Stop the worker.
break;

@ -0,0 +1,48 @@
#ifndef WEBCC_REQUEST_HANDLER_H_
#define WEBCC_REQUEST_HANDLER_H_
#include <list>
#include <thread>
#include <vector>
#include "webcc/connection.h"
#include "webcc/queue.h"
namespace webcc {
class Request;
class Response;
// The common handler for all incoming requests.
class RequestHandler {
public:
RequestHandler() = default;
virtual ~RequestHandler() = default;
RequestHandler(const RequestHandler&) = delete;
RequestHandler& operator=(const RequestHandler&) = delete;
// Put the connection into the queue.
void Enqueue(ConnectionPtr connection);
// Start worker threads.
void Start(std::size_t count);
// Clear pending connections from the queue and stop worker threads.
void Stop();
private:
void WorkerRoutine();
// Called by the worker routine.
virtual void HandleConnection(ConnectionPtr connection) = 0;
private:
Queue<ConnectionPtr> queue_;
std::vector<std::thread> workers_;
};
} // namespace webcc
#endif // WEBCC_REQUEST_HANDLER_H_

@ -1,24 +1,24 @@
#include "webcc/http_request_parser.h"
#include "webcc/request_parser.h"
#include <vector>
#include "boost/algorithm/string.hpp"
#include "webcc/http_request.h"
#include "webcc/request.h"
#include "webcc/logger.h"
namespace webcc {
HttpRequestParser::HttpRequestParser(HttpRequest* request)
: HttpParser(request), request_(request) {
RequestParser::RequestParser(Request* request)
: Parser(request), request_(request) {
}
void HttpRequestParser::Init(HttpRequest* request) {
HttpParser::Init(request);
void RequestParser::Init(Request* request) {
Parser::Init(request);
request_ = request;
}
bool HttpRequestParser::ParseStartLine(const std::string& line) {
bool RequestParser::ParseStartLine(const std::string& line) {
std::vector<std::string> strs;
boost::split(strs, line, boost::is_any_of(" "), boost::token_compress_on);
@ -34,7 +34,7 @@ bool HttpRequestParser::ParseStartLine(const std::string& line) {
return true;
}
bool HttpRequestParser::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 {
@ -46,7 +46,7 @@ bool HttpRequestParser::ParseContent(const char* data, std::size_t length) {
}
}
bool HttpRequestParser::ParseMultipartContent(const char* data,
bool RequestParser::ParseMultipartContent(const char* data,
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.
@ -139,7 +139,7 @@ bool HttpRequestParser::ParseMultipartContent(const char* data,
return true;
}
bool HttpRequestParser::ParsePartHeaders(bool* need_more_data) {
bool RequestParser::ParsePartHeaders(bool* need_more_data) {
std::size_t off = 0;
while (true) {
@ -157,7 +157,7 @@ bool HttpRequestParser::ParsePartHeaders(bool* need_more_data) {
break;
}
HttpHeader header;
Header header;
if (!Split2(line, ':', &header.first, &header.second)) {
LOG_ERRO("Invalid part header line: %s", line.c_str());
return false;
@ -167,7 +167,7 @@ bool HttpRequestParser::ParsePartHeaders(bool* need_more_data) {
header.second.c_str());
// Parse Content-Disposition.
if (boost::iequals(header.first, http::headers::kContentDisposition)) {
if (boost::iequals(header.first, headers::kContentDisposition)) {
ContentDisposition content_disposition(header.second);
if (!content_disposition.valid()) {
LOG_ERRO("Invalid content-disposition header: %s",
@ -189,7 +189,7 @@ bool HttpRequestParser::ParsePartHeaders(bool* need_more_data) {
return true;
}
bool HttpRequestParser::GetNextBoundaryLine(std::size_t* b_off,
bool RequestParser::GetNextBoundaryLine(std::size_t* b_off,
std::size_t* b_count,
bool* ended) {
std::size_t off = 0;
@ -228,14 +228,14 @@ bool HttpRequestParser::GetNextBoundaryLine(std::size_t* b_off,
return false;
}
bool HttpRequestParser::IsBoundary(const std::string& line) const {
bool RequestParser::IsBoundary(const std::string& line) const {
if (line == "--" + request_->content_type().boundary()) {
return true;
}
return false;
}
bool HttpRequestParser::IsBoundaryEnd(const std::string& line) const {
bool RequestParser::IsBoundaryEnd(const std::string& line) const {
if (line == "--" + request_->content_type().boundary() + "--") {
return true;
}

@ -1,21 +1,21 @@
#ifndef WEBCC_HTTP_REQUEST_PARSER_H_
#define WEBCC_HTTP_REQUEST_PARSER_H_
#ifndef WEBCC_REQUEST_PARSER_H_
#define WEBCC_REQUEST_PARSER_H_
#include <string>
#include "webcc/http_parser.h"
#include "webcc/parser.h"
namespace webcc {
class HttpRequest;
class Request;
class HttpRequestParser : public HttpParser {
class RequestParser : public Parser {
public:
explicit HttpRequestParser(HttpRequest* request = nullptr);
explicit RequestParser(Request* request = nullptr);
~HttpRequestParser() override = default;
~RequestParser() override = default;
void Init(HttpRequest* request);
void Init(Request* request);
private:
bool ParseStartLine(const std::string& line) final;
@ -33,7 +33,7 @@ private:
bool IsBoundaryEnd(const std::string& line) const;
private:
HttpRequest* request_;
Request* request_;
enum Step {
kStart,
@ -48,4 +48,4 @@ private:
} // namespace webcc
#endif // WEBCC_HTTP_REQUEST_PARSER_H_
#endif // WEBCC_REQUEST_PARSER_H_

@ -1,4 +1,4 @@
#include "webcc/http_response.h"
#include "webcc/response.h"
#include "webcc/utility.h"
@ -20,34 +20,34 @@ const std::string SERVICE_UNAVAILABLE = "HTTP/1.1 503 Service Unavailable";
const std::string& ToString(int status) {
switch (status) {
case http::Status::kOK:
case Status::kOK:
return OK;
case http::Status::kCreated:
case Status::kCreated:
return CREATED;
case http::Status::kAccepted:
case Status::kAccepted:
return ACCEPTED;
case http::Status::kNoContent:
case Status::kNoContent:
return NO_CONTENT;
case http::Status::kNotModified:
case Status::kNotModified:
return NOT_MODIFIED;
case http::Status::kBadRequest:
case Status::kBadRequest:
return BAD_REQUEST;
case http::Status::kNotFound:
case Status::kNotFound:
return NOT_FOUND;
case http::Status::kInternalServerError:
case Status::kInternalServerError:
return INTERNAL_SERVER_ERROR;
case http::Status::kNotImplemented:
case Status::kNotImplemented:
return NOT_IMPLEMENTED;
case http::Status::kServiceUnavailable:
case Status::kServiceUnavailable:
return SERVICE_UNAVAILABLE;
default:
@ -57,15 +57,15 @@ const std::string& ToString(int status) {
} // namespace status_strings
void HttpResponse::Prepare() {
void Response::Prepare() {
if (start_line_.empty()) {
start_line_ = status_strings::ToString(status_);
}
SetHeader(http::headers::kServer, http::UserAgent());
SetHeader(http::headers::kDate, GetHttpDateTimestamp());
SetHeader(headers::kServer, UserAgent());
SetHeader(headers::kDate, GetTimestamp());
HttpMessage::Prepare();
Message::Prepare();
}
} // namespace webcc

@ -0,0 +1,39 @@
#ifndef WEBCC_RESPONSE_H_
#define WEBCC_RESPONSE_H_
#include <memory>
#include <string>
#include "webcc/message.h"
namespace webcc {
class Response;
using ResponsePtr = std::shared_ptr<Response>;
class Response : public Message {
public:
explicit Response(Status status = Status::kOK)
: status_(status) {
}
~Response() override = default;
int status() const {
return status_;
}
void set_status(int status) {
status_ = status;
}
// Set start line according to status code.
void Prepare() final;
private:
int status_;
};
} // namespace webcc
#endif // WEBCC_RESPONSE_H_

@ -1,22 +1,22 @@
#include "webcc/http_response_parser.h"
#include "webcc/response_parser.h"
#include "boost/algorithm/string.hpp"
#include "webcc/http_response.h"
#include "webcc/response.h"
#include "webcc/logger.h"
namespace webcc {
HttpResponseParser::HttpResponseParser(HttpResponse* response)
: HttpParser(response), response_(response) {
ResponseParser::ResponseParser(Response* response)
: Parser(response), response_(response) {
}
void HttpResponseParser::Init(HttpResponse* response) {
HttpParser::Init(response);
void ResponseParser::Init(Response* response) {
Parser::Init(response);
response_ = response;
}
bool HttpResponseParser::ParseStartLine(const std::string& line) {
bool ResponseParser::ParseStartLine(const std::string& line) {
std::vector<std::string> parts;
boost::split(parts, line, boost::is_any_of(" "), boost::token_compress_on);

@ -0,0 +1,30 @@
#ifndef WEBCC_RESPONSE_PARSER_H_
#define WEBCC_RESPONSE_PARSER_H_
#include <string>
#include "webcc/parser.h"
namespace webcc {
class Response;
class ResponseParser : public Parser {
public:
explicit ResponseParser(Response* response = nullptr);
~ResponseParser() override = default;
void Init(Response* response);
private:
// Parse HTTP start line; E.g., "HTTP/1.1 200 OK".
bool ParseStartLine(const std::string& line) final;
// The result response message.
Response* response_;
};
} // namespace webcc
#endif // WEBCC_RESPONSE_PARSER_H_

@ -14,8 +14,8 @@ bool RestRequestHandler::Bind(RestServicePtr service, const std::string& url,
return service_manager_.AddService(service, url, is_regex);
}
void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
HttpRequestPtr http_request = connection->request();
void RestRequestHandler::HandleConnection(ConnectionPtr connection) {
RequestPtr http_request = connection->request();
assert(http_request);
const Url& url = http_request->url();
@ -28,14 +28,14 @@ void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
if (!service) {
LOG_WARN("No service matches the URL path: %s", url.path().c_str());
connection->SendResponse(http::Status::kNotFound);
connection->SendResponse(Status::kNotFound);
return;
}
RestResponse rest_response;
service->Handle(rest_request, &rest_response);
auto http_response = std::make_shared<HttpResponse>(rest_response.status);
auto http_response = std::make_shared<Response>(rest_response.status);
if (!rest_response.content.empty()) {
if (!rest_response.media_type.empty()) {
@ -48,7 +48,7 @@ void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
http_request->AcceptEncodingGzip()) {
std::string compressed;
if (Compress(rest_response.content, &compressed)) {
http_response->SetHeader(http::headers::kContentEncoding, "gzip");
http_response->SetHeader(headers::kContentEncoding, "gzip");
http_response->SetContent(std::move(compressed), true);
}
} else {

@ -5,12 +5,12 @@
#include <string>
#include "webcc/http_request_handler.h"
#include "webcc/request_handler.h"
#include "webcc/rest_service_manager.h"
namespace webcc {
class RestRequestHandler : public HttpRequestHandler {
class RestRequestHandler : public RequestHandler {
public:
RestRequestHandler() = default;
@ -19,7 +19,7 @@ public:
bool Bind(RestServicePtr service, const std::string& url, bool is_regex);
private:
void HandleConnection(HttpConnectionPtr connection) final;
void HandleConnection(ConnectionPtr connection) final;
private:
RestServiceManager service_manager_;

@ -5,16 +5,16 @@
#include <string>
#include "webcc/http_server.h"
#include "webcc/server.h"
#include "webcc/rest_request_handler.h"
#include "webcc/rest_service.h"
namespace webcc {
class RestServer : public HttpServer {
class RestServer : public Server {
public:
RestServer(std::uint16_t port, std::size_t workers)
: HttpServer(port, workers) {
: Server(port, workers) {
}
~RestServer() override = default;
@ -32,7 +32,7 @@ public:
}
private:
HttpRequestHandler* GetRequestHandler() final {
RequestHandler* GetRequestHandler() final {
return &request_handler_;
}

@ -10,10 +10,10 @@ void RestListService::Handle(const RestRequest& request,
RestResponse* response) {
const std::string& method = request.http->method();
if (method == http::methods::kGet) {
if (method == methods::kGet) {
Get(UrlQuery(request.http->url().query()), response);
} else if (method == http::methods::kPost) {
} else if (method == methods::kPost) {
Post(request.http->content(), response);
} else {
@ -27,16 +27,16 @@ void RestDetailService::Handle(const RestRequest& request,
RestResponse* response) {
const std::string& method = request.http->method();
if (method == http::methods::kGet) {
if (method == methods::kGet) {
Get(request.url_matches, UrlQuery(request.http->url().query()), response);
} else if (method == http::methods::kPut) {
} else if (method == methods::kPut) {
Put(request.url_matches, request.http->content(), response);
} else if (method == http::methods::kPatch) {
} else if (method == methods::kPatch) {
Patch(request.url_matches, request.http->content(), response);
} else if (method == http::methods::kDelete) {
} else if (method == methods::kDelete) {
Delete(request.url_matches, response);
} else {

@ -14,7 +14,7 @@
#include <vector>
#include "webcc/globals.h"
#include "webcc/http_request.h"
#include "webcc/request.h"
#include "webcc/url.h"
namespace webcc {
@ -22,18 +22,18 @@ namespace webcc {
// -----------------------------------------------------------------------------
// Regex sub-matches of the URL.
typedef std::vector<std::string> UrlMatches;
using UrlMatches = std::vector<std::string>;
struct RestRequest {
// Original HTTP request.
HttpRequestPtr http;
RequestPtr http;
// Regex sub-matches of the URL (usually resource ID's).
UrlMatches url_matches;
};
struct RestResponse {
http::Status status;
Status status;
std::string content;
@ -52,7 +52,7 @@ public:
virtual void Handle(const RestRequest& request, RestResponse* response) = 0;
};
typedef std::shared_ptr<RestService> RestServicePtr;
using RestServicePtr = std::shared_ptr<RestService>;
// -----------------------------------------------------------------------------

@ -1,9 +1,9 @@
#include "webcc/http_server.h"
#include "webcc/server.h"
#include <csignal>
#include <utility>
#include "webcc/http_request_handler.h"
#include "webcc/request_handler.h"
#include "webcc/logger.h"
#include "webcc/utility.h"
@ -11,7 +11,7 @@ using tcp = boost::asio::ip::tcp;
namespace webcc {
HttpServer::HttpServer(std::uint16_t port, std::size_t workers)
Server::Server(std::uint16_t port, std::size_t workers)
: acceptor_(io_context_), signals_(io_context_), workers_(workers) {
RegisterSignals();
@ -50,7 +50,7 @@ HttpServer::HttpServer(std::uint16_t port, std::size_t workers)
}
}
void HttpServer::Run() {
void Server::Run() {
assert(GetRequestHandler() != nullptr);
if (!acceptor_.is_open()) {
@ -74,7 +74,7 @@ void HttpServer::Run() {
io_context_.run();
}
void HttpServer::RegisterSignals() {
void Server::RegisterSignals() {
signals_.add(SIGINT); // Ctrl+C
signals_.add(SIGTERM);
@ -83,7 +83,7 @@ void HttpServer::RegisterSignals() {
#endif
}
void HttpServer::DoAccept() {
void Server::DoAccept() {
acceptor_.async_accept(
[this](boost::system::error_code ec, tcp::socket socket) {
// Check whether the server was stopped by a signal before this
@ -95,7 +95,7 @@ void HttpServer::DoAccept() {
if (!ec) {
LOG_INFO("Accepted a connection.");
auto connection = std::make_shared<HttpConnection>(
auto connection = std::make_shared<Connection>(
std::move(socket), &pool_, GetRequestHandler());
pool_.Start(connection);
@ -105,7 +105,7 @@ void HttpServer::DoAccept() {
});
}
void HttpServer::DoAwaitStop() {
void Server::DoAwaitStop() {
signals_.async_wait(
[this](boost::system::error_code, int signo) {
// The server is stopped by canceling all outstanding asynchronous

@ -1,5 +1,5 @@
#ifndef WEBCC_HTTP_SERVER_H_
#define WEBCC_HTTP_SERVER_H_
#ifndef WEBCC_SERVER_H_
#define WEBCC_SERVER_H_
#include <string>
@ -8,23 +8,23 @@
#include "boost/asio/signal_set.hpp"
#include "webcc/globals.h"
#include "webcc/http_connection.h"
#include "webcc/http_connection_pool.h"
#include "webcc/connection.h"
#include "webcc/connection_pool.h"
namespace webcc {
class HttpRequestHandler;
class RequestHandler;
// HTTP server accepts TCP connections from TCP clients.
// NOTE: Only support IPv4.
class HttpServer {
class Server {
public:
HttpServer(std::uint16_t port, std::size_t workers);
Server(std::uint16_t port, std::size_t workers);
virtual ~HttpServer() = default;
virtual ~Server() = default;
HttpServer(const HttpServer&) = delete;
HttpServer& operator=(const HttpServer&) = delete;
Server(const Server&) = delete;
Server& operator=(const Server&) = delete;
// Run the server's io_service loop.
void Run();
@ -40,7 +40,7 @@ private:
void DoAwaitStop();
// Get the handler for incoming requests.
virtual HttpRequestHandler* GetRequestHandler() = 0;
virtual RequestHandler* GetRequestHandler() = 0;
// The io_context used to perform asynchronous operations.
boost::asio::io_context io_context_;
@ -49,7 +49,7 @@ private:
boost::asio::ip::tcp::acceptor acceptor_;
// The connection pool which owns all live connections.
HttpConnectionPool pool_;
ConnectionPool pool_;
// The signal_set is used to register for process termination notifications.
boost::asio::signal_set signals_;
@ -60,4 +60,4 @@ private:
} // namespace webcc
#endif // WEBCC_HTTP_SERVER_H_
#endif // WEBCC_SERVER_H_

@ -1,4 +1,4 @@
#include "webcc/http_socket.h"
#include "webcc/socket.h"
#include "boost/asio/connect.hpp"
#include "boost/asio/read.hpp"
@ -10,26 +10,26 @@ namespace webcc {
// -----------------------------------------------------------------------------
HttpSocket::HttpSocket(boost::asio::io_context& io_context)
Socket::Socket(boost::asio::io_context& io_context)
: socket_(io_context) {
}
void HttpSocket::Connect(const std::string& /*host*/,
void Socket::Connect(const std::string& /*host*/,
const Endpoints& endpoints,
boost::system::error_code* ec) {
boost::asio::connect(socket_, endpoints, *ec);
}
void HttpSocket::Write(const HttpRequest& request,
void Socket::Write(const Request& request,
boost::system::error_code* ec) {
boost::asio::write(socket_, request.payload(), *ec);
}
void HttpSocket::AsyncReadSome(ReadHandler&& handler, std::vector<char>* buffer) {
void Socket::AsyncReadSome(ReadHandler&& handler, std::vector<char>* buffer) {
socket_.async_read_some(boost::asio::buffer(*buffer), std::move(handler));
}
void HttpSocket::Close(boost::system::error_code* ec) {
void Socket::Close(boost::system::error_code* ec) {
socket_.close(*ec);
}
@ -39,8 +39,7 @@ void HttpSocket::Close(boost::system::error_code* ec) {
namespace ssl = boost::asio::ssl;
HttpSslSocket::HttpSslSocket(boost::asio::io_context& io_context,
bool ssl_verify)
SslSocket::SslSocket(boost::asio::io_context& io_context, bool ssl_verify)
: ssl_context_(ssl::context::sslv23),
ssl_socket_(io_context, ssl_context_),
ssl_verify_(ssl_verify) {
@ -48,9 +47,8 @@ HttpSslSocket::HttpSslSocket(boost::asio::io_context& io_context,
ssl_context_.set_default_verify_paths();
}
void HttpSslSocket::Connect(const std::string& host,
const Endpoints& endpoints,
boost::system::error_code* ec) {
void SslSocket::Connect(const std::string& host, const Endpoints& endpoints,
boost::system::error_code* ec) {
boost::asio::connect(ssl_socket_.lowest_layer(), endpoints, *ec);
if (*ec) {
@ -60,21 +58,21 @@ void HttpSslSocket::Connect(const std::string& host,
Handshake(host, ec);
}
void HttpSslSocket::Write(const HttpRequest& request,
boost::system::error_code* ec) {
void SslSocket::Write(const Request& request, boost::system::error_code* ec) {
boost::asio::write(ssl_socket_, request.payload(), *ec);
}
void HttpSslSocket::AsyncReadSome(ReadHandler&& handler, std::vector<char>* buffer) {
void SslSocket::AsyncReadSome(ReadHandler&& handler,
std::vector<char>* buffer) {
ssl_socket_.async_read_some(boost::asio::buffer(*buffer), std::move(handler));
}
void HttpSslSocket::Close(boost::system::error_code* ec) {
void SslSocket::Close(boost::system::error_code* ec) {
ssl_socket_.lowest_layer().close(*ec);
}
void HttpSslSocket::Handshake(const std::string& host,
boost::system::error_code* ec) {
void SslSocket::Handshake(const std::string& host,
boost::system::error_code* ec) {
if (ssl_verify_) {
ssl_socket_.set_verify_mode(ssl::verify_peer);
} else {

@ -1,12 +1,12 @@
#ifndef WEBCC_HTTP_SOCKET_H_
#define WEBCC_HTTP_SOCKET_H_
#ifndef WEBCC_SOCKET_H_
#define WEBCC_SOCKET_H_
#include <vector>
#include "boost/asio/ip/tcp.hpp"
#include "webcc/config.h"
#include "webcc/http_request.h"
#include "webcc/request.h"
#if WEBCC_ENABLE_SSL
#include "boost/asio/ssl.hpp"
@ -16,21 +16,21 @@ namespace webcc {
// -----------------------------------------------------------------------------
class HttpSocketBase {
class SocketBase {
public:
virtual ~HttpSocketBase() = default;
virtual ~SocketBase() = default;
typedef boost::asio::ip::tcp::resolver::results_type Endpoints;
using Endpoints = boost::asio::ip::tcp::resolver::results_type;
typedef std::function<void(boost::system::error_code, std::size_t)>
ReadHandler;
using ReadHandler =
std::function<void(boost::system::error_code, std::size_t)>;
// TODO: Remove |host|
virtual void Connect(const std::string& host,
const Endpoints& endpoints,
boost::system::error_code* ec) = 0;
virtual void Write(const HttpRequest& request,
virtual void Write(const Request& request,
boost::system::error_code* ec) = 0;
virtual void AsyncReadSome(ReadHandler&& handler,
@ -41,15 +41,15 @@ public:
// -----------------------------------------------------------------------------
class HttpSocket : public HttpSocketBase {
class Socket : public SocketBase {
public:
explicit HttpSocket(boost::asio::io_context& io_context);
explicit Socket(boost::asio::io_context& io_context);
void Connect(const std::string& host,
const Endpoints& endpoints,
boost::system::error_code* ec) final;
void Write(const HttpRequest& request,
void Write(const Request& request,
boost::system::error_code* ec) final;
void AsyncReadSome(ReadHandler&& handler, std::vector<char>* buffer) final;
@ -64,16 +64,16 @@ private:
#if WEBCC_ENABLE_SSL
class HttpSslSocket : public HttpSocketBase {
class SslSocket : public SocketBase {
public:
explicit HttpSslSocket(boost::asio::io_context& io_context,
bool ssl_verify = true);
explicit SslSocket(boost::asio::io_context& io_context,
bool ssl_verify = true);
void Connect(const std::string& host,
const Endpoints& endpoints,
boost::system::error_code* ec) final;
void Write(const HttpRequest& request,
void Write(const Request& request,
boost::system::error_code* ec) final;
void AsyncReadSome(ReadHandler&& handler, std::vector<char>* buffer) final;
@ -95,4 +95,4 @@ private:
} // namespace webcc
#endif // WEBCC_HTTP_SOCKET_H_
#endif // WEBCC_SOCKET_H_

@ -16,8 +16,8 @@ namespace webcc {
// URL query parameters.
class UrlQuery {
public:
typedef std::pair<std::string, std::string> Parameter;
typedef std::vector<Parameter> Parameters;
using Parameter = std::pair<std::string, std::string>;
using Parameters = std::vector<Parameter>;
UrlQuery() = default;
@ -47,7 +47,7 @@ public:
std::string ToString() const;
private:
typedef Parameters::const_iterator ConstIterator;
using ConstIterator = Parameters::const_iterator;
ConstIterator Find(const std::string& key) const;
Parameters parameters_;

@ -1,52 +1,12 @@
#include "webcc/utility.h"
#include <algorithm>
#include <ctime>
#include <iomanip> // for put_time
#include <ostream>
#include <sstream>
#include "boost/uuid/random_generator.hpp"
#include "boost/uuid/uuid_io.hpp"
#include "webcc/logger.h"
using tcp = boost::asio::ip::tcp;
namespace webcc {
void PrintEndpoint(std::ostream& ostream, const TcpEndpoint& endpoint) {
ostream << endpoint;
if (endpoint.protocol() == tcp::v4()) {
ostream << ", v4";
} else if (endpoint.protocol() == tcp::v6()) {
ostream << ", v6";
}
}
void PrintEndpoints(std::ostream& ostream, const TcpEndpoints& endpoints) {
ostream << "Endpoints: " << endpoints.size() << std::endl;
tcp::resolver::results_type::iterator it = endpoints.begin();
for (; it != endpoints.end(); ++it) {
ostream << " - ";
PrintEndpoint(ostream, it->endpoint());
ostream << std::endl;
}
}
std::string EndpointToString(const TcpEndpoint& endpoint) {
std::stringstream ss;
PrintEndpoint(ss, endpoint);
return ss.str();
}
std::string GetHttpDateTimestamp() {
std::time_t t = std::time(nullptr);
std::stringstream ss;
ss << std::put_time(std::gmtime(&t), "%a, %d %b %Y %H:%M:%S") << " GMT";
return ss.str();
}
std::string RandomUuid() {
boost::uuids::uuid u = boost::uuids::random_generator()();
std::stringstream ss;

@ -4,19 +4,8 @@
#include <iosfwd>
#include <string>
#include "boost/asio/ip/tcp.hpp"
namespace webcc {
typedef boost::asio::ip::tcp::endpoint TcpEndpoint;
typedef boost::asio::ip::tcp::resolver::results_type TcpEndpoints;
void PrintEndpoint(std::ostream& ostream, const TcpEndpoint& endpoint);
void PrintEndpoints(std::ostream& ostream, const TcpEndpoints& endpoints);
std::string EndpointToString(const TcpEndpoint& endpoint);
// Get the timestamp for HTTP Date header field.
// E.g., Wed, 21 Oct 2015 07:28:00 GMT
// See: https://tools.ietf.org/html/rfc7231#section-7.1.1.2

Loading…
Cancel
Save