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