Remove http prefix

master
Chunting Gu 6 years ago
parent 2ea04b9705
commit e621025cc3

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

@ -12,7 +12,7 @@ A complete client example:
```cpp ```cpp
#include <iostream> #include <iostream>
#include "webcc/http_client_session.h" #include "webcc/client_session.h"
#include "webcc/logger.h" #include "webcc/logger.h"
int main() { int main() {
@ -21,7 +21,7 @@ int main() {
// Session provides convenient request APIs, stores request configurations // Session provides convenient request APIs, stores request configurations
// and manages persistent connenction. // and manages persistent connenction.
webcc::HttpClientSession session; webcc::ClientSession session;
// Catch exceptions for error handling. // Catch exceptions for error handling.
try { try {
@ -41,11 +41,11 @@ int main() {
The `Get()` method is nothing but a shortcut of `Request()`. Using `Request()` directly is more complicated: The `Get()` method is nothing but a shortcut of `Request()`. Using `Request()` directly is more complicated:
```cpp ```cpp
auto r = session.Request(webcc::HttpRequestBuilder{}.Get(). auto r = session.Request(webcc::RequestBuilder{}.Get().
Url("http://httpbin.org/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: 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. // Query parameters are passed using a std::vector.
session.Get("http://httpbin.org/get", { "key1", "value1", "key2", "value2" }); 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"). Url("http://httpbin.org/get").
Query("key1", "value1"). Query("key1", "value1").
Query("key2", "value2") Query("key2", "value2")
@ -66,7 +66,7 @@ session.Get("http://httpbin.org/get",
{"key1", "value1", "key2", "value2"}, {"key1", "value1", "key2", "value2"},
{"Accept", "application/json"}); // Also a std::vector {"Accept", "application/json"}); // Also a std::vector
session.Request(webcc::HttpRequestBuilder{}.Get(). session.Request(webcc::RequestBuilder{}.Get().
Url("http://httpbin.org/get"). Url("http://httpbin.org/get").
Query("key1", "value1"). Query("key1", "value1").
Query("key2", "value2"). Query("key2", "value2").

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

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

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

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

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

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

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

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

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

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

@ -8,7 +8,7 @@ class MyRestService : public webcc::RestService {
public: public:
void Handle(const webcc::RestRequest& request, void Handle(const webcc::RestRequest& request,
webcc::RestResponse* response) final { 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); 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) { SizePair Decode(const char* src, std::size_t len, void* dst) {
auto in = reinterpret_cast<const unsigned char*>(src); 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 } // 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; std::string dst;
dst.resize(base64::EncodedSize(len)); dst.resize(base64::EncodedSize(length));
dst.resize(base64::Encode(data, len, &dst[0])); dst.resize(base64::Encode(data, length, &dst[0]));
return dst; return dst;
} }
std::string Base64Encode(const std::string& s) { std::string Base64Encode(const std::string& input) {
return Base64Encode(reinterpret_cast<const std::uint8_t*>(s.data()), s.size()); 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; std::string dst;
dst.resize(base64::DecodedSize(data.size())); dst.resize(base64::DecodedSize(input.size()));
auto result = base64::Decode(data.data(), data.size(), &dst[0]); auto result = base64::Decode(input.data(), input.size(), &dst[0]);
dst.resize(result.first); dst.resize(result.first);
return dst; return dst;
} }

@ -6,11 +6,11 @@
namespace webcc { 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 Base64Encode(const std::string& input);
std::string Base64Decode(const std::string& data); std::string Base64Decode(const std::string& input);
} // namespace webcc } // namespace webcc

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

@ -1,5 +1,5 @@
#ifndef WEBCC_HTTP_CLIENT_H_ #ifndef WEBCC_CLIENT_H_
#define WEBCC_HTTP_CLIENT_H_ #define WEBCC_CLIENT_H_
#include <cassert> #include <cassert>
#include <memory> #include <memory>
@ -11,28 +11,28 @@
#include "boost/asio/ip/tcp.hpp" #include "boost/asio/ip/tcp.hpp"
#include "webcc/globals.h" #include "webcc/globals.h"
#include "webcc/http_request.h" #include "webcc/request.h"
#include "webcc/http_response.h" #include "webcc/response.h"
#include "webcc/http_response_parser.h" #include "webcc/response_parser.h"
#include "webcc/http_socket.h" #include "webcc/socket.h"
namespace webcc { namespace webcc {
class HttpClient; class Client;
typedef std::shared_ptr<HttpClient> HttpClientPtr; using ClientPtr = std::shared_ptr<Client>;
// Synchronous HTTP & HTTPS client. // Synchronous HTTP & HTTPS client.
// In synchronous mode, a request won't return until the response is received // In synchronous mode, a request won't return until the response is received
// or timeout occurs. // or timeout occurs.
// Please don't use the same client object in multiple threads. // Please don't use the same client object in multiple threads.
class HttpClient { class Client {
public: public:
HttpClient(); Client();
virtual ~HttpClient() = default; virtual ~Client() = default;
HttpClient(const HttpClient&) = delete; Client(const Client&) = delete;
HttpClient& operator=(const HttpClient&) = delete; Client& operator=(const Client&) = delete;
void set_ssl_verify(bool ssl_verify) { void set_ssl_verify(bool ssl_verify) {
ssl_verify_ = ssl_verify; ssl_verify_ = ssl_verify;
@ -52,12 +52,12 @@ public:
} }
// Connect to server, send request, wait until response is received. // 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. // Close the socket.
void Close(); void Close();
HttpResponsePtr response() const { return response_; } ResponsePtr response() const { return response_; }
bool closed() const { return closed_; } bool closed() const { return closed_; }
@ -66,11 +66,11 @@ public:
Error error() const { return error_; } Error error() const { return error_; }
private: 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(); Error ReadResponse();
@ -86,10 +86,10 @@ private:
boost::asio::io_context io_context_; boost::asio::io_context io_context_;
// Socket connection. // Socket connection.
std::unique_ptr<HttpSocketBase> socket_; std::unique_ptr<SocketBase> socket_;
HttpResponsePtr response_; ResponsePtr response_;
HttpResponseParser response_parser_; ResponseParser response_parser_;
// Timer for the timeout control. // Timer for the timeout control.
boost::asio::deadline_timer timer_; boost::asio::deadline_timer timer_;
@ -122,4 +122,4 @@ private:
} // namespace webcc } // 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" #include "webcc/logger.h"
namespace webcc { namespace webcc {
HttpClientPool::~HttpClientPool() { ClientPool::~ClientPool() {
if (!clients_.empty()) { if (!clients_.empty()) {
LOG_INFO("Close socket for all (%u) connections in the pool.", LOG_INFO("Close socket for all (%u) connections in the pool.",
clients_.size()); 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); auto it = clients_.find(key);
if (it != clients_.end()) { if (it != clients_.end()) {
return it->second; return it->second;
} else { } else {
return HttpClientPtr{}; return ClientPtr{};
} }
} }
void HttpClientPool::Add(const Key& key, HttpClientPtr client) { void ClientPool::Add(const Key& key, ClientPtr client) {
clients_[key] = client; clients_[key] = client;
LOG_INFO("Added connection to pool (%s, %s, %s).", LOG_INFO("Added connection to pool (%s, %s, %s).",
key.scheme.c_str(), key.host.c_str(), key.port.c_str()); 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); clients_.erase(key);
LOG_INFO("Removed connection from pool (%s, %s, %s).", LOG_INFO("Removed connection from pool (%s, %s, %s).",

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

@ -1,24 +1,23 @@
#ifndef WEBCC_HTTP_CLIENT_SESSION_H_ #ifndef WEBCC_CLIENT_SESSION_H_
#define WEBCC_HTTP_CLIENT_SESSION_H_ #define WEBCC_CLIENT_SESSION_H_
#include <string> #include <string>
#include <vector> #include <vector>
#include "webcc/http_client_pool.h" #include "webcc/client_pool.h"
#include "webcc/http_request_builder.h" #include "webcc/request_builder.h"
#include "webcc/http_response.h" #include "webcc/response.h"
namespace webcc { namespace webcc {
// HTTP requests session providing connection-pooling, configuration and more. // HTTP requests session providing connection-pooling, configuration and more.
// NOTE:
// A session shouldn't be shared by multiple threads. Please create a new // A session shouldn't be shared by multiple threads. Please create a new
// session for each thread instead. // session for each thread instead.
class HttpClientSession { class ClientSession {
public: public:
HttpClientSession(); ClientSession();
~HttpClientSession() = default; ~ClientSession() = default;
void set_ssl_verify(bool ssl_verify) { void set_ssl_verify(bool ssl_verify) {
ssl_verify_ = ssl_verify; ssl_verify_ = ssl_verify;
@ -56,44 +55,44 @@ public:
void AuthToken(const std::string& token); void AuthToken(const std::string& token);
// Send a request. // Send a request.
// Please use HttpRequestBuilder to build the request. // Please use RequestBuilder to build the request.
HttpResponsePtr Request(HttpRequestPtr request); ResponsePtr Request(RequestPtr request);
// Shortcut for GET request. // Shortcut for GET request.
HttpResponsePtr Get(const std::string& url, ResponsePtr Get(const std::string& url,
const std::vector<std::string>& parameters = {}, const std::vector<std::string>& parameters = {},
const std::vector<std::string>& headers = {}); const std::vector<std::string>& headers = {});
// Shortcut for POST request. // Shortcut for POST request.
HttpResponsePtr Post(const std::string& url, std::string&& data, bool json, ResponsePtr Post(const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers = {}); const std::vector<std::string>& headers = {});
// Shortcut for PUT request. // Shortcut for PUT request.
HttpResponsePtr Put(const std::string& url, std::string&& data, bool json, ResponsePtr Put(const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers = {}); const std::vector<std::string>& headers = {});
// Shortcut for DELETE request. // Shortcut for DELETE request.
HttpResponsePtr Delete(const std::string& url, ResponsePtr Delete(const std::string& url,
const std::vector<std::string>& headers = {}); const std::vector<std::string>& headers = {});
// Shortcut for PATCH request. // Shortcut for PATCH request.
HttpResponsePtr Patch(const std::string& url, std::string&& data, bool json, ResponsePtr Patch(const std::string& url, std::string&& data, bool json,
const std::vector<std::string>& headers = {}); const std::vector<std::string>& headers = {});
// Post a file. // Post a file.
HttpResponsePtr PostFile(const std::string& url, const std::string& name, ResponsePtr PostFile(const std::string& url, const std::string& name,
const Path& path, const Path& path,
const std::vector<std::string>& headers = {}); const std::vector<std::string>& headers = {});
// Post multiple files. // Post multiple files.
HttpResponsePtr PostFiles(const std::string& url, ResponsePtr PostFiles(const std::string& url,
const std::map<std::string, Path>& paths, const std::map<std::string, Path>& paths,
const std::vector<std::string>& headers = {}); const std::vector<std::string>& headers = {});
private: private:
void InitHeaders(); void InitHeaders();
HttpResponsePtr Send(HttpRequestPtr request); ResponsePtr Send(RequestPtr request);
private: private:
// E.g., "application/json". // E.g., "application/json".
@ -103,7 +102,7 @@ private:
std::string charset_; std::string charset_;
// Additional headers for each request. // Additional headers for each request.
HttpHeaders headers_; Headers headers_;
// Verify the certificate of the peer or not. // Verify the certificate of the peer or not.
bool ssl_verify_ = true; bool ssl_verify_ = true;
@ -116,9 +115,9 @@ private:
int timeout_ = 0; int timeout_ = 0;
// Connection pool for keep-alive. // Connection pool for keep-alive.
HttpClientPool pool_; ClientPool pool_;
}; };
} // namespace webcc } // 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); auto it = Find(key);
if (it != headers_.end()) { if (it != headers_.end()) {
it->second = value; 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); auto it = Find(key);
if (it != headers_.end()) { if (it != headers_.end()) {
it->second = std::move(value); 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 { bool Headers::Has(const std::string& key) const {
return const_cast<HttpHeaders*>(this)->Find(key) != headers_.end(); 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 { bool* existed) const {
auto it = const_cast<HttpHeaders*>(this)->Find(key); auto it = const_cast<Headers*>(this)->Find(key);
if (existed != nullptr) { if (existed != nullptr) {
*existed = (it != headers_.end()); *existed = (it != headers_.end());
@ -94,7 +94,7 @@ const std::string& HttpHeaders::Get(const std::string& key,
return s_no_value; 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(); auto it = headers_.begin();
for (; it != headers_.end(); ++it) { for (; it != headers_.end(); ++it) {
if (boost::iequals(it->first, key)) { 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. // Determine media type from file extension.
if (media_type_.empty()) { if (media_type_.empty()) {
std::string extension = path.extension().string(); 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; 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(h.first));
payload->push_back(buffer(misc_strings::HEADER_SEPARATOR)); payload->push_back(buffer(misc_strings::HEADER_SEPARATOR));
payload->push_back(buffer(h.second)); payload->push_back(buffer(h.second));
@ -270,12 +270,12 @@ void FormPart::SetHeaders() {
if (!file_name_.empty()) { if (!file_name_.empty()) {
content_disposition.append("; filename=\"" + file_name_ + "\""); content_disposition.append("; filename=\"" + file_name_ + "\"");
} }
headers_.Set(http::headers::kContentDisposition, content_disposition); headers_.Set(headers::kContentDisposition, content_disposition);
// Header: Content-Type // Header: Content-Type
if (!media_type_.empty()) { 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: public:
std::size_t size() const { std::size_t size() const {
return headers_.size(); return headers_.size();
@ -42,7 +42,7 @@ public:
return headers_.empty(); return headers_.empty();
} }
const std::vector<HttpHeader>& data() const { const std::vector<Header>& data() const {
return headers_; return headers_;
} }
@ -53,7 +53,7 @@ public:
bool Has(const std::string& key) const; bool Has(const std::string& key) const;
// Get header by index. // Get header by index.
const HttpHeader& Get(std::size_t index) const { const Header& Get(std::size_t index) const {
assert(index < size()); assert(index < size());
return headers_[index]; return headers_[index];
} }
@ -68,9 +68,9 @@ public:
} }
private: 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. // Headers generated from the above properties.
// Only Used to prepare payload. // Only Used to prepare payload.
HttpHeaders headers_; Headers headers_;
std::string data_; std::string data_;
}; };

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

@ -68,8 +68,6 @@ const std::size_t kGzipThreshold = 1400;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
namespace http {
namespace methods { namespace methods {
// HTTP methods (verbs) in string. // HTTP methods (verbs) in string.
@ -156,8 +154,6 @@ enum class ContentEncoding {
// Return default user agent for HTTP headers. // Return default user agent for HTTP headers.
const std::string& UserAgent(); const std::string& UserAgent();
} // namespace http
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Client side error codes. // 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() { static std::string GetTimestamp() {
auto now = std::chrono::system_clock::now(); using system_clock = std::chrono::system_clock;
std::time_t t = std::chrono::system_clock::to_time_t(now); using milliseconds = std::chrono::milliseconds;
auto now = system_clock::now();
std::time_t t = system_clock::to_time_t(now);
std::stringstream ss; std::stringstream ss;
ss << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S"); ss << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S");
std::chrono::milliseconds milli_seconds = milliseconds milli_seconds =
std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::duration_cast<milliseconds>(now.time_since_epoch());
now.time_since_epoch());
std::string micro_seconds_str = std::to_string(milli_seconds.count() % 1000); std::string micro_seconds_str = std::to_string(milli_seconds.count() % 1000);
while (micro_seconds_str.size() < 3) { while (micro_seconds_str.size() < 3) {
micro_seconds_str = "0" + micro_seconds_str; micro_seconds_str = "0" + micro_seconds_str;
@ -233,7 +235,7 @@ static std::string GetTimestamp() {
return ss.str(); 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); assert(format != nullptr);
std::string timestamp = GetTimestamp(); 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. // If |dir| is empty, log file will be generated in current directory.
void LogInit(const std::string& dir, int modes); 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 } // namespace webcc
@ -58,35 +58,35 @@ void LogWrite(int level, const char* file, int line, const char* format, ...);
#if WEBCC_LOG_LEVEL <= WEBCC_VERB #if WEBCC_LOG_LEVEL <= WEBCC_VERB
#define LOG_VERB(format, ...) \ #define LOG_VERB(format, ...) \
webcc::LogWrite(WEBCC_VERB, __FILENAME__, __LINE__, format, ##__VA_ARGS__); webcc::Log(WEBCC_VERB, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
#else #else
#define LOG_VERB(format, ...) #define LOG_VERB(format, ...)
#endif #endif
#if WEBCC_LOG_LEVEL <= WEBCC_INFO #if WEBCC_LOG_LEVEL <= WEBCC_INFO
#define LOG_INFO(format, ...) \ #define LOG_INFO(format, ...) \
webcc::LogWrite(WEBCC_INFO, __FILENAME__, __LINE__, format, ##__VA_ARGS__); webcc::Log(WEBCC_INFO, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
#else #else
#define LOG_INFO(format, ...) #define LOG_INFO(format, ...)
#endif #endif
#if WEBCC_LOG_LEVEL <= WEBCC_WARN #if WEBCC_LOG_LEVEL <= WEBCC_WARN
#define LOG_WARN(format, ...) \ #define LOG_WARN(format, ...) \
webcc::LogWrite(WEBCC_WARN, __FILENAME__, __LINE__, format, ##__VA_ARGS__); webcc::Log(WEBCC_WARN, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
#else #else
#define LOG_WARN(format, ...) #define LOG_WARN(format, ...)
#endif #endif
#if WEBCC_LOG_LEVEL <= WEBCC_ERRO #if WEBCC_LOG_LEVEL <= WEBCC_ERRO
#define LOG_ERRO(format, ...) \ #define LOG_ERRO(format, ...) \
webcc::LogWrite(WEBCC_ERRO, __FILENAME__, __LINE__, format, ##__VA_ARGS__); webcc::Log(WEBCC_ERRO, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
#else #else
#define LOG_ERRO(format, ...) #define LOG_ERRO(format, ...)
#endif #endif
#if WEBCC_LOG_LEVEL <= WEBCC_FATA #if WEBCC_LOG_LEVEL <= WEBCC_FATA
#define LOG_FATA(format, ...) \ #define LOG_FATA(format, ...) \
webcc::LogWrite(WEBCC_FATA, __FILENAME__, __LINE__, format, ##__VA_ARGS__); webcc::Log(WEBCC_FATA, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
#else #else
#define LOG_FATA(format, ...) #define LOG_FATA(format, ...)
#endif #endif
@ -98,35 +98,35 @@ void LogWrite(int level, const char* file, int line, const char* format, ...);
#if WEBCC_LOG_LEVEL <= WEBCC_VERB #if WEBCC_LOG_LEVEL <= WEBCC_VERB
#define LOG_VERB(format, args...) \ #define LOG_VERB(format, args...) \
webcc::LogWrite(WEBCC_VERB, __FILENAME__, __LINE__, format, ##args); webcc::Log(WEBCC_VERB, __FILENAME__, __LINE__, format, ##args);
#else #else
#define LOG_VERB(format, args...) #define LOG_VERB(format, args...)
#endif #endif
#if WEBCC_LOG_LEVEL <= WEBCC_INFO #if WEBCC_LOG_LEVEL <= WEBCC_INFO
#define LOG_INFO(format, args...) \ #define LOG_INFO(format, args...) \
webcc::LogWrite(WEBCC_INFO, __FILENAME__, __LINE__, format, ##args); webcc::Log(WEBCC_INFO, __FILENAME__, __LINE__, format, ##args);
#else #else
#define LOG_INFO(format, args...) #define LOG_INFO(format, args...)
#endif #endif
#if WEBCC_LOG_LEVEL <= WEBCC_WARN #if WEBCC_LOG_LEVEL <= WEBCC_WARN
#define LOG_WARN(format, args...) \ #define LOG_WARN(format, args...) \
webcc::LogWrite(WEBCC_WARN, __FILENAME__, __LINE__, format, ##args); webcc::Log(WEBCC_WARN, __FILENAME__, __LINE__, format, ##args);
#else #else
#define LOG_WARN(format, args...) #define LOG_WARN(format, args...)
#endif #endif
#if WEBCC_LOG_LEVEL <= WEBCC_ERRO #if WEBCC_LOG_LEVEL <= WEBCC_ERRO
#define LOG_ERRO(format, args...) \ #define LOG_ERRO(format, args...) \
webcc::LogWrite(WEBCC_ERRO, __FILENAME__, __LINE__, format, ##args); webcc::Log(WEBCC_ERRO, __FILENAME__, __LINE__, format, ##args);
#else #else
#define LOG_ERRO(format, args...) #define LOG_ERRO(format, args...)
#endif #endif
#if WEBCC_LOG_LEVEL <= WEBCC_FATA #if WEBCC_LOG_LEVEL <= WEBCC_FATA
#define LOG_FATA(format, args...) \ #define LOG_FATA(format, args...) \
webcc::LogWrite(WEBCC_FATA, __FILENAME__, __LINE__, format, ##args); webcc::Log(WEBCC_FATA, __FILENAME__, __LINE__, format, ##args);
#else #else
#define LOG_FATA(format, args...) #define LOG_FATA(format, args...)
#endif #endif

@ -1,5 +1,7 @@
#include "webcc/http_message.h" #include "webcc/message.h"
#include <ctime>
#include <iomanip> // for put_time
#include <sstream> #include <sstream>
#include "boost/algorithm/string.hpp" #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); message.Dump(os);
return os; return os;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool HttpMessage::IsConnectionKeepAlive() const { bool Message::IsConnectionKeepAlive() const {
using http::headers::kConnection; using headers::kConnection;
bool existed = false; bool existed = false;
const std::string& connection = GetHeader(kConnection, &existed); const std::string& connection = GetHeader(kConnection, &existed);
@ -47,29 +49,29 @@ bool HttpMessage::IsConnectionKeepAlive() const {
return false; return false;
} }
http::ContentEncoding HttpMessage::GetContentEncoding() const { ContentEncoding Message::GetContentEncoding() const {
using http::headers::kContentEncoding; using headers::kContentEncoding;
const std::string& encoding = GetHeader(kContentEncoding); const std::string& encoding = GetHeader(kContentEncoding);
if (encoding == "gzip") { if (encoding == "gzip") {
return http::ContentEncoding::kGzip; return ContentEncoding::kGzip;
} }
if (encoding == "deflate") { if (encoding == "deflate") {
return http::ContentEncoding::kDeflate; return ContentEncoding::kDeflate;
} }
return http::ContentEncoding::kUnknown; return ContentEncoding::kUnknown;
} }
bool HttpMessage::AcceptEncodingGzip() const { bool Message::AcceptEncodingGzip() const {
using http::headers::kAcceptEncoding; using headers::kAcceptEncoding;
return GetHeader(kAcceptEncoding).find("gzip") != std::string::npos; return GetHeader(kAcceptEncoding).find("gzip") != std::string::npos;
} }
// See: https://tools.ietf.org/html/rfc7231#section-3.1.1.1 // See: https://tools.ietf.org/html/rfc7231#section-3.1.1.1
void HttpMessage::SetContentType(const std::string& media_type, void Message::SetContentType(const std::string& media_type,
const std::string& charset) { const std::string& charset) {
using http::headers::kContentType; using headers::kContentType;
if (charset.empty()) { if (charset.empty()) {
SetHeader(kContentType, media_type); 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); content_ = std::move(content);
if (set_length) { if (set_length) {
SetContentLength(content_.size()); SetContentLength(content_.size());
} }
} }
void HttpMessage::Prepare() { void Message::Prepare() {
assert(!start_line_.empty()); assert(!start_line_.empty());
using boost::asio::buffer; using boost::asio::buffer;
@ -95,7 +97,7 @@ void HttpMessage::Prepare() {
payload_.push_back(buffer(start_line_)); payload_.push_back(buffer(start_line_));
payload_.push_back(buffer(misc_strings::CRLF)); 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(h.first));
payload_.push_back(buffer(misc_strings::HEADER_SEPARATOR)); payload_.push_back(buffer(misc_strings::HEADER_SEPARATOR));
payload_.push_back(buffer(h.second)); payload_.push_back(buffer(h.second));
@ -109,19 +111,19 @@ 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_) { for (const boost::asio::const_buffer& b : payload_) {
os.write(static_cast<const char*>(b.data()), b.size()); 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; std::stringstream ss;
CopyPayload(ss); CopyPayload(ss);
*str = ss.str(); *str = ss.str();
} }
void HttpMessage::Dump(std::ostream& os, std::size_t indent, void Message::Dump(std::ostream& os, std::size_t indent,
const std::string& prefix) const { const std::string& prefix) const {
std::string indent_str; std::string indent_str;
if (indent > 0) { if (indent > 0) {
@ -131,7 +133,7 @@ void HttpMessage::Dump(std::ostream& os, std::size_t indent,
os << indent_str << start_line_ << std::endl; 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; 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, std::string Message::Dump(std::size_t indent,
const std::string& prefix) const { const std::string& prefix) const {
std::stringstream ss; std::stringstream ss;
Dump(ss, indent, prefix); Dump(ss, indent, prefix);
return ss.str(); 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 } // namespace webcc

@ -1,5 +1,5 @@
#ifndef WEBCC_HTTP_MESSAGE_H_ #ifndef WEBCC_MESSAGE_H_
#define WEBCC_HTTP_MESSAGE_H_ #define WEBCC_MESSAGE_H_
#include <cassert> #include <cassert>
#include <string> #include <string>
@ -11,16 +11,16 @@
namespace webcc { namespace webcc {
class HttpMessage; class Message;
std::ostream& operator<<(std::ostream& os, const HttpMessage& message); std::ostream& operator<<(std::ostream& os, const Message& message);
// Base class for HTTP request and response messages. // Base class for HTTP request and response messages.
class HttpMessage { class Message {
public: public:
HttpMessage() : content_length_(kInvalidLength) { Message() : content_length_(kInvalidLength) {
} }
virtual ~HttpMessage() = default; virtual ~Message() = default;
const std::string& start_line() const { const std::string& start_line() const {
return start_line_; return start_line_;
@ -40,7 +40,7 @@ public:
bool IsConnectionKeepAlive() const; bool IsConnectionKeepAlive() const;
void SetHeader(HttpHeader&& header) { void SetHeader(Header&& header) {
headers_.Set(std::move(header.first), std::move(header.second)); headers_.Set(std::move(header.first), std::move(header.second));
} }
@ -57,7 +57,7 @@ public:
return headers_.Has(key); return headers_.Has(key);
} }
http::ContentEncoding GetContentEncoding() const; ContentEncoding GetContentEncoding() const;
// Return true if header Accept-Encoding contains "gzip". // Return true if header Accept-Encoding contains "gzip".
bool AcceptEncodingGzip() const; bool AcceptEncodingGzip() const;
@ -72,7 +72,7 @@ public:
} }
void SetContentType(const std::string& content_type) { void SetContentType(const std::string& content_type) {
SetHeader(http::headers::kContentType, content_type); SetHeader(headers::kContentType, content_type);
} }
// Example: SetContentType("application/json", "utf-8") // Example: SetContentType("application/json", "utf-8")
@ -105,9 +105,14 @@ public:
protected: protected:
void SetContentLength(std::size_t content_length) { void SetContentLength(std::size_t content_length) {
content_length_ = 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: protected:
std::string start_line_; std::string start_line_;
@ -117,7 +122,7 @@ protected:
std::size_t content_length_; std::size_t content_length_;
HttpHeaders headers_; Headers headers_;
// NOTE: The payload itself doesn't hold the memory! // NOTE: The payload itself doesn't hold the memory!
Payload payload_; Payload payload_;
@ -125,4 +130,4 @@ protected:
} // namespace webcc } // 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 "boost/algorithm/string.hpp"
#include "webcc/http_message.h"
#include "webcc/logger.h" #include "webcc/logger.h"
#include "webcc/message.h"
#include "webcc/utility.h" #include "webcc/utility.h"
#include "webcc/zlib_wrapper.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), : message_(message),
content_length_(kInvalidLength), content_length_(kInvalidLength),
start_line_parsed_(false), start_line_parsed_(false),
@ -37,12 +37,12 @@ HttpParser::HttpParser(HttpMessage* message)
finished_(false) { finished_(false) {
} }
void HttpParser::Init(HttpMessage* message) { void Parser::Init(Message* message) {
Reset(); Reset();
message_ = message; 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_) { if (header_ended_) {
return ParseContent(data, length); 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(); pending_data_.clear();
content_.clear(); content_.clear();
@ -77,7 +77,7 @@ void HttpParser::Reset() {
finished_ = false; finished_ = false;
} }
bool HttpParser::ParseHeaders() { bool Parser::ParseHeaders() {
std::size_t off = 0; std::size_t off = 0;
while (true) { while (true) {
@ -113,8 +113,7 @@ bool HttpParser::ParseHeaders() {
return true; return true;
} }
bool HttpParser::GetNextLine(std::size_t off, std::string* line, bool Parser::GetNextLine(std::size_t off, std::string* line, bool erase) {
bool erase) {
std::size_t pos = pending_data_.find(kCRLF, off); std::size_t pos = pending_data_.find(kCRLF, off);
if (pos == std::string::npos) { if (pos == std::string::npos) {
@ -134,15 +133,15 @@ bool HttpParser::GetNextLine(std::size_t off, std::string* line,
return true; return true;
} }
bool HttpParser::ParseHeaderLine(const std::string& line) { bool Parser::ParseHeaderLine(const std::string& line) {
HttpHeader header; Header header;
if (!Split2(line, ':', &header.first, &header.second)) { if (!Split2(line, ':', &header.first, &header.second)) {
return false; return false;
} }
do { do {
if (!chunked_ && !content_length_parsed_) { if (!chunked_ && !content_length_parsed_) {
if (boost::iequals(header.first, http::headers::kContentLength)) { if (boost::iequals(header.first, headers::kContentLength)) {
content_length_parsed_ = true; content_length_parsed_ = true;
if (!StringToSizeT(header.second, 10, &content_length_)) { if (!StringToSizeT(header.second, 10, &content_length_)) {
@ -166,7 +165,7 @@ bool HttpParser::ParseHeaderLine(const std::string& line) {
// TODO: Replace `!chunked_` with <TransferEncodingParsed>. // TODO: Replace `!chunked_` with <TransferEncodingParsed>.
if (!chunked_ && !content_length_parsed_) { if (!chunked_ && !content_length_parsed_) {
if (boost::iequals(header.first, http::headers::kTransferEncoding)) { if (boost::iequals(header.first, headers::kTransferEncoding)) {
if (header.second == "chunked") { if (header.second == "chunked") {
// The content is chunked. // The content is chunked.
chunked_ = true; chunked_ = true;
@ -178,7 +177,7 @@ bool HttpParser::ParseHeaderLine(const std::string& line) {
} while (false); } while (false);
// Parse Content-Type. // Parse Content-Type.
if (boost::iequals(header.first, http::headers::kContentType)) { if (boost::iequals(header.first, headers::kContentType)) {
ContentType content_type(header.second); ContentType content_type(header.second);
if (!content_type.Valid()) { if (!content_type.Valid()) {
LOG_ERRO("Invalid content-type header: %s", header.second.c_str()); LOG_ERRO("Invalid content-type header: %s", header.second.c_str());
@ -192,7 +191,7 @@ bool HttpParser::ParseHeaderLine(const std::string& line) {
return true; return true;
} }
bool HttpParser::ParseContent(const char* data, std::size_t length) { bool Parser::ParseContent(const char* data, std::size_t length) {
if (chunked_) { if (chunked_) {
return ParseChunkedContent(data, length); return ParseChunkedContent(data, length);
} else { } 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_) { if (!content_length_parsed_) {
// No Content-Length, no content. // No Content-Length, no content.
Finish(); Finish();
@ -229,7 +228,7 @@ bool HttpParser::ParseFixedContent(const char* data, std::size_t length) {
return true; 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. // Append the new data to the pending data.
// NOTE: It's more difficult to avoid this than fixed-length content. // NOTE: It's more difficult to avoid this than fixed-length content.
pending_data_.append(data, length); pending_data_.append(data, length);
@ -285,7 +284,7 @@ bool HttpParser::ParseChunkedContent(const char* data, std::size_t length) {
return true; return true;
} }
bool HttpParser::ParseChunkSize() { bool Parser::ParseChunkSize() {
LOG_VERB("Parse chunk size."); LOG_VERB("Parse chunk size.");
std::string line; std::string line;
@ -312,7 +311,7 @@ bool HttpParser::ParseChunkSize() {
return true; return true;
} }
bool HttpParser::Finish() { bool Parser::Finish() {
finished_ = true; finished_ = true;
if (content_.empty()) { if (content_.empty()) {
@ -336,21 +335,21 @@ bool HttpParser::Finish() {
return true; 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); content_.append(data, count);
} }
void HttpParser::AppendContent(const std::string& data) { void Parser::AppendContent(const std::string& data) {
content_.append(data); content_.append(data);
} }
bool HttpParser::IsContentFull() const { bool Parser::IsContentFull() const {
return content_length_ != kInvalidLength && return content_length_ != kInvalidLength &&
content_length_ <= content_.length(); content_length_ <= content_.length();
} }
bool HttpParser::IsContentCompressed() const { bool Parser::IsContentCompressed() const {
return message_->GetContentEncoding() != http::ContentEncoding::kUnknown; return message_->GetContentEncoding() != ContentEncoding::kUnknown;
} }
} // namespace webcc } // namespace webcc

@ -1,5 +1,5 @@
#ifndef WEBCC_HTTP_PARSER_H_ #ifndef WEBCC_PARSER_H_
#define WEBCC_HTTP_PARSER_H_ #define WEBCC_PARSER_H_
#include <string> #include <string>
@ -8,19 +8,19 @@
namespace webcc { namespace webcc {
class HttpMessage; class Message;
// HTTP request and response parser. // HTTP request and response parser.
class HttpParser { class Parser {
public: public:
explicit HttpParser(HttpMessage* message); explicit Parser(Message* message);
virtual ~HttpParser() = default; virtual ~Parser() = default;
HttpParser(const HttpParser&) = delete; Parser(const Parser&) = delete;
HttpParser& operator=(const HttpParser&) = delete; Parser& operator=(const Parser&) = delete;
void Init(HttpMessage* message); void Init(Message* message);
bool finished() const { return finished_; } bool finished() const { return finished_; }
@ -66,7 +66,7 @@ protected:
protected: protected:
// The result HTTP message. // The result HTTP message.
HttpMessage* message_; Message* message_;
// Data waiting to be parsed. // Data waiting to be parsed.
std::string pending_data_; std::string pending_data_;
@ -84,4 +84,4 @@ protected:
} // namespace webcc } // 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/logger.h"
#include "webcc/utility.h" #include "webcc/utility.h"
@ -18,17 +18,17 @@ const char DOUBLE_DASHES[] = { '-', '-' };
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void HttpRequest::Prepare() { void Request::Prepare() {
CreateStartLine(); CreateStartLine();
if (url_.port().empty()) { if (url_.port().empty()) {
SetHeader(http::headers::kHost, url_.host()); SetHeader(headers::kHost, url_.host());
} else { } else {
SetHeader(http::headers::kHost, url_.host() + ":" + url_.port()); SetHeader(headers::kHost, url_.host() + ":" + url_.port());
} }
if (form_parts_.empty()) { if (form_parts_.empty()) {
HttpMessage::Prepare(); Message::Prepare();
return; return;
} }
@ -69,13 +69,13 @@ void HttpRequest::Prepare() {
SetContentLength(content_length); SetContentLength(content_length);
// Prepare start line and headers. // Prepare start line and headers.
HttpMessage::Prepare(); Message::Prepare();
// Append payload of content data. // Append payload of content data.
payload_.insert(payload_.end(), data_payload.begin(), data_payload.end()); payload_.insert(payload_.end(), data_payload.begin(), data_payload.end());
} }
void HttpRequest::CreateStartLine() { void Request::CreateStartLine() {
if (!start_line_.empty()) { if (!start_line_.empty()) {
return; return;
} }

@ -1,27 +1,27 @@
#ifndef WEBCC_HTTP_REQUEST_H_ #ifndef WEBCC_REQUEST_H_
#define WEBCC_HTTP_REQUEST_H_ #define WEBCC_REQUEST_H_
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include "webcc/http_message.h" #include "webcc/message.h"
#include "webcc/url.h" #include "webcc/url.h"
namespace webcc { namespace webcc {
class HttpRequest; class Request;
typedef std::shared_ptr<HttpRequest> HttpRequestPtr; using RequestPtr = std::shared_ptr<Request>;
class HttpRequest : public HttpMessage { class Request : public Message {
public: 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) { : method_(method), url_(url) {
} }
~HttpRequest() override = default; ~Request() override = default;
const std::string& method() const { const std::string& method() const {
return method_; return method_;
@ -86,4 +86,4 @@ private:
} // namespace webcc } // 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 <sstream>
#include "webcc/globals.h" #include "webcc/globals.h"
#include "webcc/http_request.h" #include "webcc/request.h"
#include "webcc/http_response.h" #include "webcc/response.h"
#include "webcc/logger.h" #include "webcc/logger.h"
namespace webcc { namespace webcc {
void HttpRequestHandler::Enqueue(HttpConnectionPtr connection) { void RequestHandler::Enqueue(ConnectionPtr connection) {
queue_.Push(connection); queue_.Push(connection);
} }
void HttpRequestHandler::Start(std::size_t count) { void RequestHandler::Start(std::size_t count) {
assert(count > 0 && workers_.size() == 0); assert(count > 0 && workers_.size() == 0);
for (std::size_t i = 0; i < count; ++i) { 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..."); LOG_INFO("Stopping workers...");
// Clear pending connections. // 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..."); LOG_INFO("Clear pending connections...");
queue_.Clear(); queue_.Clear();
// Enqueue a null connection to trigger the first worker to stop. // Enqueue a null connection to trigger the first worker to stop.
queue_.Push(HttpConnectionPtr()); queue_.Push(ConnectionPtr());
for (auto& worker : workers_) { for (auto& worker : workers_) {
if (worker.joinable()) { if (worker.joinable()) {
@ -41,17 +41,17 @@ void HttpRequestHandler::Stop() {
LOG_INFO("All workers have been stopped."); LOG_INFO("All workers have been stopped.");
} }
void HttpRequestHandler::WorkerRoutine() { void RequestHandler::WorkerRoutine() {
LOG_INFO("Worker is running."); LOG_INFO("Worker is running.");
for (;;) { for (;;) {
HttpConnectionPtr connection = queue_.PopOrWait(); ConnectionPtr connection = queue_.PopOrWait();
if (!connection) { if (!connection) {
LOG_INFO("Worker is going to stop."); LOG_INFO("Worker is going to stop.");
// For stopping next worker. // For stopping next worker.
queue_.Push(HttpConnectionPtr()); queue_.Push(ConnectionPtr());
// Stop the worker. // Stop the worker.
break; 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 <vector>
#include "boost/algorithm/string.hpp" #include "boost/algorithm/string.hpp"
#include "webcc/http_request.h" #include "webcc/request.h"
#include "webcc/logger.h" #include "webcc/logger.h"
namespace webcc { namespace webcc {
HttpRequestParser::HttpRequestParser(HttpRequest* request) RequestParser::RequestParser(Request* request)
: HttpParser(request), request_(request) { : Parser(request), request_(request) {
} }
void HttpRequestParser::Init(HttpRequest* request) { void RequestParser::Init(Request* request) {
HttpParser::Init(request); Parser::Init(request);
request_ = request; request_ = request;
} }
bool HttpRequestParser::ParseStartLine(const std::string& line) { bool RequestParser::ParseStartLine(const std::string& line) {
std::vector<std::string> strs; std::vector<std::string> strs;
boost::split(strs, line, boost::is_any_of(" "), boost::token_compress_on); 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; return true;
} }
bool HttpRequestParser::ParseContent(const char* data, std::size_t length) { bool RequestParser::ParseContent(const char* data, std::size_t length) {
if (chunked_) { if (chunked_) {
return ParseChunkedContent(data, length); return ParseChunkedContent(data, length);
} else { } 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) { std::size_t length) {
// Append the new data to the pending data. // Append the new data to the pending data.
// NOTE: It's more difficult to avoid this than normal fixed-length content. // 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; return true;
} }
bool HttpRequestParser::ParsePartHeaders(bool* need_more_data) { bool RequestParser::ParsePartHeaders(bool* need_more_data) {
std::size_t off = 0; std::size_t off = 0;
while (true) { while (true) {
@ -157,7 +157,7 @@ bool HttpRequestParser::ParsePartHeaders(bool* need_more_data) {
break; break;
} }
HttpHeader header; Header header;
if (!Split2(line, ':', &header.first, &header.second)) { if (!Split2(line, ':', &header.first, &header.second)) {
LOG_ERRO("Invalid part header line: %s", line.c_str()); LOG_ERRO("Invalid part header line: %s", line.c_str());
return false; return false;
@ -167,7 +167,7 @@ bool HttpRequestParser::ParsePartHeaders(bool* need_more_data) {
header.second.c_str()); header.second.c_str());
// Parse Content-Disposition. // Parse Content-Disposition.
if (boost::iequals(header.first, http::headers::kContentDisposition)) { if (boost::iequals(header.first, headers::kContentDisposition)) {
ContentDisposition content_disposition(header.second); ContentDisposition content_disposition(header.second);
if (!content_disposition.valid()) { if (!content_disposition.valid()) {
LOG_ERRO("Invalid content-disposition header: %s", LOG_ERRO("Invalid content-disposition header: %s",
@ -189,7 +189,7 @@ bool HttpRequestParser::ParsePartHeaders(bool* need_more_data) {
return true; return true;
} }
bool HttpRequestParser::GetNextBoundaryLine(std::size_t* b_off, bool RequestParser::GetNextBoundaryLine(std::size_t* b_off,
std::size_t* b_count, std::size_t* b_count,
bool* ended) { bool* ended) {
std::size_t off = 0; std::size_t off = 0;
@ -228,14 +228,14 @@ bool HttpRequestParser::GetNextBoundaryLine(std::size_t* b_off,
return false; return false;
} }
bool HttpRequestParser::IsBoundary(const std::string& line) const { bool RequestParser::IsBoundary(const std::string& line) const {
if (line == "--" + request_->content_type().boundary()) { if (line == "--" + request_->content_type().boundary()) {
return true; return true;
} }
return false; return false;
} }
bool HttpRequestParser::IsBoundaryEnd(const std::string& line) const { bool RequestParser::IsBoundaryEnd(const std::string& line) const {
if (line == "--" + request_->content_type().boundary() + "--") { if (line == "--" + request_->content_type().boundary() + "--") {
return true; return true;
} }

@ -1,21 +1,21 @@
#ifndef WEBCC_HTTP_REQUEST_PARSER_H_ #ifndef WEBCC_REQUEST_PARSER_H_
#define WEBCC_HTTP_REQUEST_PARSER_H_ #define WEBCC_REQUEST_PARSER_H_
#include <string> #include <string>
#include "webcc/http_parser.h" #include "webcc/parser.h"
namespace webcc { namespace webcc {
class HttpRequest; class Request;
class HttpRequestParser : public HttpParser { class RequestParser : public Parser {
public: 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: private:
bool ParseStartLine(const std::string& line) final; bool ParseStartLine(const std::string& line) final;
@ -33,7 +33,7 @@ private:
bool IsBoundaryEnd(const std::string& line) const; bool IsBoundaryEnd(const std::string& line) const;
private: private:
HttpRequest* request_; Request* request_;
enum Step { enum Step {
kStart, kStart,
@ -48,4 +48,4 @@ private:
} // namespace webcc } // 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" #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) { const std::string& ToString(int status) {
switch (status) { switch (status) {
case http::Status::kOK: case Status::kOK:
return OK; return OK;
case http::Status::kCreated: case Status::kCreated:
return CREATED; return CREATED;
case http::Status::kAccepted: case Status::kAccepted:
return ACCEPTED; return ACCEPTED;
case http::Status::kNoContent: case Status::kNoContent:
return NO_CONTENT; return NO_CONTENT;
case http::Status::kNotModified: case Status::kNotModified:
return NOT_MODIFIED; return NOT_MODIFIED;
case http::Status::kBadRequest: case Status::kBadRequest:
return BAD_REQUEST; return BAD_REQUEST;
case http::Status::kNotFound: case Status::kNotFound:
return NOT_FOUND; return NOT_FOUND;
case http::Status::kInternalServerError: case Status::kInternalServerError:
return INTERNAL_SERVER_ERROR; return INTERNAL_SERVER_ERROR;
case http::Status::kNotImplemented: case Status::kNotImplemented:
return NOT_IMPLEMENTED; return NOT_IMPLEMENTED;
case http::Status::kServiceUnavailable: case Status::kServiceUnavailable:
return SERVICE_UNAVAILABLE; return SERVICE_UNAVAILABLE;
default: default:
@ -57,15 +57,15 @@ const std::string& ToString(int status) {
} // namespace status_strings } // namespace status_strings
void HttpResponse::Prepare() { void Response::Prepare() {
if (start_line_.empty()) { if (start_line_.empty()) {
start_line_ = status_strings::ToString(status_); start_line_ = status_strings::ToString(status_);
} }
SetHeader(http::headers::kServer, http::UserAgent()); SetHeader(headers::kServer, UserAgent());
SetHeader(http::headers::kDate, GetHttpDateTimestamp()); SetHeader(headers::kDate, GetTimestamp());
HttpMessage::Prepare(); Message::Prepare();
} }
} // namespace webcc } // 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 "boost/algorithm/string.hpp"
#include "webcc/http_response.h" #include "webcc/response.h"
#include "webcc/logger.h" #include "webcc/logger.h"
namespace webcc { namespace webcc {
HttpResponseParser::HttpResponseParser(HttpResponse* response) ResponseParser::ResponseParser(Response* response)
: HttpParser(response), response_(response) { : Parser(response), response_(response) {
} }
void HttpResponseParser::Init(HttpResponse* response) { void ResponseParser::Init(Response* response) {
HttpParser::Init(response); Parser::Init(response);
response_ = response; response_ = response;
} }
bool HttpResponseParser::ParseStartLine(const std::string& line) { bool ResponseParser::ParseStartLine(const std::string& line) {
std::vector<std::string> parts; std::vector<std::string> parts;
boost::split(parts, line, boost::is_any_of(" "), boost::token_compress_on); 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); return service_manager_.AddService(service, url, is_regex);
} }
void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) { void RestRequestHandler::HandleConnection(ConnectionPtr connection) {
HttpRequestPtr http_request = connection->request(); RequestPtr http_request = connection->request();
assert(http_request); assert(http_request);
const Url& url = http_request->url(); const Url& url = http_request->url();
@ -28,14 +28,14 @@ void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
if (!service) { if (!service) {
LOG_WARN("No service matches the URL path: %s", url.path().c_str()); LOG_WARN("No service matches the URL path: %s", url.path().c_str());
connection->SendResponse(http::Status::kNotFound); connection->SendResponse(Status::kNotFound);
return; return;
} }
RestResponse rest_response; RestResponse rest_response;
service->Handle(rest_request, &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.content.empty()) {
if (!rest_response.media_type.empty()) { if (!rest_response.media_type.empty()) {
@ -48,7 +48,7 @@ void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
http_request->AcceptEncodingGzip()) { http_request->AcceptEncodingGzip()) {
std::string compressed; std::string compressed;
if (Compress(rest_response.content, &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); http_response->SetContent(std::move(compressed), true);
} }
} else { } else {

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

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

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

@ -14,7 +14,7 @@
#include <vector> #include <vector>
#include "webcc/globals.h" #include "webcc/globals.h"
#include "webcc/http_request.h" #include "webcc/request.h"
#include "webcc/url.h" #include "webcc/url.h"
namespace webcc { namespace webcc {
@ -22,18 +22,18 @@ namespace webcc {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Regex sub-matches of the URL. // Regex sub-matches of the URL.
typedef std::vector<std::string> UrlMatches; using UrlMatches = std::vector<std::string>;
struct RestRequest { struct RestRequest {
// Original HTTP request. // Original HTTP request.
HttpRequestPtr http; RequestPtr http;
// Regex sub-matches of the URL (usually resource ID's). // Regex sub-matches of the URL (usually resource ID's).
UrlMatches url_matches; UrlMatches url_matches;
}; };
struct RestResponse { struct RestResponse {
http::Status status; Status status;
std::string content; std::string content;
@ -52,7 +52,7 @@ public:
virtual void Handle(const RestRequest& request, RestResponse* response) = 0; 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 <csignal>
#include <utility> #include <utility>
#include "webcc/http_request_handler.h" #include "webcc/request_handler.h"
#include "webcc/logger.h" #include "webcc/logger.h"
#include "webcc/utility.h" #include "webcc/utility.h"
@ -11,7 +11,7 @@ using tcp = boost::asio::ip::tcp;
namespace webcc { 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) { : acceptor_(io_context_), signals_(io_context_), workers_(workers) {
RegisterSignals(); RegisterSignals();
@ -50,7 +50,7 @@ HttpServer::HttpServer(std::uint16_t port, std::size_t workers)
} }
} }
void HttpServer::Run() { void Server::Run() {
assert(GetRequestHandler() != nullptr); assert(GetRequestHandler() != nullptr);
if (!acceptor_.is_open()) { if (!acceptor_.is_open()) {
@ -74,7 +74,7 @@ void HttpServer::Run() {
io_context_.run(); io_context_.run();
} }
void HttpServer::RegisterSignals() { void Server::RegisterSignals() {
signals_.add(SIGINT); // Ctrl+C signals_.add(SIGINT); // Ctrl+C
signals_.add(SIGTERM); signals_.add(SIGTERM);
@ -83,7 +83,7 @@ void HttpServer::RegisterSignals() {
#endif #endif
} }
void HttpServer::DoAccept() { void Server::DoAccept() {
acceptor_.async_accept( acceptor_.async_accept(
[this](boost::system::error_code ec, tcp::socket socket) { [this](boost::system::error_code ec, tcp::socket socket) {
// Check whether the server was stopped by a signal before this // Check whether the server was stopped by a signal before this
@ -95,7 +95,7 @@ void HttpServer::DoAccept() {
if (!ec) { if (!ec) {
LOG_INFO("Accepted a connection."); LOG_INFO("Accepted a connection.");
auto connection = std::make_shared<HttpConnection>( auto connection = std::make_shared<Connection>(
std::move(socket), &pool_, GetRequestHandler()); std::move(socket), &pool_, GetRequestHandler());
pool_.Start(connection); pool_.Start(connection);
@ -105,7 +105,7 @@ void HttpServer::DoAccept() {
}); });
} }
void HttpServer::DoAwaitStop() { void Server::DoAwaitStop() {
signals_.async_wait( signals_.async_wait(
[this](boost::system::error_code, int signo) { [this](boost::system::error_code, int signo) {
// The server is stopped by canceling all outstanding asynchronous // The server is stopped by canceling all outstanding asynchronous

@ -1,5 +1,5 @@
#ifndef WEBCC_HTTP_SERVER_H_ #ifndef WEBCC_SERVER_H_
#define WEBCC_HTTP_SERVER_H_ #define WEBCC_SERVER_H_
#include <string> #include <string>
@ -8,23 +8,23 @@
#include "boost/asio/signal_set.hpp" #include "boost/asio/signal_set.hpp"
#include "webcc/globals.h" #include "webcc/globals.h"
#include "webcc/http_connection.h" #include "webcc/connection.h"
#include "webcc/http_connection_pool.h" #include "webcc/connection_pool.h"
namespace webcc { namespace webcc {
class HttpRequestHandler; class RequestHandler;
// HTTP server accepts TCP connections from TCP clients. // HTTP server accepts TCP connections from TCP clients.
// NOTE: Only support IPv4. // NOTE: Only support IPv4.
class HttpServer { class Server {
public: 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; Server(const Server&) = delete;
HttpServer& operator=(const HttpServer&) = delete; Server& operator=(const Server&) = delete;
// Run the server's io_service loop. // Run the server's io_service loop.
void Run(); void Run();
@ -40,7 +40,7 @@ private:
void DoAwaitStop(); void DoAwaitStop();
// Get the handler for incoming requests. // Get the handler for incoming requests.
virtual HttpRequestHandler* GetRequestHandler() = 0; virtual RequestHandler* GetRequestHandler() = 0;
// The io_context used to perform asynchronous operations. // The io_context used to perform asynchronous operations.
boost::asio::io_context io_context_; boost::asio::io_context io_context_;
@ -49,7 +49,7 @@ private:
boost::asio::ip::tcp::acceptor acceptor_; boost::asio::ip::tcp::acceptor acceptor_;
// The connection pool which owns all live connections. // The connection pool which owns all live connections.
HttpConnectionPool pool_; ConnectionPool pool_;
// The signal_set is used to register for process termination notifications. // The signal_set is used to register for process termination notifications.
boost::asio::signal_set signals_; boost::asio::signal_set signals_;
@ -60,4 +60,4 @@ private:
} // namespace webcc } // 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/connect.hpp"
#include "boost/asio/read.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) { : socket_(io_context) {
} }
void HttpSocket::Connect(const std::string& /*host*/, void Socket::Connect(const std::string& /*host*/,
const Endpoints& endpoints, const Endpoints& endpoints,
boost::system::error_code* ec) { boost::system::error_code* ec) {
boost::asio::connect(socket_, endpoints, *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::system::error_code* ec) {
boost::asio::write(socket_, request.payload(), *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)); 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); socket_.close(*ec);
} }
@ -39,8 +39,7 @@ void HttpSocket::Close(boost::system::error_code* ec) {
namespace ssl = boost::asio::ssl; namespace ssl = boost::asio::ssl;
HttpSslSocket::HttpSslSocket(boost::asio::io_context& io_context, SslSocket::SslSocket(boost::asio::io_context& io_context, bool ssl_verify)
bool ssl_verify)
: ssl_context_(ssl::context::sslv23), : ssl_context_(ssl::context::sslv23),
ssl_socket_(io_context, ssl_context_), ssl_socket_(io_context, ssl_context_),
ssl_verify_(ssl_verify) { ssl_verify_(ssl_verify) {
@ -48,8 +47,7 @@ HttpSslSocket::HttpSslSocket(boost::asio::io_context& io_context,
ssl_context_.set_default_verify_paths(); ssl_context_.set_default_verify_paths();
} }
void HttpSslSocket::Connect(const std::string& host, void SslSocket::Connect(const std::string& host, const Endpoints& endpoints,
const Endpoints& endpoints,
boost::system::error_code* ec) { boost::system::error_code* ec) {
boost::asio::connect(ssl_socket_.lowest_layer(), endpoints, *ec); boost::asio::connect(ssl_socket_.lowest_layer(), endpoints, *ec);
@ -60,20 +58,20 @@ void HttpSslSocket::Connect(const std::string& host,
Handshake(host, ec); Handshake(host, ec);
} }
void HttpSslSocket::Write(const HttpRequest& request, void SslSocket::Write(const Request& request, boost::system::error_code* ec) {
boost::system::error_code* ec) {
boost::asio::write(ssl_socket_, request.payload(), *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)); 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); ssl_socket_.lowest_layer().close(*ec);
} }
void HttpSslSocket::Handshake(const std::string& host, void SslSocket::Handshake(const std::string& host,
boost::system::error_code* ec) { boost::system::error_code* ec) {
if (ssl_verify_) { if (ssl_verify_) {
ssl_socket_.set_verify_mode(ssl::verify_peer); ssl_socket_.set_verify_mode(ssl::verify_peer);

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

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

@ -1,52 +1,12 @@
#include "webcc/utility.h" #include "webcc/utility.h"
#include <algorithm>
#include <ctime>
#include <iomanip> // for put_time
#include <ostream>
#include <sstream> #include <sstream>
#include "boost/uuid/random_generator.hpp" #include "boost/uuid/random_generator.hpp"
#include "boost/uuid/uuid_io.hpp" #include "boost/uuid/uuid_io.hpp"
#include "webcc/logger.h"
using tcp = boost::asio::ip::tcp;
namespace webcc { 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() { std::string RandomUuid() {
boost::uuids::uuid u = boost::uuids::random_generator()(); boost::uuids::uuid u = boost::uuids::random_generator()();
std::stringstream ss; std::stringstream ss;

@ -4,19 +4,8 @@
#include <iosfwd> #include <iosfwd>
#include <string> #include <string>
#include "boost/asio/ip/tcp.hpp"
namespace webcc { 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. // Get the timestamp for HTTP Date header field.
// E.g., Wed, 21 Oct 2015 07:28:00 GMT // E.g., Wed, 21 Oct 2015 07:28:00 GMT
// See: https://tools.ietf.org/html/rfc7231#section-7.1.1.2 // See: https://tools.ietf.org/html/rfc7231#section-7.1.1.2

Loading…
Cancel
Save