introduce string_view
This commit is contained in:
+3
-2
@@ -31,7 +31,8 @@ set(WEBCC_ENABLE_GZIP 0 CACHE STRING "Enable gzip compression (need Zlib)? (1:Ye
|
|||||||
|
|
||||||
set(WEBCC_LOG_LEVEL 2 CACHE STRING "Log level (0:VERB, 1:INFO, 2:USER, 3:WARN or 4:ERRO)")
|
set(WEBCC_LOG_LEVEL 2 CACHE STRING "Log level (0:VERB, 1:INFO, 2:USER, 3:WARN or 4:ERRO)")
|
||||||
|
|
||||||
set(WEBCC_USE_STD_FILESYSTEM 1 CACHE STRING "Use C++17 filesystem? (1:Yes, 0:No)")
|
set(WEBCC_USE_STD_FILESYSTEM 1 CACHE STRING "Use std::filesystem? (1:Yes, 0:No)")
|
||||||
|
set(WEBCC_USE_STD_STRING_VIEW 1 CACHE STRING "Use std::string_view? (1:Yes, 0:No)")
|
||||||
|
|
||||||
if(BUILD_UNITTEST)
|
if(BUILD_UNITTEST)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
@@ -45,7 +46,7 @@ endif()
|
|||||||
|
|
||||||
# C++ standard requirements.
|
# C++ standard requirements.
|
||||||
|
|
||||||
if(WEBCC_USE_STD_FILESYSTEM)
|
if(WEBCC_USE_STD_FILESYSTEM OR WEBCC_USE_STD_STRING_VIEW)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
else()
|
else()
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ static Json::Value StringToJson(const std::string& str) {
|
|||||||
Json::Value json;
|
Json::Value json;
|
||||||
|
|
||||||
Json::CharReaderBuilder builder;
|
Json::CharReaderBuilder builder;
|
||||||
std::stringstream stream(str);
|
std::istringstream stream{ str };
|
||||||
std::string errors;
|
std::string errors;
|
||||||
if (!Json::parseFromStream(builder, stream, &json, &errors)) {
|
if (!Json::parseFromStream(builder, stream, &json, &errors)) {
|
||||||
std::cerr << errors << std::endl;
|
std::cerr << errors << std::endl;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Json::Value StringToJson(const std::string& str) {
|
|||||||
Json::Value json;
|
Json::Value json;
|
||||||
|
|
||||||
Json::CharReaderBuilder builder;
|
Json::CharReaderBuilder builder;
|
||||||
std::stringstream stream(str);
|
std::istringstream stream{ str };
|
||||||
std::string errs;
|
std::string errs;
|
||||||
if (!Json::parseFromStream(builder, stream, &json, &errs)) {
|
if (!Json::parseFromStream(builder, stream, &json, &errs)) {
|
||||||
std::cerr << errs << std::endl;
|
std::cerr << errs << std::endl;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Json::Value StringToJson(const std::string& str) {
|
|||||||
Json::Value json;
|
Json::Value json;
|
||||||
|
|
||||||
Json::CharReaderBuilder builder;
|
Json::CharReaderBuilder builder;
|
||||||
std::stringstream stream(str);
|
std::istringstream stream{ str };
|
||||||
std::string errs;
|
std::string errs;
|
||||||
if (!Json::parseFromStream(builder, stream, &json, &errs)) {
|
if (!Json::parseFromStream(builder, stream, &json, &errs)) {
|
||||||
std::cerr << errs << std::endl;
|
std::cerr << errs << std::endl;
|
||||||
|
|||||||
@@ -39,11 +39,11 @@ int main(int argc, char* argv[]) {
|
|||||||
std::make_shared<BookListView>(),
|
std::make_shared<BookListView>(),
|
||||||
{ "GET", "POST" });
|
{ "GET", "POST" });
|
||||||
|
|
||||||
server.Route(webcc::R("/books/(\\d+)"),
|
server.Route(webcc::R{ "/books/(\\d+)" },
|
||||||
std::make_shared<BookDetailView>(photo_dir),
|
std::make_shared<BookDetailView>(photo_dir),
|
||||||
{ "GET", "PUT", "DELETE" });
|
{ "GET", "PUT", "DELETE" });
|
||||||
|
|
||||||
server.Route(webcc::R("/books/(\\d+)/photo"),
|
server.Route(webcc::R{ "/books/(\\d+)/photo" },
|
||||||
std::make_shared<BookPhotoView>(photo_dir),
|
std::make_shared<BookPhotoView>(photo_dir),
|
||||||
{ "GET", "PUT", "DELETE" });
|
{ "GET", "PUT", "DELETE" });
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ int main(int argc, char* argv[]) {
|
|||||||
std::cout << " $ ./form_client path/to/webcc/data/upload" << std::endl;
|
std::cout << " $ ./form_client path/to/webcc/data/upload" << std::endl;
|
||||||
std::cout << " $ ./form_client path/to/webcc/data/upload "
|
std::cout << " $ ./form_client path/to/webcc/data/upload "
|
||||||
<< "http://httpbin.org/post" << std::endl;
|
<< "http://httpbin.org/post" << std::endl;
|
||||||
std::cout << "(Post the example 'form_server')" << std::endl;
|
std::cout << "(Post to the example 'form_server')" << std::endl;
|
||||||
std::cout << " $ ./form_client path/to/webcc/data/upload "
|
std::cout << " $ ./form_client path/to/webcc/data/upload "
|
||||||
"http://localhost:8080/upload"
|
"http://localhost:8080/upload"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
@@ -43,8 +43,7 @@ int main(int argc, char* argv[]) {
|
|||||||
webcc::ClientSession session;
|
webcc::ClientSession session;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto r = session.Send(webcc::RequestBuilder{}
|
auto r = session.Send(WEBCC_POST(url)
|
||||||
.Post(url)
|
|
||||||
.FormFile("file", upload_dir / "remember.txt")
|
.FormFile("file", upload_dir / "remember.txt")
|
||||||
.FormData("json", "{}", "application/json")());
|
.FormData("json", "{}", "application/json")());
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Json::Value StringToJson(const std::string& str) {
|
|||||||
Json::Value json;
|
Json::Value json;
|
||||||
|
|
||||||
Json::CharReaderBuilder builder;
|
Json::CharReaderBuilder builder;
|
||||||
std::stringstream stream(str);
|
std::istringstream stream{ str };
|
||||||
std::string errors;
|
std::string errors;
|
||||||
if (!Json::parseFromStream(builder, stream, &json, &errors)) {
|
if (!Json::parseFromStream(builder, stream, &json, &errors)) {
|
||||||
std::cerr << errors << std::endl;
|
std::cerr << errors << std::endl;
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ public:
|
|||||||
TEST(RouterTest, URL_RegexBasic) {
|
TEST(RouterTest, URL_RegexBasic) {
|
||||||
webcc::Router router;
|
webcc::Router router;
|
||||||
|
|
||||||
router.Route(webcc::R("/instance/(\\d+)"),
|
router.Route(webcc::R{ "/instance/(\\d+)" }, std::make_shared<MyView>());
|
||||||
std::make_shared<MyView>());
|
|
||||||
|
|
||||||
std::string url = "/instance/12345";
|
std::string url = "/instance/12345";
|
||||||
webcc::UrlArgs args;
|
webcc::UrlArgs args;
|
||||||
@@ -40,7 +39,7 @@ TEST(RouterTest, URL_RegexBasic) {
|
|||||||
TEST(RouterTest, URL_RegexMultiple) {
|
TEST(RouterTest, URL_RegexMultiple) {
|
||||||
webcc::Router router;
|
webcc::Router router;
|
||||||
|
|
||||||
router.Route(webcc::R("/study/(\\d+)/series/(\\d+)/instance/(\\d+)"),
|
router.Route(webcc::R{ "/study/(\\d+)/series/(\\d+)/instance/(\\d+)" },
|
||||||
std::make_shared<MyView>());
|
std::make_shared<MyView>());
|
||||||
|
|
||||||
std::string url = "/study/1/series/2/instance/3";
|
std::string url = "/study/1/series/2/instance/3";
|
||||||
|
|||||||
+66
-19
@@ -6,8 +6,36 @@
|
|||||||
|
|
||||||
#include "webcc/string.h"
|
#include "webcc/string.h"
|
||||||
|
|
||||||
|
TEST(StringTest, Trim) {
|
||||||
|
std::string str = " trim me ";
|
||||||
|
webcc::string_view sv = str;
|
||||||
|
webcc::Trim(sv);
|
||||||
|
EXPECT_EQ("trim me", sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StringTest, Trim_Left) {
|
||||||
|
std::string str = " trim me";
|
||||||
|
webcc::string_view sv = str;
|
||||||
|
webcc::Trim(sv);
|
||||||
|
EXPECT_EQ("trim me", sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StringTest, Trim_Right) {
|
||||||
|
std::string str = "trim me ";
|
||||||
|
webcc::string_view sv = str;
|
||||||
|
webcc::Trim(sv);
|
||||||
|
EXPECT_EQ("trim me", sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StringTest, Trim_Empty) {
|
||||||
|
std::string str = "";
|
||||||
|
webcc::string_view sv = str;
|
||||||
|
webcc::Trim(sv);
|
||||||
|
EXPECT_EQ("", sv);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(StringTest, Split) {
|
TEST(StringTest, Split) {
|
||||||
std::vector<boost::string_view> parts;
|
std::vector<webcc::string_view> parts;
|
||||||
webcc::Split("GET /path/to HTTP/1.1", ' ', false, &parts);
|
webcc::Split("GET /path/to HTTP/1.1", ' ', false, &parts);
|
||||||
|
|
||||||
EXPECT_EQ(3, parts.size());
|
EXPECT_EQ(3, parts.size());
|
||||||
@@ -18,12 +46,11 @@ TEST(StringTest, Split) {
|
|||||||
|
|
||||||
TEST(StringTest, Split_TokenCompressOff) {
|
TEST(StringTest, Split_TokenCompressOff) {
|
||||||
std::string str = "one,two,,three,,";
|
std::string str = "one,two,,three,,";
|
||||||
std::vector<boost::string_view> parts;
|
std::vector<webcc::string_view> parts;
|
||||||
|
|
||||||
// Same as:
|
// Same as:
|
||||||
// boost::split(parts, str, boost::is_any_of(","),
|
// boost::split(parts, str, boost::is_any_of(","),
|
||||||
// boost::token_compress_off);
|
// boost::token_compress_off);
|
||||||
|
|
||||||
webcc::Split(str, ',', false, &parts);
|
webcc::Split(str, ',', false, &parts);
|
||||||
|
|
||||||
EXPECT_EQ(6, parts.size());
|
EXPECT_EQ(6, parts.size());
|
||||||
@@ -37,7 +64,7 @@ TEST(StringTest, Split_TokenCompressOff) {
|
|||||||
|
|
||||||
TEST(StringTest, Split_TokenCompressOn) {
|
TEST(StringTest, Split_TokenCompressOn) {
|
||||||
std::string str = "one,two,,three,,";
|
std::string str = "one,two,,three,,";
|
||||||
std::vector<boost::string_view> parts;
|
std::vector<webcc::string_view> parts;
|
||||||
|
|
||||||
// Same as:
|
// Same as:
|
||||||
// boost::split(parts, str, boost::is_any_of(","),
|
// boost::split(parts, str, boost::is_any_of(","),
|
||||||
@@ -51,8 +78,28 @@ TEST(StringTest, Split_TokenCompressOn) {
|
|||||||
EXPECT_EQ("", parts[3]);
|
EXPECT_EQ("", parts[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(StringTest, Split_TokensOnly) {
|
||||||
|
std::string str = ",,,,,";
|
||||||
|
std::vector<webcc::string_view> parts;
|
||||||
|
|
||||||
|
// Token compress on
|
||||||
|
webcc::Split(str, ',', true, &parts);
|
||||||
|
EXPECT_EQ(2, parts.size());
|
||||||
|
EXPECT_EQ("", parts[0]);
|
||||||
|
EXPECT_EQ("", parts[1]);
|
||||||
|
|
||||||
|
parts.clear();
|
||||||
|
|
||||||
|
// Token compress off
|
||||||
|
webcc::Split(str, ',', false, &parts);
|
||||||
|
EXPECT_EQ(6, parts.size());
|
||||||
|
EXPECT_EQ("", parts[0]);
|
||||||
|
EXPECT_EQ("", parts[1]);
|
||||||
|
EXPECT_EQ("", parts[5]);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(StringTest, Split_NewLine) {
|
TEST(StringTest, Split_NewLine) {
|
||||||
std::vector<boost::string_view> lines;
|
std::vector<webcc::string_view> lines;
|
||||||
webcc::Split("line one\nline two\nline 3", '\n', false, &lines);
|
webcc::Split("line one\nline two\nline 3", '\n', false, &lines);
|
||||||
|
|
||||||
EXPECT_EQ(3, lines.size());
|
EXPECT_EQ(3, lines.size());
|
||||||
@@ -64,8 +111,8 @@ TEST(StringTest, Split_NewLine) {
|
|||||||
TEST(StringTest, SplitKV) {
|
TEST(StringTest, SplitKV) {
|
||||||
const std::string str = "key=value";
|
const std::string str = "key=value";
|
||||||
|
|
||||||
std::string key;
|
webcc::string_view key;
|
||||||
std::string value;
|
webcc::string_view value;
|
||||||
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
|
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
|
||||||
|
|
||||||
EXPECT_EQ(true, ok);
|
EXPECT_EQ(true, ok);
|
||||||
@@ -76,8 +123,8 @@ TEST(StringTest, SplitKV) {
|
|||||||
TEST(StringTest, SplitKV_OtherDelim) {
|
TEST(StringTest, SplitKV_OtherDelim) {
|
||||||
const std::string str = "key:value";
|
const std::string str = "key:value";
|
||||||
|
|
||||||
std::string key;
|
webcc::string_view key;
|
||||||
std::string value;
|
webcc::string_view value;
|
||||||
bool ok = webcc::SplitKV(str, ':', true, &key, &value);
|
bool ok = webcc::SplitKV(str, ':', true, &key, &value);
|
||||||
|
|
||||||
EXPECT_TRUE(ok);
|
EXPECT_TRUE(ok);
|
||||||
@@ -89,8 +136,8 @@ TEST(StringTest, SplitKV_OtherDelim) {
|
|||||||
TEST(StringTest, SplitKV_Spaces) {
|
TEST(StringTest, SplitKV_Spaces) {
|
||||||
const std::string str = " key = value ";
|
const std::string str = " key = value ";
|
||||||
|
|
||||||
std::string key;
|
webcc::string_view key;
|
||||||
std::string value;
|
webcc::string_view value;
|
||||||
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
|
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
|
||||||
|
|
||||||
EXPECT_TRUE(ok);
|
EXPECT_TRUE(ok);
|
||||||
@@ -102,8 +149,8 @@ TEST(StringTest, SplitKV_Spaces) {
|
|||||||
TEST(StringTest, SplitKV_SpacesNoTrim) {
|
TEST(StringTest, SplitKV_SpacesNoTrim) {
|
||||||
const std::string str = " key = value ";
|
const std::string str = " key = value ";
|
||||||
|
|
||||||
std::string key;
|
webcc::string_view key;
|
||||||
std::string value;
|
webcc::string_view value;
|
||||||
bool ok = webcc::SplitKV(str, '=', false, &key, &value);
|
bool ok = webcc::SplitKV(str, '=', false, &key, &value);
|
||||||
|
|
||||||
EXPECT_TRUE(ok);
|
EXPECT_TRUE(ok);
|
||||||
@@ -115,8 +162,8 @@ TEST(StringTest, SplitKV_SpacesNoTrim) {
|
|||||||
TEST(StringTest, SplitKV_NoKey) {
|
TEST(StringTest, SplitKV_NoKey) {
|
||||||
const std::string str = "=value";
|
const std::string str = "=value";
|
||||||
|
|
||||||
std::string key;
|
webcc::string_view key;
|
||||||
std::string value;
|
webcc::string_view value;
|
||||||
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
|
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
|
||||||
|
|
||||||
EXPECT_TRUE(ok);
|
EXPECT_TRUE(ok);
|
||||||
@@ -128,8 +175,8 @@ TEST(StringTest, SplitKV_NoKey) {
|
|||||||
TEST(StringTest, SplitKV_NoValue) {
|
TEST(StringTest, SplitKV_NoValue) {
|
||||||
const std::string str = "key=";
|
const std::string str = "key=";
|
||||||
|
|
||||||
std::string key;
|
webcc::string_view key;
|
||||||
std::string value;
|
webcc::string_view value;
|
||||||
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
|
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
|
||||||
|
|
||||||
EXPECT_TRUE(ok);
|
EXPECT_TRUE(ok);
|
||||||
@@ -141,8 +188,8 @@ TEST(StringTest, SplitKV_NoValue) {
|
|||||||
TEST(StringTest, SplitKV_NoKeyNoValue) {
|
TEST(StringTest, SplitKV_NoKeyNoValue) {
|
||||||
const std::string str = "=";
|
const std::string str = "=";
|
||||||
|
|
||||||
std::string key;
|
webcc::string_view key;
|
||||||
std::string value;
|
webcc::string_view value;
|
||||||
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
|
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
|
||||||
|
|
||||||
EXPECT_TRUE(ok);
|
EXPECT_TRUE(ok);
|
||||||
|
|||||||
+2
-2
@@ -114,10 +114,10 @@ void Client::AsyncConnect() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::AsyncResolve(const std::string& default_port) {
|
void Client::AsyncResolve(string_view default_port) {
|
||||||
std::string port = request_->port();
|
std::string port = request_->port();
|
||||||
if (port.empty()) {
|
if (port.empty()) {
|
||||||
port = default_port;
|
port = ToString(default_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_VERB("Resolve host (%s)", request_->host().c_str());
|
LOG_VERB("Resolve host (%s)", request_->host().c_str());
|
||||||
|
|||||||
+1
-1
@@ -89,7 +89,7 @@ private:
|
|||||||
|
|
||||||
void AsyncConnect();
|
void AsyncConnect();
|
||||||
|
|
||||||
void AsyncResolve(const std::string& default_port);
|
void AsyncResolve(string_view default_port);
|
||||||
|
|
||||||
void OnResolve(boost::system::error_code ec,
|
void OnResolve(boost::system::error_code ec,
|
||||||
boost::asio::ip::tcp::resolver::results_type endpoints);
|
boost::asio::ip::tcp::resolver::results_type endpoints);
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ void ClientSession::Stop() {
|
|||||||
started_ = false;
|
started_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSession::Accept(const std::string& content_types) {
|
void ClientSession::Accept(string_view content_types) {
|
||||||
if (!content_types.empty()) {
|
if (!content_types.empty()) {
|
||||||
headers_.Set(headers::kAccept, content_types);
|
headers_.Set(headers::kAccept, content_types);
|
||||||
}
|
}
|
||||||
@@ -168,18 +168,18 @@ void ClientSession::AcceptGzip(bool gzip) {
|
|||||||
|
|
||||||
#endif // WEBCC_ENABLE_GZIP
|
#endif // WEBCC_ENABLE_GZIP
|
||||||
|
|
||||||
void ClientSession::Auth(const std::string& type,
|
void ClientSession::Auth(string_view type, string_view credentials) {
|
||||||
const std::string& credentials) {
|
headers_.Set(headers::kAuthorization,
|
||||||
headers_.Set(headers::kAuthorization, type + " " + credentials);
|
ToString(type) + " " + ToString(credentials));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSession::AuthBasic(const std::string& login,
|
void ClientSession::AuthBasic(string_view login, string_view password) {
|
||||||
const std::string& password) {
|
auto credentials =
|
||||||
auto credentials = Base64Encode(login + ":" + password);
|
Base64Encode(ToString(login) + ":" + ToString(password));
|
||||||
return Auth("Basic", credentials);
|
return Auth("Basic", credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSession::AuthToken(const std::string& token) {
|
void ClientSession::AuthToken(string_view token) {
|
||||||
return Auth("Token", token);
|
return Auth("Token", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public:
|
|||||||
buffer_size_ = buffer_size;
|
buffer_size_ = buffer_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetHeader(const std::string& key, const std::string& value) {
|
void SetHeader(string_view key, string_view value) {
|
||||||
headers_.Set(key, value);
|
headers_.Set(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,14 +60,13 @@ public:
|
|||||||
// Only applied when:
|
// Only applied when:
|
||||||
// - the request to send has no `Content-Type` header, and
|
// - the request to send has no `Content-Type` header, and
|
||||||
// - the request has a body.
|
// - the request has a body.
|
||||||
void SetContentType(const std::string& media_type,
|
void SetContentType(string_view media_type, string_view charset = "") {
|
||||||
const std::string& charset = "") {
|
media_type_ = ToString(media_type);
|
||||||
media_type_ = media_type;
|
charset_ = ToString(charset);
|
||||||
charset_ = charset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set content types to accept.
|
// Set content types to accept.
|
||||||
void Accept(const std::string& content_types);
|
void Accept(string_view content_types);
|
||||||
|
|
||||||
#if WEBCC_ENABLE_GZIP
|
#if WEBCC_ENABLE_GZIP
|
||||||
|
|
||||||
@@ -77,13 +76,13 @@ public:
|
|||||||
#endif // WEBCC_ENABLE_GZIP
|
#endif // WEBCC_ENABLE_GZIP
|
||||||
|
|
||||||
// Set authorization.
|
// Set authorization.
|
||||||
void Auth(const std::string& type, const std::string& credentials);
|
void Auth(string_view type, string_view credentials);
|
||||||
|
|
||||||
// Set Basic authorization.
|
// Set Basic authorization.
|
||||||
void AuthBasic(const std::string& login, const std::string& password);
|
void AuthBasic(string_view login, string_view password);
|
||||||
|
|
||||||
// Set Token authorization.
|
// Set Token authorization.
|
||||||
void AuthToken(const std::string& token);
|
void AuthToken(string_view token);
|
||||||
|
|
||||||
// Send a request.
|
// Send a request.
|
||||||
// Please use RequestBuilder to build the request.
|
// Please use RequestBuilder to build the request.
|
||||||
|
|||||||
+42
-54
@@ -12,41 +12,26 @@ namespace webcc {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
bool Headers::Set(const std::string& key, const std::string& value) {
|
bool Headers::Set(string_view key, string_view value) {
|
||||||
if (value.empty()) {
|
if (value.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = Find(key);
|
auto it = Find(key);
|
||||||
if (it != headers_.end()) {
|
if (it != headers_.end()) {
|
||||||
it->second = value;
|
it->second = ToString(value);
|
||||||
} else {
|
} else {
|
||||||
headers_.push_back({ key, value });
|
headers_.push_back({ ToString(key), ToString(value) });
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Headers::Set(std::string&& key, std::string&& value) {
|
bool Headers::Has(string_view key) const {
|
||||||
if (value.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto it = Find(key);
|
|
||||||
if (it != headers_.end()) {
|
|
||||||
it->second = std::move(value);
|
|
||||||
} else {
|
|
||||||
headers_.push_back({ std::move(key), std::move(value) });
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Headers::Has(const std::string& key) const {
|
|
||||||
return const_cast<Headers*>(this)->Find(key) != headers_.end();
|
return const_cast<Headers*>(this)->Find(key) != headers_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& Headers::Get(const std::string& key, bool* existed) const {
|
const std::string& Headers::Get(string_view key, bool* existed) const {
|
||||||
auto it = const_cast<Headers*>(this)->Find(key);
|
auto it = const_cast<Headers*>(this)->Find(key);
|
||||||
|
|
||||||
if (existed != nullptr) {
|
if (existed != nullptr) {
|
||||||
@@ -61,7 +46,7 @@ const std::string& Headers::Get(const std::string& key, bool* existed) const {
|
|||||||
return s_no_value;
|
return s_no_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Header>::iterator Headers::Find(const std::string& key) {
|
std::vector<Header>::iterator Headers::Find(string_view 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)) {
|
||||||
@@ -73,25 +58,23 @@ std::vector<Header>::iterator Headers::Find(const std::string& key) {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
static bool ParseValue(const std::string& str, const std::string& expected_key,
|
static bool ParseValue(const std::string& str, const char* expected_key,
|
||||||
std::string* value) {
|
string_view* value) {
|
||||||
std::string key;
|
string_view key;
|
||||||
if (!SplitKV(str, '=', true, &key, value)) {
|
if (!SplitKV(str, '=', true, &key, value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key != expected_key) {
|
if (key != expected_key) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !value->empty();
|
return !value->empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentType::ContentType(const std::string& str) {
|
ContentType::ContentType(string_view str) {
|
||||||
Init(str);
|
Init(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContentType::Parse(const std::string& str) {
|
void ContentType::Parse(string_view str) {
|
||||||
Reset();
|
Reset();
|
||||||
Init(str);
|
Init(str);
|
||||||
}
|
}
|
||||||
@@ -114,15 +97,15 @@ bool ContentType::Valid() const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContentType::Init(const std::string& str) {
|
void ContentType::Init(string_view str) {
|
||||||
std::string other;
|
std::string other;
|
||||||
|
|
||||||
std::size_t pos = str.find(';');
|
std::size_t pos = str.find(';');
|
||||||
if (pos == std::string::npos) {
|
if (pos == str.npos) {
|
||||||
media_type_ = str;
|
media_type_ = ToString(str);
|
||||||
} else {
|
} else {
|
||||||
media_type_ = str.substr(0, pos);
|
media_type_ = ToString(str.substr(0, pos));
|
||||||
other = str.substr(pos + 1);
|
other = ToString(str.substr(pos + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::trim(media_type_);
|
boost::trim(media_type_);
|
||||||
@@ -130,26 +113,31 @@ void ContentType::Init(const std::string& str) {
|
|||||||
|
|
||||||
if (media_type_ == "multipart/form-data") {
|
if (media_type_ == "multipart/form-data") {
|
||||||
multipart_ = true;
|
multipart_ = true;
|
||||||
if (!ParseValue(other, "boundary", &additional_)) {
|
string_view boundary;
|
||||||
LOG_ERRO("Invalid 'multipart/form-data' content-type (no boundary).");
|
if (ParseValue(other, "boundary", &boundary)) {
|
||||||
|
additional_ = ToString(boundary);
|
||||||
|
LOG_INFO("Content-type multipart boundary: %s", additional_.c_str());
|
||||||
} else {
|
} else {
|
||||||
LOG_INFO("Content-type multipart boundary: %s.", additional_.c_str());
|
LOG_ERRO("Invalid 'multipart/form-data' content-type (no boundary)");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (ParseValue(other, "charset", &additional_)) {
|
string_view charset;
|
||||||
LOG_INFO("Content-type charset: %s.", additional_.c_str());
|
if (ParseValue(other, "charset", &charset)) {
|
||||||
|
additional_ = ToString(charset);
|
||||||
|
LOG_INFO("Content-type charset: %s", additional_.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// TODO: Use string_view
|
||||||
static inline void Unquote(std::string& str) {
|
static inline void Unquote(std::string& str) {
|
||||||
boost::trim_if(str, boost::is_any_of("\""));
|
boost::trim_if(str, boost::is_any_of("\""));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContentDisposition::Init(const std::string& str) {
|
bool ContentDisposition::Init(string_view str) {
|
||||||
std::vector<boost::string_view> parts;
|
std::vector<string_view> parts;
|
||||||
Split(str, ';', false, &parts);
|
Split(str, ';', false, &parts);
|
||||||
|
|
||||||
if (parts.empty()) {
|
if (parts.empty()) {
|
||||||
@@ -160,18 +148,18 @@ bool ContentDisposition::Init(const std::string& str) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string key;
|
string_view key;
|
||||||
std::string value;
|
string_view value;
|
||||||
for (std::size_t i = 1; i < parts.size(); ++i) {
|
for (std::size_t i = 1; i < parts.size(); ++i) {
|
||||||
if (!SplitKV(parts[i].to_string(), '=', true, &key, &value)) {
|
if (!SplitKV(parts[i], '=', true, &key, &value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key == "name") {
|
if (key == "name") {
|
||||||
name_ = value;
|
name_ = ToString(value);
|
||||||
Unquote(name_);
|
Unquote(name_);
|
||||||
} else if (key == "filename") {
|
} else if (key == "filename") {
|
||||||
file_name_ = value;
|
file_name_ = ToString(value);
|
||||||
Unquote(file_name_);
|
Unquote(file_name_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,24 +169,24 @@ bool ContentDisposition::Init(const std::string& str) {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
FormPartPtr FormPart::New(const std::string& name, std::string&& data,
|
FormPartPtr FormPart::New(string_view name, std::string&& data,
|
||||||
const std::string& media_type) {
|
string_view media_type) {
|
||||||
auto form_part = std::make_shared<FormPart>();
|
auto form_part = std::make_shared<FormPart>();
|
||||||
|
|
||||||
form_part->name_ = name;
|
form_part->name_ = ToString(name);
|
||||||
form_part->data_ = std::move(data);
|
form_part->data_ = std::move(data);
|
||||||
form_part->media_type_ = media_type;
|
form_part->media_type_ = ToString(media_type);
|
||||||
|
|
||||||
return form_part;
|
return form_part;
|
||||||
}
|
}
|
||||||
|
|
||||||
FormPartPtr FormPart::NewFile(const std::string& name, const fs::path& path,
|
FormPartPtr FormPart::NewFile(string_view name, const fs::path& path,
|
||||||
const std::string& media_type) {
|
string_view media_type) {
|
||||||
auto form_part = std::make_shared<FormPart>();
|
auto form_part = std::make_shared<FormPart>();
|
||||||
|
|
||||||
form_part->name_ = name;
|
form_part->name_ = ToString(name);
|
||||||
form_part->path_ = path;
|
form_part->path_ = path;
|
||||||
form_part->media_type_ = media_type;
|
form_part->media_type_ = ToString(media_type);
|
||||||
|
|
||||||
// Determine file name from file path.
|
// Determine file name from file path.
|
||||||
// TODO: encoding
|
// TODO: encoding
|
||||||
@@ -288,7 +276,7 @@ std::size_t FormPart::GetDataSize() {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormPart::Dump(std::ostream& os, const std::string& prefix) const {
|
void FormPart::Dump(std::ostream& os, string_view prefix) const {
|
||||||
for (auto& h : headers_.data()) {
|
for (auto& h : headers_.data()) {
|
||||||
os << prefix << h.first << ": " << h.second << std::endl;
|
os << prefix << h.first << ": " << h.second << std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-16
@@ -29,11 +29,9 @@ public:
|
|||||||
return headers_;
|
return headers_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Set(const std::string& key, const std::string& value);
|
bool Set(string_view key, string_view value);
|
||||||
|
|
||||||
bool Set(std::string&& key, std::string&& value);
|
bool Has(string_view key) const;
|
||||||
|
|
||||||
bool Has(const std::string& key) const;
|
|
||||||
|
|
||||||
// Get header by index.
|
// Get header by index.
|
||||||
const Header& Get(std::size_t index) const {
|
const Header& Get(std::size_t index) const {
|
||||||
@@ -44,14 +42,14 @@ public:
|
|||||||
// Get header value by key.
|
// Get header value by key.
|
||||||
// If there's no such header with the given key, besides return empty, the
|
// If there's no such header with the given key, besides return empty, the
|
||||||
// optional |existed| parameter will be set to false.
|
// optional |existed| parameter will be set to false.
|
||||||
const std::string& Get(const std::string& key, bool* existed = nullptr) const;
|
const std::string& Get(string_view key, bool* existed = nullptr) const;
|
||||||
|
|
||||||
void Clear() {
|
void Clear() {
|
||||||
headers_.clear();
|
headers_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Header>::iterator Find(const std::string& key);
|
std::vector<Header>::iterator Find(string_view key);
|
||||||
|
|
||||||
std::vector<Header> headers_;
|
std::vector<Header> headers_;
|
||||||
};
|
};
|
||||||
@@ -64,9 +62,9 @@ private:
|
|||||||
// Content-Type: multipart/form-data; boundary=something
|
// Content-Type: multipart/form-data; boundary=something
|
||||||
class ContentType {
|
class ContentType {
|
||||||
public:
|
public:
|
||||||
explicit ContentType(const std::string& str = "");
|
explicit ContentType(string_view str = "");
|
||||||
|
|
||||||
void Parse(const std::string& str);
|
void Parse(string_view str);
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
@@ -91,7 +89,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Init(const std::string& str);
|
void Init(string_view str);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string media_type_;
|
std::string media_type_;
|
||||||
@@ -109,7 +107,7 @@ private:
|
|||||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
|
||||||
class ContentDisposition {
|
class ContentDisposition {
|
||||||
public:
|
public:
|
||||||
explicit ContentDisposition(const std::string& str) {
|
explicit ContentDisposition(string_view str) {
|
||||||
valid_ = Init(str);
|
valid_ = Init(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +124,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool Init(const std::string& str);
|
bool Init(string_view str);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string name_;
|
std::string name_;
|
||||||
@@ -151,14 +149,14 @@ public:
|
|||||||
// The data will be moved, no file name is needed.
|
// The data will be moved, no file name is needed.
|
||||||
// The media type is optional. If the data is a JSON string, you can specify
|
// The media type is optional. If the data is a JSON string, you can specify
|
||||||
// media type as "application/json".
|
// media type as "application/json".
|
||||||
static FormPartPtr New(const std::string& name, std::string&& data,
|
static FormPartPtr New(string_view name, std::string&& data,
|
||||||
const std::string& media_type = "");
|
string_view media_type = "");
|
||||||
|
|
||||||
// Construct a file part.
|
// Construct a file part.
|
||||||
// The file name will be extracted from path.
|
// The file name will be extracted from path.
|
||||||
// The media type, if not provided, will be inferred from file extension.
|
// The media type, if not provided, will be inferred from file extension.
|
||||||
static FormPartPtr NewFile(const std::string& name, const fs::path& path,
|
static FormPartPtr NewFile(string_view name, const fs::path& path,
|
||||||
const std::string& media_type = "");
|
string_view media_type = "");
|
||||||
|
|
||||||
// API: SERVER
|
// API: SERVER
|
||||||
const std::string& name() const {
|
const std::string& name() const {
|
||||||
@@ -214,7 +212,7 @@ public:
|
|||||||
std::size_t GetDataSize();
|
std::size_t GetDataSize();
|
||||||
|
|
||||||
// Dump to output stream for logging purpose.
|
// Dump to output stream for logging purpose.
|
||||||
void Dump(std::ostream& os, const std::string& prefix) const;
|
void Dump(std::ostream& os, string_view prefix) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Generate headers from properties.
|
// Generate headers from properties.
|
||||||
|
|||||||
@@ -17,7 +17,10 @@
|
|||||||
// Set 1/0 to enable/disable GZIP compression.
|
// Set 1/0 to enable/disable GZIP compression.
|
||||||
#define WEBCC_ENABLE_GZIP 0
|
#define WEBCC_ENABLE_GZIP 0
|
||||||
|
|
||||||
// Set 1 to use C++17 filesystem or 0 to use Boost filesystem.
|
// Set 1 to use std::filesystem or 0 to use boost::filesystem.
|
||||||
#define WEBCC_USE_STD_FILESYSTEM 1
|
#define WEBCC_USE_STD_FILESYSTEM 1
|
||||||
|
|
||||||
|
// Set 1 to use std::string_view or 0 to use boost::string_view.
|
||||||
|
#define WEBCC_USE_STD_STRING_VIEW 1
|
||||||
|
|
||||||
#endif // WEBCC_CONFIG_H_
|
#endif // WEBCC_CONFIG_H_
|
||||||
|
|||||||
+4
-1
@@ -19,7 +19,10 @@
|
|||||||
// Set 1/0 to enable/disable GZIP compression.
|
// Set 1/0 to enable/disable GZIP compression.
|
||||||
#define WEBCC_ENABLE_GZIP @WEBCC_ENABLE_GZIP@
|
#define WEBCC_ENABLE_GZIP @WEBCC_ENABLE_GZIP@
|
||||||
|
|
||||||
// Set 1 to use C++17 filesystem or 0 to use Boost filesystem.
|
// Set 1 to use std::filesystem or 0 to use boost::filesystem.
|
||||||
#define WEBCC_USE_STD_FILESYSTEM @WEBCC_USE_STD_FILESYSTEM@
|
#define WEBCC_USE_STD_FILESYSTEM @WEBCC_USE_STD_FILESYSTEM@
|
||||||
|
|
||||||
|
// Set 1 to use std::string_view or 0 to use boost::string_view.
|
||||||
|
#define WEBCC_USE_STD_STRING_VIEW @WEBCC_USE_STD_STRING_VIEW@
|
||||||
|
|
||||||
#endif // WEBCC_CONFIG_H_
|
#endif // WEBCC_CONFIG_H_
|
||||||
|
|||||||
+2
-1
@@ -24,10 +24,11 @@ void Connection::Start() {
|
|||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
auto endpoint = socket_.remote_endpoint(ec);
|
auto endpoint = socket_.remote_endpoint(ec);
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
request_->set_ip(endpoint.address().to_string());
|
request_->set_address(endpoint.address().to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
request_parser_.Init(request_.get(), view_matcher_);
|
request_parser_.Init(request_.get(), view_matcher_);
|
||||||
|
|
||||||
AsyncRead();
|
AsyncRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+25
-3
@@ -12,10 +12,32 @@
|
|||||||
|
|
||||||
#include "webcc/config.h"
|
#include "webcc/config.h"
|
||||||
|
|
||||||
|
#if WEBCC_USE_STD_STRING_VIEW
|
||||||
|
#include <string_view>
|
||||||
|
#else
|
||||||
|
#include "boost/utility/string_view.hpp"
|
||||||
|
#endif // WEBCC_USE_STD_STRING_VIEW
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if WEBCC_USE_STD_STRING_VIEW
|
||||||
|
using string_view = std::string_view;
|
||||||
|
#else
|
||||||
|
using string_view = boost::string_view;
|
||||||
|
#endif // WEBCC_USE_STD_STRING_VIEW
|
||||||
|
|
||||||
|
inline std::string ToString(string_view sv) {
|
||||||
|
#if WEBCC_USE_STD_STRING_VIEW
|
||||||
|
return std::string{ sv.begin(), sv.end() };
|
||||||
|
#else
|
||||||
|
return sv.to_string();
|
||||||
|
#endif // WEBCC_USE_STD_STRING_VIEW
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
using Strings = std::vector<std::string>;
|
using Strings = std::vector<std::string>;
|
||||||
|
|
||||||
// Regex sub-matches of the URL (usually resource ID's).
|
// Regex sub-matches of the URL (usually resource ID's).
|
||||||
@@ -169,7 +191,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Error(Code code = kOK, const std::string& message = "")
|
explicit Error(Code code = kOK, string_view message = "")
|
||||||
: code_(code), message_(message) {
|
: code_(code), message_(message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,9 +208,9 @@ public:
|
|||||||
return message_;
|
return message_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Set(Code code, const std::string& message) {
|
void Set(Code code, string_view message) {
|
||||||
code_ = code;
|
code_ = code;
|
||||||
message_ = message;
|
message_ = ToString(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool timeout() const {
|
bool timeout() const {
|
||||||
|
|||||||
+2
-2
@@ -135,7 +135,7 @@ static const bool g_terminal_has_color = []() {
|
|||||||
static std::string DoGetThreadID() {
|
static std::string DoGetThreadID() {
|
||||||
#if (defined(_WIN32) || defined(_WIN64))
|
#if (defined(_WIN32) || defined(_WIN64))
|
||||||
auto thread_id = std::this_thread::get_id();
|
auto thread_id = std::this_thread::get_id();
|
||||||
std::stringstream ss;
|
std::ostringstream ss;
|
||||||
ss << thread_id;
|
ss << thread_id;
|
||||||
return ss.str();
|
return ss.str();
|
||||||
#else
|
#else
|
||||||
@@ -184,7 +184,7 @@ static std::string GetTimestamp() {
|
|||||||
auto now = system_clock::now();
|
auto now = system_clock::now();
|
||||||
std::time_t t = system_clock::to_time_t(now);
|
std::time_t t = system_clock::to_time_t(now);
|
||||||
|
|
||||||
std::stringstream ss;
|
std::ostringstream 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");
|
||||||
|
|
||||||
milliseconds milli_seconds =
|
milliseconds milli_seconds =
|
||||||
|
|||||||
+20
-31
@@ -31,6 +31,7 @@ void Message::SetBody(BodyPtr body, bool set_length) {
|
|||||||
|
|
||||||
const std::string& Message::data() const {
|
const std::string& Message::data() const {
|
||||||
static const std::string kEmptyData;
|
static const std::string kEmptyData;
|
||||||
|
|
||||||
auto string_body = std::dynamic_pointer_cast<StringBody>(body_);
|
auto string_body = std::dynamic_pointer_cast<StringBody>(body_);
|
||||||
if (!string_body) {
|
if (!string_body) {
|
||||||
return kEmptyData;
|
return kEmptyData;
|
||||||
@@ -43,54 +44,42 @@ std::shared_ptr<FileBody> Message::file_body() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Message::IsConnectionKeepAlive() const {
|
bool Message::IsConnectionKeepAlive() const {
|
||||||
using headers::kConnection;
|
|
||||||
|
|
||||||
bool existed = false;
|
bool existed = false;
|
||||||
const std::string& connection = GetHeader(kConnection, &existed);
|
const auto& connection = GetHeader(headers::kConnection, &existed);
|
||||||
|
|
||||||
if (!existed) {
|
if (!existed) {
|
||||||
// Keep-Alive is by default for HTTP/1.1.
|
// Keep-Alive is by default for HTTP/1.1.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (boost::iequals(connection, "Keep-Alive")) {
|
return boost::iequals(connection, "Keep-Alive");
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentEncoding Message::GetContentEncoding() const {
|
ContentEncoding Message::GetContentEncoding() const {
|
||||||
using headers::kContentEncoding;
|
const auto& encoding = GetHeader(headers::kContentEncoding);
|
||||||
|
|
||||||
const std::string& encoding = GetHeader(kContentEncoding);
|
|
||||||
|
|
||||||
if (encoding == "gzip") {
|
if (encoding == "gzip") {
|
||||||
return ContentEncoding::kGzip;
|
return ContentEncoding::kGzip;
|
||||||
}
|
} else if (encoding == "deflate") {
|
||||||
|
|
||||||
if (encoding == "deflate") {
|
|
||||||
return ContentEncoding::kDeflate;
|
return ContentEncoding::kDeflate;
|
||||||
}
|
} else {
|
||||||
|
|
||||||
return ContentEncoding::kUnknown;
|
return ContentEncoding::kUnknown;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Message::AcceptEncodingGzip() const {
|
bool Message::AcceptEncodingGzip() const {
|
||||||
using headers::kAcceptEncoding;
|
return GetHeader(headers::kAcceptEncoding).find("gzip") != std::string::npos;
|
||||||
|
|
||||||
return GetHeader(kAcceptEncoding).find("gzip") != std::string::npos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Message::SetContentType(const std::string& media_type,
|
void Message::SetContentType(string_view media_type, string_view charset) {
|
||||||
const std::string& charset) {
|
|
||||||
using headers::kContentType;
|
|
||||||
|
|
||||||
if (!media_type.empty()) {
|
if (!media_type.empty()) {
|
||||||
if (charset.empty()) {
|
if (charset.empty()) {
|
||||||
SetHeader(kContentType, media_type);
|
SetHeader(headers::kContentType, media_type);
|
||||||
} else {
|
} else {
|
||||||
SetHeader(kContentType, media_type + "; charset=" + charset);
|
std::string value = ToString(media_type);
|
||||||
|
value += "; charset=";
|
||||||
|
value += ToString(charset);
|
||||||
|
SetHeader(headers::kContentType, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,21 +105,21 @@ Payload Message::GetPayload() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Message::Dump(std::ostream& os) const {
|
void Message::Dump(std::ostream& os) const {
|
||||||
const std::string prefix = " > ";
|
static const char* const s_prefix = " > ";
|
||||||
|
|
||||||
os << prefix << start_line_ << std::endl;
|
os << s_prefix << start_line_ << std::endl;
|
||||||
|
|
||||||
for (const Header& h : headers_.data()) {
|
for (const Header& h : headers_.data()) {
|
||||||
os << prefix << h.first << ": " << h.second << std::endl;
|
os << s_prefix << h.first << ": " << h.second << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
os << prefix << std::endl;
|
os << s_prefix << std::endl;
|
||||||
|
|
||||||
body_->Dump(os, prefix);
|
body_->Dump(os, s_prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Message::Dump() const {
|
std::string Message::Dump() const {
|
||||||
std::stringstream ss;
|
std::ostringstream ss;
|
||||||
Dump(ss);
|
Dump(ss);
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-47
@@ -21,7 +21,37 @@ public:
|
|||||||
|
|
||||||
virtual ~Message() = default;
|
virtual ~Message() = default;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
const std::string& start_line() const {
|
||||||
|
return start_line_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_start_line(string_view start_line) {
|
||||||
|
start_line_ = ToString(start_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetHeader(Header&& header) {
|
||||||
|
headers_.Set(std::move(header.first), std::move(header.second));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetHeader(string_view key, string_view value) {
|
||||||
|
headers_.Set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& GetHeader(string_view key, bool* existed = nullptr) const {
|
||||||
|
return headers_.Get(key, existed);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasHeader(string_view key) const {
|
||||||
|
return headers_.Has(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t content_length() const {
|
||||||
|
return content_length_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_content_length(std::size_t content_length) {
|
||||||
|
content_length_ = content_length;
|
||||||
|
}
|
||||||
|
|
||||||
void SetBody(BodyPtr body, bool set_length);
|
void SetBody(BodyPtr body, bool set_length);
|
||||||
|
|
||||||
@@ -37,45 +67,6 @@ public:
|
|||||||
// Return null if the body is not a FileBody.
|
// Return null if the body is not a FileBody.
|
||||||
std::shared_ptr<FileBody> file_body() const;
|
std::shared_ptr<FileBody> file_body() const;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void SetHeader(Header&& header) {
|
|
||||||
headers_.Set(std::move(header.first), std::move(header.second));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetHeader(const std::string& key, const std::string& value) {
|
|
||||||
headers_.Set(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& GetHeader(const std::string& key,
|
|
||||||
bool* existed = nullptr) const {
|
|
||||||
return headers_.Get(key, existed);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasHeader(const std::string& key) const {
|
|
||||||
return headers_.Has(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const std::string& start_line() const {
|
|
||||||
return start_line_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_start_line(const std::string& start_line) {
|
|
||||||
start_line_ = start_line;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t content_length() const {
|
|
||||||
return content_length_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_content_length(std::size_t content_length) {
|
|
||||||
content_length_ = content_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Check `Connection` header to see if it's "Keep-Alive".
|
// Check `Connection` header to see if it's "Keep-Alive".
|
||||||
bool IsConnectionKeepAlive() const;
|
bool IsConnectionKeepAlive() const;
|
||||||
|
|
||||||
@@ -88,16 +79,13 @@ public:
|
|||||||
|
|
||||||
// Set `Content-Type` header. E.g.,
|
// Set `Content-Type` header. E.g.,
|
||||||
// SetContentType("application/json; charset=utf-8")
|
// SetContentType("application/json; charset=utf-8")
|
||||||
void SetContentType(const std::string& content_type) {
|
void SetContentType(string_view content_type) {
|
||||||
SetHeader(headers::kContentType, content_type);
|
SetHeader(headers::kContentType, content_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set `Content-Type` header. E.g.,
|
// Set `Content-Type` header. E.g.,
|
||||||
// SetContentType("application/json", "utf-8")
|
// SetContentType("application/json", "utf-8")
|
||||||
void SetContentType(const std::string& media_type,
|
void SetContentType(string_view media_type, string_view charset);
|
||||||
const std::string& charset);
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Make the message complete in order to be sent.
|
// Make the message complete in order to be sent.
|
||||||
virtual void Prepare() = 0;
|
virtual void Prepare() = 0;
|
||||||
@@ -106,8 +94,6 @@ public:
|
|||||||
// This doesn't include the payload(s) of the body!
|
// This doesn't include the payload(s) of the body!
|
||||||
Payload GetPayload() const;
|
Payload GetPayload() const;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Dump to output stream for logging purpose.
|
// Dump to output stream for logging purpose.
|
||||||
void Dump(std::ostream& os) const;
|
void Dump(std::ostream& os) const;
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -23,8 +23,8 @@ public:
|
|||||||
return method_;
|
return method_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_method(const std::string& method) {
|
void set_method(string_view method) {
|
||||||
method_ = method;
|
method_ = ToString(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Url& url() const {
|
const Url& url() const {
|
||||||
@@ -44,23 +44,23 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
UrlQuery query() const {
|
UrlQuery query() const {
|
||||||
return UrlQuery(url_.query());
|
return UrlQuery{ url_.query() };
|
||||||
}
|
}
|
||||||
|
|
||||||
const UrlArgs& args() const {
|
const UrlArgs& args() const {
|
||||||
return args_;
|
return args_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_args(const UrlArgs& args) {
|
void set_args(UrlArgs&& args) {
|
||||||
args_ = args;
|
args_ = std::move(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& ip() const {
|
const std::string& address() const {
|
||||||
return ip_;
|
return address_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_ip(const std::string& ip) {
|
void set_address(std::string&& address) {
|
||||||
ip_ = ip;
|
address_ = std::move(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the body is a multi-part form data.
|
// Check if the body is a multi-part form data.
|
||||||
@@ -83,7 +83,7 @@ private:
|
|||||||
UrlArgs args_;
|
UrlArgs args_;
|
||||||
|
|
||||||
// Client IP address.
|
// Client IP address.
|
||||||
std::string ip_;
|
std::string address_;
|
||||||
};
|
};
|
||||||
|
|
||||||
using RequestPtr = std::shared_ptr<Request>;
|
using RequestPtr = std::shared_ptr<Request>;
|
||||||
|
|||||||
+17
-17
@@ -19,7 +19,7 @@ RequestPtr RequestBuilder::operator()() {
|
|||||||
request->set_url(std::move(url_));
|
request->set_url(std::move(url_));
|
||||||
|
|
||||||
for (std::size_t i = 1; i < headers_.size(); i += 2) {
|
for (std::size_t i = 1; i < headers_.size(); i += 2) {
|
||||||
request->SetHeader(std::move(headers_[i - 1]), std::move(headers_[i]));
|
request->SetHeader(headers_[i - 1], headers_[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no Keep-Alive, explicitly set `Connection` to "Close".
|
// If no Keep-Alive, explicitly set `Connection` to "Close".
|
||||||
@@ -34,7 +34,7 @@ RequestPtr RequestBuilder::operator()() {
|
|||||||
if (gzip_ && body_->Compress()) {
|
if (gzip_ && body_->Compress()) {
|
||||||
request->SetHeader(headers::kContentEncoding, "gzip");
|
request->SetHeader(headers::kContentEncoding, "gzip");
|
||||||
}
|
}
|
||||||
#endif
|
#endif // WEBCC_ENABLE_GZIP
|
||||||
} else if (!form_parts_.empty()) {
|
} else if (!form_parts_.empty()) {
|
||||||
// Another choice to generate the boundary is like what Apache does.
|
// Another choice to generate the boundary is like what Apache does.
|
||||||
// See: https://stackoverflow.com/a/5686863
|
// See: https://stackoverflow.com/a/5686863
|
||||||
@@ -77,41 +77,41 @@ RequestBuilder& RequestBuilder::File(const fs::path& path,
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& RequestBuilder::FormFile(const std::string& name,
|
RequestBuilder& RequestBuilder::FormFile(string_view name,
|
||||||
const fs::path& path,
|
const fs::path& path,
|
||||||
const std::string& media_type) {
|
string_view media_type) {
|
||||||
assert(!name.empty());
|
assert(!name.empty());
|
||||||
return Form(FormPart::NewFile(name, path, media_type));
|
return Form(FormPart::NewFile(name, path, media_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& RequestBuilder::FormData(const std::string& name,
|
RequestBuilder& RequestBuilder::FormData(string_view name,
|
||||||
std::string&& data,
|
std::string&& data,
|
||||||
const std::string& media_type) {
|
string_view media_type) {
|
||||||
assert(!name.empty());
|
assert(!name.empty());
|
||||||
return Form(FormPart::New(name, std::move(data), media_type));
|
return Form(FormPart::New(name, std::move(data), media_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& RequestBuilder::Header(const std::string& key,
|
RequestBuilder& RequestBuilder::Header(string_view key, string_view value) {
|
||||||
const std::string& value) {
|
headers_.push_back(ToString(key));
|
||||||
headers_.push_back(key);
|
headers_.push_back(ToString(value));
|
||||||
headers_.push_back(value);
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& RequestBuilder::Auth(const std::string& type,
|
RequestBuilder& RequestBuilder::Auth(string_view type,
|
||||||
const std::string& credentials) {
|
string_view credentials) {
|
||||||
headers_.push_back(headers::kAuthorization);
|
headers_.push_back(headers::kAuthorization);
|
||||||
headers_.push_back(type + " " + credentials);
|
headers_.push_back(ToString(type) + " " + ToString(credentials));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& RequestBuilder::AuthBasic(const std::string& login,
|
RequestBuilder& RequestBuilder::AuthBasic(string_view login,
|
||||||
const std::string& password) {
|
string_view password) {
|
||||||
auto credentials = Base64Encode(login + ":" + password);
|
auto credentials =
|
||||||
|
Base64Encode(ToString(login) + ":" + ToString(password));
|
||||||
return Auth("Basic", credentials);
|
return Auth("Basic", credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& RequestBuilder::AuthToken(const std::string& token) {
|
RequestBuilder& RequestBuilder::AuthToken(string_view token) {
|
||||||
return Auth("Token", token);
|
return Auth("Token", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+26
-27
@@ -39,44 +39,44 @@ public:
|
|||||||
RequestBuilder(const RequestBuilder&) = delete;
|
RequestBuilder(const RequestBuilder&) = delete;
|
||||||
RequestBuilder& operator=(const RequestBuilder&) = delete;
|
RequestBuilder& operator=(const RequestBuilder&) = delete;
|
||||||
|
|
||||||
// Build
|
// Build and return the request object.
|
||||||
RequestPtr operator()();
|
RequestPtr operator()();
|
||||||
|
|
||||||
RequestBuilder& Method(const std::string& method) {
|
RequestBuilder& Method(string_view method) {
|
||||||
method_ = method;
|
method_ = ToString(method);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& Get(const std::string& url, bool encode = false) {
|
RequestBuilder& Get(string_view url, bool encode = false) {
|
||||||
return Method(methods::kGet).Url(url, encode);
|
return Method(methods::kGet).Url(url, encode);
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& Head(const std::string& url, bool encode = false) {
|
RequestBuilder& Head(string_view url, bool encode = false) {
|
||||||
return Method(methods::kHead).Url(url, encode);
|
return Method(methods::kHead).Url(url, encode);
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& Post(const std::string& url, bool encode = false) {
|
RequestBuilder& Post(string_view url, bool encode = false) {
|
||||||
return Method(methods::kPost).Url(url, encode);
|
return Method(methods::kPost).Url(url, encode);
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& Put(const std::string& url, bool encode = false) {
|
RequestBuilder& Put(string_view url, bool encode = false) {
|
||||||
return Method(methods::kPut).Url(url, encode);
|
return Method(methods::kPut).Url(url, encode);
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& Delete(const std::string& url, bool encode = false) {
|
RequestBuilder& Delete(string_view url, bool encode = false) {
|
||||||
return Method(methods::kDelete).Url(url, encode);
|
return Method(methods::kDelete).Url(url, encode);
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& Patch(const std::string& url, bool encode = false) {
|
RequestBuilder& Patch(string_view url, bool encode = false) {
|
||||||
return Method(methods::kPatch).Url(url, encode);
|
return Method(methods::kPatch).Url(url, encode);
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& Url(const std::string& url, bool encode = false) {
|
RequestBuilder& Url(string_view url, bool encode = false) {
|
||||||
url_ = webcc::Url{ url, encode };
|
url_ = webcc::Url{ url, encode };
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& Port(const std::string& port) {
|
RequestBuilder& Port(string_view port) {
|
||||||
url_.set_port(port);
|
url_.set_port(port);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -87,25 +87,25 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Append a piece to the path.
|
// Append a piece to the path.
|
||||||
RequestBuilder& Path(const std::string& path, bool encode = false) {
|
RequestBuilder& Path(string_view path, bool encode = false) {
|
||||||
url_.AppendPath(path, encode);
|
url_.AppendPath(path, encode);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append a parameter to the query.
|
// Append a parameter to the query.
|
||||||
RequestBuilder& Query(const std::string& key, const std::string& value,
|
RequestBuilder& Query(string_view key, string_view value,
|
||||||
bool encode = false) {
|
bool encode = false) {
|
||||||
url_.AppendQuery(key, value, encode);
|
url_.AppendQuery(key, value, encode);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& MediaType(const std::string& media_type) {
|
RequestBuilder& MediaType(string_view media_type) {
|
||||||
media_type_ = media_type;
|
media_type_ = ToString(media_type);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& Charset(const std::string& charset) {
|
RequestBuilder& Charset(string_view charset) {
|
||||||
charset_ = charset;
|
charset_ = ToString(charset);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ public:
|
|||||||
|
|
||||||
// Set (comma separated) content types to accept.
|
// Set (comma separated) content types to accept.
|
||||||
// E.g., "application/json", "text/html, application/xhtml+xml".
|
// E.g., "application/json", "text/html, application/xhtml+xml".
|
||||||
RequestBuilder& Accept(const std::string& content_types) {
|
RequestBuilder& Accept(string_view content_types) {
|
||||||
return Header(headers::kAccept, content_types);
|
return Header(headers::kAccept, content_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,26 +156,25 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add a form part of file.
|
// Add a form part of file.
|
||||||
RequestBuilder& FormFile(const std::string& name, const fs::path& path,
|
RequestBuilder& FormFile(string_view name, const fs::path& path,
|
||||||
const std::string& media_type = "");
|
string_view media_type = "");
|
||||||
|
|
||||||
// Add a form part of string data.
|
// Add a form part of string data.
|
||||||
RequestBuilder& FormData(const std::string& name, std::string&& data,
|
RequestBuilder& FormData(string_view name, std::string&& data,
|
||||||
const std::string& media_type = "");
|
string_view media_type = "");
|
||||||
|
|
||||||
RequestBuilder& Header(const std::string& key, const std::string& value);
|
RequestBuilder& Header(string_view key, string_view value);
|
||||||
|
|
||||||
RequestBuilder& KeepAlive(bool keep_alive = true) {
|
RequestBuilder& KeepAlive(bool keep_alive = true) {
|
||||||
keep_alive_ = keep_alive;
|
keep_alive_ = keep_alive;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestBuilder& Auth(const std::string& type, const std::string& credentials);
|
RequestBuilder& Auth(string_view type, string_view credentials);
|
||||||
|
|
||||||
RequestBuilder& AuthBasic(const std::string& login,
|
RequestBuilder& AuthBasic(string_view login, string_view password);
|
||||||
const std::string& password);
|
|
||||||
|
|
||||||
RequestBuilder& AuthToken(const std::string& token);
|
RequestBuilder& AuthToken(string_view token);
|
||||||
|
|
||||||
// Add the `Date` header to the request.
|
// Add the `Date` header to the request.
|
||||||
RequestBuilder& Date();
|
RequestBuilder& Date();
|
||||||
|
|||||||
@@ -36,15 +36,15 @@ bool RequestParser::OnHeadersEnd() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool RequestParser::ParseStartLine(const std::string& line) {
|
bool RequestParser::ParseStartLine(const std::string& line) {
|
||||||
std::vector<boost::string_view> parts;
|
std::vector<string_view> parts;
|
||||||
Split(line, ' ', true, &parts);
|
Split(line, ' ', true, &parts);
|
||||||
|
|
||||||
if (parts.size() != 3) {
|
if (parts.size() != 3) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
request_->set_method(parts[0].to_string());
|
request_->set_method(parts[0]);
|
||||||
request_->set_url(Url{ parts[1].to_string() });
|
request_->set_url(Url{ parts[1] });
|
||||||
|
|
||||||
// HTTP version is ignored.
|
// HTTP version is ignored.
|
||||||
|
|
||||||
@@ -198,7 +198,7 @@ bool RequestParser::ParsePartHeaders(bool* need_more_data) {
|
|||||||
|
|
||||||
// Parse Content-Disposition.
|
// Parse Content-Disposition.
|
||||||
if (boost::iequals(header.first, 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",
|
||||||
header.second.c_str());
|
header.second.c_str());
|
||||||
|
|||||||
@@ -42,21 +42,22 @@ private:
|
|||||||
std::size_t count, bool* end = nullptr) const;
|
std::size_t count, bool* end = nullptr) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Request* request_;
|
// The result request message.
|
||||||
|
Request* request_ = nullptr;
|
||||||
|
|
||||||
// A function for matching view once the headers of a request has been
|
// A function for matching view once the headers of a request has been
|
||||||
// received. The parsing will stop and fail if no view can be matched.
|
// received. The parsing will stop and fail if no view can be matched.
|
||||||
ViewMatcher view_matcher_;
|
ViewMatcher view_matcher_;
|
||||||
|
|
||||||
// Form data parsing steps.
|
// Form data parsing steps.
|
||||||
enum Step {
|
enum class Step {
|
||||||
kStart,
|
kStart,
|
||||||
kBoundaryParsed,
|
kBoundaryParsed,
|
||||||
kHeadersParsed,
|
kHeadersParsed,
|
||||||
kEnded,
|
kEnded,
|
||||||
};
|
};
|
||||||
|
|
||||||
Step step_ = kStart;
|
Step step_ = Step::kStart;
|
||||||
|
|
||||||
// The current form part being parsed.
|
// The current form part being parsed.
|
||||||
FormPartPtr part_;
|
FormPartPtr part_;
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ ResponseBuilder& ResponseBuilder::File(const fs::path& path,
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResponseBuilder& ResponseBuilder::Header(string_view key, string_view value) {
|
||||||
|
headers_.push_back(ToString(key));
|
||||||
|
headers_.push_back(ToString(value));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
ResponseBuilder& ResponseBuilder::Date() {
|
ResponseBuilder& ResponseBuilder::Date() {
|
||||||
headers_.push_back(headers::kDate);
|
headers_.push_back(headers::kDate);
|
||||||
headers_.push_back(utility::HttpDate());
|
headers_.push_back(utility::HttpDate());
|
||||||
|
|||||||
@@ -61,13 +61,13 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResponseBuilder& MediaType(const std::string& media_type) {
|
ResponseBuilder& MediaType(string_view media_type) {
|
||||||
media_type_ = media_type;
|
media_type_ = ToString(media_type);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResponseBuilder& Charset(const std::string& charset) {
|
ResponseBuilder& Charset(string_view charset) {
|
||||||
charset_ = charset;
|
charset_ = ToString(charset);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,11 +98,7 @@ public:
|
|||||||
ResponseBuilder& File(const fs::path& path, bool infer_media_type = true,
|
ResponseBuilder& File(const fs::path& path, bool infer_media_type = true,
|
||||||
std::size_t chunk_size = 1024);
|
std::size_t chunk_size = 1024);
|
||||||
|
|
||||||
ResponseBuilder& Header(const std::string& key, const std::string& value) {
|
ResponseBuilder& Header(string_view key, string_view value);
|
||||||
headers_.push_back(key);
|
|
||||||
headers_.push_back(value);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the `Date` header to the response.
|
// Add the `Date` header to the response.
|
||||||
ResponseBuilder& Date();
|
ResponseBuilder& Date();
|
||||||
|
|||||||
+2
-4
@@ -8,13 +8,12 @@
|
|||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
bool Router::Route(const std::string& url, ViewPtr view,
|
bool Router::Route(string_view url, ViewPtr view, const Strings& methods) {
|
||||||
const Strings& methods) {
|
|
||||||
assert(view);
|
assert(view);
|
||||||
|
|
||||||
// TODO: More error check
|
// TODO: More error check
|
||||||
|
|
||||||
routes_.push_back({ url, {}, view, methods });
|
routes_.push_back({ ToString(url), {}, view, methods });
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -26,7 +25,6 @@ bool Router::Route(const UrlRegex& regex_url, ViewPtr view,
|
|||||||
// TODO: More error check
|
// TODO: More error check
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
routes_.push_back({ "", regex_url(), view, methods });
|
routes_.push_back({ "", regex_url(), view, methods });
|
||||||
|
|
||||||
} catch (const std::regex_error& e) {
|
} catch (const std::regex_error& e) {
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ public:
|
|||||||
|
|
||||||
// Route a URL to a view.
|
// Route a URL to a view.
|
||||||
// The URL should start with "/". E.g., "/instances".
|
// The URL should start with "/". E.g., "/instances".
|
||||||
bool Route(const std::string& url, ViewPtr view,
|
bool Route(string_view url, ViewPtr view,
|
||||||
const Strings& methods = { "GET" });
|
const Strings& methods = { "GET" });
|
||||||
|
|
||||||
// Route a URL (as regular expression) to a view.
|
// Route a URL (as regular expression) to a view.
|
||||||
|
|||||||
+1
-1
@@ -300,7 +300,7 @@ void Server::Handle(ConnectionPtr connection) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save the (regex matched) URL args to request object.
|
// Save the (regex matched) URL args to request object.
|
||||||
request->set_args(args);
|
request->set_args(std::move(args));
|
||||||
|
|
||||||
// Ask the matched view to process the request.
|
// Ask the matched view to process the request.
|
||||||
ResponsePtr response = view->Handle(request);
|
ResponsePtr response = view->Handle(request);
|
||||||
|
|||||||
+30
-9
@@ -36,19 +36,19 @@ bool ToSizeT(const std::string& str, int base, std::size_t* size) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Split(boost::string_view input, char delim, bool compress_token,
|
void Split(string_view input, char delim, bool compress_token,
|
||||||
std::vector<boost::string_view>* output) {
|
std::vector<string_view>* output) {
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
std::size_t p = 0;
|
std::size_t p = 0;
|
||||||
|
|
||||||
i = input.find(delim);
|
i = input.find(delim);
|
||||||
|
|
||||||
while (i != boost::string_view::npos) {
|
while (i != string_view::npos) {
|
||||||
output->emplace_back(input.substr(p, i - p));
|
output->emplace_back(input.substr(p, i - p));
|
||||||
p = i + 1;
|
p = i + 1;
|
||||||
|
|
||||||
if (compress_token) {
|
if (compress_token) {
|
||||||
while (input[p] == delim) {
|
while (p < input.size() && input[p] == delim) {
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,10 +59,19 @@ void Split(boost::string_view input, char delim, bool compress_token,
|
|||||||
output->emplace_back(input.substr(p, i - p));
|
output->emplace_back(input.substr(p, i - p));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SplitKV(const std::string& input, char delim, bool trim_spaces,
|
void Trim(string_view& sv, const char* spaces) {
|
||||||
std::string* key, std::string* value) {
|
sv.remove_prefix(std::min(sv.find_first_not_of(spaces), sv.size()));
|
||||||
|
|
||||||
|
std::size_t pos = sv.find_last_not_of(spaces);
|
||||||
|
if (pos != sv.npos) {
|
||||||
|
sv.remove_suffix(sv.size() - pos - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SplitKV(string_view input, char delim, bool trim_spaces, string_view* key,
|
||||||
|
string_view* value) {
|
||||||
std::size_t pos = input.find(delim);
|
std::size_t pos = input.find(delim);
|
||||||
if (pos == std::string::npos) {
|
if (pos == input.npos) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,11 +79,23 @@ bool SplitKV(const std::string& input, char delim, bool trim_spaces,
|
|||||||
*value = input.substr(pos + 1);
|
*value = input.substr(pos + 1);
|
||||||
|
|
||||||
if (trim_spaces) {
|
if (trim_spaces) {
|
||||||
boost::trim(*key);
|
Trim(*key);
|
||||||
boost::trim(*value);
|
Trim(*value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SplitKV(string_view input, char delim, bool trim_spaces, std::string* key,
|
||||||
|
std::string* value) {
|
||||||
|
string_view key_view;
|
||||||
|
string_view value_view;
|
||||||
|
if (SplitKV(input, delim, trim_spaces, &key_view, &value_view)) {
|
||||||
|
*key = ToString(key_view);
|
||||||
|
*value = ToString(value_view);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
+11
-5
@@ -4,7 +4,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "boost/utility/string_view.hpp"
|
#include "webcc/globals.h" // for string_view
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
@@ -15,15 +15,21 @@ std::string RandomString(std::size_t length);
|
|||||||
// Just a wrapper of std::stoul.
|
// Just a wrapper of std::stoul.
|
||||||
bool ToSizeT(const std::string& str, int base, std::size_t* size);
|
bool ToSizeT(const std::string& str, int base, std::size_t* size);
|
||||||
|
|
||||||
|
void Trim(string_view& sv, const char* spaces = " ");
|
||||||
|
|
||||||
// Split string without copy.
|
// Split string without copy.
|
||||||
// |compress_token| is the same as boost::token_compress_on for boost::split.
|
// |compress_token| is the same as boost::token_compress_on for boost::split.
|
||||||
void Split(boost::string_view input, char delim, bool compress_token,
|
void Split(string_view input, char delim, bool compress_token,
|
||||||
std::vector<boost::string_view>* output);
|
std::vector<string_view>* output);
|
||||||
|
|
||||||
// Split a key-value string.
|
// Split a key-value string.
|
||||||
// E.g., split "Connection: Keep-Alive".
|
// E.g., split "Connection: Keep-Alive".
|
||||||
// TODO: Use string_view (blocked by trim)
|
bool SplitKV(string_view input, char delim, bool trim_spaces, string_view* key,
|
||||||
bool SplitKV(const std::string& input, char delim, bool trim_spaces,
|
string_view* value);
|
||||||
|
|
||||||
|
// Split a key-value string.
|
||||||
|
// E.g., split "Connection: Keep-Alive".
|
||||||
|
bool SplitKV(string_view input, char delim, bool trim_spaces,
|
||||||
std::string* key, std::string* value);
|
std::string* key, std::string* value);
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
+21
-20
@@ -29,7 +29,7 @@ bool HexToDecimal(char hex, int* decimal) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Decode(const std::string& encoded, std::string* raw) {
|
bool Decode(string_view encoded, std::string* raw) {
|
||||||
for (auto iter = encoded.begin(); iter != encoded.end(); ++iter) {
|
for (auto iter = encoded.begin(); iter != encoded.end(); ++iter) {
|
||||||
if (*iter == '%') {
|
if (*iter == '%') {
|
||||||
if (++iter == encoded.end()) {
|
if (++iter == encoded.end()) {
|
||||||
@@ -67,16 +67,16 @@ bool Decode(const std::string& encoded, std::string* raw) {
|
|||||||
|
|
||||||
// Unsafe decode.
|
// Unsafe decode.
|
||||||
// Return the original string on failure.
|
// Return the original string on failure.
|
||||||
std::string DecodeUnsafe(const std::string& encoded) {
|
std::string DecodeUnsafe(string_view encoded) {
|
||||||
std::string raw;
|
std::string raw;
|
||||||
if (Decode(encoded, &raw)) {
|
if (Decode(encoded, &raw)) {
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
return encoded;
|
return ToString(encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode all characters which should be encoded.
|
// Encode all characters which should be encoded.
|
||||||
std::string EncodeImpl(const std::string& raw, // UTF8
|
std::string EncodeImpl(string_view raw, // UTF8
|
||||||
std::function<bool(int)> should_encode) {
|
std::function<bool(int)> should_encode) {
|
||||||
const char kHex[] = "0123456789ABCDEF";
|
const char kHex[] = "0123456789ABCDEF";
|
||||||
|
|
||||||
@@ -102,14 +102,16 @@ std::string EncodeImpl(const std::string& raw, // UTF8
|
|||||||
// Our own implementation of alpha numeric instead of std::isalnum to avoid
|
// Our own implementation of alpha numeric instead of std::isalnum to avoid
|
||||||
// taking locale into account.
|
// taking locale into account.
|
||||||
inline bool IsAlNum(int c) {
|
inline bool IsAlNum(int c) {
|
||||||
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
|
||||||
|
(c >= 'a' && c <= 'z');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Characters that are allowed in a URI but do not have a reserved purpose are
|
// Characters that are allowed in a URI but do not have a reserved purpose are
|
||||||
// are called unreserved. These include uppercase and lowercase letters, decimal
|
// are called unreserved. These include uppercase and lowercase letters, decimal
|
||||||
// digits, hyphen, period, underscore, and tilde.
|
// digits, hyphen, period, underscore, and tilde.
|
||||||
inline bool IsUnreserved(int c) {
|
inline bool IsUnreserved(int c) {
|
||||||
return IsAlNum((unsigned char)c) || c == '-' || c == '.' || c == '_' || c == '~';
|
return IsAlNum((unsigned char)c) || c == '-' || c == '.' || c == '_' ||
|
||||||
|
c == '~';
|
||||||
}
|
}
|
||||||
|
|
||||||
// General delimiters serve as the delimiters between different uri components.
|
// General delimiters serve as the delimiters between different uri components.
|
||||||
@@ -171,23 +173,23 @@ inline bool IsQueryChar(int c) {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
std::string Url::EncodeHost(const std::string& utf8_str) {
|
std::string Url::EncodeHost(string_view utf8_str) {
|
||||||
return EncodeImpl(utf8_str, [](int c) -> bool { return c > 127; });
|
return EncodeImpl(utf8_str, [](int c) -> bool { return c > 127; });
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Url::EncodePath(const std::string& utf8_str) {
|
std::string Url::EncodePath(string_view utf8_str) {
|
||||||
return EncodeImpl(utf8_str, [](int c) -> bool {
|
return EncodeImpl(utf8_str, [](int c) -> bool {
|
||||||
return !IsPathChar(c) || c == '%' || c == '+';
|
return !IsPathChar(c) || c == '%' || c == '+';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Url::EncodeQuery(const std::string& utf8_str) {
|
std::string Url::EncodeQuery(string_view utf8_str) {
|
||||||
return EncodeImpl(utf8_str, [](int c) -> bool {
|
return EncodeImpl(utf8_str, [](int c) -> bool {
|
||||||
return !IsQueryChar(c) || c == '%' || c == '+';
|
return !IsQueryChar(c) || c == '%' || c == '+';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Url::EncodeFull(const std::string& utf8_str) {
|
std::string Url::EncodeFull(string_view utf8_str) {
|
||||||
return EncodeImpl(utf8_str, [](int c) -> bool {
|
return EncodeImpl(utf8_str, [](int c) -> bool {
|
||||||
return !IsUnreserved(c) && !IsReserved(c);
|
return !IsUnreserved(c) && !IsReserved(c);
|
||||||
});
|
});
|
||||||
@@ -195,7 +197,7 @@ std::string Url::EncodeFull(const std::string& utf8_str) {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
Url::Url(const std::string& str, bool encode) {
|
Url::Url(string_view str, bool encode) {
|
||||||
if (encode) {
|
if (encode) {
|
||||||
Parse(Url::EncodeFull(str));
|
Parse(Url::EncodeFull(str));
|
||||||
} else {
|
} else {
|
||||||
@@ -203,7 +205,7 @@ Url::Url(const std::string& str, bool encode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Url::AppendPath(const std::string& piece, bool encode) {
|
void Url::AppendPath(string_view piece, bool encode) {
|
||||||
if (piece.empty() || piece == "/") {
|
if (piece.empty() || piece == "/") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -222,24 +224,23 @@ void Url::AppendPath(const std::string& piece, bool encode) {
|
|||||||
if (encode) {
|
if (encode) {
|
||||||
path_.append(Url::EncodePath(piece));
|
path_.append(Url::EncodePath(piece));
|
||||||
} else {
|
} else {
|
||||||
path_.append(piece);
|
path_.append(ToString(piece));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Url::AppendQuery(const std::string& key, const std::string& value,
|
void Url::AppendQuery(string_view key, string_view value, bool encode) {
|
||||||
bool encode) {
|
|
||||||
if (!query_.empty()) {
|
if (!query_.empty()) {
|
||||||
query_ += "&";
|
query_ += "&";
|
||||||
}
|
}
|
||||||
if (encode) {
|
if (encode) {
|
||||||
query_ += Url::EncodeQuery(key) + "=" + Url::EncodeQuery(value);
|
query_ += Url::EncodeQuery(key) + "=" + Url::EncodeQuery(value);
|
||||||
} else {
|
} else {
|
||||||
query_ += key + "=" + value;
|
query_ += ToString(key) + "=" + ToString(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Url::Parse(const std::string& str) {
|
void Url::Parse(string_view str) {
|
||||||
std::string tmp = str;
|
std::string tmp = ToString(str);
|
||||||
boost::trim_left(tmp);
|
boost::trim_left(tmp);
|
||||||
|
|
||||||
std::size_t p = std::string::npos;
|
std::size_t p = std::string::npos;
|
||||||
@@ -314,8 +315,8 @@ UrlQuery::UrlQuery(const std::string& encoded_str) {
|
|||||||
i = j + 1;
|
i = j + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string key;
|
string_view key;
|
||||||
std::string value;
|
string_view value;
|
||||||
if (SplitKV(kv, '=', false, &key, &value)) {
|
if (SplitKV(kv, '=', false, &key, &value)) {
|
||||||
parameters_.push_back({ DecodeUnsafe(key), DecodeUnsafe(value) });
|
parameters_.push_back({ DecodeUnsafe(key), DecodeUnsafe(value) });
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-13
@@ -17,15 +17,15 @@ namespace webcc {
|
|||||||
class Url {
|
class Url {
|
||||||
public:
|
public:
|
||||||
// Encode URL different components.
|
// Encode URL different components.
|
||||||
static std::string EncodeHost(const std::string& utf8_str);
|
static std::string EncodeHost(string_view utf8_str);
|
||||||
static std::string EncodePath(const std::string& utf8_str);
|
static std::string EncodePath(string_view utf8_str);
|
||||||
static std::string EncodeQuery(const std::string& utf8_str);
|
static std::string EncodeQuery(string_view utf8_str);
|
||||||
static std::string EncodeFull(const std::string& utf8_str);
|
static std::string EncodeFull(string_view utf8_str);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Url() = default;
|
Url() = default;
|
||||||
|
|
||||||
explicit Url(const std::string& str, bool encode = false);
|
explicit Url(string_view str, bool encode = false);
|
||||||
|
|
||||||
const std::string& scheme() const {
|
const std::string& scheme() const {
|
||||||
return scheme_;
|
return scheme_;
|
||||||
@@ -47,19 +47,18 @@ public:
|
|||||||
return query_;
|
return query_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_port(const std::string& port) {
|
void set_port(string_view port) {
|
||||||
port_ = port;
|
port_ = ToString(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append a piece of path.
|
// Append a piece of path.
|
||||||
void AppendPath(const std::string& piece, bool encode = false);
|
void AppendPath(string_view piece, bool encode = false);
|
||||||
|
|
||||||
// Append a query parameter.
|
// Append a query parameter.
|
||||||
void AppendQuery(const std::string& key, const std::string& value,
|
void AppendQuery(string_view key, string_view value, bool encode = false);
|
||||||
bool encode = false);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Parse(const std::string& str);
|
void Parse(string_view str);
|
||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
@@ -125,12 +124,11 @@ private:
|
|||||||
// Used by Server::Route().
|
// Used by Server::Route().
|
||||||
class UrlRegex {
|
class UrlRegex {
|
||||||
public:
|
public:
|
||||||
explicit UrlRegex(const std::string& url) : url_(url) {
|
explicit UrlRegex(string_view url) : url_(url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::regex operator()() const {
|
std::regex operator()() const {
|
||||||
std::regex::flag_type flags = std::regex::ECMAScript | std::regex::icase;
|
std::regex::flag_type flags = std::regex::ECMAScript | std::regex::icase;
|
||||||
|
|
||||||
return std::regex{ url_, flags };
|
return std::regex{ url_, flags };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -10,6 +10,8 @@
|
|||||||
#include "webcc/string.h"
|
#include "webcc/string.h"
|
||||||
#include "webcc/version.h"
|
#include "webcc/version.h"
|
||||||
|
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
namespace utility {
|
namespace utility {
|
||||||
|
|
||||||
@@ -22,7 +24,7 @@ std::string HttpDate() {
|
|||||||
std::time_t t = std::time(nullptr);
|
std::time_t t = std::time(nullptr);
|
||||||
std::tm gmt = *std::gmtime(&t);
|
std::tm gmt = *std::gmtime(&t);
|
||||||
|
|
||||||
std::stringstream date;
|
std::ostringstream date;
|
||||||
date.imbue(std::locale::classic()); // Use classic C locale
|
date.imbue(std::locale::classic()); // Use classic C locale
|
||||||
date << std::put_time(&gmt, "%a, %d %b %Y %H:%M:%S GMT");
|
date << std::put_time(&gmt, "%a, %d %b %Y %H:%M:%S GMT");
|
||||||
return date.str();
|
return date.str();
|
||||||
@@ -54,9 +56,8 @@ bool ReadFile(const fs::path& path, std::string* output) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DumpByLine(const std::string& data, std::ostream& os,
|
void DumpByLine(const std::string& data, std::ostream& os, string_view prefix) {
|
||||||
const std::string& prefix) {
|
std::vector<string_view> lines;
|
||||||
std::vector<boost::string_view> lines;
|
|
||||||
Split(data, '\n', false, &lines);
|
Split(data, '\n', false, &lines);
|
||||||
|
|
||||||
std::size_t size = 0;
|
std::size_t size = 0;
|
||||||
@@ -75,18 +76,17 @@ void DumpByLine(const std::string& data, std::ostream& os,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintEndpoint(std::ostream& ostream,
|
void PrintEndpoint(std::ostream& ostream, const tcp::endpoint& endpoint) {
|
||||||
const boost::asio::ip::tcp::endpoint& endpoint) {
|
|
||||||
ostream << endpoint;
|
ostream << endpoint;
|
||||||
if (endpoint.protocol() == boost::asio::ip::tcp::v4()) {
|
if (endpoint.protocol() == tcp::v4()) {
|
||||||
ostream << ", v4";
|
ostream << ", v4";
|
||||||
} else if (endpoint.protocol() == boost::asio::ip::tcp::v6()) {
|
} else if (endpoint.protocol() == tcp::v6()) {
|
||||||
ostream << ", v6";
|
ostream << ", v6";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string EndpointToString(const boost::asio::ip::tcp::endpoint& endpoint) {
|
std::string EndpointToString(const tcp::endpoint& endpoint) {
|
||||||
std::stringstream ss;
|
std::ostringstream ss;
|
||||||
PrintEndpoint(ss, endpoint);
|
PrintEndpoint(ss, endpoint);
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -29,8 +29,7 @@ bool ReadFile(const fs::path& path, std::string* output);
|
|||||||
|
|
||||||
// Dump the string data line by line to achieve more readability.
|
// Dump the string data line by line to achieve more readability.
|
||||||
// Also limit the maximum size of the data to be dumped.
|
// Also limit the maximum size of the data to be dumped.
|
||||||
void DumpByLine(const std::string& data, std::ostream& os,
|
void DumpByLine(const std::string& data, std::ostream& os, string_view prefix);
|
||||||
const std::string& prefix);
|
|
||||||
|
|
||||||
// Print TCP endpoint.
|
// Print TCP endpoint.
|
||||||
// Usage: PrintEndpoint(std::cout, endpoint)
|
// Usage: PrintEndpoint(std::cout, endpoint)
|
||||||
|
|||||||
Reference in New Issue
Block a user