Remove http prefix
This commit is contained in:
+1
-3
@@ -96,7 +96,7 @@ if(WEBCC_ENABLE_SSL)
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
# For including its own headers as "webcc/http_client.h".
|
||||
# For including its own headers as "webcc/client.h".
|
||||
${PROJECT_SOURCE_DIR}
|
||||
# For including config.h as "webcc/config.h".
|
||||
${PROJECT_BINARY_DIR}
|
||||
@@ -109,8 +109,6 @@ if(WIN32)
|
||||
link_directories(${THIRD_PARTY_DIR}/win32/lib)
|
||||
endif()
|
||||
|
||||
# For including pugixml as "pugixml/pugixml.hpp" or gtest as
|
||||
# "gtest/gtest.h".
|
||||
include_directories(${THIRD_PARTY_DIR}/src)
|
||||
|
||||
# For using zlib on Windows.
|
||||
|
||||
@@ -12,7 +12,7 @@ A complete client example:
|
||||
```cpp
|
||||
#include <iostream>
|
||||
|
||||
#include "webcc/http_client_session.h"
|
||||
#include "webcc/client_session.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
int main() {
|
||||
@@ -21,7 +21,7 @@ int main() {
|
||||
|
||||
// Session provides convenient request APIs, stores request configurations
|
||||
// and manages persistent connenction.
|
||||
webcc::HttpClientSession session;
|
||||
webcc::ClientSession session;
|
||||
|
||||
// Catch exceptions for error handling.
|
||||
try {
|
||||
@@ -41,11 +41,11 @@ int main() {
|
||||
|
||||
The `Get()` method is nothing but a shortcut of `Request()`. Using `Request()` directly is more complicated:
|
||||
```cpp
|
||||
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
auto r = session.Request(webcc::RequestBuilder{}.Get().
|
||||
Url("http://httpbin.org/get")
|
||||
());
|
||||
```
|
||||
As you can see, a helper class named `HttpRequestBuilder` is used to chain the parameters and finally build (don't miss the `()` operator) a request object.
|
||||
As you can see, a helper class named `RequestBuilder` is used to chain the parameters and finally build (don't miss the `()` operator) a request object.
|
||||
|
||||
Both the shortcut and `Request()` accept URL query parameters:
|
||||
|
||||
@@ -53,7 +53,7 @@ Both the shortcut and `Request()` accept URL query parameters:
|
||||
// Query parameters are passed using a std::vector.
|
||||
session.Get("http://httpbin.org/get", { "key1", "value1", "key2", "value2" });
|
||||
|
||||
session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
session.Request(webcc::RequestBuilder{}.Get().
|
||||
Url("http://httpbin.org/get").
|
||||
Query("key1", "value1").
|
||||
Query("key2", "value2")
|
||||
@@ -66,7 +66,7 @@ session.Get("http://httpbin.org/get",
|
||||
{"key1", "value1", "key2", "value2"},
|
||||
{"Accept", "application/json"}); // Also a std::vector
|
||||
|
||||
session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
session.Request(webcc::RequestBuilder{}.Get().
|
||||
Url("http://httpbin.org/get").
|
||||
Query("key1", "value1").
|
||||
Query("key2", "value2").
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Auto test
|
||||
|
||||
set(AT_SRCS
|
||||
http_client_autotest.cc
|
||||
client_autotest.cc
|
||||
)
|
||||
|
||||
set(AT_TARGET_NAME webcc_autotest)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "boost/algorithm/string.hpp"
|
||||
#include "json/json.h"
|
||||
|
||||
#include "webcc/http_client_session.h"
|
||||
#include "webcc/client_session.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -36,7 +36,7 @@ static Json::Value StringToJson(const std::string& str) {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void AssertGet(webcc::HttpResponsePtr r) {
|
||||
static void AssertGet(webcc::ResponsePtr r) {
|
||||
Json::Value json = StringToJson(r->content());
|
||||
|
||||
Json::Value args = json["args"];
|
||||
@@ -52,11 +52,11 @@ static void AssertGet(webcc::HttpResponsePtr r) {
|
||||
EXPECT_EQ("httpbin.org", headers["Host"].asString());
|
||||
}
|
||||
|
||||
TEST(HttpClientTest, Get_RequestFunc) {
|
||||
webcc::HttpClientSession session;
|
||||
TEST(ClientTest, Get_RequestFunc) {
|
||||
webcc::ClientSession session;
|
||||
|
||||
try {
|
||||
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
auto r = session.Request(webcc::RequestBuilder{}.Get().
|
||||
Url("http://httpbin.org/get").
|
||||
Query("key1", "value1").
|
||||
Query("key2", "value2").
|
||||
@@ -70,8 +70,8 @@ TEST(HttpClientTest, Get_RequestFunc) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(HttpClientTest, Get_Shortcut) {
|
||||
webcc::HttpClientSession session;
|
||||
TEST(ClientTest, Get_Shortcut) {
|
||||
webcc::ClientSession session;
|
||||
|
||||
try {
|
||||
auto r = session.Get("http://httpbin.org/get",
|
||||
@@ -86,14 +86,14 @@ TEST(HttpClientTest, Get_Shortcut) {
|
||||
}
|
||||
|
||||
#if WEBCC_ENABLE_SSL
|
||||
TEST(HttpClientTest, Get_SSL) {
|
||||
webcc::HttpClientSession session;
|
||||
TEST(ClientTest, Get_SSL) {
|
||||
webcc::ClientSession session;
|
||||
|
||||
session.set_ssl_verify(kSslVerify);
|
||||
|
||||
try {
|
||||
// HTTPS is auto-detected from the URL scheme.
|
||||
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
auto r = session.Request(webcc::RequestBuilder{}.Get().
|
||||
Url("https://httpbin.org/get").
|
||||
Query("key1", "value1").
|
||||
Query("key2", "value2").
|
||||
@@ -111,8 +111,8 @@ TEST(HttpClientTest, Get_SSL) {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Test Gzip compressed response.
|
||||
TEST(HttpClientTest, Compression_Gzip) {
|
||||
webcc::HttpClientSession session;
|
||||
TEST(ClientTest, Compression_Gzip) {
|
||||
webcc::ClientSession session;
|
||||
|
||||
try {
|
||||
auto r = session.Get("http://httpbin.org/gzip");
|
||||
@@ -127,8 +127,8 @@ TEST(HttpClientTest, Compression_Gzip) {
|
||||
}
|
||||
|
||||
// Test Deflate compressed response.
|
||||
TEST(HttpClientTest, Compression_Deflate) {
|
||||
webcc::HttpClientSession session;
|
||||
TEST(ClientTest, Compression_Deflate) {
|
||||
webcc::ClientSession session;
|
||||
|
||||
try {
|
||||
auto r = session.Get("http://httpbin.org/deflate");
|
||||
@@ -157,8 +157,8 @@ TEST(HttpClientTest, Compression_Deflate) {
|
||||
// "https://www.google.com";
|
||||
// "https://api.github.com/events";
|
||||
//
|
||||
TEST(HttpClientTest, KeepAlive) {
|
||||
webcc::HttpClientSession session;
|
||||
TEST(ClientTest, KeepAlive) {
|
||||
webcc::ClientSession session;
|
||||
|
||||
std::string url = "http://httpbin.org/get";
|
||||
try {
|
||||
@@ -176,14 +176,14 @@ TEST(HttpClientTest, KeepAlive) {
|
||||
EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Close"));
|
||||
|
||||
// Close by using request builder.
|
||||
r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
r = session.Request(webcc::RequestBuilder{}.Get().
|
||||
Url(url).KeepAlive(false)
|
||||
());
|
||||
|
||||
EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Close"));
|
||||
|
||||
// Keep-Alive explicitly by using request builder.
|
||||
r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
r = session.Request(webcc::RequestBuilder{}.Get().
|
||||
Url(url).KeepAlive(true)
|
||||
());
|
||||
|
||||
@@ -197,8 +197,8 @@ TEST(HttpClientTest, KeepAlive) {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Get a JPEG image.
|
||||
TEST(HttpClientTest, GetImageJpeg) {
|
||||
webcc::HttpClientSession session;
|
||||
TEST(ClientTest, GetImageJpeg) {
|
||||
webcc::ClientSession session;
|
||||
|
||||
std::string url = "http://httpbin.org/get";
|
||||
try {
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "webcc/http_client_session.h"
|
||||
#include "webcc/client_session.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
#if (defined(WIN32) || defined(_WIN64))
|
||||
@@ -19,7 +19,7 @@ static void PrintSeparator() {
|
||||
int main() {
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
|
||||
webcc::HttpClientSession session;
|
||||
webcc::ClientSession session;
|
||||
|
||||
session.set_ssl_verify(kSslVerify);
|
||||
|
||||
@@ -27,7 +27,7 @@ int main() {
|
||||
PrintSeparator();
|
||||
|
||||
// Using request builder:
|
||||
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
auto r = session.Request(webcc::RequestBuilder{}.Get().
|
||||
Url("http://httpbin.org/get").
|
||||
Query("key1", "value1").
|
||||
Query("key2", "value2").
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "boost/filesystem.hpp"
|
||||
|
||||
#include "webcc/http_client_session.h"
|
||||
#include "webcc/client_session.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
#if (defined(WIN32) || defined(_WIN64))
|
||||
@@ -47,12 +47,12 @@ int main(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
webcc::HttpClientSession session;
|
||||
webcc::ClientSession session;
|
||||
|
||||
try {
|
||||
//auto r = session.PostFile(url, "file", upload_dir / "remember.txt");
|
||||
|
||||
auto r = session.Request(webcc::HttpRequestBuilder{}.Post().
|
||||
auto r = session.Request(webcc::RequestBuilder{}.Post().
|
||||
Url(url).
|
||||
File("file", upload_dir / "remember.txt").
|
||||
Form("json", "{}", "application/json")
|
||||
|
||||
@@ -20,9 +20,9 @@ public:
|
||||
}
|
||||
|
||||
response->content = "OK";
|
||||
response->media_type = webcc::http::media_types::kTextPlain;
|
||||
response->media_type = webcc::media_types::kTextPlain;
|
||||
response->charset = "utf-8";
|
||||
response->status = webcc::http::Status::kCreated;
|
||||
response->status = webcc::Status::kCreated;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+15
-11
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "json/json.h"
|
||||
|
||||
#include "webcc/http_client_session.h"
|
||||
#include "webcc/client_session.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -61,7 +61,7 @@ void PrettyPrintJsonString(const std::string& str) {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// List public events.
|
||||
void ListEvents(webcc::HttpClientSession& session) {
|
||||
void ListEvents(webcc::ClientSession& session) {
|
||||
try {
|
||||
auto r = session.Get(kUrlRoot + "/events");
|
||||
|
||||
@@ -75,8 +75,7 @@ void ListEvents(webcc::HttpClientSession& session) {
|
||||
// List the followers of the given user.
|
||||
// Example:
|
||||
// ListUserFollowers(session, "<login>")
|
||||
void ListUserFollowers(webcc::HttpClientSession& session,
|
||||
const std::string& user) {
|
||||
void ListUserFollowers(webcc::ClientSession& session, const std::string& user) {
|
||||
try {
|
||||
auto r = session.Get(kUrlRoot + "/users/" + user + "/followers");
|
||||
|
||||
@@ -90,11 +89,11 @@ void ListUserFollowers(webcc::HttpClientSession& session,
|
||||
// List the followers of the current authorized user using Basic authorization.
|
||||
// E.g., list my own followers:
|
||||
// ListAuthUserFollowers(session, "sprinfall@gmail.com", "<MyPassword>");
|
||||
void ListAuthUserFollowers(webcc::HttpClientSession& session,
|
||||
void ListAuthUserFollowers(webcc::ClientSession& session,
|
||||
const std::string& login,
|
||||
const std::string& password) {
|
||||
try {
|
||||
auto r = session.Request(webcc::HttpRequestBuilder{}.Get().
|
||||
auto r = session.Request(webcc::RequestBuilder{}.Get().
|
||||
Url(kUrlRoot + "/user/followers").
|
||||
AuthBasic(login, password)
|
||||
());
|
||||
@@ -106,8 +105,9 @@ void ListAuthUserFollowers(webcc::HttpClientSession& session,
|
||||
}
|
||||
}
|
||||
|
||||
void CreateAuthorization(webcc::HttpClientSession& session,
|
||||
const std::string& auth) {
|
||||
void CreateAuthorization(webcc::ClientSession& session,
|
||||
const std::string& login,
|
||||
const std::string& password) {
|
||||
try {
|
||||
std::string data =
|
||||
"{\n"
|
||||
@@ -115,8 +115,12 @@ void CreateAuthorization(webcc::HttpClientSession& session,
|
||||
" 'scopes': ['public_repo', 'repo', 'repo:status', 'user']\n"
|
||||
"}";
|
||||
|
||||
auto r = session.Post(kUrlRoot + "/authorizations", std::move(data), true,
|
||||
{"Authorization", auth});
|
||||
auto r = session.Request(webcc::RequestBuilder{}.Post().
|
||||
Url(kUrlRoot + "/authorizations").
|
||||
Data(std::move(data)).
|
||||
Json(true).
|
||||
AuthBasic(login, password)
|
||||
());
|
||||
|
||||
std::cout << r->content() << std::endl;
|
||||
|
||||
@@ -130,7 +134,7 @@ void CreateAuthorization(webcc::HttpClientSession& session,
|
||||
int main() {
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||
|
||||
webcc::HttpClientSession session;
|
||||
webcc::ClientSession session;
|
||||
|
||||
session.set_ssl_verify(kSslVerify);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "json/json.h"
|
||||
|
||||
#include "webcc/http_client_session.h"
|
||||
#include "webcc/client_session.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
#include "examples/common/book.h"
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
class BookClientBase {
|
||||
public:
|
||||
BookClientBase(webcc::HttpClientSession& session, const std::string& url)
|
||||
BookClientBase(webcc::ClientSession& session, const std::string& url)
|
||||
: session_(session), url_(url) {
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
|
||||
protected:
|
||||
// Check HTTP response status.
|
||||
bool CheckStatus(webcc::HttpResponsePtr response, int expected_status) {
|
||||
bool CheckStatus(webcc::ResponsePtr response, int expected_status) {
|
||||
int status = response->status();
|
||||
if (status != expected_status) {
|
||||
LOG_ERRO("HTTP status error (actual: %d, expected: %d).",
|
||||
@@ -42,14 +42,14 @@ protected:
|
||||
protected:
|
||||
std::string url_;
|
||||
|
||||
webcc::HttpClientSession& session_;
|
||||
webcc::ClientSession& session_;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class BookListClient : public BookClientBase {
|
||||
public:
|
||||
BookListClient(webcc::HttpClientSession& session, const std::string& url)
|
||||
BookListClient(webcc::ClientSession& session, const std::string& url)
|
||||
: BookClientBase(session, url) {
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
try {
|
||||
auto r = session_.Get(url_ + "/books");
|
||||
|
||||
if (!CheckStatus(r, webcc::http::Status::kOK)) {
|
||||
if (!CheckStatus(r, webcc::Status::kOK)) {
|
||||
// Response HTTP status error.
|
||||
return false;
|
||||
}
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
try {
|
||||
auto r = session_.Post(url_ + "/books", JsonToString(req_json), true);
|
||||
|
||||
if (!CheckStatus(r, webcc::http::Status::kCreated)) {
|
||||
if (!CheckStatus(r, webcc::Status::kCreated)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
|
||||
class BookDetailClient : public BookClientBase {
|
||||
public:
|
||||
BookDetailClient(webcc::HttpClientSession& session, const std::string& url)
|
||||
BookDetailClient(webcc::ClientSession& session, const std::string& url)
|
||||
: BookClientBase(session, url) {
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
try {
|
||||
auto r = session_.Get(url_ + "/books/" + id);
|
||||
|
||||
if (!CheckStatus(r, webcc::http::Status::kOK)) {
|
||||
if (!CheckStatus(r, webcc::Status::kOK)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ public:
|
||||
try {
|
||||
auto r = session_.Put(url_ + "/books/" + id, JsonToString(json), true);
|
||||
|
||||
if (!CheckStatus(r, webcc::http::Status::kOK)) {
|
||||
if (!CheckStatus(r, webcc::Status::kOK)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ public:
|
||||
try {
|
||||
auto r = session_.Delete(url_ + "/books/" + id);
|
||||
|
||||
if (!CheckStatus(r, webcc::http::Status::kOK)) {
|
||||
if (!CheckStatus(r, webcc::Status::kOK)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ int main(int argc, char* argv[]) {
|
||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE_FILE_OVERWRITE);
|
||||
|
||||
// Share the same session.
|
||||
webcc::HttpClientSession session;
|
||||
webcc::ClientSession session;
|
||||
|
||||
// Session-level settings.
|
||||
session.set_timeout(timeout);
|
||||
|
||||
@@ -100,9 +100,9 @@ void BookListService::Get(const webcc::UrlQuery& /*query*/,
|
||||
|
||||
// TODO: Simplify
|
||||
response->content = JsonToString(json);
|
||||
response->media_type = webcc::http::media_types::kApplicationJson;
|
||||
response->media_type = webcc::media_types::kApplicationJson;
|
||||
response->charset = "utf-8";
|
||||
response->status = webcc::http::Status::kOK;
|
||||
response->status = webcc::Status::kOK;
|
||||
}
|
||||
|
||||
void BookListService::Post(const std::string& request_content,
|
||||
@@ -117,12 +117,12 @@ void BookListService::Post(const std::string& request_content,
|
||||
json["id"] = id;
|
||||
|
||||
response->content = JsonToString(json);
|
||||
response->media_type = webcc::http::media_types::kApplicationJson;
|
||||
response->media_type = webcc::media_types::kApplicationJson;
|
||||
response->charset = "utf-8";
|
||||
response->status = webcc::http::Status::kCreated;
|
||||
response->status = webcc::Status::kCreated;
|
||||
} else {
|
||||
// Invalid JSON
|
||||
response->status = webcc::http::Status::kBadRequest;
|
||||
response->status = webcc::Status::kBadRequest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ void BookDetailService::Get(const webcc::UrlMatches& url_matches,
|
||||
if (url_matches.size() != 1) {
|
||||
// Using kNotFound means the resource specified by the URL cannot be found.
|
||||
// kBadRequest could be another choice.
|
||||
response->status = webcc::http::Status::kNotFound;
|
||||
response->status = webcc::Status::kNotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -144,14 +144,14 @@ void BookDetailService::Get(const webcc::UrlMatches& url_matches,
|
||||
|
||||
const Book& book = g_book_store.GetBook(book_id);
|
||||
if (book.IsNull()) {
|
||||
response->status = webcc::http::Status::kNotFound;
|
||||
response->status = webcc::Status::kNotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
response->content = BookToJsonString(book);
|
||||
response->media_type = webcc::http::media_types::kApplicationJson;
|
||||
response->media_type = webcc::media_types::kApplicationJson;
|
||||
response->charset = "utf-8";
|
||||
response->status = webcc::http::Status::kOK;
|
||||
response->status = webcc::Status::kOK;
|
||||
}
|
||||
|
||||
void BookDetailService::Put(const webcc::UrlMatches& url_matches,
|
||||
@@ -160,7 +160,7 @@ void BookDetailService::Put(const webcc::UrlMatches& url_matches,
|
||||
Sleep(sleep_seconds_);
|
||||
|
||||
if (url_matches.size() != 1) {
|
||||
response->status = webcc::http::Status::kNotFound;
|
||||
response->status = webcc::Status::kNotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -168,14 +168,14 @@ void BookDetailService::Put(const webcc::UrlMatches& url_matches,
|
||||
|
||||
Book book;
|
||||
if (!JsonStringToBook(request_content, &book)) {
|
||||
response->status = webcc::http::Status::kBadRequest;
|
||||
response->status = webcc::Status::kBadRequest;
|
||||
return;
|
||||
}
|
||||
|
||||
book.id = book_id;
|
||||
g_book_store.UpdateBook(book);
|
||||
|
||||
response->status = webcc::http::Status::kOK;
|
||||
response->status = webcc::Status::kOK;
|
||||
}
|
||||
|
||||
void BookDetailService::Delete(const webcc::UrlMatches& url_matches,
|
||||
@@ -183,18 +183,18 @@ void BookDetailService::Delete(const webcc::UrlMatches& url_matches,
|
||||
Sleep(sleep_seconds_);
|
||||
|
||||
if (url_matches.size() != 1) {
|
||||
response->status = webcc::http::Status::kNotFound;
|
||||
response->status = webcc::Status::kNotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string& book_id = url_matches[0];
|
||||
|
||||
if (!g_book_store.DeleteBook(book_id)) {
|
||||
response->status = webcc::http::Status::kNotFound;
|
||||
response->status = webcc::Status::kNotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
response->status = webcc::http::Status::kOK;
|
||||
response->status = webcc::Status::kOK;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
set(UT_SRCS
|
||||
base64_unittest.cc
|
||||
http_parser_unittest.cc
|
||||
parser_unittest.cc
|
||||
rest_service_manager_unittest.cc
|
||||
url_unittest.cc
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "webcc/http_request.h"
|
||||
#include "webcc/http_request_parser.h"
|
||||
#include "webcc/http_response.h"
|
||||
#include "webcc/http_response_parser.h"
|
||||
#include "webcc/request.h"
|
||||
#include "webcc/request_parser.h"
|
||||
#include "webcc/response.h"
|
||||
#include "webcc/response_parser.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@@ -35,8 +35,8 @@ protected:
|
||||
|
||||
std::string payload_;
|
||||
|
||||
webcc::HttpRequest request_;
|
||||
webcc::HttpRequestParser parser_;
|
||||
webcc::Request request_;
|
||||
webcc::RequestParser parser_;
|
||||
};
|
||||
|
||||
TEST_F(GetRequestParserTest, ParseFullDataOnce) {
|
||||
@@ -121,8 +121,8 @@ protected:
|
||||
std::string payload_;
|
||||
std::string data_;
|
||||
|
||||
webcc::HttpRequest request_;
|
||||
webcc::HttpRequestParser parser_;
|
||||
webcc::Request request_;
|
||||
webcc::RequestParser parser_;
|
||||
};
|
||||
|
||||
TEST_F(PostRequestParserTest, ParseFullDataOnce) {
|
||||
@@ -8,7 +8,7 @@ class MyRestService : public webcc::RestService {
|
||||
public:
|
||||
void Handle(const webcc::RestRequest& request,
|
||||
webcc::RestResponse* response) final {
|
||||
response->status = webcc::http::Status::kOK;
|
||||
response->status = webcc::Status::kOK;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+10
-9
@@ -116,7 +116,7 @@ std::size_t Encode(const void* src, std::size_t len, void* dst) {
|
||||
return out - static_cast<char*>(dst);
|
||||
}
|
||||
|
||||
typedef std::pair<std::size_t, std::size_t> SizePair;
|
||||
using SizePair = std::pair<std::size_t, std::size_t>;
|
||||
|
||||
SizePair Decode(const char* src, std::size_t len, void* dst) {
|
||||
auto in = reinterpret_cast<const unsigned char*>(src);
|
||||
@@ -161,21 +161,22 @@ SizePair Decode(const char* src, std::size_t len, void* dst) {
|
||||
|
||||
} // namespace base64
|
||||
|
||||
std::string Base64Encode(const std::uint8_t* data, std::size_t len) {
|
||||
std::string Base64Encode(const std::uint8_t* data, std::size_t length) {
|
||||
std::string dst;
|
||||
dst.resize(base64::EncodedSize(len));
|
||||
dst.resize(base64::Encode(data, len, &dst[0]));
|
||||
dst.resize(base64::EncodedSize(length));
|
||||
dst.resize(base64::Encode(data, length, &dst[0]));
|
||||
return dst;
|
||||
}
|
||||
|
||||
std::string Base64Encode(const std::string& s) {
|
||||
return Base64Encode(reinterpret_cast<const std::uint8_t*>(s.data()), s.size());
|
||||
std::string Base64Encode(const std::string& input) {
|
||||
return Base64Encode(reinterpret_cast<const std::uint8_t*>(input.data()),
|
||||
input.size());
|
||||
}
|
||||
|
||||
std::string Base64Decode(const std::string& data) {
|
||||
std::string Base64Decode(const std::string& input) {
|
||||
std::string dst;
|
||||
dst.resize(base64::DecodedSize(data.size()));
|
||||
auto result = base64::Decode(data.data(), data.size(), &dst[0]);
|
||||
dst.resize(base64::DecodedSize(input.size()));
|
||||
auto result = base64::Decode(input.data(), input.size(), &dst[0]);
|
||||
dst.resize(result.first);
|
||||
return dst;
|
||||
}
|
||||
|
||||
+2
-2
@@ -6,11 +6,11 @@
|
||||
|
||||
namespace webcc {
|
||||
|
||||
std::string Base64Encode(const std::uint8_t* data, std::size_t len);
|
||||
std::string Base64Encode(const std::uint8_t* data, std::size_t length);
|
||||
|
||||
std::string Base64Encode(const std::string& input);
|
||||
|
||||
std::string Base64Decode(const std::string& data);
|
||||
std::string Base64Decode(const std::string& input);
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "webcc/http_client.h"
|
||||
#include "webcc/client.h"
|
||||
|
||||
#include "boost/date_time/posix_time/posix_time.hpp"
|
||||
|
||||
@@ -9,7 +9,7 @@ using boost::asio::ip::tcp;
|
||||
|
||||
namespace webcc {
|
||||
|
||||
HttpClient::HttpClient()
|
||||
Client::Client()
|
||||
: timer_(io_context_),
|
||||
ssl_verify_(true),
|
||||
buffer_size_(kBufferSize),
|
||||
@@ -20,10 +20,10 @@ HttpClient::HttpClient()
|
||||
error_(kNoError) {
|
||||
}
|
||||
|
||||
bool HttpClient::Request(HttpRequestPtr request, bool connect) {
|
||||
bool Client::Request(RequestPtr request, bool connect) {
|
||||
io_context_.restart();
|
||||
|
||||
response_.reset(new HttpResponse{});
|
||||
response_.reset(new Response{});
|
||||
response_parser_.Init(response_.get());
|
||||
|
||||
closed_ = false;
|
||||
@@ -54,7 +54,7 @@ bool HttpClient::Request(HttpRequestPtr request, bool connect) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void HttpClient::Close() {
|
||||
void Client::Close() {
|
||||
if (closed_) {
|
||||
return;
|
||||
}
|
||||
@@ -71,23 +71,22 @@ void HttpClient::Close() {
|
||||
}
|
||||
}
|
||||
|
||||
Error HttpClient::Connect(HttpRequestPtr request) {
|
||||
Error Client::Connect(RequestPtr request) {
|
||||
if (request->url().scheme() == "https") {
|
||||
#if WEBCC_ENABLE_SSL
|
||||
socket_.reset(new HttpSslSocket{ io_context_, ssl_verify_ });
|
||||
socket_.reset(new SslSocket{ io_context_, ssl_verify_ });
|
||||
return DoConnect(request, kPort443);
|
||||
#else
|
||||
LOG_ERRO("SSL/HTTPS support is not enabled.");
|
||||
return kSchemaError;
|
||||
#endif // WEBCC_ENABLE_SSL
|
||||
} else {
|
||||
socket_.reset(new HttpSocket{ io_context_ });
|
||||
socket_.reset(new Socket{ io_context_ });
|
||||
return DoConnect(request, kPort80);
|
||||
}
|
||||
}
|
||||
|
||||
Error HttpClient::DoConnect(HttpRequestPtr request,
|
||||
const std::string& default_port) {
|
||||
Error Client::DoConnect(RequestPtr request, const std::string& default_port) {
|
||||
tcp::resolver resolver(io_context_);
|
||||
|
||||
std::string port = request->port(default_port);
|
||||
@@ -118,7 +117,7 @@ Error HttpClient::DoConnect(HttpRequestPtr request,
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
Error HttpClient::WriteReqeust(HttpRequestPtr request) {
|
||||
Error Client::WriteReqeust(RequestPtr request) {
|
||||
LOG_VERB("HTTP request:\n%s", request->Dump(4, "> ").c_str());
|
||||
|
||||
// NOTE:
|
||||
@@ -142,7 +141,7 @@ Error HttpClient::WriteReqeust(HttpRequestPtr request) {
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
Error HttpClient::ReadResponse() {
|
||||
Error Client::ReadResponse() {
|
||||
LOG_VERB("Read response (timeout: %ds)...", timeout_);
|
||||
|
||||
timer_.expires_from_now(boost::posix_time::seconds(timeout_));
|
||||
@@ -158,7 +157,7 @@ Error HttpClient::ReadResponse() {
|
||||
return error;
|
||||
}
|
||||
|
||||
void HttpClient::DoReadResponse(Error* error) {
|
||||
void Client::DoReadResponse(Error* error) {
|
||||
boost::system::error_code ec = boost::asio::error::would_block;
|
||||
|
||||
auto handler = [this, &ec, error](boost::system::error_code inner_ec,
|
||||
@@ -218,13 +217,12 @@ void HttpClient::DoReadResponse(Error* error) {
|
||||
} while (ec == boost::asio::error::would_block);
|
||||
}
|
||||
|
||||
void HttpClient::DoWaitTimer() {
|
||||
void Client::DoWaitTimer() {
|
||||
LOG_VERB("Wait timer asynchronously.");
|
||||
timer_.async_wait(
|
||||
std::bind(&HttpClient::OnTimer, this, std::placeholders::_1));
|
||||
timer_.async_wait(std::bind(&Client::OnTimer, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void HttpClient::OnTimer(boost::system::error_code ec) {
|
||||
void Client::OnTimer(boost::system::error_code ec) {
|
||||
LOG_VERB("On deadline timer.");
|
||||
|
||||
// timer_.cancel() was called.
|
||||
@@ -252,7 +250,7 @@ void HttpClient::OnTimer(boost::system::error_code ec) {
|
||||
DoWaitTimer();
|
||||
}
|
||||
|
||||
void HttpClient::CancelTimer() {
|
||||
void Client::CancelTimer() {
|
||||
if (timer_canceled_) {
|
||||
return;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef WEBCC_HTTP_CLIENT_H_
|
||||
#define WEBCC_HTTP_CLIENT_H_
|
||||
#ifndef WEBCC_CLIENT_H_
|
||||
#define WEBCC_CLIENT_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
@@ -11,28 +11,28 @@
|
||||
#include "boost/asio/ip/tcp.hpp"
|
||||
|
||||
#include "webcc/globals.h"
|
||||
#include "webcc/http_request.h"
|
||||
#include "webcc/http_response.h"
|
||||
#include "webcc/http_response_parser.h"
|
||||
#include "webcc/http_socket.h"
|
||||
#include "webcc/request.h"
|
||||
#include "webcc/response.h"
|
||||
#include "webcc/response_parser.h"
|
||||
#include "webcc/socket.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class HttpClient;
|
||||
typedef std::shared_ptr<HttpClient> HttpClientPtr;
|
||||
class Client;
|
||||
using ClientPtr = std::shared_ptr<Client>;
|
||||
|
||||
// Synchronous HTTP & HTTPS client.
|
||||
// In synchronous mode, a request won't return until the response is received
|
||||
// or timeout occurs.
|
||||
// Please don't use the same client object in multiple threads.
|
||||
class HttpClient {
|
||||
class Client {
|
||||
public:
|
||||
HttpClient();
|
||||
Client();
|
||||
|
||||
virtual ~HttpClient() = default;
|
||||
virtual ~Client() = default;
|
||||
|
||||
HttpClient(const HttpClient&) = delete;
|
||||
HttpClient& operator=(const HttpClient&) = delete;
|
||||
Client(const Client&) = delete;
|
||||
Client& operator=(const Client&) = delete;
|
||||
|
||||
void set_ssl_verify(bool ssl_verify) {
|
||||
ssl_verify_ = ssl_verify;
|
||||
@@ -52,12 +52,12 @@ public:
|
||||
}
|
||||
|
||||
// Connect to server, send request, wait until response is received.
|
||||
bool Request(HttpRequestPtr request, bool connect = true);
|
||||
bool Request(RequestPtr request, bool connect = true);
|
||||
|
||||
// Close the socket.
|
||||
void Close();
|
||||
|
||||
HttpResponsePtr response() const { return response_; }
|
||||
ResponsePtr response() const { return response_; }
|
||||
|
||||
bool closed() const { return closed_; }
|
||||
|
||||
@@ -66,11 +66,11 @@ public:
|
||||
Error error() const { return error_; }
|
||||
|
||||
private:
|
||||
Error Connect(HttpRequestPtr request);
|
||||
Error Connect(RequestPtr request);
|
||||
|
||||
Error DoConnect(HttpRequestPtr request, const std::string& default_port);
|
||||
Error DoConnect(RequestPtr request, const std::string& default_port);
|
||||
|
||||
Error WriteReqeust(HttpRequestPtr request);
|
||||
Error WriteReqeust(RequestPtr request);
|
||||
|
||||
Error ReadResponse();
|
||||
|
||||
@@ -86,10 +86,10 @@ private:
|
||||
boost::asio::io_context io_context_;
|
||||
|
||||
// Socket connection.
|
||||
std::unique_ptr<HttpSocketBase> socket_;
|
||||
std::unique_ptr<SocketBase> socket_;
|
||||
|
||||
HttpResponsePtr response_;
|
||||
HttpResponseParser response_parser_;
|
||||
ResponsePtr response_;
|
||||
ResponseParser response_parser_;
|
||||
|
||||
// Timer for the timeout control.
|
||||
boost::asio::deadline_timer timer_;
|
||||
@@ -122,4 +122,4 @@ private:
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_CLIENT_H_
|
||||
#endif // WEBCC_CLIENT_H_
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "webcc/http_client_pool.h"
|
||||
#include "webcc/client_pool.h"
|
||||
|
||||
#include "webcc/logger.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
HttpClientPool::~HttpClientPool() {
|
||||
ClientPool::~ClientPool() {
|
||||
if (!clients_.empty()) {
|
||||
LOG_INFO("Close socket for all (%u) connections in the pool.",
|
||||
clients_.size());
|
||||
@@ -15,24 +15,24 @@ HttpClientPool::~HttpClientPool() {
|
||||
}
|
||||
}
|
||||
|
||||
HttpClientPtr HttpClientPool::Get(const Key& key) const {
|
||||
ClientPtr ClientPool::Get(const Key& key) const {
|
||||
auto it = clients_.find(key);
|
||||
|
||||
if (it != clients_.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return HttpClientPtr{};
|
||||
return ClientPtr{};
|
||||
}
|
||||
}
|
||||
|
||||
void HttpClientPool::Add(const Key& key, HttpClientPtr client) {
|
||||
void ClientPool::Add(const Key& key, ClientPtr client) {
|
||||
clients_[key] = client;
|
||||
|
||||
LOG_INFO("Added connection to pool (%s, %s, %s).",
|
||||
key.scheme.c_str(), key.host.c_str(), key.port.c_str());
|
||||
}
|
||||
|
||||
void HttpClientPool::Remove(const Key& key) {
|
||||
void ClientPool::Remove(const Key& key) {
|
||||
clients_.erase(key);
|
||||
|
||||
LOG_INFO("Removed connection from pool (%s, %s, %s).",
|
||||
@@ -1,16 +1,16 @@
|
||||
#ifndef WEBCC_HTTP_CLIENT_POOL_H_
|
||||
#define WEBCC_HTTP_CLIENT_POOL_H_
|
||||
#ifndef WEBCC_CLIENT_POOL_H_
|
||||
#define WEBCC_CLIENT_POOL_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "webcc/http_client.h"
|
||||
#include "webcc/client.h"
|
||||
#include "webcc/url.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
// Connection pool for keep-alive connections.
|
||||
class HttpClientPool {
|
||||
class ClientPool {
|
||||
public:
|
||||
struct Key {
|
||||
std::string scheme;
|
||||
@@ -42,20 +42,20 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
HttpClientPool() = default;
|
||||
ClientPool() = default;
|
||||
|
||||
~HttpClientPool();
|
||||
~ClientPool();
|
||||
|
||||
HttpClientPtr Get(const Key& key) const;
|
||||
ClientPtr Get(const Key& key) const;
|
||||
|
||||
void Add(const Key& key, HttpClientPtr client);
|
||||
void Add(const Key& key, ClientPtr client);
|
||||
|
||||
void Remove(const Key& key);
|
||||
|
||||
private:
|
||||
std::map<Key, HttpClientPtr> clients_;
|
||||
std::map<Key, ClientPtr> clients_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_CLIENT_POOL_H_
|
||||
#endif // WEBCC_CLIENT_POOL_H_
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "webcc/http_client_session.h"
|
||||
#include "webcc/client_session.h"
|
||||
|
||||
#include "webcc/base64.h"
|
||||
#include "webcc/logger.h"
|
||||
@@ -6,26 +6,26 @@
|
||||
|
||||
namespace webcc {
|
||||
|
||||
HttpClientSession::HttpClientSession() {
|
||||
ClientSession::ClientSession() {
|
||||
InitHeaders();
|
||||
}
|
||||
|
||||
void HttpClientSession::Auth(const std::string& type,
|
||||
void ClientSession::Auth(const std::string& type,
|
||||
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) {
|
||||
auto credentials = Base64Encode(login + ":" + password);
|
||||
return Auth("Basic", credentials);
|
||||
}
|
||||
|
||||
void HttpClientSession::AuthToken(const std::string& token) {
|
||||
void ClientSession::AuthToken(const std::string& token) {
|
||||
return Auth("Token", token);
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpClientSession::Request(HttpRequestPtr request) {
|
||||
ResponsePtr ClientSession::Request(RequestPtr request) {
|
||||
assert(request);
|
||||
|
||||
for (const auto& h : headers_.data()) {
|
||||
@@ -35,7 +35,7 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestPtr request) {
|
||||
}
|
||||
|
||||
if (!content_type_.empty() &&
|
||||
!request->HasHeader(http::headers::kContentType)) {
|
||||
!request->HasHeader(headers::kContentType)) {
|
||||
request->SetContentType(content_type_, charset_);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ HttpResponsePtr HttpClientSession::Request(HttpRequestPtr request) {
|
||||
}
|
||||
|
||||
static void SetHeaders(const std::vector<std::string>& headers,
|
||||
HttpRequestBuilder* builder) {
|
||||
RequestBuilder* builder) {
|
||||
assert(headers.size() % 2 == 0);
|
||||
|
||||
for (std::size_t i = 1; i < headers.size(); i += 2) {
|
||||
@@ -53,10 +53,10 @@ static void SetHeaders(const std::vector<std::string>& headers,
|
||||
}
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpClientSession::Get(
|
||||
const std::string& url, const std::vector<std::string>& parameters,
|
||||
ResponsePtr ClientSession::Get(const std::string& url,
|
||||
const std::vector<std::string>& parameters,
|
||||
const std::vector<std::string>& headers) {
|
||||
HttpRequestBuilder builder;
|
||||
RequestBuilder builder;
|
||||
builder.Get().Url(url);
|
||||
|
||||
assert(parameters.size() % 2 == 0);
|
||||
@@ -69,10 +69,10 @@ HttpResponsePtr HttpClientSession::Get(
|
||||
return Request(builder());
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpClientSession::Post(
|
||||
const std::string& url, std::string&& data, bool json,
|
||||
ResponsePtr ClientSession::Post(const std::string& url, std::string&& data,
|
||||
bool json,
|
||||
const std::vector<std::string>& headers) {
|
||||
HttpRequestBuilder builder;
|
||||
RequestBuilder builder;
|
||||
builder.Post().Url(url);
|
||||
|
||||
SetHeaders(headers, &builder);
|
||||
@@ -83,10 +83,10 @@ HttpResponsePtr HttpClientSession::Post(
|
||||
return Request(builder());
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpClientSession::Put(
|
||||
const std::string& url, std::string&& data, bool json,
|
||||
ResponsePtr ClientSession::Put(const std::string& url, std::string&& data,
|
||||
bool json,
|
||||
const std::vector<std::string>& headers) {
|
||||
HttpRequestBuilder builder;
|
||||
RequestBuilder builder;
|
||||
builder.Put().Url(url);
|
||||
|
||||
SetHeaders(headers, &builder);
|
||||
@@ -97,9 +97,9 @@ HttpResponsePtr HttpClientSession::Put(
|
||||
return Request(builder());
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpClientSession::Delete(
|
||||
const std::string& url, const std::vector<std::string>& headers) {
|
||||
HttpRequestBuilder builder;
|
||||
ResponsePtr ClientSession::Delete(const std::string& url,
|
||||
const std::vector<std::string>& headers) {
|
||||
RequestBuilder builder;
|
||||
builder.Delete().Url(url);
|
||||
|
||||
SetHeaders(headers, &builder);
|
||||
@@ -107,10 +107,10 @@ HttpResponsePtr HttpClientSession::Delete(
|
||||
return Request(builder());
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpClientSession::Patch(
|
||||
const std::string& url, std::string&& data, bool json,
|
||||
ResponsePtr ClientSession::Patch(const std::string& url, std::string&& data,
|
||||
bool json,
|
||||
const std::vector<std::string>& headers) {
|
||||
HttpRequestBuilder builder;
|
||||
RequestBuilder builder;
|
||||
builder.Patch().Url(url);
|
||||
|
||||
SetHeaders(headers, &builder);
|
||||
@@ -121,10 +121,10 @@ HttpResponsePtr HttpClientSession::Patch(
|
||||
return Request(builder());
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpClientSession::PostFile(
|
||||
const std::string& url, const std::string& name, const Path& path,
|
||||
ResponsePtr ClientSession::PostFile(const std::string& url,
|
||||
const std::string& name, const Path& path,
|
||||
const std::vector<std::string>& headers) {
|
||||
HttpRequestBuilder builder;
|
||||
RequestBuilder builder;
|
||||
builder.Post().Url(url);
|
||||
|
||||
SetHeaders(headers, &builder);
|
||||
@@ -134,12 +134,12 @@ HttpResponsePtr HttpClientSession::PostFile(
|
||||
return Request(builder());
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpClientSession::PostFiles(
|
||||
const std::string& url, const std::map<std::string, Path>& paths,
|
||||
ResponsePtr ClientSession::PostFiles(const std::string& url,
|
||||
const std::map<std::string, Path>& paths,
|
||||
const std::vector<std::string>& headers) {
|
||||
assert(!paths.empty());
|
||||
|
||||
HttpRequestBuilder builder;
|
||||
RequestBuilder builder;
|
||||
builder.Post().Url(url);
|
||||
|
||||
SetHeaders(headers, &builder);
|
||||
@@ -151,10 +151,10 @@ HttpResponsePtr HttpClientSession::PostFiles(
|
||||
return Request(builder());
|
||||
}
|
||||
|
||||
void HttpClientSession::InitHeaders() {
|
||||
using namespace http::headers;
|
||||
void ClientSession::InitHeaders() {
|
||||
using namespace headers;
|
||||
|
||||
headers_.Set(kUserAgent, http::UserAgent());
|
||||
headers_.Set(kUserAgent, UserAgent());
|
||||
|
||||
// Content-Encoding Tokens:
|
||||
// (https://en.wikipedia.org/wiki/HTTP_compression)
|
||||
@@ -184,15 +184,15 @@ void HttpClientSession::InitHeaders() {
|
||||
headers_.Set(kConnection, "Keep-Alive");
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpClientSession::Send(HttpRequestPtr request) {
|
||||
const HttpClientPool::Key key{request->url()};
|
||||
ResponsePtr ClientSession::Send(RequestPtr request) {
|
||||
const ClientPool::Key key{request->url()};
|
||||
|
||||
// Reuse a pooled connection.
|
||||
bool reuse = false;
|
||||
|
||||
HttpClientPtr client = pool_.Get(key);
|
||||
ClientPtr client = pool_.Get(key);
|
||||
if (!client) {
|
||||
client.reset(new HttpClient{});
|
||||
client.reset(new Client{});
|
||||
reuse = false;
|
||||
} else {
|
||||
LOG_VERB("Reuse an existing connection.");
|
||||
@@ -1,24 +1,23 @@
|
||||
#ifndef WEBCC_HTTP_CLIENT_SESSION_H_
|
||||
#define WEBCC_HTTP_CLIENT_SESSION_H_
|
||||
#ifndef WEBCC_CLIENT_SESSION_H_
|
||||
#define WEBCC_CLIENT_SESSION_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/http_client_pool.h"
|
||||
#include "webcc/http_request_builder.h"
|
||||
#include "webcc/http_response.h"
|
||||
#include "webcc/client_pool.h"
|
||||
#include "webcc/request_builder.h"
|
||||
#include "webcc/response.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
// HTTP requests session providing connection-pooling, configuration and more.
|
||||
// NOTE:
|
||||
// A session shouldn't be shared by multiple threads. Please create a new
|
||||
// session for each thread instead.
|
||||
class HttpClientSession {
|
||||
class ClientSession {
|
||||
public:
|
||||
HttpClientSession();
|
||||
ClientSession();
|
||||
|
||||
~HttpClientSession() = default;
|
||||
~ClientSession() = default;
|
||||
|
||||
void set_ssl_verify(bool ssl_verify) {
|
||||
ssl_verify_ = ssl_verify;
|
||||
@@ -56,44 +55,44 @@ public:
|
||||
void AuthToken(const std::string& token);
|
||||
|
||||
// Send a request.
|
||||
// Please use HttpRequestBuilder to build the request.
|
||||
HttpResponsePtr Request(HttpRequestPtr request);
|
||||
// Please use RequestBuilder to build the request.
|
||||
ResponsePtr Request(RequestPtr request);
|
||||
|
||||
// Shortcut for GET request.
|
||||
HttpResponsePtr Get(const std::string& url,
|
||||
ResponsePtr Get(const std::string& url,
|
||||
const std::vector<std::string>& parameters = {},
|
||||
const std::vector<std::string>& headers = {});
|
||||
|
||||
// Shortcut for POST request.
|
||||
HttpResponsePtr Post(const std::string& url, std::string&& data, bool json,
|
||||
ResponsePtr Post(const std::string& url, std::string&& data, bool json,
|
||||
const std::vector<std::string>& headers = {});
|
||||
|
||||
// Shortcut for PUT request.
|
||||
HttpResponsePtr Put(const std::string& url, std::string&& data, bool json,
|
||||
ResponsePtr Put(const std::string& url, std::string&& data, bool json,
|
||||
const std::vector<std::string>& headers = {});
|
||||
|
||||
// Shortcut for DELETE request.
|
||||
HttpResponsePtr Delete(const std::string& url,
|
||||
ResponsePtr Delete(const std::string& url,
|
||||
const std::vector<std::string>& headers = {});
|
||||
|
||||
// Shortcut for PATCH request.
|
||||
HttpResponsePtr Patch(const std::string& url, std::string&& data, bool json,
|
||||
ResponsePtr Patch(const std::string& url, std::string&& data, bool json,
|
||||
const std::vector<std::string>& headers = {});
|
||||
|
||||
// Post a file.
|
||||
HttpResponsePtr PostFile(const std::string& url, const std::string& name,
|
||||
ResponsePtr PostFile(const std::string& url, const std::string& name,
|
||||
const Path& path,
|
||||
const std::vector<std::string>& headers = {});
|
||||
|
||||
// Post multiple files.
|
||||
HttpResponsePtr PostFiles(const std::string& url,
|
||||
ResponsePtr PostFiles(const std::string& url,
|
||||
const std::map<std::string, Path>& paths,
|
||||
const std::vector<std::string>& headers = {});
|
||||
|
||||
private:
|
||||
void InitHeaders();
|
||||
|
||||
HttpResponsePtr Send(HttpRequestPtr request);
|
||||
ResponsePtr Send(RequestPtr request);
|
||||
|
||||
private:
|
||||
// E.g., "application/json".
|
||||
@@ -103,7 +102,7 @@ private:
|
||||
std::string charset_;
|
||||
|
||||
// Additional headers for each request.
|
||||
HttpHeaders headers_;
|
||||
Headers headers_;
|
||||
|
||||
// Verify the certificate of the peer or not.
|
||||
bool ssl_verify_ = true;
|
||||
@@ -116,9 +115,9 @@ private:
|
||||
int timeout_ = 0;
|
||||
|
||||
// Connection pool for keep-alive.
|
||||
HttpClientPool pool_;
|
||||
ClientPool pool_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_CLIENT_SESSION_H_
|
||||
#endif // WEBCC_CLIENT_SESSION_H_
|
||||
+11
-11
@@ -56,7 +56,7 @@ bool ReadFile(const Path& path, std::string* output) {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void HttpHeaders::Set(const std::string& key, const std::string& value) {
|
||||
void Headers::Set(const std::string& key, const std::string& value) {
|
||||
auto it = Find(key);
|
||||
if (it != headers_.end()) {
|
||||
it->second = value;
|
||||
@@ -65,7 +65,7 @@ void HttpHeaders::Set(const std::string& key, const std::string& value) {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpHeaders::Set(std::string&& key, std::string&& value) {
|
||||
void Headers::Set(std::string&& key, std::string&& value) {
|
||||
auto it = Find(key);
|
||||
if (it != headers_.end()) {
|
||||
it->second = std::move(value);
|
||||
@@ -74,13 +74,13 @@ void HttpHeaders::Set(std::string&& key, std::string&& value) {
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpHeaders::Has(const std::string& key) const {
|
||||
return const_cast<HttpHeaders*>(this)->Find(key) != headers_.end();
|
||||
bool Headers::Has(const std::string& key) const {
|
||||
return const_cast<Headers*>(this)->Find(key) != headers_.end();
|
||||
}
|
||||
|
||||
const std::string& HttpHeaders::Get(const std::string& key,
|
||||
const std::string& Headers::Get(const std::string& key,
|
||||
bool* existed) const {
|
||||
auto it = const_cast<HttpHeaders*>(this)->Find(key);
|
||||
auto it = const_cast<Headers*>(this)->Find(key);
|
||||
|
||||
if (existed != nullptr) {
|
||||
*existed = (it != headers_.end());
|
||||
@@ -94,7 +94,7 @@ const std::string& HttpHeaders::Get(const std::string& key,
|
||||
return s_no_value;
|
||||
}
|
||||
|
||||
std::vector<HttpHeader>::iterator HttpHeaders::Find(const std::string& key) {
|
||||
std::vector<Header>::iterator Headers::Find(const std::string& key) {
|
||||
auto it = headers_.begin();
|
||||
for (; it != headers_.end(); ++it) {
|
||||
if (boost::iequals(it->first, key)) {
|
||||
@@ -225,7 +225,7 @@ FormPart::FormPart(const std::string& name, const Path& path,
|
||||
// Determine media type from file extension.
|
||||
if (media_type_.empty()) {
|
||||
std::string extension = path.extension().string();
|
||||
media_type_ = http::media_types::FromExtension(extension, false);
|
||||
media_type_ = media_types::FromExtension(extension, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ void FormPart::Prepare(Payload* payload) {
|
||||
|
||||
using boost::asio::buffer;
|
||||
|
||||
for (const HttpHeader& h : headers_.data()) {
|
||||
for (const Header& h : headers_.data()) {
|
||||
payload->push_back(buffer(h.first));
|
||||
payload->push_back(buffer(misc_strings::HEADER_SEPARATOR));
|
||||
payload->push_back(buffer(h.second));
|
||||
@@ -270,12 +270,12 @@ void FormPart::SetHeaders() {
|
||||
if (!file_name_.empty()) {
|
||||
content_disposition.append("; filename=\"" + file_name_ + "\"");
|
||||
}
|
||||
headers_.Set(http::headers::kContentDisposition, content_disposition);
|
||||
headers_.Set(headers::kContentDisposition, content_disposition);
|
||||
|
||||
// Header: Content-Type
|
||||
|
||||
if (!media_type_.empty()) {
|
||||
headers_.Set(http::headers::kContentType, media_type_);
|
||||
headers_.Set(headers::kContentType, media_type_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -30,9 +30,9 @@ bool ReadFile(const Path& path, std::string* output);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef std::pair<std::string, std::string> HttpHeader;
|
||||
using Header = std::pair<std::string, std::string>;
|
||||
|
||||
class HttpHeaders {
|
||||
class Headers {
|
||||
public:
|
||||
std::size_t size() const {
|
||||
return headers_.size();
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
return headers_.empty();
|
||||
}
|
||||
|
||||
const std::vector<HttpHeader>& data() const {
|
||||
const std::vector<Header>& data() const {
|
||||
return headers_;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
bool Has(const std::string& key) const;
|
||||
|
||||
// Get header by index.
|
||||
const HttpHeader& Get(std::size_t index) const {
|
||||
const Header& Get(std::size_t index) const {
|
||||
assert(index < size());
|
||||
return headers_[index];
|
||||
}
|
||||
@@ -68,9 +68,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<HttpHeader>::iterator Find(const std::string& key);
|
||||
std::vector<Header>::iterator Find(const std::string& key);
|
||||
|
||||
std::vector<HttpHeader> headers_;
|
||||
std::vector<Header> headers_;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -261,7 +261,7 @@ private:
|
||||
|
||||
// Headers generated from the above properties.
|
||||
// Only Used to prepare payload.
|
||||
HttpHeaders headers_;
|
||||
Headers headers_;
|
||||
|
||||
std::string data_;
|
||||
};
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
#include "webcc/http_connection.h"
|
||||
#include "webcc/connection.h"
|
||||
|
||||
#include <utility> // for move()
|
||||
|
||||
#include "boost/asio/write.hpp"
|
||||
|
||||
#include "webcc/http_connection_pool.h"
|
||||
#include "webcc/http_request_handler.h"
|
||||
#include "webcc/connection_pool.h"
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/request_handler.h"
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
namespace webcc {
|
||||
|
||||
HttpConnection::HttpConnection(tcp::socket socket, HttpConnectionPool* pool,
|
||||
HttpRequestHandler* handler)
|
||||
Connection::Connection(tcp::socket socket, ConnectionPool* pool,
|
||||
RequestHandler* handler)
|
||||
: socket_(std::move(socket)),
|
||||
pool_(pool),
|
||||
buffer_(kBufferSize),
|
||||
request_handler_(handler) {
|
||||
}
|
||||
|
||||
void HttpConnection::Start() {
|
||||
request_.reset(new HttpRequest{});
|
||||
void Connection::Start() {
|
||||
request_.reset(new Request{});
|
||||
request_parser_.Init(request_.get());
|
||||
DoRead();
|
||||
}
|
||||
|
||||
void HttpConnection::Close() {
|
||||
void Connection::Close() {
|
||||
LOG_INFO("Close socket...");
|
||||
|
||||
boost::system::error_code ec;
|
||||
@@ -36,15 +36,15 @@ void HttpConnection::Close() {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpConnection::SendResponse(HttpResponsePtr response) {
|
||||
void Connection::SendResponse(ResponsePtr response) {
|
||||
assert(response);
|
||||
|
||||
response_ = response;
|
||||
|
||||
if (request_->IsConnectionKeepAlive()) {
|
||||
response_->SetHeader(http::headers::kConnection, "Keep-Alive");
|
||||
response_->SetHeader(headers::kConnection, "Keep-Alive");
|
||||
} else {
|
||||
response_->SetHeader(http::headers::kConnection, "Close");
|
||||
response_->SetHeader(headers::kConnection, "Close");
|
||||
}
|
||||
|
||||
response_->Prepare();
|
||||
@@ -52,18 +52,18 @@ void HttpConnection::SendResponse(HttpResponsePtr response) {
|
||||
DoWrite();
|
||||
}
|
||||
|
||||
void HttpConnection::SendResponse(http::Status status) {
|
||||
SendResponse(std::make_shared<HttpResponse>(status));
|
||||
void Connection::SendResponse(Status status) {
|
||||
SendResponse(std::make_shared<Response>(status));
|
||||
}
|
||||
|
||||
void HttpConnection::DoRead() {
|
||||
void Connection::DoRead() {
|
||||
socket_.async_read_some(boost::asio::buffer(buffer_),
|
||||
std::bind(&HttpConnection::OnRead, shared_from_this(),
|
||||
std::bind(&Connection::OnRead, shared_from_this(),
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2));
|
||||
}
|
||||
|
||||
void HttpConnection::OnRead(boost::system::error_code ec, std::size_t length) {
|
||||
void Connection::OnRead(boost::system::error_code ec, std::size_t length) {
|
||||
if (ec) {
|
||||
LOG_ERRO("Socket read error (%s).", ec.message().c_str());
|
||||
if (ec != boost::asio::error::operation_aborted) {
|
||||
@@ -75,7 +75,7 @@ void HttpConnection::OnRead(boost::system::error_code ec, std::size_t length) {
|
||||
if (!request_parser_.Parse(buffer_.data(), length)) {
|
||||
// Bad request.
|
||||
LOG_ERRO("Failed to parse HTTP request.");
|
||||
SendResponse(http::Status::kBadRequest);
|
||||
SendResponse(Status::kBadRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -92,11 +92,11 @@ void HttpConnection::OnRead(boost::system::error_code ec, std::size_t length) {
|
||||
request_handler_->Enqueue(shared_from_this());
|
||||
}
|
||||
|
||||
void HttpConnection::DoWrite() {
|
||||
void Connection::DoWrite() {
|
||||
LOG_VERB("HTTP response:\n%s", response_->Dump(4, "> ").c_str());
|
||||
|
||||
boost::asio::async_write(socket_, response_->payload(),
|
||||
std::bind(&HttpConnection::OnWrite, shared_from_this(),
|
||||
std::bind(&Connection::OnWrite, shared_from_this(),
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2));
|
||||
}
|
||||
@@ -105,7 +105,7 @@ void HttpConnection::DoWrite() {
|
||||
// This write handler will be called from main thread (the thread calling
|
||||
// io_context.run), even though AsyncWrite() is invoked by worker threads.
|
||||
// This is ensured by Asio.
|
||||
void HttpConnection::OnWrite(boost::system::error_code ec, std::size_t length) {
|
||||
void Connection::OnWrite(boost::system::error_code ec, std::size_t length) {
|
||||
if (ec) {
|
||||
LOG_ERRO("Socket write error (%s).", ec.message().c_str());
|
||||
|
||||
@@ -128,7 +128,7 @@ void HttpConnection::OnWrite(boost::system::error_code ec, std::size_t length) {
|
||||
|
||||
// Socket close VS. shutdown:
|
||||
// https://stackoverflow.com/questions/4160347/close-vs-shutdown-socket
|
||||
void HttpConnection::Shutdown() {
|
||||
void Connection::Shutdown() {
|
||||
LOG_INFO("Shutdown socket...");
|
||||
|
||||
// Initiate graceful connection closure.
|
||||
@@ -0,0 +1,82 @@
|
||||
#ifndef WEBCC_CONNECTION_H_
|
||||
#define WEBCC_CONNECTION_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "boost/asio/ip/tcp.hpp" // for ip::tcp::socket
|
||||
|
||||
#include "webcc/globals.h"
|
||||
#include "webcc/request.h"
|
||||
#include "webcc/request_parser.h"
|
||||
#include "webcc/response.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class Connection;
|
||||
class ConnectionPool;
|
||||
class RequestHandler;
|
||||
|
||||
using ConnectionPtr = std::shared_ptr<Connection>;
|
||||
|
||||
class Connection : public std::enable_shared_from_this<Connection> {
|
||||
public:
|
||||
Connection(boost::asio::ip::tcp::socket socket, ConnectionPool* pool,
|
||||
RequestHandler* handler);
|
||||
|
||||
~Connection() = default;
|
||||
|
||||
Connection(const Connection&) = delete;
|
||||
Connection& operator=(const Connection&) = delete;
|
||||
|
||||
RequestPtr request() const {
|
||||
return request_;
|
||||
}
|
||||
|
||||
// Start to read and process the client request.
|
||||
void Start();
|
||||
|
||||
// Close the socket.
|
||||
void Close();
|
||||
|
||||
// Send response to client.
|
||||
void SendResponse(ResponsePtr response);
|
||||
|
||||
void SendResponse(Status status);
|
||||
|
||||
private:
|
||||
void DoRead();
|
||||
void OnRead(boost::system::error_code ec, std::size_t length);
|
||||
|
||||
void DoWrite();
|
||||
void OnWrite(boost::system::error_code ec, std::size_t length);
|
||||
|
||||
// Shutdown the socket.
|
||||
void Shutdown();
|
||||
|
||||
// Socket for the connection.
|
||||
boost::asio::ip::tcp::socket socket_;
|
||||
|
||||
// The pool for this connection.
|
||||
ConnectionPool* pool_;
|
||||
|
||||
// Buffer for incoming data.
|
||||
std::vector<char> buffer_;
|
||||
|
||||
// The handler used to process the incoming request.
|
||||
RequestHandler* request_handler_;
|
||||
|
||||
// The incoming request.
|
||||
RequestPtr request_;
|
||||
|
||||
// The parser for the incoming request.
|
||||
RequestParser request_parser_;
|
||||
|
||||
// The response to be sent back to the client.
|
||||
ResponsePtr response_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_CONNECTION_H_
|
||||
@@ -1,25 +1,25 @@
|
||||
#include "webcc/http_connection_pool.h"
|
||||
#include "webcc/connection_pool.h"
|
||||
|
||||
#include "webcc/logger.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
HttpConnectionPool::HttpConnectionPool() {
|
||||
ConnectionPool::ConnectionPool() {
|
||||
}
|
||||
|
||||
void HttpConnectionPool::Start(HttpConnectionPtr c) {
|
||||
void ConnectionPool::Start(ConnectionPtr c) {
|
||||
LOG_VERB("Starting connection...");
|
||||
connections_.insert(c);
|
||||
c->Start();
|
||||
}
|
||||
|
||||
void HttpConnectionPool::Close(HttpConnectionPtr c) {
|
||||
void ConnectionPool::Close(ConnectionPtr c) {
|
||||
LOG_VERB("Closing connection...");
|
||||
connections_.erase(c);
|
||||
c->Close();
|
||||
}
|
||||
|
||||
void HttpConnectionPool::CloseAll() {
|
||||
void ConnectionPool::CloseAll() {
|
||||
LOG_VERB("Closing all (%u) connections...", connections_.size());
|
||||
for (auto& c : connections_) {
|
||||
c->Close();
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef WEBCC_CONNECTION_POOL_H_
|
||||
#define WEBCC_CONNECTION_POOL_H_
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "webcc/connection.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class ConnectionPool {
|
||||
public:
|
||||
ConnectionPool(const ConnectionPool&) = delete;
|
||||
ConnectionPool& operator=(const ConnectionPool&) = delete;
|
||||
|
||||
ConnectionPool();
|
||||
|
||||
// Add a connection to the pool and start it.
|
||||
void Start(ConnectionPtr c);
|
||||
|
||||
// Close a connection.
|
||||
void Close(ConnectionPtr c);
|
||||
|
||||
// Close all connections.
|
||||
void CloseAll();
|
||||
|
||||
private:
|
||||
/// The managed connections.
|
||||
std::set<ConnectionPtr> connections_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_CONNECTION_POOL_H_
|
||||
@@ -8,8 +8,6 @@ namespace webcc {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace http {
|
||||
|
||||
const std::string& UserAgent() {
|
||||
static std::string s_user_agent = std::string("Webcc/") + WEBCC_VERSION;
|
||||
return s_user_agent;
|
||||
@@ -52,8 +50,6 @@ std::string FromExtension(const std::string& extension,
|
||||
|
||||
} // namespace media_types
|
||||
|
||||
} // namespace http
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const char* DescribeError(Error error) {
|
||||
|
||||
@@ -68,8 +68,6 @@ const std::size_t kGzipThreshold = 1400;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace http {
|
||||
|
||||
namespace methods {
|
||||
|
||||
// HTTP methods (verbs) in string.
|
||||
@@ -156,8 +154,6 @@ enum class ContentEncoding {
|
||||
// Return default user agent for HTTP headers.
|
||||
const std::string& UserAgent();
|
||||
|
||||
} // namespace http
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Client side error codes.
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#ifndef WEBCC_HTTP_CONNECTION_H_
|
||||
#define WEBCC_HTTP_CONNECTION_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "boost/asio/ip/tcp.hpp" // for ip::tcp::socket
|
||||
|
||||
#include "webcc/globals.h"
|
||||
#include "webcc/http_request.h"
|
||||
#include "webcc/http_request_parser.h"
|
||||
#include "webcc/http_response.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class HttpConnection;
|
||||
class HttpConnectionPool;
|
||||
class HttpRequestHandler;
|
||||
|
||||
typedef std::shared_ptr<HttpConnection> HttpConnectionPtr;
|
||||
|
||||
class HttpConnection : public std::enable_shared_from_this<HttpConnection> {
|
||||
public:
|
||||
HttpConnection(boost::asio::ip::tcp::socket socket, HttpConnectionPool* pool,
|
||||
HttpRequestHandler* handler);
|
||||
|
||||
~HttpConnection() = default;
|
||||
|
||||
HttpConnection(const HttpConnection&) = delete;
|
||||
HttpConnection& operator=(const HttpConnection&) = delete;
|
||||
|
||||
HttpRequestPtr request() const {
|
||||
return request_;
|
||||
}
|
||||
|
||||
// Start to read and process the client request.
|
||||
void Start();
|
||||
|
||||
// Close the socket.
|
||||
void Close();
|
||||
|
||||
// Send response to client.
|
||||
void SendResponse(HttpResponsePtr response);
|
||||
|
||||
void SendResponse(http::Status status);
|
||||
|
||||
private:
|
||||
void DoRead();
|
||||
void OnRead(boost::system::error_code ec, std::size_t length);
|
||||
|
||||
void DoWrite();
|
||||
void OnWrite(boost::system::error_code ec, std::size_t length);
|
||||
|
||||
// Shutdown the socket.
|
||||
void Shutdown();
|
||||
|
||||
// Socket for the connection.
|
||||
boost::asio::ip::tcp::socket socket_;
|
||||
|
||||
// The pool for this connection.
|
||||
HttpConnectionPool* pool_;
|
||||
|
||||
// Buffer for incoming data.
|
||||
std::vector<char> buffer_;
|
||||
|
||||
// The handler used to process the incoming request.
|
||||
HttpRequestHandler* request_handler_;
|
||||
|
||||
// The incoming request.
|
||||
HttpRequestPtr request_;
|
||||
|
||||
// The parser for the incoming request.
|
||||
HttpRequestParser request_parser_;
|
||||
|
||||
// The response to be sent back to the client.
|
||||
HttpResponsePtr response_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_CONNECTION_H_
|
||||
@@ -1,33 +0,0 @@
|
||||
#ifndef WEBCC_HTTP_CONNECTION_POOL_H_
|
||||
#define WEBCC_HTTP_CONNECTION_POOL_H_
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "webcc/http_connection.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class HttpConnectionPool {
|
||||
public:
|
||||
HttpConnectionPool(const HttpConnectionPool&) = delete;
|
||||
HttpConnectionPool& operator=(const HttpConnectionPool&) = delete;
|
||||
|
||||
HttpConnectionPool();
|
||||
|
||||
// Add a connection to the pool and start it.
|
||||
void Start(HttpConnectionPtr c);
|
||||
|
||||
// Close a connection.
|
||||
void Close(HttpConnectionPtr c);
|
||||
|
||||
// Close all connections.
|
||||
void CloseAll();
|
||||
|
||||
private:
|
||||
/// The managed connections.
|
||||
std::set<HttpConnectionPtr> connections_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_CONNECTION_POOL_H_
|
||||
@@ -1,92 +0,0 @@
|
||||
#include "webcc/http_request_builder.h"
|
||||
|
||||
#include "webcc/base64.h"
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/utility.h"
|
||||
#include "webcc/zlib_wrapper.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
HttpRequestPtr HttpRequestBuilder::Build() {
|
||||
assert(parameters_.size() % 2 == 0);
|
||||
assert(headers_.size() % 2 == 0);
|
||||
|
||||
auto request = std::make_shared<HttpRequest>(method_, url_);
|
||||
|
||||
for (std::size_t i = 1; i < parameters_.size(); i += 2) {
|
||||
request->AddQuery(parameters_[i - 1], parameters_[i]);
|
||||
}
|
||||
|
||||
for (std::size_t i = 1; i < headers_.size(); i += 2) {
|
||||
request->SetHeader(std::move(headers_[i - 1]), std::move(headers_[i]));
|
||||
}
|
||||
|
||||
// No keep-alive?
|
||||
if (!keep_alive_) {
|
||||
request->SetHeader(http::headers::kConnection, "Close");
|
||||
}
|
||||
|
||||
if (!data_.empty()) {
|
||||
SetContent(request, std::move(data_));
|
||||
|
||||
// TODO: Request-level charset.
|
||||
if (json_) {
|
||||
request->SetContentType(http::media_types::kApplicationJson, "");
|
||||
}
|
||||
} else if (!form_parts_.empty()) {
|
||||
request->set_form_parts(std::move(form_parts_));
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& HttpRequestBuilder::File(const std::string& name,
|
||||
const Path& path,
|
||||
const std::string& mime_type) {
|
||||
assert(!name.empty());
|
||||
form_parts_.push_back(FormPart{ name, path, mime_type });
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& HttpRequestBuilder::Form(const std::string& name,
|
||||
std::string&& data,
|
||||
const std::string& mime_type) {
|
||||
assert(!name.empty());
|
||||
form_parts_.push_back(FormPart{ name, std::move(data), mime_type });
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& HttpRequestBuilder::Auth(const std::string& type,
|
||||
const std::string& credentials) {
|
||||
headers_.push_back(http::headers::kAuthorization);
|
||||
headers_.push_back(type + " " + credentials);
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& HttpRequestBuilder::AuthBasic(const std::string& login,
|
||||
const std::string& password) {
|
||||
auto credentials = Base64Encode(login + ":" + password);
|
||||
return Auth("Basic", credentials);
|
||||
}
|
||||
|
||||
HttpRequestBuilder& HttpRequestBuilder::AuthToken(const std::string& token) {
|
||||
return Auth("Token", token);
|
||||
}
|
||||
|
||||
void HttpRequestBuilder::SetContent(HttpRequestPtr request,
|
||||
std::string&& data) {
|
||||
if (gzip_ && data.size() > kGzipThreshold) {
|
||||
std::string compressed;
|
||||
if (Compress(data, &compressed)) {
|
||||
request->SetContent(std::move(compressed), true);
|
||||
request->SetHeader(http::headers::kContentEncoding, "gzip");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_WARN("Cannot compress the content data!");
|
||||
}
|
||||
|
||||
request->SetContent(std::move(data), true);
|
||||
}
|
||||
|
||||
} // namespace webcc
|
||||
@@ -1,144 +0,0 @@
|
||||
#ifndef WEBCC_HTTP_REQUEST_BUILDER_H_
|
||||
#define WEBCC_HTTP_REQUEST_BUILDER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/http_request.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class HttpRequestBuilder {
|
||||
public:
|
||||
explicit HttpRequestBuilder(const std::string& method = "")
|
||||
: method_(method) {
|
||||
}
|
||||
|
||||
~HttpRequestBuilder() = default;
|
||||
|
||||
HttpRequestBuilder(const HttpRequestBuilder&) = delete;
|
||||
HttpRequestBuilder& operator=(const HttpRequestBuilder&) = delete;
|
||||
|
||||
// Build the request.
|
||||
HttpRequestPtr Build();
|
||||
|
||||
HttpRequestPtr operator()() {
|
||||
return Build();
|
||||
}
|
||||
|
||||
HttpRequestBuilder& Get() { return Method(http::methods::kGet); }
|
||||
HttpRequestBuilder& Head() { return Method(http::methods::kHead); }
|
||||
HttpRequestBuilder& Post() { return Method(http::methods::kPost); }
|
||||
HttpRequestBuilder& Put() { return Method(http::methods::kPut); }
|
||||
HttpRequestBuilder& Delete() { return Method(http::methods::kDelete); }
|
||||
HttpRequestBuilder& Patch() { return Method(http::methods::kPatch); }
|
||||
|
||||
// NOTE:
|
||||
// The naming convention doesn't follow Google C++ Style for
|
||||
// consistency and simplicity.
|
||||
|
||||
HttpRequestBuilder& Method(const std::string& method) {
|
||||
method_ = method;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& Url(const std::string& url) {
|
||||
url_ = url;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& Query(const std::string& key,
|
||||
const std::string& value) {
|
||||
parameters_.push_back(key);
|
||||
parameters_.push_back(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& Data(const std::string& data) {
|
||||
data_ = data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& Data(std::string&& data) {
|
||||
data_ = std::move(data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& Json(bool json = true) {
|
||||
json_ = json;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Upload a file.
|
||||
HttpRequestBuilder& File(const std::string& name, const Path& path,
|
||||
const std::string& mime_type = "");
|
||||
|
||||
HttpRequestBuilder& Form(FormPart&& part) {
|
||||
form_parts_.push_back(std::move(part));
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& Form(const std::string& name, std::string&& data,
|
||||
const std::string& mime_type = "");
|
||||
|
||||
HttpRequestBuilder& Gzip(bool gzip = true) {
|
||||
gzip_ = gzip;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& Header(const std::string& key,
|
||||
const std::string& value) {
|
||||
headers_.push_back(key);
|
||||
headers_.push_back(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& KeepAlive(bool keep_alive = true) {
|
||||
keep_alive_ = keep_alive;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HttpRequestBuilder& Auth(const std::string& type,
|
||||
const std::string& credentials);
|
||||
|
||||
HttpRequestBuilder& AuthBasic(const std::string& login,
|
||||
const std::string& password);
|
||||
|
||||
HttpRequestBuilder& AuthToken(const std::string& token);
|
||||
|
||||
private:
|
||||
void SetContent(HttpRequestPtr request, std::string&& data);
|
||||
|
||||
private:
|
||||
std::string method_;
|
||||
|
||||
std::string url_;
|
||||
|
||||
// URL query parameters.
|
||||
std::vector<std::string> parameters_;
|
||||
|
||||
// Data to send in the body of the request.
|
||||
std::string data_;
|
||||
|
||||
// Is the data to send a JSON string?
|
||||
bool json_ = false;
|
||||
|
||||
// Files to upload for a POST request.
|
||||
std::vector<FormPart> form_parts_;
|
||||
|
||||
// Compress the request content.
|
||||
// NOTE: Most servers don't support compressed requests.
|
||||
// Even the requests module from Python doesn't have a built-in support.
|
||||
// See: https://github.com/kennethreitz/requests/issues/1753
|
||||
bool gzip_ = false;
|
||||
|
||||
// Additional request headers.
|
||||
std::vector<std::string> headers_;
|
||||
|
||||
// Persistent connection.
|
||||
bool keep_alive_ = true;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_REQUEST_BUILDER_H_
|
||||
@@ -1,48 +0,0 @@
|
||||
#ifndef WEBCC_HTTP_REQUEST_HANDLER_H_
|
||||
#define WEBCC_HTTP_REQUEST_HANDLER_H_
|
||||
|
||||
#include <list>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/http_connection.h"
|
||||
#include "webcc/queue.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class HttpRequest;
|
||||
class HttpResponse;
|
||||
|
||||
// The common handler for all incoming requests.
|
||||
class HttpRequestHandler {
|
||||
public:
|
||||
HttpRequestHandler() = default;
|
||||
virtual ~HttpRequestHandler() = default;
|
||||
|
||||
HttpRequestHandler(const HttpRequestHandler&) = delete;
|
||||
HttpRequestHandler& operator=(const HttpRequestHandler&) = delete;
|
||||
|
||||
// Put the connection into the queue.
|
||||
void Enqueue(HttpConnectionPtr connection);
|
||||
|
||||
// Start worker threads.
|
||||
void Start(std::size_t count);
|
||||
|
||||
// Clear pending connections from the queue and stop worker threads.
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
void WorkerRoutine();
|
||||
|
||||
// Called by the worker routine.
|
||||
virtual void HandleConnection(HttpConnectionPtr connection) = 0;
|
||||
|
||||
private:
|
||||
Queue<HttpConnectionPtr> queue_;
|
||||
|
||||
std::vector<std::thread> workers_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_REQUEST_HANDLER_H_
|
||||
@@ -1,39 +0,0 @@
|
||||
#ifndef WEBCC_HTTP_RESPONSE_H_
|
||||
#define WEBCC_HTTP_RESPONSE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "webcc/http_message.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class HttpResponse;
|
||||
typedef std::shared_ptr<HttpResponse> HttpResponsePtr;
|
||||
|
||||
class HttpResponse : public HttpMessage {
|
||||
public:
|
||||
explicit HttpResponse(http::Status status = http::Status::kOK)
|
||||
: status_(status) {
|
||||
}
|
||||
|
||||
~HttpResponse() override = default;
|
||||
|
||||
int status() const {
|
||||
return status_;
|
||||
}
|
||||
|
||||
void set_status(int status) {
|
||||
status_ = status;
|
||||
}
|
||||
|
||||
// Set start line according to status code.
|
||||
void Prepare() final;
|
||||
|
||||
private:
|
||||
int status_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_RESPONSE_H_
|
||||
@@ -1,30 +0,0 @@
|
||||
#ifndef WEBCC_HTTP_RESPONSE_PARSER_H_
|
||||
#define WEBCC_HTTP_RESPONSE_PARSER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "webcc/http_parser.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class HttpResponse;
|
||||
|
||||
class HttpResponseParser : public HttpParser {
|
||||
public:
|
||||
explicit HttpResponseParser(HttpResponse* response = nullptr);
|
||||
|
||||
~HttpResponseParser() override = default;
|
||||
|
||||
void Init(HttpResponse* response);
|
||||
|
||||
private:
|
||||
// Parse HTTP start line; E.g., "HTTP/1.1 200 OK".
|
||||
bool ParseStartLine(const std::string& line) final;
|
||||
|
||||
// The result response message.
|
||||
HttpResponse* response_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_RESPONSE_PARSER_H_
|
||||
+8
-6
@@ -214,15 +214,17 @@ void LogInit(const std::string& dir, int modes) {
|
||||
}
|
||||
|
||||
static std::string GetTimestamp() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
std::time_t t = std::chrono::system_clock::to_time_t(now);
|
||||
using system_clock = std::chrono::system_clock;
|
||||
using milliseconds = std::chrono::milliseconds;
|
||||
|
||||
auto now = system_clock::now();
|
||||
std::time_t t = system_clock::to_time_t(now);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S");
|
||||
|
||||
std::chrono::milliseconds milli_seconds =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch());
|
||||
milliseconds milli_seconds =
|
||||
std::chrono::duration_cast<milliseconds>(now.time_since_epoch());
|
||||
std::string micro_seconds_str = std::to_string(milli_seconds.count() % 1000);
|
||||
while (micro_seconds_str.size() < 3) {
|
||||
micro_seconds_str = "0" + micro_seconds_str;
|
||||
@@ -233,7 +235,7 @@ static std::string GetTimestamp() {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void LogWrite(int level, const char* file, int line, const char* format, ...) {
|
||||
void Log(int level, const char* file, int line, const char* format, ...) {
|
||||
assert(format != nullptr);
|
||||
|
||||
std::string timestamp = GetTimestamp();
|
||||
|
||||
+11
-11
@@ -41,7 +41,7 @@ const int LOG_FILE_OVERWRITE = LOG_FILE | LOG_OVERWRITE;
|
||||
// If |dir| is empty, log file will be generated in current directory.
|
||||
void LogInit(const std::string& dir, int modes);
|
||||
|
||||
void LogWrite(int level, const char* file, int line, const char* format, ...);
|
||||
void Log(int level, const char* file, int line, const char* format, ...);
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
@@ -58,35 +58,35 @@ void LogWrite(int level, const char* file, int line, const char* format, ...);
|
||||
|
||||
#if WEBCC_LOG_LEVEL <= WEBCC_VERB
|
||||
#define LOG_VERB(format, ...) \
|
||||
webcc::LogWrite(WEBCC_VERB, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
|
||||
webcc::Log(WEBCC_VERB, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
|
||||
#else
|
||||
#define LOG_VERB(format, ...)
|
||||
#endif
|
||||
|
||||
#if WEBCC_LOG_LEVEL <= WEBCC_INFO
|
||||
#define LOG_INFO(format, ...) \
|
||||
webcc::LogWrite(WEBCC_INFO, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
|
||||
webcc::Log(WEBCC_INFO, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
|
||||
#else
|
||||
#define LOG_INFO(format, ...)
|
||||
#endif
|
||||
|
||||
#if WEBCC_LOG_LEVEL <= WEBCC_WARN
|
||||
#define LOG_WARN(format, ...) \
|
||||
webcc::LogWrite(WEBCC_WARN, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
|
||||
webcc::Log(WEBCC_WARN, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
|
||||
#else
|
||||
#define LOG_WARN(format, ...)
|
||||
#endif
|
||||
|
||||
#if WEBCC_LOG_LEVEL <= WEBCC_ERRO
|
||||
#define LOG_ERRO(format, ...) \
|
||||
webcc::LogWrite(WEBCC_ERRO, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
|
||||
webcc::Log(WEBCC_ERRO, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
|
||||
#else
|
||||
#define LOG_ERRO(format, ...)
|
||||
#endif
|
||||
|
||||
#if WEBCC_LOG_LEVEL <= WEBCC_FATA
|
||||
#define LOG_FATA(format, ...) \
|
||||
webcc::LogWrite(WEBCC_FATA, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
|
||||
webcc::Log(WEBCC_FATA, __FILENAME__, __LINE__, format, ##__VA_ARGS__);
|
||||
#else
|
||||
#define LOG_FATA(format, ...)
|
||||
#endif
|
||||
@@ -98,35 +98,35 @@ void LogWrite(int level, const char* file, int line, const char* format, ...);
|
||||
|
||||
#if WEBCC_LOG_LEVEL <= WEBCC_VERB
|
||||
#define LOG_VERB(format, args...) \
|
||||
webcc::LogWrite(WEBCC_VERB, __FILENAME__, __LINE__, format, ##args);
|
||||
webcc::Log(WEBCC_VERB, __FILENAME__, __LINE__, format, ##args);
|
||||
#else
|
||||
#define LOG_VERB(format, args...)
|
||||
#endif
|
||||
|
||||
#if WEBCC_LOG_LEVEL <= WEBCC_INFO
|
||||
#define LOG_INFO(format, args...) \
|
||||
webcc::LogWrite(WEBCC_INFO, __FILENAME__, __LINE__, format, ##args);
|
||||
webcc::Log(WEBCC_INFO, __FILENAME__, __LINE__, format, ##args);
|
||||
#else
|
||||
#define LOG_INFO(format, args...)
|
||||
#endif
|
||||
|
||||
#if WEBCC_LOG_LEVEL <= WEBCC_WARN
|
||||
#define LOG_WARN(format, args...) \
|
||||
webcc::LogWrite(WEBCC_WARN, __FILENAME__, __LINE__, format, ##args);
|
||||
webcc::Log(WEBCC_WARN, __FILENAME__, __LINE__, format, ##args);
|
||||
#else
|
||||
#define LOG_WARN(format, args...)
|
||||
#endif
|
||||
|
||||
#if WEBCC_LOG_LEVEL <= WEBCC_ERRO
|
||||
#define LOG_ERRO(format, args...) \
|
||||
webcc::LogWrite(WEBCC_ERRO, __FILENAME__, __LINE__, format, ##args);
|
||||
webcc::Log(WEBCC_ERRO, __FILENAME__, __LINE__, format, ##args);
|
||||
#else
|
||||
#define LOG_ERRO(format, args...)
|
||||
#endif
|
||||
|
||||
#if WEBCC_LOG_LEVEL <= WEBCC_FATA
|
||||
#define LOG_FATA(format, args...) \
|
||||
webcc::LogWrite(WEBCC_FATA, __FILENAME__, __LINE__, format, ##args);
|
||||
webcc::Log(WEBCC_FATA, __FILENAME__, __LINE__, format, ##args);
|
||||
#else
|
||||
#define LOG_FATA(format, args...)
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "webcc/http_message.h"
|
||||
#include "webcc/message.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <iomanip> // for put_time
|
||||
#include <sstream>
|
||||
|
||||
#include "boost/algorithm/string.hpp"
|
||||
@@ -22,15 +24,15 @@ const char CRLF[] = { '\r', '\n' };
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const HttpMessage& message) {
|
||||
std::ostream& operator<<(std::ostream& os, const Message& message) {
|
||||
message.Dump(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool HttpMessage::IsConnectionKeepAlive() const {
|
||||
using http::headers::kConnection;
|
||||
bool Message::IsConnectionKeepAlive() const {
|
||||
using headers::kConnection;
|
||||
|
||||
bool existed = false;
|
||||
const std::string& connection = GetHeader(kConnection, &existed);
|
||||
@@ -47,29 +49,29 @@ bool HttpMessage::IsConnectionKeepAlive() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
http::ContentEncoding HttpMessage::GetContentEncoding() const {
|
||||
using http::headers::kContentEncoding;
|
||||
ContentEncoding Message::GetContentEncoding() const {
|
||||
using headers::kContentEncoding;
|
||||
|
||||
const std::string& encoding = GetHeader(kContentEncoding);
|
||||
if (encoding == "gzip") {
|
||||
return http::ContentEncoding::kGzip;
|
||||
return ContentEncoding::kGzip;
|
||||
}
|
||||
if (encoding == "deflate") {
|
||||
return http::ContentEncoding::kDeflate;
|
||||
return ContentEncoding::kDeflate;
|
||||
}
|
||||
return http::ContentEncoding::kUnknown;
|
||||
return ContentEncoding::kUnknown;
|
||||
}
|
||||
|
||||
bool HttpMessage::AcceptEncodingGzip() const {
|
||||
using http::headers::kAcceptEncoding;
|
||||
bool Message::AcceptEncodingGzip() const {
|
||||
using headers::kAcceptEncoding;
|
||||
|
||||
return GetHeader(kAcceptEncoding).find("gzip") != std::string::npos;
|
||||
}
|
||||
|
||||
// See: https://tools.ietf.org/html/rfc7231#section-3.1.1.1
|
||||
void HttpMessage::SetContentType(const std::string& media_type,
|
||||
void Message::SetContentType(const std::string& media_type,
|
||||
const std::string& charset) {
|
||||
using http::headers::kContentType;
|
||||
using headers::kContentType;
|
||||
|
||||
if (charset.empty()) {
|
||||
SetHeader(kContentType, media_type);
|
||||
@@ -78,14 +80,14 @@ void HttpMessage::SetContentType(const std::string& media_type,
|
||||
}
|
||||
}
|
||||
|
||||
void HttpMessage::SetContent(std::string&& content, bool set_length) {
|
||||
void Message::SetContent(std::string&& content, bool set_length) {
|
||||
content_ = std::move(content);
|
||||
if (set_length) {
|
||||
SetContentLength(content_.size());
|
||||
}
|
||||
}
|
||||
|
||||
void HttpMessage::Prepare() {
|
||||
void Message::Prepare() {
|
||||
assert(!start_line_.empty());
|
||||
|
||||
using boost::asio::buffer;
|
||||
@@ -95,7 +97,7 @@ void HttpMessage::Prepare() {
|
||||
payload_.push_back(buffer(start_line_));
|
||||
payload_.push_back(buffer(misc_strings::CRLF));
|
||||
|
||||
for (const HttpHeader& h : headers_.data()) {
|
||||
for (const Header& h : headers_.data()) {
|
||||
payload_.push_back(buffer(h.first));
|
||||
payload_.push_back(buffer(misc_strings::HEADER_SEPARATOR));
|
||||
payload_.push_back(buffer(h.second));
|
||||
@@ -109,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_) {
|
||||
os.write(static_cast<const char*>(b.data()), b.size());
|
||||
}
|
||||
}
|
||||
|
||||
void HttpMessage::CopyPayload(std::string* str) const {
|
||||
void Message::CopyPayload(std::string* str) const {
|
||||
std::stringstream ss;
|
||||
CopyPayload(ss);
|
||||
*str = ss.str();
|
||||
}
|
||||
|
||||
void HttpMessage::Dump(std::ostream& os, std::size_t indent,
|
||||
void Message::Dump(std::ostream& os, std::size_t indent,
|
||||
const std::string& prefix) const {
|
||||
std::string indent_str;
|
||||
if (indent > 0) {
|
||||
@@ -131,7 +133,7 @@ void HttpMessage::Dump(std::ostream& os, std::size_t indent,
|
||||
|
||||
os << indent_str << start_line_ << std::endl;
|
||||
|
||||
for (const HttpHeader& h : headers_.data()) {
|
||||
for (const Header& h : headers_.data()) {
|
||||
os << indent_str << h.first << ": " << h.second << std::endl;
|
||||
}
|
||||
|
||||
@@ -172,11 +174,18 @@ void HttpMessage::Dump(std::ostream& os, std::size_t indent,
|
||||
}
|
||||
}
|
||||
|
||||
std::string HttpMessage::Dump(std::size_t indent,
|
||||
std::string Message::Dump(std::size_t indent,
|
||||
const std::string& prefix) const {
|
||||
std::stringstream ss;
|
||||
Dump(ss, indent, prefix);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string Message::GetTimestamp() {
|
||||
std::time_t t = std::time(nullptr);
|
||||
std::stringstream ss;
|
||||
ss << std::put_time(std::gmtime(&t), "%a, %d %b %Y %H:%M:%S") << " GMT";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace webcc
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef WEBCC_HTTP_MESSAGE_H_
|
||||
#define WEBCC_HTTP_MESSAGE_H_
|
||||
#ifndef WEBCC_MESSAGE_H_
|
||||
#define WEBCC_MESSAGE_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
@@ -11,16 +11,16 @@
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class HttpMessage;
|
||||
std::ostream& operator<<(std::ostream& os, const HttpMessage& message);
|
||||
class Message;
|
||||
std::ostream& operator<<(std::ostream& os, const Message& message);
|
||||
|
||||
// Base class for HTTP request and response messages.
|
||||
class HttpMessage {
|
||||
class Message {
|
||||
public:
|
||||
HttpMessage() : content_length_(kInvalidLength) {
|
||||
Message() : content_length_(kInvalidLength) {
|
||||
}
|
||||
|
||||
virtual ~HttpMessage() = default;
|
||||
virtual ~Message() = default;
|
||||
|
||||
const std::string& start_line() const {
|
||||
return start_line_;
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
|
||||
bool IsConnectionKeepAlive() const;
|
||||
|
||||
void SetHeader(HttpHeader&& header) {
|
||||
void SetHeader(Header&& header) {
|
||||
headers_.Set(std::move(header.first), std::move(header.second));
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
return headers_.Has(key);
|
||||
}
|
||||
|
||||
http::ContentEncoding GetContentEncoding() const;
|
||||
ContentEncoding GetContentEncoding() const;
|
||||
|
||||
// Return true if header Accept-Encoding contains "gzip".
|
||||
bool AcceptEncodingGzip() const;
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
}
|
||||
|
||||
void SetContentType(const std::string& content_type) {
|
||||
SetHeader(http::headers::kContentType, content_type);
|
||||
SetHeader(headers::kContentType, content_type);
|
||||
}
|
||||
|
||||
// Example: SetContentType("application/json", "utf-8")
|
||||
@@ -105,9 +105,14 @@ public:
|
||||
protected:
|
||||
void SetContentLength(std::size_t content_length) {
|
||||
content_length_ = content_length;
|
||||
SetHeader(http::headers::kContentLength, std::to_string(content_length));
|
||||
SetHeader(headers::kContentLength, std::to_string(content_length));
|
||||
}
|
||||
|
||||
// Get the timestamp for HTTP Date header field.
|
||||
// E.g., Wed, 21 Oct 2015 07:28:00 GMT
|
||||
// See: https://tools.ietf.org/html/rfc7231#section-7.1.1.2
|
||||
std::string GetTimestamp();
|
||||
|
||||
protected:
|
||||
std::string start_line_;
|
||||
|
||||
@@ -117,7 +122,7 @@ protected:
|
||||
|
||||
std::size_t content_length_;
|
||||
|
||||
HttpHeaders headers_;
|
||||
Headers headers_;
|
||||
|
||||
// NOTE: The payload itself doesn't hold the memory!
|
||||
Payload payload_;
|
||||
@@ -125,4 +130,4 @@ protected:
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_MESSAGE_H_
|
||||
#endif // WEBCC_MESSAGE_H_
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "webcc/http_parser.h"
|
||||
#include "webcc/parser.h"
|
||||
|
||||
#include "boost/algorithm/string.hpp"
|
||||
|
||||
#include "webcc/http_message.h"
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/message.h"
|
||||
#include "webcc/utility.h"
|
||||
#include "webcc/zlib_wrapper.h"
|
||||
|
||||
@@ -26,7 +26,7 @@ bool StringToSizeT(const std::string& str, int base, std::size_t* output) {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
HttpParser::HttpParser(HttpMessage* message)
|
||||
Parser::Parser(Message* message)
|
||||
: message_(message),
|
||||
content_length_(kInvalidLength),
|
||||
start_line_parsed_(false),
|
||||
@@ -37,12 +37,12 @@ HttpParser::HttpParser(HttpMessage* message)
|
||||
finished_(false) {
|
||||
}
|
||||
|
||||
void HttpParser::Init(HttpMessage* message) {
|
||||
void Parser::Init(Message* message) {
|
||||
Reset();
|
||||
message_ = message;
|
||||
}
|
||||
|
||||
bool HttpParser::Parse(const char* data, std::size_t length) {
|
||||
bool Parser::Parse(const char* data, std::size_t length) {
|
||||
if (header_ended_) {
|
||||
return ParseContent(data, length);
|
||||
}
|
||||
@@ -64,7 +64,7 @@ bool HttpParser::Parse(const char* data, std::size_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
void HttpParser::Reset() {
|
||||
void Parser::Reset() {
|
||||
pending_data_.clear();
|
||||
content_.clear();
|
||||
|
||||
@@ -77,7 +77,7 @@ void HttpParser::Reset() {
|
||||
finished_ = false;
|
||||
}
|
||||
|
||||
bool HttpParser::ParseHeaders() {
|
||||
bool Parser::ParseHeaders() {
|
||||
std::size_t off = 0;
|
||||
|
||||
while (true) {
|
||||
@@ -113,8 +113,7 @@ bool HttpParser::ParseHeaders() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HttpParser::GetNextLine(std::size_t off, std::string* line,
|
||||
bool erase) {
|
||||
bool Parser::GetNextLine(std::size_t off, std::string* line, bool erase) {
|
||||
std::size_t pos = pending_data_.find(kCRLF, off);
|
||||
|
||||
if (pos == std::string::npos) {
|
||||
@@ -134,15 +133,15 @@ bool HttpParser::GetNextLine(std::size_t off, std::string* line,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HttpParser::ParseHeaderLine(const std::string& line) {
|
||||
HttpHeader header;
|
||||
bool Parser::ParseHeaderLine(const std::string& line) {
|
||||
Header header;
|
||||
if (!Split2(line, ':', &header.first, &header.second)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
if (!chunked_ && !content_length_parsed_) {
|
||||
if (boost::iequals(header.first, http::headers::kContentLength)) {
|
||||
if (boost::iequals(header.first, headers::kContentLength)) {
|
||||
content_length_parsed_ = true;
|
||||
|
||||
if (!StringToSizeT(header.second, 10, &content_length_)) {
|
||||
@@ -166,7 +165,7 @@ bool HttpParser::ParseHeaderLine(const std::string& line) {
|
||||
|
||||
// TODO: Replace `!chunked_` with <TransferEncodingParsed>.
|
||||
if (!chunked_ && !content_length_parsed_) {
|
||||
if (boost::iequals(header.first, http::headers::kTransferEncoding)) {
|
||||
if (boost::iequals(header.first, headers::kTransferEncoding)) {
|
||||
if (header.second == "chunked") {
|
||||
// The content is chunked.
|
||||
chunked_ = true;
|
||||
@@ -178,7 +177,7 @@ bool HttpParser::ParseHeaderLine(const std::string& line) {
|
||||
} while (false);
|
||||
|
||||
// Parse Content-Type.
|
||||
if (boost::iequals(header.first, http::headers::kContentType)) {
|
||||
if (boost::iequals(header.first, headers::kContentType)) {
|
||||
ContentType content_type(header.second);
|
||||
if (!content_type.Valid()) {
|
||||
LOG_ERRO("Invalid content-type header: %s", header.second.c_str());
|
||||
@@ -192,7 +191,7 @@ bool HttpParser::ParseHeaderLine(const std::string& line) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HttpParser::ParseContent(const char* data, std::size_t length) {
|
||||
bool Parser::ParseContent(const char* data, std::size_t length) {
|
||||
if (chunked_) {
|
||||
return ParseChunkedContent(data, length);
|
||||
} else {
|
||||
@@ -200,7 +199,7 @@ bool HttpParser::ParseContent(const char* data, std::size_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpParser::ParseFixedContent(const char* data, std::size_t length) {
|
||||
bool Parser::ParseFixedContent(const char* data, std::size_t length) {
|
||||
if (!content_length_parsed_) {
|
||||
// No Content-Length, no content.
|
||||
Finish();
|
||||
@@ -229,7 +228,7 @@ bool HttpParser::ParseFixedContent(const char* data, std::size_t length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HttpParser::ParseChunkedContent(const char* data, std::size_t length) {
|
||||
bool Parser::ParseChunkedContent(const char* data, std::size_t length) {
|
||||
// Append the new data to the pending data.
|
||||
// NOTE: It's more difficult to avoid this than fixed-length content.
|
||||
pending_data_.append(data, length);
|
||||
@@ -285,7 +284,7 @@ bool HttpParser::ParseChunkedContent(const char* data, std::size_t length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HttpParser::ParseChunkSize() {
|
||||
bool Parser::ParseChunkSize() {
|
||||
LOG_VERB("Parse chunk size.");
|
||||
|
||||
std::string line;
|
||||
@@ -312,7 +311,7 @@ bool HttpParser::ParseChunkSize() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HttpParser::Finish() {
|
||||
bool Parser::Finish() {
|
||||
finished_ = true;
|
||||
|
||||
if (content_.empty()) {
|
||||
@@ -336,21 +335,21 @@ bool HttpParser::Finish() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void HttpParser::AppendContent(const char* data, std::size_t count) {
|
||||
void Parser::AppendContent(const char* data, std::size_t count) {
|
||||
content_.append(data, count);
|
||||
}
|
||||
|
||||
void HttpParser::AppendContent(const std::string& data) {
|
||||
void Parser::AppendContent(const std::string& data) {
|
||||
content_.append(data);
|
||||
}
|
||||
|
||||
bool HttpParser::IsContentFull() const {
|
||||
bool Parser::IsContentFull() const {
|
||||
return content_length_ != kInvalidLength &&
|
||||
content_length_ <= content_.length();
|
||||
}
|
||||
|
||||
bool HttpParser::IsContentCompressed() const {
|
||||
return message_->GetContentEncoding() != http::ContentEncoding::kUnknown;
|
||||
bool Parser::IsContentCompressed() const {
|
||||
return message_->GetContentEncoding() != ContentEncoding::kUnknown;
|
||||
}
|
||||
|
||||
} // namespace webcc
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef WEBCC_HTTP_PARSER_H_
|
||||
#define WEBCC_HTTP_PARSER_H_
|
||||
#ifndef WEBCC_PARSER_H_
|
||||
#define WEBCC_PARSER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -8,19 +8,19 @@
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class HttpMessage;
|
||||
class Message;
|
||||
|
||||
// HTTP request and response parser.
|
||||
class HttpParser {
|
||||
class Parser {
|
||||
public:
|
||||
explicit HttpParser(HttpMessage* message);
|
||||
explicit Parser(Message* message);
|
||||
|
||||
virtual ~HttpParser() = default;
|
||||
virtual ~Parser() = default;
|
||||
|
||||
HttpParser(const HttpParser&) = delete;
|
||||
HttpParser& operator=(const HttpParser&) = delete;
|
||||
Parser(const Parser&) = delete;
|
||||
Parser& operator=(const Parser&) = delete;
|
||||
|
||||
void Init(HttpMessage* message);
|
||||
void Init(Message* message);
|
||||
|
||||
bool finished() const { return finished_; }
|
||||
|
||||
@@ -66,7 +66,7 @@ protected:
|
||||
|
||||
protected:
|
||||
// The result HTTP message.
|
||||
HttpMessage* message_;
|
||||
Message* message_;
|
||||
|
||||
// Data waiting to be parsed.
|
||||
std::string pending_data_;
|
||||
@@ -84,4 +84,4 @@ protected:
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_PARSER_H_
|
||||
#endif // WEBCC_PARSER_H_
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "webcc/http_request.h"
|
||||
#include "webcc/request.h"
|
||||
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/utility.h"
|
||||
@@ -18,17 +18,17 @@ const char DOUBLE_DASHES[] = { '-', '-' };
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void HttpRequest::Prepare() {
|
||||
void Request::Prepare() {
|
||||
CreateStartLine();
|
||||
|
||||
if (url_.port().empty()) {
|
||||
SetHeader(http::headers::kHost, url_.host());
|
||||
SetHeader(headers::kHost, url_.host());
|
||||
} else {
|
||||
SetHeader(http::headers::kHost, url_.host() + ":" + url_.port());
|
||||
SetHeader(headers::kHost, url_.host() + ":" + url_.port());
|
||||
}
|
||||
|
||||
if (form_parts_.empty()) {
|
||||
HttpMessage::Prepare();
|
||||
Message::Prepare();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,13 +69,13 @@ void HttpRequest::Prepare() {
|
||||
SetContentLength(content_length);
|
||||
|
||||
// Prepare start line and headers.
|
||||
HttpMessage::Prepare();
|
||||
Message::Prepare();
|
||||
|
||||
// Append payload of content data.
|
||||
payload_.insert(payload_.end(), data_payload.begin(), data_payload.end());
|
||||
}
|
||||
|
||||
void HttpRequest::CreateStartLine() {
|
||||
void Request::CreateStartLine() {
|
||||
if (!start_line_.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1,27 +1,27 @@
|
||||
#ifndef WEBCC_HTTP_REQUEST_H_
|
||||
#define WEBCC_HTTP_REQUEST_H_
|
||||
#ifndef WEBCC_REQUEST_H_
|
||||
#define WEBCC_REQUEST_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/http_message.h"
|
||||
#include "webcc/message.h"
|
||||
#include "webcc/url.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class HttpRequest;
|
||||
typedef std::shared_ptr<HttpRequest> HttpRequestPtr;
|
||||
class Request;
|
||||
using RequestPtr = std::shared_ptr<Request>;
|
||||
|
||||
class HttpRequest : public HttpMessage {
|
||||
class Request : public Message {
|
||||
public:
|
||||
HttpRequest() = default;
|
||||
Request() = default;
|
||||
|
||||
HttpRequest(const std::string& method, const std::string& url)
|
||||
Request(const std::string& method, const std::string& url)
|
||||
: method_(method), url_(url) {
|
||||
}
|
||||
|
||||
~HttpRequest() override = default;
|
||||
~Request() override = default;
|
||||
|
||||
const std::string& method() const {
|
||||
return method_;
|
||||
@@ -86,4 +86,4 @@ private:
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_REQUEST_H_
|
||||
#endif // WEBCC_REQUEST_H_
|
||||
@@ -0,0 +1,91 @@
|
||||
#include "webcc/request_builder.h"
|
||||
|
||||
#include "webcc/base64.h"
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/utility.h"
|
||||
#include "webcc/zlib_wrapper.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
RequestPtr RequestBuilder::Build() {
|
||||
assert(parameters_.size() % 2 == 0);
|
||||
assert(headers_.size() % 2 == 0);
|
||||
|
||||
auto request = std::make_shared<Request>(method_, url_);
|
||||
|
||||
for (std::size_t i = 1; i < parameters_.size(); i += 2) {
|
||||
request->AddQuery(parameters_[i - 1], parameters_[i]);
|
||||
}
|
||||
|
||||
for (std::size_t i = 1; i < headers_.size(); i += 2) {
|
||||
request->SetHeader(std::move(headers_[i - 1]), std::move(headers_[i]));
|
||||
}
|
||||
|
||||
// No keep-alive?
|
||||
if (!keep_alive_) {
|
||||
request->SetHeader(headers::kConnection, "Close");
|
||||
}
|
||||
|
||||
if (!data_.empty()) {
|
||||
SetContent(request, std::move(data_));
|
||||
|
||||
// TODO: Request-level charset.
|
||||
if (json_) {
|
||||
request->SetContentType(media_types::kApplicationJson, "");
|
||||
}
|
||||
} else if (!form_parts_.empty()) {
|
||||
request->set_form_parts(std::move(form_parts_));
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
RequestBuilder& RequestBuilder::File(const std::string& name,
|
||||
const Path& path,
|
||||
const std::string& media_type) {
|
||||
assert(!name.empty());
|
||||
form_parts_.push_back(FormPart{ name, path, media_type });
|
||||
return *this;
|
||||
}
|
||||
|
||||
RequestBuilder& RequestBuilder::Form(const std::string& name,
|
||||
std::string&& data,
|
||||
const std::string& media_type) {
|
||||
assert(!name.empty());
|
||||
form_parts_.push_back(FormPart{ name, std::move(data), media_type });
|
||||
return *this;
|
||||
}
|
||||
|
||||
RequestBuilder& RequestBuilder::Auth(const std::string& type,
|
||||
const std::string& credentials) {
|
||||
headers_.push_back(headers::kAuthorization);
|
||||
headers_.push_back(type + " " + credentials);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RequestBuilder& RequestBuilder::AuthBasic(const std::string& login,
|
||||
const std::string& password) {
|
||||
auto credentials = Base64Encode(login + ":" + password);
|
||||
return Auth("Basic", credentials);
|
||||
}
|
||||
|
||||
RequestBuilder& RequestBuilder::AuthToken(const std::string& token) {
|
||||
return Auth("Token", token);
|
||||
}
|
||||
|
||||
void RequestBuilder::SetContent(RequestPtr request, std::string&& data) {
|
||||
if (gzip_ && data.size() > kGzipThreshold) {
|
||||
std::string compressed;
|
||||
if (Compress(data, &compressed)) {
|
||||
request->SetContent(std::move(compressed), true);
|
||||
request->SetHeader(headers::kContentEncoding, "gzip");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_WARN("Cannot compress the content data!");
|
||||
}
|
||||
|
||||
request->SetContent(std::move(data), true);
|
||||
}
|
||||
|
||||
} // namespace webcc
|
||||
@@ -0,0 +1,141 @@
|
||||
#ifndef WEBCC_REQUEST_BUILDER_H_
|
||||
#define WEBCC_REQUEST_BUILDER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/request.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class RequestBuilder {
|
||||
public:
|
||||
explicit RequestBuilder(const std::string& method = "")
|
||||
: method_(method) {
|
||||
}
|
||||
|
||||
~RequestBuilder() = default;
|
||||
|
||||
RequestBuilder(const RequestBuilder&) = delete;
|
||||
RequestBuilder& operator=(const RequestBuilder&) = delete;
|
||||
|
||||
// Build the request.
|
||||
RequestPtr Build();
|
||||
|
||||
RequestPtr operator()() {
|
||||
return Build();
|
||||
}
|
||||
|
||||
RequestBuilder& Get() { return Method(methods::kGet); }
|
||||
RequestBuilder& Head() { return Method(methods::kHead); }
|
||||
RequestBuilder& Post() { return Method(methods::kPost); }
|
||||
RequestBuilder& Put() { return Method(methods::kPut); }
|
||||
RequestBuilder& Delete() { return Method(methods::kDelete); }
|
||||
RequestBuilder& Patch() { return Method(methods::kPatch); }
|
||||
|
||||
// NOTE:
|
||||
// The naming convention doesn't follow Google C++ Style for
|
||||
// consistency and simplicity.
|
||||
|
||||
RequestBuilder& Method(const std::string& method) {
|
||||
method_ = method;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RequestBuilder& Url(const std::string& url) {
|
||||
url_ = url;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RequestBuilder& Query(const std::string& key, const std::string& value) {
|
||||
parameters_.push_back(key);
|
||||
parameters_.push_back(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RequestBuilder& Data(const std::string& data) {
|
||||
data_ = data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RequestBuilder& Data(std::string&& data) {
|
||||
data_ = std::move(data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RequestBuilder& Json(bool json = true) {
|
||||
json_ = json;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Upload a file.
|
||||
RequestBuilder& File(const std::string& name, const Path& path,
|
||||
const std::string& media_type = "");
|
||||
|
||||
RequestBuilder& Form(FormPart&& part) {
|
||||
form_parts_.push_back(std::move(part));
|
||||
return *this;
|
||||
}
|
||||
|
||||
RequestBuilder& Form(const std::string& name, std::string&& data,
|
||||
const std::string& media_type = "");
|
||||
|
||||
RequestBuilder& Gzip(bool gzip = true) {
|
||||
gzip_ = gzip;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RequestBuilder& Header(const std::string& key, const std::string& value) {
|
||||
headers_.push_back(key);
|
||||
headers_.push_back(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RequestBuilder& KeepAlive(bool keep_alive = true) {
|
||||
keep_alive_ = keep_alive;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RequestBuilder& Auth(const std::string& type, const std::string& credentials);
|
||||
|
||||
RequestBuilder& AuthBasic(const std::string& login,
|
||||
const std::string& password);
|
||||
|
||||
RequestBuilder& AuthToken(const std::string& token);
|
||||
|
||||
private:
|
||||
void SetContent(RequestPtr request, std::string&& data);
|
||||
|
||||
private:
|
||||
std::string method_;
|
||||
|
||||
std::string url_;
|
||||
|
||||
// URL query parameters.
|
||||
std::vector<std::string> parameters_;
|
||||
|
||||
// Data to send in the body of the request.
|
||||
std::string data_;
|
||||
|
||||
// Is the data to send a JSON string?
|
||||
bool json_ = false;
|
||||
|
||||
// Files to upload for a POST request.
|
||||
std::vector<FormPart> form_parts_;
|
||||
|
||||
// Compress the request content.
|
||||
// NOTE: Most servers don't support compressed requests.
|
||||
// Even the requests module from Python doesn't have a built-in support.
|
||||
// See: https://github.com/kennethreitz/requests/issues/1753
|
||||
bool gzip_ = false;
|
||||
|
||||
// Additional request headers.
|
||||
std::vector<std::string> headers_;
|
||||
|
||||
// Persistent connection.
|
||||
bool keep_alive_ = true;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_REQUEST_BUILDER_H_
|
||||
@@ -1,36 +1,36 @@
|
||||
#include "webcc/http_request_handler.h"
|
||||
#include "webcc/request_handler.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "webcc/globals.h"
|
||||
#include "webcc/http_request.h"
|
||||
#include "webcc/http_response.h"
|
||||
#include "webcc/request.h"
|
||||
#include "webcc/response.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
void HttpRequestHandler::Enqueue(HttpConnectionPtr connection) {
|
||||
void RequestHandler::Enqueue(ConnectionPtr connection) {
|
||||
queue_.Push(connection);
|
||||
}
|
||||
|
||||
void HttpRequestHandler::Start(std::size_t count) {
|
||||
void RequestHandler::Start(std::size_t count) {
|
||||
assert(count > 0 && workers_.size() == 0);
|
||||
|
||||
for (std::size_t i = 0; i < count; ++i) {
|
||||
workers_.emplace_back(std::bind(&HttpRequestHandler::WorkerRoutine, this));
|
||||
workers_.emplace_back(std::bind(&RequestHandler::WorkerRoutine, this));
|
||||
}
|
||||
}
|
||||
|
||||
void HttpRequestHandler::Stop() {
|
||||
void RequestHandler::Stop() {
|
||||
LOG_INFO("Stopping workers...");
|
||||
|
||||
// Clear pending connections.
|
||||
// The connections will be closed later (see HttpServer::DoAwaitStop).
|
||||
// The connections will be closed later (see Server::DoAwaitStop).
|
||||
LOG_INFO("Clear pending connections...");
|
||||
queue_.Clear();
|
||||
|
||||
// Enqueue a null connection to trigger the first worker to stop.
|
||||
queue_.Push(HttpConnectionPtr());
|
||||
queue_.Push(ConnectionPtr());
|
||||
|
||||
for (auto& worker : workers_) {
|
||||
if (worker.joinable()) {
|
||||
@@ -41,17 +41,17 @@ void HttpRequestHandler::Stop() {
|
||||
LOG_INFO("All workers have been stopped.");
|
||||
}
|
||||
|
||||
void HttpRequestHandler::WorkerRoutine() {
|
||||
void RequestHandler::WorkerRoutine() {
|
||||
LOG_INFO("Worker is running.");
|
||||
|
||||
for (;;) {
|
||||
HttpConnectionPtr connection = queue_.PopOrWait();
|
||||
ConnectionPtr connection = queue_.PopOrWait();
|
||||
|
||||
if (!connection) {
|
||||
LOG_INFO("Worker is going to stop.");
|
||||
|
||||
// For stopping next worker.
|
||||
queue_.Push(HttpConnectionPtr());
|
||||
queue_.Push(ConnectionPtr());
|
||||
|
||||
// Stop the worker.
|
||||
break;
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef WEBCC_REQUEST_HANDLER_H_
|
||||
#define WEBCC_REQUEST_HANDLER_H_
|
||||
|
||||
#include <list>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/connection.h"
|
||||
#include "webcc/queue.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class Request;
|
||||
class Response;
|
||||
|
||||
// The common handler for all incoming requests.
|
||||
class RequestHandler {
|
||||
public:
|
||||
RequestHandler() = default;
|
||||
virtual ~RequestHandler() = default;
|
||||
|
||||
RequestHandler(const RequestHandler&) = delete;
|
||||
RequestHandler& operator=(const RequestHandler&) = delete;
|
||||
|
||||
// Put the connection into the queue.
|
||||
void Enqueue(ConnectionPtr connection);
|
||||
|
||||
// Start worker threads.
|
||||
void Start(std::size_t count);
|
||||
|
||||
// Clear pending connections from the queue and stop worker threads.
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
void WorkerRoutine();
|
||||
|
||||
// Called by the worker routine.
|
||||
virtual void HandleConnection(ConnectionPtr connection) = 0;
|
||||
|
||||
private:
|
||||
Queue<ConnectionPtr> queue_;
|
||||
|
||||
std::vector<std::thread> workers_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_REQUEST_HANDLER_H_
|
||||
@@ -1,24 +1,24 @@
|
||||
#include "webcc/http_request_parser.h"
|
||||
#include "webcc/request_parser.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "boost/algorithm/string.hpp"
|
||||
|
||||
#include "webcc/http_request.h"
|
||||
#include "webcc/request.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
HttpRequestParser::HttpRequestParser(HttpRequest* request)
|
||||
: HttpParser(request), request_(request) {
|
||||
RequestParser::RequestParser(Request* request)
|
||||
: Parser(request), request_(request) {
|
||||
}
|
||||
|
||||
void HttpRequestParser::Init(HttpRequest* request) {
|
||||
HttpParser::Init(request);
|
||||
void RequestParser::Init(Request* request) {
|
||||
Parser::Init(request);
|
||||
request_ = request;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::ParseStartLine(const std::string& line) {
|
||||
bool RequestParser::ParseStartLine(const std::string& line) {
|
||||
std::vector<std::string> strs;
|
||||
boost::split(strs, line, boost::is_any_of(" "), boost::token_compress_on);
|
||||
|
||||
@@ -34,7 +34,7 @@ bool HttpRequestParser::ParseStartLine(const std::string& line) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::ParseContent(const char* data, std::size_t length) {
|
||||
bool RequestParser::ParseContent(const char* data, std::size_t length) {
|
||||
if (chunked_) {
|
||||
return ParseChunkedContent(data, length);
|
||||
} else {
|
||||
@@ -46,7 +46,7 @@ bool HttpRequestParser::ParseContent(const char* data, std::size_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpRequestParser::ParseMultipartContent(const char* data,
|
||||
bool RequestParser::ParseMultipartContent(const char* data,
|
||||
std::size_t length) {
|
||||
// Append the new data to the pending data.
|
||||
// NOTE: It's more difficult to avoid this than normal fixed-length content.
|
||||
@@ -139,7 +139,7 @@ bool HttpRequestParser::ParseMultipartContent(const char* data,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::ParsePartHeaders(bool* need_more_data) {
|
||||
bool RequestParser::ParsePartHeaders(bool* need_more_data) {
|
||||
std::size_t off = 0;
|
||||
|
||||
while (true) {
|
||||
@@ -157,7 +157,7 @@ bool HttpRequestParser::ParsePartHeaders(bool* need_more_data) {
|
||||
break;
|
||||
}
|
||||
|
||||
HttpHeader header;
|
||||
Header header;
|
||||
if (!Split2(line, ':', &header.first, &header.second)) {
|
||||
LOG_ERRO("Invalid part header line: %s", line.c_str());
|
||||
return false;
|
||||
@@ -167,7 +167,7 @@ bool HttpRequestParser::ParsePartHeaders(bool* need_more_data) {
|
||||
header.second.c_str());
|
||||
|
||||
// Parse Content-Disposition.
|
||||
if (boost::iequals(header.first, http::headers::kContentDisposition)) {
|
||||
if (boost::iequals(header.first, headers::kContentDisposition)) {
|
||||
ContentDisposition content_disposition(header.second);
|
||||
if (!content_disposition.valid()) {
|
||||
LOG_ERRO("Invalid content-disposition header: %s",
|
||||
@@ -189,7 +189,7 @@ bool HttpRequestParser::ParsePartHeaders(bool* need_more_data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::GetNextBoundaryLine(std::size_t* b_off,
|
||||
bool RequestParser::GetNextBoundaryLine(std::size_t* b_off,
|
||||
std::size_t* b_count,
|
||||
bool* ended) {
|
||||
std::size_t off = 0;
|
||||
@@ -228,14 +228,14 @@ bool HttpRequestParser::GetNextBoundaryLine(std::size_t* b_off,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::IsBoundary(const std::string& line) const {
|
||||
bool RequestParser::IsBoundary(const std::string& line) const {
|
||||
if (line == "--" + request_->content_type().boundary()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HttpRequestParser::IsBoundaryEnd(const std::string& line) const {
|
||||
bool RequestParser::IsBoundaryEnd(const std::string& line) const {
|
||||
if (line == "--" + request_->content_type().boundary() + "--") {
|
||||
return true;
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
#ifndef WEBCC_HTTP_REQUEST_PARSER_H_
|
||||
#define WEBCC_HTTP_REQUEST_PARSER_H_
|
||||
#ifndef WEBCC_REQUEST_PARSER_H_
|
||||
#define WEBCC_REQUEST_PARSER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "webcc/http_parser.h"
|
||||
#include "webcc/parser.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class HttpRequest;
|
||||
class Request;
|
||||
|
||||
class HttpRequestParser : public HttpParser {
|
||||
class RequestParser : public Parser {
|
||||
public:
|
||||
explicit HttpRequestParser(HttpRequest* request = nullptr);
|
||||
explicit RequestParser(Request* request = nullptr);
|
||||
|
||||
~HttpRequestParser() override = default;
|
||||
~RequestParser() override = default;
|
||||
|
||||
void Init(HttpRequest* request);
|
||||
void Init(Request* request);
|
||||
|
||||
private:
|
||||
bool ParseStartLine(const std::string& line) final;
|
||||
@@ -33,7 +33,7 @@ private:
|
||||
bool IsBoundaryEnd(const std::string& line) const;
|
||||
|
||||
private:
|
||||
HttpRequest* request_;
|
||||
Request* request_;
|
||||
|
||||
enum Step {
|
||||
kStart,
|
||||
@@ -48,4 +48,4 @@ private:
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_REQUEST_PARSER_H_
|
||||
#endif // WEBCC_REQUEST_PARSER_H_
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "webcc/http_response.h"
|
||||
#include "webcc/response.h"
|
||||
|
||||
#include "webcc/utility.h"
|
||||
|
||||
@@ -20,34 +20,34 @@ const std::string SERVICE_UNAVAILABLE = "HTTP/1.1 503 Service Unavailable";
|
||||
|
||||
const std::string& ToString(int status) {
|
||||
switch (status) {
|
||||
case http::Status::kOK:
|
||||
case Status::kOK:
|
||||
return OK;
|
||||
|
||||
case http::Status::kCreated:
|
||||
case Status::kCreated:
|
||||
return CREATED;
|
||||
|
||||
case http::Status::kAccepted:
|
||||
case Status::kAccepted:
|
||||
return ACCEPTED;
|
||||
|
||||
case http::Status::kNoContent:
|
||||
case Status::kNoContent:
|
||||
return NO_CONTENT;
|
||||
|
||||
case http::Status::kNotModified:
|
||||
case Status::kNotModified:
|
||||
return NOT_MODIFIED;
|
||||
|
||||
case http::Status::kBadRequest:
|
||||
case Status::kBadRequest:
|
||||
return BAD_REQUEST;
|
||||
|
||||
case http::Status::kNotFound:
|
||||
case Status::kNotFound:
|
||||
return NOT_FOUND;
|
||||
|
||||
case http::Status::kInternalServerError:
|
||||
case Status::kInternalServerError:
|
||||
return INTERNAL_SERVER_ERROR;
|
||||
|
||||
case http::Status::kNotImplemented:
|
||||
case Status::kNotImplemented:
|
||||
return NOT_IMPLEMENTED;
|
||||
|
||||
case http::Status::kServiceUnavailable:
|
||||
case Status::kServiceUnavailable:
|
||||
return SERVICE_UNAVAILABLE;
|
||||
|
||||
default:
|
||||
@@ -57,15 +57,15 @@ const std::string& ToString(int status) {
|
||||
|
||||
} // namespace status_strings
|
||||
|
||||
void HttpResponse::Prepare() {
|
||||
void Response::Prepare() {
|
||||
if (start_line_.empty()) {
|
||||
start_line_ = status_strings::ToString(status_);
|
||||
}
|
||||
|
||||
SetHeader(http::headers::kServer, http::UserAgent());
|
||||
SetHeader(http::headers::kDate, GetHttpDateTimestamp());
|
||||
SetHeader(headers::kServer, UserAgent());
|
||||
SetHeader(headers::kDate, GetTimestamp());
|
||||
|
||||
HttpMessage::Prepare();
|
||||
Message::Prepare();
|
||||
}
|
||||
|
||||
} // namespace webcc
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef WEBCC_RESPONSE_H_
|
||||
#define WEBCC_RESPONSE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "webcc/message.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class Response;
|
||||
using ResponsePtr = std::shared_ptr<Response>;
|
||||
|
||||
class Response : public Message {
|
||||
public:
|
||||
explicit Response(Status status = Status::kOK)
|
||||
: status_(status) {
|
||||
}
|
||||
|
||||
~Response() override = default;
|
||||
|
||||
int status() const {
|
||||
return status_;
|
||||
}
|
||||
|
||||
void set_status(int status) {
|
||||
status_ = status;
|
||||
}
|
||||
|
||||
// Set start line according to status code.
|
||||
void Prepare() final;
|
||||
|
||||
private:
|
||||
int status_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_RESPONSE_H_
|
||||
@@ -1,22 +1,22 @@
|
||||
#include "webcc/http_response_parser.h"
|
||||
#include "webcc/response_parser.h"
|
||||
|
||||
#include "boost/algorithm/string.hpp"
|
||||
|
||||
#include "webcc/http_response.h"
|
||||
#include "webcc/response.h"
|
||||
#include "webcc/logger.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
HttpResponseParser::HttpResponseParser(HttpResponse* response)
|
||||
: HttpParser(response), response_(response) {
|
||||
ResponseParser::ResponseParser(Response* response)
|
||||
: Parser(response), response_(response) {
|
||||
}
|
||||
|
||||
void HttpResponseParser::Init(HttpResponse* response) {
|
||||
HttpParser::Init(response);
|
||||
void ResponseParser::Init(Response* response) {
|
||||
Parser::Init(response);
|
||||
response_ = response;
|
||||
}
|
||||
|
||||
bool HttpResponseParser::ParseStartLine(const std::string& line) {
|
||||
bool ResponseParser::ParseStartLine(const std::string& line) {
|
||||
std::vector<std::string> parts;
|
||||
boost::split(parts, line, boost::is_any_of(" "), boost::token_compress_on);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef WEBCC_RESPONSE_PARSER_H_
|
||||
#define WEBCC_RESPONSE_PARSER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "webcc/parser.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class Response;
|
||||
|
||||
class ResponseParser : public Parser {
|
||||
public:
|
||||
explicit ResponseParser(Response* response = nullptr);
|
||||
|
||||
~ResponseParser() override = default;
|
||||
|
||||
void Init(Response* response);
|
||||
|
||||
private:
|
||||
// Parse HTTP start line; E.g., "HTTP/1.1 200 OK".
|
||||
bool ParseStartLine(const std::string& line) final;
|
||||
|
||||
// The result response message.
|
||||
Response* response_;
|
||||
};
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_RESPONSE_PARSER_H_
|
||||
@@ -14,8 +14,8 @@ bool RestRequestHandler::Bind(RestServicePtr service, const std::string& url,
|
||||
return service_manager_.AddService(service, url, is_regex);
|
||||
}
|
||||
|
||||
void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
|
||||
HttpRequestPtr http_request = connection->request();
|
||||
void RestRequestHandler::HandleConnection(ConnectionPtr connection) {
|
||||
RequestPtr http_request = connection->request();
|
||||
assert(http_request);
|
||||
|
||||
const Url& url = http_request->url();
|
||||
@@ -28,14 +28,14 @@ void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
|
||||
|
||||
if (!service) {
|
||||
LOG_WARN("No service matches the URL path: %s", url.path().c_str());
|
||||
connection->SendResponse(http::Status::kNotFound);
|
||||
connection->SendResponse(Status::kNotFound);
|
||||
return;
|
||||
}
|
||||
|
||||
RestResponse rest_response;
|
||||
service->Handle(rest_request, &rest_response);
|
||||
|
||||
auto http_response = std::make_shared<HttpResponse>(rest_response.status);
|
||||
auto http_response = std::make_shared<Response>(rest_response.status);
|
||||
|
||||
if (!rest_response.content.empty()) {
|
||||
if (!rest_response.media_type.empty()) {
|
||||
@@ -48,7 +48,7 @@ void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
|
||||
http_request->AcceptEncodingGzip()) {
|
||||
std::string compressed;
|
||||
if (Compress(rest_response.content, &compressed)) {
|
||||
http_response->SetHeader(http::headers::kContentEncoding, "gzip");
|
||||
http_response->SetHeader(headers::kContentEncoding, "gzip");
|
||||
http_response->SetContent(std::move(compressed), true);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "webcc/http_request_handler.h"
|
||||
#include "webcc/request_handler.h"
|
||||
#include "webcc/rest_service_manager.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class RestRequestHandler : public HttpRequestHandler {
|
||||
class RestRequestHandler : public RequestHandler {
|
||||
public:
|
||||
RestRequestHandler() = default;
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
bool Bind(RestServicePtr service, const std::string& url, bool is_regex);
|
||||
|
||||
private:
|
||||
void HandleConnection(HttpConnectionPtr connection) final;
|
||||
void HandleConnection(ConnectionPtr connection) final;
|
||||
|
||||
private:
|
||||
RestServiceManager service_manager_;
|
||||
|
||||
+4
-4
@@ -5,16 +5,16 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "webcc/http_server.h"
|
||||
#include "webcc/server.h"
|
||||
#include "webcc/rest_request_handler.h"
|
||||
#include "webcc/rest_service.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class RestServer : public HttpServer {
|
||||
class RestServer : public Server {
|
||||
public:
|
||||
RestServer(std::uint16_t port, std::size_t workers)
|
||||
: HttpServer(port, workers) {
|
||||
: Server(port, workers) {
|
||||
}
|
||||
|
||||
~RestServer() override = default;
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
HttpRequestHandler* GetRequestHandler() final {
|
||||
RequestHandler* GetRequestHandler() final {
|
||||
return &request_handler_;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ void RestListService::Handle(const RestRequest& request,
|
||||
RestResponse* response) {
|
||||
const std::string& method = request.http->method();
|
||||
|
||||
if (method == http::methods::kGet) {
|
||||
if (method == methods::kGet) {
|
||||
Get(UrlQuery(request.http->url().query()), response);
|
||||
|
||||
} else if (method == http::methods::kPost) {
|
||||
} else if (method == methods::kPost) {
|
||||
Post(request.http->content(), response);
|
||||
|
||||
} else {
|
||||
@@ -27,16 +27,16 @@ void RestDetailService::Handle(const RestRequest& request,
|
||||
RestResponse* response) {
|
||||
const std::string& method = request.http->method();
|
||||
|
||||
if (method == http::methods::kGet) {
|
||||
if (method == methods::kGet) {
|
||||
Get(request.url_matches, UrlQuery(request.http->url().query()), response);
|
||||
|
||||
} else if (method == http::methods::kPut) {
|
||||
} else if (method == methods::kPut) {
|
||||
Put(request.url_matches, request.http->content(), response);
|
||||
|
||||
} else if (method == http::methods::kPatch) {
|
||||
} else if (method == methods::kPatch) {
|
||||
Patch(request.url_matches, request.http->content(), response);
|
||||
|
||||
} else if (method == http::methods::kDelete) {
|
||||
} else if (method == methods::kDelete) {
|
||||
Delete(request.url_matches, response);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "webcc/globals.h"
|
||||
#include "webcc/http_request.h"
|
||||
#include "webcc/request.h"
|
||||
#include "webcc/url.h"
|
||||
|
||||
namespace webcc {
|
||||
@@ -22,18 +22,18 @@ namespace webcc {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Regex sub-matches of the URL.
|
||||
typedef std::vector<std::string> UrlMatches;
|
||||
using UrlMatches = std::vector<std::string>;
|
||||
|
||||
struct RestRequest {
|
||||
// Original HTTP request.
|
||||
HttpRequestPtr http;
|
||||
RequestPtr http;
|
||||
|
||||
// Regex sub-matches of the URL (usually resource ID's).
|
||||
UrlMatches url_matches;
|
||||
};
|
||||
|
||||
struct RestResponse {
|
||||
http::Status status;
|
||||
Status status;
|
||||
|
||||
std::string content;
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
virtual void Handle(const RestRequest& request, RestResponse* response) = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<RestService> RestServicePtr;
|
||||
using RestServicePtr = std::shared_ptr<RestService>;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "webcc/http_server.h"
|
||||
#include "webcc/server.h"
|
||||
|
||||
#include <csignal>
|
||||
#include <utility>
|
||||
|
||||
#include "webcc/http_request_handler.h"
|
||||
#include "webcc/request_handler.h"
|
||||
#include "webcc/logger.h"
|
||||
#include "webcc/utility.h"
|
||||
|
||||
@@ -11,7 +11,7 @@ using tcp = boost::asio::ip::tcp;
|
||||
|
||||
namespace webcc {
|
||||
|
||||
HttpServer::HttpServer(std::uint16_t port, std::size_t workers)
|
||||
Server::Server(std::uint16_t port, std::size_t workers)
|
||||
: acceptor_(io_context_), signals_(io_context_), workers_(workers) {
|
||||
RegisterSignals();
|
||||
|
||||
@@ -50,7 +50,7 @@ HttpServer::HttpServer(std::uint16_t port, std::size_t workers)
|
||||
}
|
||||
}
|
||||
|
||||
void HttpServer::Run() {
|
||||
void Server::Run() {
|
||||
assert(GetRequestHandler() != nullptr);
|
||||
|
||||
if (!acceptor_.is_open()) {
|
||||
@@ -74,7 +74,7 @@ void HttpServer::Run() {
|
||||
io_context_.run();
|
||||
}
|
||||
|
||||
void HttpServer::RegisterSignals() {
|
||||
void Server::RegisterSignals() {
|
||||
signals_.add(SIGINT); // Ctrl+C
|
||||
signals_.add(SIGTERM);
|
||||
|
||||
@@ -83,7 +83,7 @@ void HttpServer::RegisterSignals() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void HttpServer::DoAccept() {
|
||||
void Server::DoAccept() {
|
||||
acceptor_.async_accept(
|
||||
[this](boost::system::error_code ec, tcp::socket socket) {
|
||||
// Check whether the server was stopped by a signal before this
|
||||
@@ -95,7 +95,7 @@ void HttpServer::DoAccept() {
|
||||
if (!ec) {
|
||||
LOG_INFO("Accepted a connection.");
|
||||
|
||||
auto connection = std::make_shared<HttpConnection>(
|
||||
auto connection = std::make_shared<Connection>(
|
||||
std::move(socket), &pool_, GetRequestHandler());
|
||||
|
||||
pool_.Start(connection);
|
||||
@@ -105,7 +105,7 @@ void HttpServer::DoAccept() {
|
||||
});
|
||||
}
|
||||
|
||||
void HttpServer::DoAwaitStop() {
|
||||
void Server::DoAwaitStop() {
|
||||
signals_.async_wait(
|
||||
[this](boost::system::error_code, int signo) {
|
||||
// The server is stopped by canceling all outstanding asynchronous
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef WEBCC_HTTP_SERVER_H_
|
||||
#define WEBCC_HTTP_SERVER_H_
|
||||
#ifndef WEBCC_SERVER_H_
|
||||
#define WEBCC_SERVER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -8,23 +8,23 @@
|
||||
#include "boost/asio/signal_set.hpp"
|
||||
|
||||
#include "webcc/globals.h"
|
||||
#include "webcc/http_connection.h"
|
||||
#include "webcc/http_connection_pool.h"
|
||||
#include "webcc/connection.h"
|
||||
#include "webcc/connection_pool.h"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
class HttpRequestHandler;
|
||||
class RequestHandler;
|
||||
|
||||
// HTTP server accepts TCP connections from TCP clients.
|
||||
// NOTE: Only support IPv4.
|
||||
class HttpServer {
|
||||
class Server {
|
||||
public:
|
||||
HttpServer(std::uint16_t port, std::size_t workers);
|
||||
Server(std::uint16_t port, std::size_t workers);
|
||||
|
||||
virtual ~HttpServer() = default;
|
||||
virtual ~Server() = default;
|
||||
|
||||
HttpServer(const HttpServer&) = delete;
|
||||
HttpServer& operator=(const HttpServer&) = delete;
|
||||
Server(const Server&) = delete;
|
||||
Server& operator=(const Server&) = delete;
|
||||
|
||||
// Run the server's io_service loop.
|
||||
void Run();
|
||||
@@ -40,7 +40,7 @@ private:
|
||||
void DoAwaitStop();
|
||||
|
||||
// Get the handler for incoming requests.
|
||||
virtual HttpRequestHandler* GetRequestHandler() = 0;
|
||||
virtual RequestHandler* GetRequestHandler() = 0;
|
||||
|
||||
// The io_context used to perform asynchronous operations.
|
||||
boost::asio::io_context io_context_;
|
||||
@@ -49,7 +49,7 @@ private:
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
|
||||
// The connection pool which owns all live connections.
|
||||
HttpConnectionPool pool_;
|
||||
ConnectionPool pool_;
|
||||
|
||||
// The signal_set is used to register for process termination notifications.
|
||||
boost::asio::signal_set signals_;
|
||||
@@ -60,4 +60,4 @@ private:
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_SERVER_H_
|
||||
#endif // WEBCC_SERVER_H_
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "webcc/http_socket.h"
|
||||
#include "webcc/socket.h"
|
||||
|
||||
#include "boost/asio/connect.hpp"
|
||||
#include "boost/asio/read.hpp"
|
||||
@@ -10,26 +10,26 @@ namespace webcc {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
HttpSocket::HttpSocket(boost::asio::io_context& io_context)
|
||||
Socket::Socket(boost::asio::io_context& io_context)
|
||||
: socket_(io_context) {
|
||||
}
|
||||
|
||||
void HttpSocket::Connect(const std::string& /*host*/,
|
||||
void Socket::Connect(const std::string& /*host*/,
|
||||
const Endpoints& endpoints,
|
||||
boost::system::error_code* ec) {
|
||||
boost::asio::connect(socket_, endpoints, *ec);
|
||||
}
|
||||
|
||||
void HttpSocket::Write(const HttpRequest& request,
|
||||
void Socket::Write(const Request& request,
|
||||
boost::system::error_code* ec) {
|
||||
boost::asio::write(socket_, request.payload(), *ec);
|
||||
}
|
||||
|
||||
void HttpSocket::AsyncReadSome(ReadHandler&& handler, std::vector<char>* buffer) {
|
||||
void Socket::AsyncReadSome(ReadHandler&& handler, std::vector<char>* buffer) {
|
||||
socket_.async_read_some(boost::asio::buffer(*buffer), std::move(handler));
|
||||
}
|
||||
|
||||
void HttpSocket::Close(boost::system::error_code* ec) {
|
||||
void Socket::Close(boost::system::error_code* ec) {
|
||||
socket_.close(*ec);
|
||||
}
|
||||
|
||||
@@ -39,8 +39,7 @@ void HttpSocket::Close(boost::system::error_code* ec) {
|
||||
|
||||
namespace ssl = boost::asio::ssl;
|
||||
|
||||
HttpSslSocket::HttpSslSocket(boost::asio::io_context& io_context,
|
||||
bool ssl_verify)
|
||||
SslSocket::SslSocket(boost::asio::io_context& io_context, bool ssl_verify)
|
||||
: ssl_context_(ssl::context::sslv23),
|
||||
ssl_socket_(io_context, ssl_context_),
|
||||
ssl_verify_(ssl_verify) {
|
||||
@@ -48,8 +47,7 @@ HttpSslSocket::HttpSslSocket(boost::asio::io_context& io_context,
|
||||
ssl_context_.set_default_verify_paths();
|
||||
}
|
||||
|
||||
void HttpSslSocket::Connect(const std::string& host,
|
||||
const Endpoints& endpoints,
|
||||
void SslSocket::Connect(const std::string& host, const Endpoints& endpoints,
|
||||
boost::system::error_code* ec) {
|
||||
boost::asio::connect(ssl_socket_.lowest_layer(), endpoints, *ec);
|
||||
|
||||
@@ -60,20 +58,20 @@ void HttpSslSocket::Connect(const std::string& host,
|
||||
Handshake(host, ec);
|
||||
}
|
||||
|
||||
void HttpSslSocket::Write(const HttpRequest& request,
|
||||
boost::system::error_code* ec) {
|
||||
void SslSocket::Write(const Request& request, boost::system::error_code* ec) {
|
||||
boost::asio::write(ssl_socket_, request.payload(), *ec);
|
||||
}
|
||||
|
||||
void HttpSslSocket::AsyncReadSome(ReadHandler&& handler, std::vector<char>* buffer) {
|
||||
void SslSocket::AsyncReadSome(ReadHandler&& handler,
|
||||
std::vector<char>* buffer) {
|
||||
ssl_socket_.async_read_some(boost::asio::buffer(*buffer), std::move(handler));
|
||||
}
|
||||
|
||||
void HttpSslSocket::Close(boost::system::error_code* ec) {
|
||||
void SslSocket::Close(boost::system::error_code* ec) {
|
||||
ssl_socket_.lowest_layer().close(*ec);
|
||||
}
|
||||
|
||||
void HttpSslSocket::Handshake(const std::string& host,
|
||||
void SslSocket::Handshake(const std::string& host,
|
||||
boost::system::error_code* ec) {
|
||||
if (ssl_verify_) {
|
||||
ssl_socket_.set_verify_mode(ssl::verify_peer);
|
||||
@@ -1,12 +1,12 @@
|
||||
#ifndef WEBCC_HTTP_SOCKET_H_
|
||||
#define WEBCC_HTTP_SOCKET_H_
|
||||
#ifndef WEBCC_SOCKET_H_
|
||||
#define WEBCC_SOCKET_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "boost/asio/ip/tcp.hpp"
|
||||
|
||||
#include "webcc/config.h"
|
||||
#include "webcc/http_request.h"
|
||||
#include "webcc/request.h"
|
||||
|
||||
#if WEBCC_ENABLE_SSL
|
||||
#include "boost/asio/ssl.hpp"
|
||||
@@ -16,21 +16,21 @@ namespace webcc {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class HttpSocketBase {
|
||||
class SocketBase {
|
||||
public:
|
||||
virtual ~HttpSocketBase() = default;
|
||||
virtual ~SocketBase() = default;
|
||||
|
||||
typedef boost::asio::ip::tcp::resolver::results_type Endpoints;
|
||||
using Endpoints = boost::asio::ip::tcp::resolver::results_type;
|
||||
|
||||
typedef std::function<void(boost::system::error_code, std::size_t)>
|
||||
ReadHandler;
|
||||
using ReadHandler =
|
||||
std::function<void(boost::system::error_code, std::size_t)>;
|
||||
|
||||
// TODO: Remove |host|
|
||||
virtual void Connect(const std::string& host,
|
||||
const Endpoints& endpoints,
|
||||
boost::system::error_code* ec) = 0;
|
||||
|
||||
virtual void Write(const HttpRequest& request,
|
||||
virtual void Write(const Request& request,
|
||||
boost::system::error_code* ec) = 0;
|
||||
|
||||
virtual void AsyncReadSome(ReadHandler&& handler,
|
||||
@@ -41,15 +41,15 @@ public:
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class HttpSocket : public HttpSocketBase {
|
||||
class Socket : public SocketBase {
|
||||
public:
|
||||
explicit HttpSocket(boost::asio::io_context& io_context);
|
||||
explicit Socket(boost::asio::io_context& io_context);
|
||||
|
||||
void Connect(const std::string& host,
|
||||
const Endpoints& endpoints,
|
||||
boost::system::error_code* ec) final;
|
||||
|
||||
void Write(const HttpRequest& request,
|
||||
void Write(const Request& request,
|
||||
boost::system::error_code* ec) final;
|
||||
|
||||
void AsyncReadSome(ReadHandler&& handler, std::vector<char>* buffer) final;
|
||||
@@ -64,16 +64,16 @@ private:
|
||||
|
||||
#if WEBCC_ENABLE_SSL
|
||||
|
||||
class HttpSslSocket : public HttpSocketBase {
|
||||
class SslSocket : public SocketBase {
|
||||
public:
|
||||
explicit HttpSslSocket(boost::asio::io_context& io_context,
|
||||
explicit SslSocket(boost::asio::io_context& io_context,
|
||||
bool ssl_verify = true);
|
||||
|
||||
void Connect(const std::string& host,
|
||||
const Endpoints& endpoints,
|
||||
boost::system::error_code* ec) final;
|
||||
|
||||
void Write(const HttpRequest& request,
|
||||
void Write(const Request& request,
|
||||
boost::system::error_code* ec) final;
|
||||
|
||||
void AsyncReadSome(ReadHandler&& handler, std::vector<char>* buffer) final;
|
||||
@@ -95,4 +95,4 @@ private:
|
||||
|
||||
} // namespace webcc
|
||||
|
||||
#endif // WEBCC_HTTP_SOCKET_H_
|
||||
#endif // WEBCC_SOCKET_H_
|
||||
+3
-3
@@ -16,8 +16,8 @@ namespace webcc {
|
||||
// URL query parameters.
|
||||
class UrlQuery {
|
||||
public:
|
||||
typedef std::pair<std::string, std::string> Parameter;
|
||||
typedef std::vector<Parameter> Parameters;
|
||||
using Parameter = std::pair<std::string, std::string>;
|
||||
using Parameters = std::vector<Parameter>;
|
||||
|
||||
UrlQuery() = default;
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
std::string ToString() const;
|
||||
|
||||
private:
|
||||
typedef Parameters::const_iterator ConstIterator;
|
||||
using ConstIterator = Parameters::const_iterator;
|
||||
ConstIterator Find(const std::string& key) const;
|
||||
|
||||
Parameters parameters_;
|
||||
|
||||
@@ -1,52 +1,12 @@
|
||||
#include "webcc/utility.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include <iomanip> // for put_time
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "boost/uuid/random_generator.hpp"
|
||||
#include "boost/uuid/uuid_io.hpp"
|
||||
|
||||
#include "webcc/logger.h"
|
||||
|
||||
using tcp = boost::asio::ip::tcp;
|
||||
|
||||
namespace webcc {
|
||||
|
||||
void PrintEndpoint(std::ostream& ostream, const TcpEndpoint& endpoint) {
|
||||
ostream << endpoint;
|
||||
if (endpoint.protocol() == tcp::v4()) {
|
||||
ostream << ", v4";
|
||||
} else if (endpoint.protocol() == tcp::v6()) {
|
||||
ostream << ", v6";
|
||||
}
|
||||
}
|
||||
|
||||
void PrintEndpoints(std::ostream& ostream, const TcpEndpoints& endpoints) {
|
||||
ostream << "Endpoints: " << endpoints.size() << std::endl;
|
||||
tcp::resolver::results_type::iterator it = endpoints.begin();
|
||||
for (; it != endpoints.end(); ++it) {
|
||||
ostream << " - ";
|
||||
PrintEndpoint(ostream, it->endpoint());
|
||||
ostream << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::string EndpointToString(const TcpEndpoint& endpoint) {
|
||||
std::stringstream ss;
|
||||
PrintEndpoint(ss, endpoint);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GetHttpDateTimestamp() {
|
||||
std::time_t t = std::time(nullptr);
|
||||
std::stringstream ss;
|
||||
ss << std::put_time(std::gmtime(&t), "%a, %d %b %Y %H:%M:%S") << " GMT";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string RandomUuid() {
|
||||
boost::uuids::uuid u = boost::uuids::random_generator()();
|
||||
std::stringstream ss;
|
||||
|
||||
@@ -4,19 +4,8 @@
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
#include "boost/asio/ip/tcp.hpp"
|
||||
|
||||
namespace webcc {
|
||||
|
||||
typedef boost::asio::ip::tcp::endpoint TcpEndpoint;
|
||||
typedef boost::asio::ip::tcp::resolver::results_type TcpEndpoints;
|
||||
|
||||
void PrintEndpoint(std::ostream& ostream, const TcpEndpoint& endpoint);
|
||||
|
||||
void PrintEndpoints(std::ostream& ostream, const TcpEndpoints& endpoints);
|
||||
|
||||
std::string EndpointToString(const TcpEndpoint& endpoint);
|
||||
|
||||
// Get the timestamp for HTTP Date header field.
|
||||
// E.g., Wed, 21 Oct 2015 07:28:00 GMT
|
||||
// See: https://tools.ietf.org/html/rfc7231#section-7.1.1.2
|
||||
|
||||
Reference in New Issue
Block a user