introduce string_view

master
Chunting Gu 4 years ago
parent 751a3539ae
commit 26bb6b341a

@ -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_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)
enable_testing()
@ -45,7 +46,7 @@ endif()
# C++ standard requirements.
if(WEBCC_USE_STD_FILESYSTEM)
if(WEBCC_USE_STD_FILESYSTEM OR WEBCC_USE_STD_STRING_VIEW)
set(CMAKE_CXX_STANDARD 17)
else()
set(CMAKE_CXX_STANDARD 11)

@ -20,7 +20,7 @@ static Json::Value StringToJson(const std::string& str) {
Json::Value json;
Json::CharReaderBuilder builder;
std::stringstream stream(str);
std::istringstream stream{ str };
std::string errors;
if (!Json::parseFromStream(builder, stream, &json, &errors)) {
std::cerr << errors << std::endl;

@ -16,7 +16,7 @@ Json::Value StringToJson(const std::string& str) {
Json::Value json;
Json::CharReaderBuilder builder;
std::stringstream stream(str);
std::istringstream stream{ str };
std::string errs;
if (!Json::parseFromStream(builder, stream, &json, &errs)) {
std::cerr << errs << std::endl;

@ -16,7 +16,7 @@ Json::Value StringToJson(const std::string& str) {
Json::Value json;
Json::CharReaderBuilder builder;
std::stringstream stream(str);
std::istringstream stream{ str };
std::string errs;
if (!Json::parseFromStream(builder, stream, &json, &errs)) {
std::cerr << errs << std::endl;

@ -39,11 +39,11 @@ int main(int argc, char* argv[]) {
std::make_shared<BookListView>(),
{ "GET", "POST" });
server.Route(webcc::R("/books/(\\d+)"),
server.Route(webcc::R{ "/books/(\\d+)" },
std::make_shared<BookDetailView>(photo_dir),
{ "GET", "PUT", "DELETE" });
server.Route(webcc::R("/books/(\\d+)/photo"),
server.Route(webcc::R{ "/books/(\\d+)/photo" },
std::make_shared<BookPhotoView>(photo_dir),
{ "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 "
<< "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 "
"http://localhost:8080/upload"
<< std::endl;
@ -43,8 +43,7 @@ int main(int argc, char* argv[]) {
webcc::ClientSession session;
try {
auto r = session.Send(webcc::RequestBuilder{}
.Post(url)
auto r = session.Send(WEBCC_POST(url)
.FormFile("file", upload_dir / "remember.txt")
.FormData("json", "{}", "application/json")());

@ -20,7 +20,7 @@ Json::Value StringToJson(const std::string& str) {
Json::Value json;
Json::CharReaderBuilder builder;
std::stringstream stream(str);
std::istringstream stream{ str };
std::string errors;
if (!Json::parseFromStream(builder, stream, &json, &errors)) {
std::cerr << errors << std::endl;

@ -17,8 +17,7 @@ public:
TEST(RouterTest, URL_RegexBasic) {
webcc::Router router;
router.Route(webcc::R("/instance/(\\d+)"),
std::make_shared<MyView>());
router.Route(webcc::R{ "/instance/(\\d+)" }, std::make_shared<MyView>());
std::string url = "/instance/12345";
webcc::UrlArgs args;
@ -40,7 +39,7 @@ TEST(RouterTest, URL_RegexBasic) {
TEST(RouterTest, URL_RegexMultiple) {
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::string url = "/study/1/series/2/instance/3";

@ -6,8 +6,36 @@
#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) {
std::vector<boost::string_view> parts;
std::vector<webcc::string_view> parts;
webcc::Split("GET /path/to HTTP/1.1", ' ', false, &parts);
EXPECT_EQ(3, parts.size());
@ -18,12 +46,11 @@ TEST(StringTest, Split) {
TEST(StringTest, Split_TokenCompressOff) {
std::string str = "one,two,,three,,";
std::vector<boost::string_view> parts;
std::vector<webcc::string_view> parts;
// Same as:
// boost::split(parts, str, boost::is_any_of(","),
// boost::token_compress_off);
webcc::Split(str, ',', false, &parts);
EXPECT_EQ(6, parts.size());
@ -37,7 +64,7 @@ TEST(StringTest, Split_TokenCompressOff) {
TEST(StringTest, Split_TokenCompressOn) {
std::string str = "one,two,,three,,";
std::vector<boost::string_view> parts;
std::vector<webcc::string_view> parts;
// Same as:
// boost::split(parts, str, boost::is_any_of(","),
@ -51,8 +78,28 @@ TEST(StringTest, Split_TokenCompressOn) {
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) {
std::vector<boost::string_view> lines;
std::vector<webcc::string_view> lines;
webcc::Split("line one\nline two\nline 3", '\n', false, &lines);
EXPECT_EQ(3, lines.size());
@ -64,8 +111,8 @@ TEST(StringTest, Split_NewLine) {
TEST(StringTest, SplitKV) {
const std::string str = "key=value";
std::string key;
std::string value;
webcc::string_view key;
webcc::string_view value;
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
EXPECT_EQ(true, ok);
@ -76,8 +123,8 @@ TEST(StringTest, SplitKV) {
TEST(StringTest, SplitKV_OtherDelim) {
const std::string str = "key:value";
std::string key;
std::string value;
webcc::string_view key;
webcc::string_view value;
bool ok = webcc::SplitKV(str, ':', true, &key, &value);
EXPECT_TRUE(ok);
@ -89,8 +136,8 @@ TEST(StringTest, SplitKV_OtherDelim) {
TEST(StringTest, SplitKV_Spaces) {
const std::string str = " key = value ";
std::string key;
std::string value;
webcc::string_view key;
webcc::string_view value;
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
EXPECT_TRUE(ok);
@ -102,8 +149,8 @@ TEST(StringTest, SplitKV_Spaces) {
TEST(StringTest, SplitKV_SpacesNoTrim) {
const std::string str = " key = value ";
std::string key;
std::string value;
webcc::string_view key;
webcc::string_view value;
bool ok = webcc::SplitKV(str, '=', false, &key, &value);
EXPECT_TRUE(ok);
@ -115,8 +162,8 @@ TEST(StringTest, SplitKV_SpacesNoTrim) {
TEST(StringTest, SplitKV_NoKey) {
const std::string str = "=value";
std::string key;
std::string value;
webcc::string_view key;
webcc::string_view value;
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
EXPECT_TRUE(ok);
@ -128,8 +175,8 @@ TEST(StringTest, SplitKV_NoKey) {
TEST(StringTest, SplitKV_NoValue) {
const std::string str = "key=";
std::string key;
std::string value;
webcc::string_view key;
webcc::string_view value;
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
EXPECT_TRUE(ok);
@ -141,8 +188,8 @@ TEST(StringTest, SplitKV_NoValue) {
TEST(StringTest, SplitKV_NoKeyNoValue) {
const std::string str = "=";
std::string key;
std::string value;
webcc::string_view key;
webcc::string_view value;
bool ok = webcc::SplitKV(str, '=', true, &key, &value);
EXPECT_TRUE(ok);

@ -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();
if (port.empty()) {
port = default_port;
port = ToString(default_port);
}
LOG_VERB("Resolve host (%s)", request_->host().c_str());

@ -89,7 +89,7 @@ private:
void AsyncConnect();
void AsyncResolve(const std::string& default_port);
void AsyncResolve(string_view default_port);
void OnResolve(boost::system::error_code ec,
boost::asio::ip::tcp::resolver::results_type endpoints);

@ -128,7 +128,7 @@ void ClientSession::Stop() {
started_ = false;
}
void ClientSession::Accept(const std::string& content_types) {
void ClientSession::Accept(string_view content_types) {
if (!content_types.empty()) {
headers_.Set(headers::kAccept, content_types);
}
@ -168,18 +168,18 @@ void ClientSession::AcceptGzip(bool gzip) {
#endif // WEBCC_ENABLE_GZIP
void ClientSession::Auth(const std::string& type,
const std::string& credentials) {
headers_.Set(headers::kAuthorization, type + " " + credentials);
void ClientSession::Auth(string_view type, string_view credentials) {
headers_.Set(headers::kAuthorization,
ToString(type) + " " + ToString(credentials));
}
void ClientSession::AuthBasic(const std::string& login,
const std::string& password) {
auto credentials = Base64Encode(login + ":" + password);
void ClientSession::AuthBasic(string_view login, string_view password) {
auto credentials =
Base64Encode(ToString(login) + ":" + ToString(password));
return Auth("Basic", credentials);
}
void ClientSession::AuthToken(const std::string& token) {
void ClientSession::AuthToken(string_view token) {
return Auth("Token", token);
}

@ -52,7 +52,7 @@ public:
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);
}
@ -60,14 +60,13 @@ public:
// Only applied when:
// - the request to send has no `Content-Type` header, and
// - the request has a body.
void SetContentType(const std::string& media_type,
const std::string& charset = "") {
media_type_ = media_type;
charset_ = charset;
void SetContentType(string_view media_type, string_view charset = "") {
media_type_ = ToString(media_type);
charset_ = ToString(charset);
}
// Set content types to accept.
void Accept(const std::string& content_types);
void Accept(string_view content_types);
#if WEBCC_ENABLE_GZIP
@ -77,13 +76,13 @@ public:
#endif // WEBCC_ENABLE_GZIP
// Set authorization.
void Auth(const std::string& type, const std::string& credentials);
void Auth(string_view type, string_view credentials);
// Set Basic authorization.
void AuthBasic(const std::string& login, const std::string& password);
void AuthBasic(string_view login, string_view password);
// Set Token authorization.
void AuthToken(const std::string& token);
void AuthToken(string_view token);
// Send a request.
// Please use RequestBuilder to build the request.

@ -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()) {
return false;
}
auto it = Find(key);
if (it != headers_.end()) {
it->second = value;
it->second = ToString(value);
} else {
headers_.push_back({ key, value });
headers_.push_back({ ToString(key), ToString(value) });
}
return true;
}
bool Headers::Set(std::string&& key, std::string&& value) {
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 {
bool Headers::Has(string_view key) const {
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);
if (existed != nullptr) {
@ -61,7 +46,7 @@ const std::string& Headers::Get(const std::string& key, bool* existed) const {
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();
for (; it != headers_.end(); ++it) {
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,
std::string* value) {
std::string key;
static bool ParseValue(const std::string& str, const char* expected_key,
string_view* value) {
string_view key;
if (!SplitKV(str, '=', true, &key, value)) {
return false;
}
if (key != expected_key) {
return false;
}
return !value->empty();
}
ContentType::ContentType(const std::string& str) {
ContentType::ContentType(string_view str) {
Init(str);
}
void ContentType::Parse(const std::string& str) {
void ContentType::Parse(string_view str) {
Reset();
Init(str);
}
@ -114,15 +97,15 @@ bool ContentType::Valid() const {
return true;
}
void ContentType::Init(const std::string& str) {
void ContentType::Init(string_view str) {
std::string other;
std::size_t pos = str.find(';');
if (pos == std::string::npos) {
media_type_ = str;
if (pos == str.npos) {
media_type_ = ToString(str);
} else {
media_type_ = str.substr(0, pos);
other = str.substr(pos + 1);
media_type_ = ToString(str.substr(0, pos));
other = ToString(str.substr(pos + 1));
}
boost::trim(media_type_);
@ -130,26 +113,31 @@ void ContentType::Init(const std::string& str) {
if (media_type_ == "multipart/form-data") {
multipart_ = true;
if (!ParseValue(other, "boundary", &additional_)) {
LOG_ERRO("Invalid 'multipart/form-data' content-type (no boundary).");
string_view boundary;
if (ParseValue(other, "boundary", &boundary)) {
additional_ = ToString(boundary);
LOG_INFO("Content-type multipart boundary: %s", additional_.c_str());
} else {
LOG_INFO("Content-type multipart boundary: %s.", additional_.c_str());
LOG_ERRO("Invalid 'multipart/form-data' content-type (no boundary)");
}
} else {
if (ParseValue(other, "charset", &additional_)) {
LOG_INFO("Content-type charset: %s.", additional_.c_str());
string_view charset;
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) {
boost::trim_if(str, boost::is_any_of("\""));
}
bool ContentDisposition::Init(const std::string& str) {
std::vector<boost::string_view> parts;
bool ContentDisposition::Init(string_view str) {
std::vector<string_view> parts;
Split(str, ';', false, &parts);
if (parts.empty()) {
@ -160,18 +148,18 @@ bool ContentDisposition::Init(const std::string& str) {
return false;
}
std::string key;
std::string value;
string_view key;
string_view value;
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;
}
if (key == "name") {
name_ = value;
name_ = ToString(value);
Unquote(name_);
} else if (key == "filename") {
file_name_ = value;
file_name_ = ToString(value);
Unquote(file_name_);
}
}
@ -181,24 +169,24 @@ bool ContentDisposition::Init(const std::string& str) {
// -----------------------------------------------------------------------------
FormPartPtr FormPart::New(const std::string& name, std::string&& data,
const std::string& media_type) {
FormPartPtr FormPart::New(string_view name, std::string&& data,
string_view media_type) {
auto form_part = std::make_shared<FormPart>();
form_part->name_ = name;
form_part->name_ = ToString(name);
form_part->data_ = std::move(data);
form_part->media_type_ = media_type;
form_part->media_type_ = ToString(media_type);
return form_part;
}
FormPartPtr FormPart::NewFile(const std::string& name, const fs::path& path,
const std::string& media_type) {
FormPartPtr FormPart::NewFile(string_view name, const fs::path& path,
string_view media_type) {
auto form_part = std::make_shared<FormPart>();
form_part->name_ = name;
form_part->name_ = ToString(name);
form_part->path_ = path;
form_part->media_type_ = media_type;
form_part->media_type_ = ToString(media_type);
// Determine file name from file path.
// TODO: encoding
@ -288,7 +276,7 @@ std::size_t FormPart::GetDataSize() {
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()) {
os << prefix << h.first << ": " << h.second << std::endl;
}

@ -29,11 +29,9 @@ public:
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(const std::string& key) const;
bool Has(string_view key) const;
// Get header by index.
const Header& Get(std::size_t index) const {
@ -44,14 +42,14 @@ public:
// Get header value by key.
// If there's no such header with the given key, besides return empty, the
// 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() {
headers_.clear();
}
private:
std::vector<Header>::iterator Find(const std::string& key);
std::vector<Header>::iterator Find(string_view key);
std::vector<Header> headers_;
};
@ -64,9 +62,9 @@ private:
// Content-Type: multipart/form-data; boundary=something
class ContentType {
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();
@ -91,7 +89,7 @@ public:
}
private:
void Init(const std::string& str);
void Init(string_view str);
private:
std::string media_type_;
@ -109,7 +107,7 @@ private:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
class ContentDisposition {
public:
explicit ContentDisposition(const std::string& str) {
explicit ContentDisposition(string_view str) {
valid_ = Init(str);
}
@ -126,7 +124,7 @@ public:
}
private:
bool Init(const std::string& str);
bool Init(string_view str);
private:
std::string name_;
@ -151,14 +149,14 @@ public:
// 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
// media type as "application/json".
static FormPartPtr New(const std::string& name, std::string&& data,
const std::string& media_type = "");
static FormPartPtr New(string_view name, std::string&& data,
string_view media_type = "");
// Construct a file part.
// The file name will be extracted from path.
// The media type, if not provided, will be inferred from file extension.
static FormPartPtr NewFile(const std::string& name, const fs::path& path,
const std::string& media_type = "");
static FormPartPtr NewFile(string_view name, const fs::path& path,
string_view media_type = "");
// API: SERVER
const std::string& name() const {
@ -214,7 +212,7 @@ public:
std::size_t GetDataSize();
// 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:
// Generate headers from properties.

@ -17,7 +17,10 @@
// Set 1/0 to enable/disable GZIP compression.
#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
// 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_

@ -19,7 +19,10 @@
// Set 1/0 to enable/disable GZIP compression.
#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@
// 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_

@ -24,10 +24,11 @@ void Connection::Start() {
boost::system::error_code ec;
auto endpoint = socket_.remote_endpoint(ec);
if (!ec) {
request_->set_ip(endpoint.address().to_string());
request_->set_address(endpoint.address().to_string());
}
request_parser_.Init(request_.get(), view_matcher_);
AsyncRead();
}

@ -12,10 +12,32 @@
#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 {
// -----------------------------------------------------------------------------
#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>;
// Regex sub-matches of the URL (usually resource ID's).
@ -169,7 +191,7 @@ public:
};
public:
explicit Error(Code code = kOK, const std::string& message = "")
explicit Error(Code code = kOK, string_view message = "")
: code_(code), message_(message) {
}
@ -186,9 +208,9 @@ public:
return message_;
}
void Set(Code code, const std::string& message) {
void Set(Code code, string_view message) {
code_ = code;
message_ = message;
message_ = ToString(message);
}
bool timeout() const {

@ -135,7 +135,7 @@ static const bool g_terminal_has_color = []() {
static std::string DoGetThreadID() {
#if (defined(_WIN32) || defined(_WIN64))
auto thread_id = std::this_thread::get_id();
std::stringstream ss;
std::ostringstream ss;
ss << thread_id;
return ss.str();
#else
@ -184,7 +184,7 @@ static std::string GetTimestamp() {
auto now = system_clock::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");
milliseconds milli_seconds =

@ -31,6 +31,7 @@ void Message::SetBody(BodyPtr body, bool set_length) {
const std::string& Message::data() const {
static const std::string kEmptyData;
auto string_body = std::dynamic_pointer_cast<StringBody>(body_);
if (!string_body) {
return kEmptyData;
@ -43,54 +44,42 @@ std::shared_ptr<FileBody> Message::file_body() const {
}
bool Message::IsConnectionKeepAlive() const {
using headers::kConnection;
bool existed = false;
const std::string& connection = GetHeader(kConnection, &existed);
const auto& connection = GetHeader(headers::kConnection, &existed);
if (!existed) {
// Keep-Alive is by default for HTTP/1.1.
return true;
}
if (boost::iequals(connection, "Keep-Alive")) {
return true;
}
return false;
return boost::iequals(connection, "Keep-Alive");
}
ContentEncoding Message::GetContentEncoding() const {
using headers::kContentEncoding;
const std::string& encoding = GetHeader(kContentEncoding);
const auto& encoding = GetHeader(headers::kContentEncoding);
if (encoding == "gzip") {
return ContentEncoding::kGzip;
}
if (encoding == "deflate") {
} else if (encoding == "deflate") {
return ContentEncoding::kDeflate;
} else {
return ContentEncoding::kUnknown;
}
return ContentEncoding::kUnknown;
}
bool Message::AcceptEncodingGzip() const {
using headers::kAcceptEncoding;
return GetHeader(kAcceptEncoding).find("gzip") != std::string::npos;
return GetHeader(headers::kAcceptEncoding).find("gzip") != std::string::npos;
}
void Message::SetContentType(const std::string& media_type,
const std::string& charset) {
using headers::kContentType;
void Message::SetContentType(string_view media_type, string_view charset) {
if (!media_type.empty()) {
if (charset.empty()) {
SetHeader(kContentType, media_type);
SetHeader(headers::kContentType, media_type);
} 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 {
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()) {
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::stringstream ss;
std::ostringstream ss;
Dump(ss);
return ss.str();
}

@ -21,51 +21,30 @@ public:
virtual ~Message() = default;
// ---------------------------------------------------------------------------
void SetBody(BodyPtr body, bool set_length);
BodyPtr body() const {
return body_;
const std::string& start_line() const {
return start_line_;
}
// Get the data from the (string) body.
// Return empty string if the body is not a StringBody.
const std::string& data() const;
// Get the body as a FileBody.
// Return null if the body is not a FileBody.
std::shared_ptr<FileBody> file_body() const;
// ---------------------------------------------------------------------------
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(const std::string& key, const std::string& value) {
void SetHeader(string_view key, string_view value) {
headers_.Set(key, value);
}
const std::string& GetHeader(const std::string& key,
bool* existed = nullptr) const {
const std::string& GetHeader(string_view key, bool* existed = nullptr) const {
return headers_.Get(key, existed);
}
bool HasHeader(const std::string& key) const {
bool HasHeader(string_view 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_;
}
@ -74,7 +53,19 @@ public:
content_length_ = content_length;
}
// ---------------------------------------------------------------------------
void SetBody(BodyPtr body, bool set_length);
BodyPtr body() const {
return body_;
}
// Get the data from the (string) body.
// Return empty string if the body is not a StringBody.
const std::string& data() const;
// Get the body as a FileBody.
// Return null if the body is not a FileBody.
std::shared_ptr<FileBody> file_body() const;
// Check `Connection` header to see if it's "Keep-Alive".
bool IsConnectionKeepAlive() const;
@ -88,16 +79,13 @@ public:
// Set `Content-Type` header. E.g.,
// SetContentType("application/json; charset=utf-8")
void SetContentType(const std::string& content_type) {
void SetContentType(string_view content_type) {
SetHeader(headers::kContentType, content_type);
}
// Set `Content-Type` header. E.g.,
// SetContentType("application/json", "utf-8")
void SetContentType(const std::string& media_type,
const std::string& charset);
// ---------------------------------------------------------------------------
void SetContentType(string_view media_type, string_view charset);
// Make the message complete in order to be sent.
virtual void Prepare() = 0;
@ -106,8 +94,6 @@ public:
// This doesn't include the payload(s) of the body!
Payload GetPayload() const;
// ---------------------------------------------------------------------------
// Dump to output stream for logging purpose.
void Dump(std::ostream& os) const;

@ -23,8 +23,8 @@ public:
return method_;
}
void set_method(const std::string& method) {
method_ = method;
void set_method(string_view method) {
method_ = ToString(method);
}
const Url& url() const {
@ -44,23 +44,23 @@ public:
}
UrlQuery query() const {
return UrlQuery(url_.query());
return UrlQuery{ url_.query() };
}
const UrlArgs& args() const {
return args_;
}
void set_args(const UrlArgs& args) {
args_ = args;
void set_args(UrlArgs&& args) {
args_ = std::move(args);
}
const std::string& ip() const {
return ip_;
const std::string& address() const {
return address_;
}
void set_ip(const std::string& ip) {
ip_ = ip;
void set_address(std::string&& address) {
address_ = std::move(address);
}
// Check if the body is a multi-part form data.
@ -83,7 +83,7 @@ private:
UrlArgs args_;
// Client IP address.
std::string ip_;
std::string address_;
};
using RequestPtr = std::shared_ptr<Request>;

@ -19,7 +19,7 @@ RequestPtr RequestBuilder::operator()() {
request->set_url(std::move(url_));
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".
@ -34,7 +34,7 @@ RequestPtr RequestBuilder::operator()() {
if (gzip_ && body_->Compress()) {
request->SetHeader(headers::kContentEncoding, "gzip");
}
#endif
#endif // WEBCC_ENABLE_GZIP
} else if (!form_parts_.empty()) {
// Another choice to generate the boundary is like what Apache does.
// See: https://stackoverflow.com/a/5686863
@ -77,41 +77,41 @@ RequestBuilder& RequestBuilder::File(const fs::path& path,
return *this;
}
RequestBuilder& RequestBuilder::FormFile(const std::string& name,
RequestBuilder& RequestBuilder::FormFile(string_view name,
const fs::path& path,
const std::string& media_type) {
string_view media_type) {
assert(!name.empty());
return Form(FormPart::NewFile(name, path, media_type));
}
RequestBuilder& RequestBuilder::FormData(const std::string& name,
RequestBuilder& RequestBuilder::FormData(string_view name,
std::string&& data,
const std::string& media_type) {
string_view media_type) {
assert(!name.empty());
return Form(FormPart::New(name, std::move(data), media_type));
}
RequestBuilder& RequestBuilder::Header(const std::string& key,
const std::string& value) {
headers_.push_back(key);
headers_.push_back(value);
RequestBuilder& RequestBuilder::Header(string_view key, string_view value) {
headers_.push_back(ToString(key));
headers_.push_back(ToString(value));
return *this;
}
RequestBuilder& RequestBuilder::Auth(const std::string& type,
const std::string& credentials) {
RequestBuilder& RequestBuilder::Auth(string_view type,
string_view credentials) {
headers_.push_back(headers::kAuthorization);
headers_.push_back(type + " " + credentials);
headers_.push_back(ToString(type) + " " + ToString(credentials));
return *this;
}
RequestBuilder& RequestBuilder::AuthBasic(const std::string& login,
const std::string& password) {
auto credentials = Base64Encode(login + ":" + password);
RequestBuilder& RequestBuilder::AuthBasic(string_view login,
string_view password) {
auto credentials =
Base64Encode(ToString(login) + ":" + ToString(password));
return Auth("Basic", credentials);
}
RequestBuilder& RequestBuilder::AuthToken(const std::string& token) {
RequestBuilder& RequestBuilder::AuthToken(string_view token) {
return Auth("Token", token);
}

@ -39,44 +39,44 @@ public:
RequestBuilder(const RequestBuilder&) = delete;
RequestBuilder& operator=(const RequestBuilder&) = delete;
// Build
// Build and return the request object.
RequestPtr operator()();
RequestBuilder& Method(const std::string& method) {
method_ = method;
RequestBuilder& Method(string_view method) {
method_ = ToString(method);
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);
}
RequestBuilder& Head(const std::string& url, bool encode = false) {
RequestBuilder& Head(string_view url, bool encode = false) {
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);
}
RequestBuilder& Put(const std::string& url, bool encode = false) {
RequestBuilder& Put(string_view url, bool encode = false) {
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);
}
RequestBuilder& Patch(const std::string& url, bool encode = false) {
RequestBuilder& Patch(string_view url, bool encode = false) {
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 };
return *this;
}
RequestBuilder& Port(const std::string& port) {
RequestBuilder& Port(string_view port) {
url_.set_port(port);
return *this;
}
@ -87,25 +87,25 @@ public:
}
// 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);
return *this;
}
// 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) {
url_.AppendQuery(key, value, encode);
return *this;
}
RequestBuilder& MediaType(const std::string& media_type) {
media_type_ = media_type;
RequestBuilder& MediaType(string_view media_type) {
media_type_ = ToString(media_type);
return *this;
}
RequestBuilder& Charset(const std::string& charset) {
charset_ = charset;
RequestBuilder& Charset(string_view charset) {
charset_ = ToString(charset);
return *this;
}
@ -123,7 +123,7 @@ public:
// Set (comma separated) content types to accept.
// 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);
}
@ -156,26 +156,25 @@ public:
}
// Add a form part of file.
RequestBuilder& FormFile(const std::string& name, const fs::path& path,
const std::string& media_type = "");
RequestBuilder& FormFile(string_view name, const fs::path& path,
string_view media_type = "");
// Add a form part of string data.
RequestBuilder& FormData(const std::string& name, std::string&& data,
const std::string& media_type = "");
RequestBuilder& FormData(string_view name, std::string&& data,
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) {
keep_alive_ = keep_alive;
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,
const std::string& password);
RequestBuilder& AuthBasic(string_view login, string_view password);
RequestBuilder& AuthToken(const std::string& token);
RequestBuilder& AuthToken(string_view token);
// Add the `Date` header to the request.
RequestBuilder& Date();

@ -36,15 +36,15 @@ bool RequestParser::OnHeadersEnd() {
}
bool RequestParser::ParseStartLine(const std::string& line) {
std::vector<boost::string_view> parts;
std::vector<string_view> parts;
Split(line, ' ', true, &parts);
if (parts.size() != 3) {
return false;
}
request_->set_method(parts[0].to_string());
request_->set_url(Url{ parts[1].to_string() });
request_->set_method(parts[0]);
request_->set_url(Url{ parts[1] });
// HTTP version is ignored.
@ -198,7 +198,7 @@ bool RequestParser::ParsePartHeaders(bool* need_more_data) {
// Parse Content-Disposition.
if (boost::iequals(header.first, headers::kContentDisposition)) {
ContentDisposition content_disposition(header.second);
ContentDisposition content_disposition{ header.second };
if (!content_disposition.valid()) {
LOG_ERRO("Invalid content-disposition header: %s",
header.second.c_str());

@ -42,21 +42,22 @@ private:
std::size_t count, bool* end = nullptr) const;
private:
Request* request_;
// The result request message.
Request* request_ = nullptr;
// 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.
ViewMatcher view_matcher_;
// Form data parsing steps.
enum Step {
enum class Step {
kStart,
kBoundaryParsed,
kHeadersParsed,
kEnded,
};
Step step_ = kStart;
Step step_ = Step::kStart;
// The current form part being parsed.
FormPartPtr part_;

@ -55,6 +55,12 @@ ResponseBuilder& ResponseBuilder::File(const fs::path& path,
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() {
headers_.push_back(headers::kDate);
headers_.push_back(utility::HttpDate());

@ -61,13 +61,13 @@ public:
return *this;
}
ResponseBuilder& MediaType(const std::string& media_type) {
media_type_ = media_type;
ResponseBuilder& MediaType(string_view media_type) {
media_type_ = ToString(media_type);
return *this;
}
ResponseBuilder& Charset(const std::string& charset) {
charset_ = charset;
ResponseBuilder& Charset(string_view charset) {
charset_ = ToString(charset);
return *this;
}
@ -98,11 +98,7 @@ public:
ResponseBuilder& File(const fs::path& path, bool infer_media_type = true,
std::size_t chunk_size = 1024);
ResponseBuilder& Header(const std::string& key, const std::string& value) {
headers_.push_back(key);
headers_.push_back(value);
return *this;
}
ResponseBuilder& Header(string_view key, string_view value);
// Add the `Date` header to the response.
ResponseBuilder& Date();

@ -8,13 +8,12 @@
namespace webcc {
bool Router::Route(const std::string& url, ViewPtr view,
const Strings& methods) {
bool Router::Route(string_view url, ViewPtr view, const Strings& methods) {
assert(view);
// TODO: More error check
routes_.push_back({ url, {}, view, methods });
routes_.push_back({ ToString(url), {}, view, methods });
return true;
}
@ -26,7 +25,6 @@ bool Router::Route(const UrlRegex& regex_url, ViewPtr view,
// TODO: More error check
try {
routes_.push_back({ "", regex_url(), view, methods });
} catch (const std::regex_error& e) {

@ -15,7 +15,7 @@ public:
// Route a URL to a view.
// 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" });
// Route a URL (as regular expression) to a view.

@ -300,7 +300,7 @@ void Server::Handle(ConnectionPtr connection) {
}
// 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.
ResponsePtr response = view->Handle(request);

@ -36,19 +36,19 @@ bool ToSizeT(const std::string& str, int base, std::size_t* size) {
return true;
}
void Split(boost::string_view input, char delim, bool compress_token,
std::vector<boost::string_view>* output) {
void Split(string_view input, char delim, bool compress_token,
std::vector<string_view>* output) {
std::size_t i = 0;
std::size_t p = 0;
i = input.find(delim);
while (i != boost::string_view::npos) {
while (i != string_view::npos) {
output->emplace_back(input.substr(p, i - p));
p = i + 1;
if (compress_token) {
while (input[p] == delim) {
while (p < input.size() && input[p] == delim) {
++p;
}
}
@ -59,10 +59,19 @@ void Split(boost::string_view input, char delim, bool compress_token,
output->emplace_back(input.substr(p, i - p));
}
bool SplitKV(const std::string& input, char delim, bool trim_spaces,
std::string* key, std::string* value) {
void Trim(string_view& sv, const char* spaces) {
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);
if (pos == std::string::npos) {
if (pos == input.npos) {
return false;
}
@ -70,11 +79,23 @@ bool SplitKV(const std::string& input, char delim, bool trim_spaces,
*value = input.substr(pos + 1);
if (trim_spaces) {
boost::trim(*key);
boost::trim(*value);
Trim(*key);
Trim(*value);
}
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

@ -4,7 +4,7 @@
#include <string>
#include <vector>
#include "boost/utility/string_view.hpp"
#include "webcc/globals.h" // for string_view
namespace webcc {
@ -15,15 +15,21 @@ std::string RandomString(std::size_t length);
// Just a wrapper of std::stoul.
bool ToSizeT(const std::string& str, int base, std::size_t* size);
void Trim(string_view& sv, const char* spaces = " ");
// Split string without copy.
// |compress_token| is the same as boost::token_compress_on for boost::split.
void Split(boost::string_view input, char delim, bool compress_token,
std::vector<boost::string_view>* output);
void Split(string_view input, char delim, bool compress_token,
std::vector<string_view>* output);
// Split a key-value string.
// E.g., split "Connection: Keep-Alive".
bool SplitKV(string_view input, char delim, bool trim_spaces, string_view* key,
string_view* value);
// Split a key-value string.
// E.g., split "Connection: Keep-Alive".
// TODO: Use string_view (blocked by trim)
bool SplitKV(const std::string& input, char delim, bool trim_spaces,
bool SplitKV(string_view input, char delim, bool trim_spaces,
std::string* key, std::string* value);
} // namespace webcc

@ -29,7 +29,7 @@ bool HexToDecimal(char hex, int* decimal) {
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) {
if (*iter == '%') {
if (++iter == encoded.end()) {
@ -67,16 +67,16 @@ bool Decode(const std::string& encoded, std::string* raw) {
// Unsafe decode.
// Return the original string on failure.
std::string DecodeUnsafe(const std::string& encoded) {
std::string DecodeUnsafe(string_view encoded) {
std::string raw;
if (Decode(encoded, &raw)) {
return raw;
}
return encoded;
return ToString(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) {
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
// taking locale into account.
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
// are called unreserved. These include uppercase and lowercase letters, decimal
// digits, hyphen, period, underscore, and tilde.
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.
@ -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; });
}
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 !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 !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 !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) {
Parse(Url::EncodeFull(str));
} 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 == "/") {
return;
}
@ -222,24 +224,23 @@ void Url::AppendPath(const std::string& piece, bool encode) {
if (encode) {
path_.append(Url::EncodePath(piece));
} else {
path_.append(piece);
path_.append(ToString(piece));
}
}
void Url::AppendQuery(const std::string& key, const std::string& value,
bool encode) {
void Url::AppendQuery(string_view key, string_view value, bool encode) {
if (!query_.empty()) {
query_ += "&";
}
if (encode) {
query_ += Url::EncodeQuery(key) + "=" + Url::EncodeQuery(value);
} else {
query_ += key + "=" + value;
query_ += ToString(key) + "=" + ToString(value);
}
}
void Url::Parse(const std::string& str) {
std::string tmp = str;
void Url::Parse(string_view str) {
std::string tmp = ToString(str);
boost::trim_left(tmp);
std::size_t p = std::string::npos;
@ -314,8 +315,8 @@ UrlQuery::UrlQuery(const std::string& encoded_str) {
i = j + 1;
}
std::string key;
std::string value;
string_view key;
string_view value;
if (SplitKV(kv, '=', false, &key, &value)) {
parameters_.push_back({ DecodeUnsafe(key), DecodeUnsafe(value) });
}

@ -17,15 +17,15 @@ namespace webcc {
class Url {
public:
// Encode URL different components.
static std::string EncodeHost(const std::string& utf8_str);
static std::string EncodePath(const std::string& utf8_str);
static std::string EncodeQuery(const std::string& utf8_str);
static std::string EncodeFull(const std::string& utf8_str);
static std::string EncodeHost(string_view utf8_str);
static std::string EncodePath(string_view utf8_str);
static std::string EncodeQuery(string_view utf8_str);
static std::string EncodeFull(string_view utf8_str);
public:
Url() = default;
explicit Url(const std::string& str, bool encode = false);
explicit Url(string_view str, bool encode = false);
const std::string& scheme() const {
return scheme_;
@ -47,19 +47,18 @@ public:
return query_;
}
void set_port(const std::string& port) {
port_ = port;
void set_port(string_view port) {
port_ = ToString(port);
}
// 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.
void AppendQuery(const std::string& key, const std::string& value,
bool encode = false);
void AppendQuery(string_view key, string_view value, bool encode = false);
private:
void Parse(const std::string& str);
void Parse(string_view str);
void Clear();
@ -125,12 +124,11 @@ private:
// Used by Server::Route().
class UrlRegex {
public:
explicit UrlRegex(const std::string& url) : url_(url) {
explicit UrlRegex(string_view url) : url_(url) {
}
std::regex operator()() const {
std::regex::flag_type flags = std::regex::ECMAScript | std::regex::icase;
return std::regex{ url_, flags };
}

@ -10,6 +10,8 @@
#include "webcc/string.h"
#include "webcc/version.h"
using boost::asio::ip::tcp;
namespace webcc {
namespace utility {
@ -22,7 +24,7 @@ std::string HttpDate() {
std::time_t t = std::time(nullptr);
std::tm gmt = *std::gmtime(&t);
std::stringstream date;
std::ostringstream date;
date.imbue(std::locale::classic()); // Use classic C locale
date << std::put_time(&gmt, "%a, %d %b %Y %H:%M:%S GMT");
return date.str();
@ -54,9 +56,8 @@ bool ReadFile(const fs::path& path, std::string* output) {
return true;
}
void DumpByLine(const std::string& data, std::ostream& os,
const std::string& prefix) {
std::vector<boost::string_view> lines;
void DumpByLine(const std::string& data, std::ostream& os, string_view prefix) {
std::vector<string_view> lines;
Split(data, '\n', false, &lines);
std::size_t size = 0;
@ -75,18 +76,17 @@ void DumpByLine(const std::string& data, std::ostream& os,
}
}
void PrintEndpoint(std::ostream& ostream,
const boost::asio::ip::tcp::endpoint& endpoint) {
void PrintEndpoint(std::ostream& ostream, const tcp::endpoint& endpoint) {
ostream << endpoint;
if (endpoint.protocol() == boost::asio::ip::tcp::v4()) {
if (endpoint.protocol() == tcp::v4()) {
ostream << ", v4";
} else if (endpoint.protocol() == boost::asio::ip::tcp::v6()) {
} else if (endpoint.protocol() == tcp::v6()) {
ostream << ", v6";
}
}
std::string EndpointToString(const boost::asio::ip::tcp::endpoint& endpoint) {
std::stringstream ss;
std::string EndpointToString(const tcp::endpoint& endpoint) {
std::ostringstream ss;
PrintEndpoint(ss, endpoint);
return ss.str();
}

@ -29,8 +29,7 @@ bool ReadFile(const fs::path& path, std::string* output);
// Dump the string data line by line to achieve more readability.
// Also limit the maximum size of the data to be dumped.
void DumpByLine(const std::string& data, std::ostream& os,
const std::string& prefix);
void DumpByLine(const std::string& data, std::ostream& os, string_view prefix);
// Print TCP endpoint.
// Usage: PrintEndpoint(std::cout, endpoint)

Loading…
Cancel
Save