diff --git a/autotest/CMakeLists.txt b/autotest/CMakeLists.txt index ae7e960..07777fc 100644 --- a/autotest/CMakeLists.txt +++ b/autotest/CMakeLists.txt @@ -13,10 +13,6 @@ set(AT_LIBS webcc jsoncpp GTest::GTest - GTest::Main - Boost::filesystem - Boost::system - Boost::date_time "${CMAKE_THREAD_LIBS_INIT}") if(WEBCC_ENABLE_SSL) diff --git a/autotest/client_autotest.cc b/autotest/client_autotest.cc index 0c63bc1..16ca404 100644 --- a/autotest/client_autotest.cc +++ b/autotest/client_autotest.cc @@ -4,8 +4,6 @@ #include "gtest/gtest.h" -#include "boost/algorithm/string.hpp" - #include "json/json.h" #include "webcc/client_session.h" @@ -314,7 +312,7 @@ TEST(ClientTest, Post) { static sfs::path GenerateTempFile(const std::string& data) { try { sfs::path path = - sfs::temp_directory_path() / webcc::string::RandomString(10); + sfs::temp_directory_path() / webcc::random_string(10); std::ofstream ofs; ofs.open(path, std::ios::binary); @@ -434,26 +432,24 @@ TEST(ClientTest, KeepAlive) { // Keep-Alive by default. auto r = session.Send(webcc::RequestBuilder{}.Get(url)()); - using boost::iequals; - - EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Keep-alive")); + EXPECT_TRUE(webcc::iequals(r->GetHeader("Connection"), "Keep-alive")); // Close by setting Connection header directly. r = session.Send(webcc::RequestBuilder{}.Get(url). Header("Connection", "Close") ()); - EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Close")); + EXPECT_TRUE(webcc::iequals(r->GetHeader("Connection"), "Close")); // Close by using request builder. r = session.Send(webcc::RequestBuilder{}.Get(url).KeepAlive(false)()); - EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Close")); + EXPECT_TRUE(webcc::iequals(r->GetHeader("Connection"), "Close")); // Keep-Alive explicitly by using request builder. r = session.Send(webcc::RequestBuilder{}.Get(url).KeepAlive(true)()); - EXPECT_TRUE(iequals(r->GetHeader("Connection"), "Keep-alive")); + EXPECT_TRUE(webcc::iequals(r->GetHeader("Connection"), "Keep-alive")); } catch (const webcc::Error& error) { std::cerr << error << std::endl; diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5b326f7..7cfd3a4 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -3,9 +3,6 @@ # Common libraries to link for examples. set(EXAMPLE_LIBS webcc - Boost::filesystem - Boost::system - Boost::date_time "${CMAKE_THREAD_LIBS_INIT}") if(WEBCC_ENABLE_SSL) diff --git a/examples/book_client/book_client.cc b/examples/book_client/book_client.cc index d718dc9..2531a20 100644 --- a/examples/book_client/book_client.cc +++ b/examples/book_client/book_client.cc @@ -2,9 +2,10 @@ #include -#include "boost/algorithm/string/predicate.hpp" #include "json/json.h" +#include "webcc/string.h" + #include "book_json.h" BookClient::BookClient(const std::string& url, int timeout) @@ -179,7 +180,7 @@ bool BookClient::CheckPhoto(const std::filesystem::path& photo) { } auto ext = photo.extension().string(); - if (!boost::iequals(ext, ".jpg") && !boost::iequals(ext, ".jpeg")) { + if (!webcc::iequals(ext, ".jpg") && !webcc::iequals(ext, ".jpeg")) { return false; } diff --git a/examples/book_client/book_client.h b/examples/book_client/book_client.h index 978ce37..1ed6207 100644 --- a/examples/book_client/book_client.h +++ b/examples/book_client/book_client.h @@ -5,7 +5,6 @@ #include #include -#include "boost/filesystem/path.hpp" #include "json/json-forwards.h" #include "webcc/client_session.h" diff --git a/examples/book_server/book.h b/examples/book_server/book.h index 11a3cba..e83437e 100644 --- a/examples/book_server/book.h +++ b/examples/book_server/book.h @@ -4,8 +4,6 @@ #include #include -#include "boost/filesystem/path.hpp" - struct Book { std::string id; std::string title; diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 921f0ad..c9859c5 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -10,9 +10,6 @@ set(UT_LIBS webcc GTest::GTest GTest::Main - Boost::filesystem - Boost::system - Boost::date_time "${CMAKE_THREAD_LIBS_INIT}") if(WEBCC_ENABLE_SSL) diff --git a/unittest/string_unittest.cc b/unittest/string_unittest.cc new file mode 100644 index 0000000..429958b --- /dev/null +++ b/unittest/string_unittest.cc @@ -0,0 +1,170 @@ +#include "gtest/gtest.h" + +#include + +#include "webcc/string.h" + +TEST(StringTest, iequals) { + EXPECT_TRUE(webcc::iequals("", "")); + EXPECT_TRUE(webcc::iequals("abc", "abc")); + EXPECT_TRUE(webcc::iequals("ABC", "abc")); + EXPECT_TRUE(webcc::iequals("123", "123")); + + EXPECT_FALSE(webcc::iequals("abc", "def")); + EXPECT_FALSE(webcc::iequals("abc", "abcdef")); +} + +TEST(StringTest, starts_with) { + EXPECT_TRUE(webcc::starts_with("123", "1")); + EXPECT_TRUE(webcc::starts_with("123", "12")); + EXPECT_TRUE(webcc::starts_with("123", "123")); + EXPECT_TRUE(webcc::starts_with(" 123", " ")); + + EXPECT_FALSE(webcc::starts_with("123", "")); + EXPECT_FALSE(webcc::starts_with("123", "2")); +} + +TEST(StringTest, split) { + std::vector parts; + webcc::split(parts, "GET /path/to HTTP/1.1"); + + EXPECT_EQ(3, parts.size()); + EXPECT_EQ("GET", parts[0]); + EXPECT_EQ("/path/to", parts[1]); + EXPECT_EQ("HTTP/1.1", parts[2]); +} + +TEST(StringTest, split_token_compress_off) { + std::string str = "one,two,,three,,"; + std::vector parts; + + // Same as: + // boost::split(parts, str, boost::is_any_of(","), + // boost::token_compress_off); + + webcc::split(parts, str, ',', false); + + EXPECT_EQ(6, parts.size()); + EXPECT_EQ("one", parts[0]); + EXPECT_EQ("two", parts[1]); + EXPECT_EQ("", parts[2]); + EXPECT_EQ("three", parts[3]); + EXPECT_EQ("", parts[4]); + EXPECT_EQ("", parts[5]); +} + +TEST(StringTest, split_token_compress_on) { + std::string str = "one,two,,three,,"; + std::vector parts; + + // Same as: + // boost::split(parts, str, boost::is_any_of(","), + // boost::token_compress_on); + webcc::split(parts, str, ',', true); + + EXPECT_EQ(4, parts.size()); + EXPECT_EQ("one", parts[0]); + EXPECT_EQ("two", parts[1]); + EXPECT_EQ("three", parts[2]); + EXPECT_EQ("", parts[3]); +} + +TEST(StringTest, split_new_line) { + std::vector lines; + webcc::split(lines, "line one\nline two\nline 3", '\n'); + + EXPECT_EQ(3, lines.size()); + EXPECT_EQ("line one", lines[0]); + EXPECT_EQ("line two", lines[1]); + EXPECT_EQ("line 3", lines[2]); +} + +TEST(UtilityTest, split_kv) { + const std::string str = "key=value"; + + std::string key; + std::string value; + bool ok = webcc::split_kv(key, value, str, '='); + + EXPECT_EQ(true, ok); + EXPECT_EQ("key", key); + EXPECT_EQ("value", value); +} + +TEST(UtilityTest, split_kv_other_delim) { + const std::string str = "key:value"; + + std::string key; + std::string value; + bool ok = webcc::split_kv(key, value, str, ':'); + + EXPECT_TRUE(ok); + + EXPECT_EQ("key", key); + EXPECT_EQ("value", value); +} + +TEST(UtilityTest, split_kv_spaces) { + const std::string str = " key = value "; + + std::string key; + std::string value; + bool ok = webcc::split_kv(key, value, str, '='); + + EXPECT_TRUE(ok); + + EXPECT_EQ("key", key); + EXPECT_EQ("value", value); +} + +TEST(UtilityTest, split_kv_spaces_no_trim) { + const std::string str = " key = value "; + + std::string key; + std::string value; + bool ok = webcc::split_kv(key, value, str, '=', false); + + EXPECT_TRUE(ok); + + EXPECT_EQ(" key ", key); + EXPECT_EQ(" value ", value); +} + +TEST(UtilityTest, split_kv_no_key) { + const std::string str = "=value"; + + std::string key; + std::string value; + bool ok = webcc::split_kv(key, value, str, '='); + + EXPECT_TRUE(ok); + + EXPECT_EQ("", key); + EXPECT_EQ("value", value); +} + +TEST(UtilityTest, split_kv_no_value) { + const std::string str = "key="; + + std::string key; + std::string value; + bool ok = webcc::split_kv(key, value, str, '='); + + EXPECT_TRUE(ok); + + EXPECT_EQ("key", key); + EXPECT_EQ("", value); +} + +TEST(UtilityTest, split_kv_no_key_no_value) { + const std::string str = "="; + + std::string key; + std::string value; + bool ok = webcc::split_kv(key, value, str, '='); + + EXPECT_TRUE(ok); + + EXPECT_EQ("", key); + EXPECT_EQ("", value); +} diff --git a/unittest/utility_unittest.cc b/unittest/utility_unittest.cc deleted file mode 100644 index c04d71b..0000000 --- a/unittest/utility_unittest.cc +++ /dev/null @@ -1,94 +0,0 @@ -#include "gtest/gtest.h" - -#include "webcc/utility.h" - -TEST(UtilityTest, SplitKV) { - const std::string str = "key=value"; - - std::string key; - std::string value; - - bool ok = webcc::utility::SplitKV(str, '=', &key, &value); - - EXPECT_EQ(true, ok); - EXPECT_EQ("key", key); - EXPECT_EQ("value", value); -} - -TEST(UtilityTest, SplitKV_OtherDelim) { - const std::string str = "key:value"; - - std::string key; - std::string value; - - bool ok = webcc::utility::SplitKV(str, ':', &key, &value); - EXPECT_TRUE(ok); - - EXPECT_EQ("key", key); - EXPECT_EQ("value", value); -} - -TEST(UtilityTest, SplitKV_Spaces) { - const std::string str = " key = value "; - - std::string key; - std::string value; - - bool ok = webcc::utility::SplitKV(str, '=', &key, &value); - EXPECT_TRUE(ok); - - EXPECT_EQ("key", key); - EXPECT_EQ("value", value); -} - -TEST(UtilityTest, SplitKV_SpacesNoTrim) { - const std::string str = " key = value "; - - std::string key; - std::string value; - - bool ok = webcc::utility::SplitKV(str, '=', &key, &value, false); - EXPECT_TRUE(ok); - - EXPECT_EQ(" key ", key); - EXPECT_EQ(" value ", value); -} - -TEST(UtilityTest, SplitKV_NoKey) { - const std::string str = "=value"; - - std::string key; - std::string value; - - bool ok = webcc::utility::SplitKV(str, '=', &key, &value); - EXPECT_TRUE(ok); - - EXPECT_EQ("", key); - EXPECT_EQ("value", value); -} - -TEST(UtilityTest, SplitKV_NoValue) { - const std::string str = "key="; - - std::string key; - std::string value; - - bool ok = webcc::utility::SplitKV(str, '=', &key, &value); - EXPECT_TRUE(ok); - - EXPECT_EQ("key", key); - EXPECT_EQ("", value); -} - -TEST(UtilityTest, SplitKV_NoKeyNoValue) { - const std::string str = "="; - - std::string key; - std::string value; - - bool ok = webcc::utility::SplitKV(str, '=', &key, &value); - EXPECT_TRUE(ok); - - EXPECT_EQ("", key); - EXPECT_EQ("", value); -} diff --git a/webcc/body.cc b/webcc/body.cc index 3332b93..d4cadb8 100644 --- a/webcc/body.cc +++ b/webcc/body.cc @@ -1,7 +1,5 @@ #include "webcc/body.h" -#include "boost/algorithm/string.hpp" - #include "webcc/logger.h" #include "webcc/utility.h" diff --git a/webcc/common.cc b/webcc/common.cc index f8f8e95..e87acf1 100644 --- a/webcc/common.cc +++ b/webcc/common.cc @@ -2,9 +2,8 @@ #include -#include "boost/algorithm/string.hpp" - #include "webcc/logger.h" +#include "webcc/string.h" #include "webcc/utility.h" namespace webcc { @@ -63,7 +62,7 @@ const std::string& Headers::Get(const std::string& key, bool* existed) const { std::vector
::iterator Headers::Find(const std::string& key) { auto it = headers_.begin(); for (; it != headers_.end(); ++it) { - if (boost::iequals(it->first, key)) { + if (iequals(it->first, key)) { break; } } @@ -75,7 +74,7 @@ std::vector
::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; - if (!utility::SplitKV(str, '=', &key, value)) { + if (!split_kv(key, *value, str, '=')) { return false; } @@ -124,8 +123,8 @@ void ContentType::Init(const std::string& str) { other = str.substr(pos + 1); } - boost::trim(media_type_); - boost::trim(other); + trim(media_type_); + trim(other); if (media_type_ == "multipart/form-data") { multipart_ = true; @@ -143,13 +142,13 @@ void ContentType::Init(const std::string& str) { // ----------------------------------------------------------------------------- -static void Unquote(std::string& str) { - boost::trim_if(str, boost::is_any_of("\"")); +static inline void Unquote(std::string& str) { + trim(str, "\""); } bool ContentDisposition::Init(const std::string& str) { std::vector parts; - boost::split(parts, str, boost::is_any_of(";")); + split(parts, str, ';'); if (parts.empty()) { return false; @@ -162,7 +161,7 @@ bool ContentDisposition::Init(const std::string& str) { std::string key; std::string value; for (std::size_t i = 1; i < parts.size(); ++i) { - if (!utility::SplitKV(parts[i], '=', &key, &value)) { + if (!split_kv(key, value, parts[i], '=')) { return false; } diff --git a/webcc/globals.cc b/webcc/globals.cc index 97bbec5..a5ada98 100644 --- a/webcc/globals.cc +++ b/webcc/globals.cc @@ -2,7 +2,7 @@ #include -#include "boost/algorithm/string.hpp" +#include "webcc/string.h" namespace webcc { @@ -21,29 +21,29 @@ const char DOUBLE_DASHES[2] = { '-', '-' }; namespace media_types { std::string FromExtension(const std::string& ext) { - using boost::iequals; - - if (iequals(ext, ".htm")) { return "text/html"; } - if (iequals(ext, ".html")) { return "text/html"; } - if (iequals(ext, ".php")) { return "text/html"; } - if (iequals(ext, ".css")) { return "text/css"; } - if (iequals(ext, ".txt")) { return "text/plain"; } - if (iequals(ext, ".js")) { return "application/javascript"; } - if (iequals(ext, ".json")) { return "application/json"; } - if (iequals(ext, ".xml")) { return "application/xml"; } - if (iequals(ext, ".swf")) { return "application/x-shockwave-flash"; } - if (iequals(ext, ".flv")) { return "video/x-flv"; } - if (iequals(ext, ".png")) { return "image/png"; } - if (iequals(ext, ".jpe")) { return "image/jpeg"; } - if (iequals(ext, ".jpeg")) { return "image/jpeg"; } - if (iequals(ext, ".jpg")) { return "image/jpeg"; } - if (iequals(ext, ".gif")) { return "image/gif"; } - if (iequals(ext, ".bmp")) { return "image/bmp"; } - if (iequals(ext, ".ico")) { return "image/vnd.microsoft.icon"; } - if (iequals(ext, ".tiff")) { return "image/tiff"; } - if (iequals(ext, ".tif")) { return "image/tiff"; } - if (iequals(ext, ".svg")) { return "image/svg+xml"; } - if (iequals(ext, ".svgz")) { return "image/svg+xml"; } + std::string lext = tolower(ext); + + if (lext == ".htm") { return "text/html"; } + if (lext == ".html") { return "text/html"; } + if (lext == ".php") { return "text/html"; } + if (lext == ".css") { return "text/css"; } + if (lext == ".txt") { return "text/plain"; } + if (lext == ".js") { return "application/javascript"; } + if (lext == ".json") { return "application/json"; } + if (lext == ".xml") { return "application/xml"; } + if (lext == ".swf") { return "application/x-shockwave-flash"; } + if (lext == ".flv") { return "video/x-flv"; } + if (lext == ".png") { return "image/png"; } + if (lext == ".jpe") { return "image/jpeg"; } + if (lext == ".jpeg") { return "image/jpeg"; } + if (lext == ".jpg") { return "image/jpeg"; } + if (lext == ".gif") { return "image/gif"; } + if (lext == ".bmp") { return "image/bmp"; } + if (lext == ".ico") { return "image/vnd.microsoft.icon"; } + if (lext == ".tiff") { return "image/tiff"; } + if (lext == ".tif") { return "image/tiff"; } + if (lext == ".svg") { return "image/svg+xml"; } + if (lext == ".svgz") { return "image/svg+xml"; } return "application/text"; } diff --git a/webcc/globals.h b/webcc/globals.h index f59faf9..0c636df 100644 --- a/webcc/globals.h +++ b/webcc/globals.h @@ -11,37 +11,6 @@ #include "webcc/config.h" -// ----------------------------------------------------------------------------- -// Macros - -#ifdef _MSC_VER - -#if _MSC_VER <= 1800 // VS 2013 - -// Does the compiler support "= default" for move copy constructor and -// move assignment operator? -#define WEBCC_DEFAULT_MOVE_COPY_ASSIGN 0 - -#define WEBCC_NOEXCEPT - -#else - -#define WEBCC_DEFAULT_MOVE_COPY_ASSIGN 1 - -#define WEBCC_NOEXCEPT noexcept - -#endif // _MSC_VER <= 1800 - -#else - -// GCC, Clang, etc. - -#define WEBCC_DEFAULT_MOVE_COPY_ASSIGN 1 - -#define WEBCC_NOEXCEPT noexcept - -#endif // _MSC_VER - namespace webcc { // ----------------------------------------------------------------------------- @@ -197,7 +166,7 @@ public: } // Note that `noexcept` is required by GCC. - const char* what() const WEBCC_NOEXCEPT override{ + const char* what() const noexcept override{ return message_.c_str(); } diff --git a/webcc/message.cc b/webcc/message.cc index cb11123..c231e79 100644 --- a/webcc/message.cc +++ b/webcc/message.cc @@ -2,9 +2,8 @@ #include -#include "boost/algorithm/string.hpp" - #include "webcc/logger.h" +#include "webcc/string.h" #include "webcc/utility.h" namespace webcc { @@ -53,7 +52,7 @@ bool Message::IsConnectionKeepAlive() const { return true; } - if (boost::iequals(connection, "Keep-Alive")) { + if (iequals(connection, "Keep-Alive")) { return true; } diff --git a/webcc/parser.cc b/webcc/parser.cc index 9f77913..beba2a0 100644 --- a/webcc/parser.cc +++ b/webcc/parser.cc @@ -1,7 +1,5 @@ #include "webcc/parser.h" -#include "boost/algorithm/string.hpp" - #include "webcc/logger.h" #include "webcc/message.h" #include "webcc/string.h" @@ -71,7 +69,7 @@ bool FileBodyHandler::OpenFile() { // Generate a random string as file name. // A replacement of boost::filesystem::unique_path(). - temp_path_ /= string::RandomString(10); + temp_path_ /= random_string(10); LOG_VERB("Generate a temp path for streaming: %s", temp_path_.string().c_str()); @@ -252,16 +250,16 @@ bool Parser::GetNextLine(std::size_t off, std::string* line, bool erase) { bool Parser::ParseHeaderLine(const std::string& line) { Header header; - if (!utility::SplitKV(line, ':', &header.first, &header.second)) { + if (!split_kv(header.first, header.second, line, ':')) { LOG_ERRO("Invalid header: %s", line.c_str()); return false; } - if (boost::iequals(header.first, headers::kContentLength)) { + if (iequals(header.first, headers::kContentLength)) { content_length_parsed_ = true; std::size_t content_length = kInvalidLength; - if (!utility::ToSize(header.second, 10, &content_length)) { + if (!to_size_t(header.second, 10, &content_length)) { LOG_ERRO("Invalid content length: %s.", header.second.c_str()); return false; } @@ -269,13 +267,13 @@ bool Parser::ParseHeaderLine(const std::string& line) { LOG_INFO("Content length: %u.", content_length); content_length_ = content_length; - } else if (boost::iequals(header.first, headers::kContentType)) { + } else if (iequals(header.first, headers::kContentType)) { content_type_.Parse(header.second); if (!content_type_.Valid()) { LOG_ERRO("Invalid content-type header: %s", header.second.c_str()); return false; } - } else if (boost::iequals(header.first, headers::kTransferEncoding)) { + } else if (iequals(header.first, headers::kTransferEncoding)) { if (header.second == "chunked") { // The content is chunked. chunked_ = true; @@ -394,7 +392,7 @@ bool Parser::ParseChunkSize() { hex_str = line; } - if (!utility::ToSize(hex_str, 16, &chunk_size_)) { + if (!to_size_t(hex_str, 16, &chunk_size_)) { LOG_ERRO("Invalid chunk-size: %s.", hex_str.c_str()); return false; } diff --git a/webcc/request_builder.cc b/webcc/request_builder.cc index 6758407..2231fea 100644 --- a/webcc/request_builder.cc +++ b/webcc/request_builder.cc @@ -2,6 +2,7 @@ #include "webcc/base64.h" #include "webcc/logger.h" +#include "webcc/string.h" #include "webcc/utility.h" #if WEBCC_ENABLE_GZIP @@ -37,7 +38,7 @@ RequestPtr RequestBuilder::operator()() { } else if (!form_parts_.empty()) { // Another choice to generate the boundary is like what Apache does. // See: https://stackoverflow.com/a/5686863 - auto boundary = utility::RandomUuid(); + auto boundary = random_string(30); request->SetContentType("multipart/form-data; boundary=" + boundary); diff --git a/webcc/request_parser.cc b/webcc/request_parser.cc index 6ee8a0b..28fbcf7 100644 --- a/webcc/request_parser.cc +++ b/webcc/request_parser.cc @@ -2,10 +2,9 @@ #include -#include "boost/algorithm/string.hpp" - #include "webcc/logger.h" #include "webcc/request.h" +#include "webcc/string.h" #include "webcc/utility.h" namespace webcc { @@ -36,7 +35,7 @@ bool RequestParser::OnHeadersEnd() { bool RequestParser::ParseStartLine(const std::string& line) { std::vector strs; - boost::split(strs, line, boost::is_any_of(" "), boost::token_compress_on); + split(strs, line, ' ', true); if (strs.size() != 3) { return false; @@ -190,7 +189,7 @@ bool RequestParser::ParsePartHeaders(bool* need_more_data) { } Header header; - if (!utility::SplitKV(line, ':', &header.first, &header.second)) { + if (!split_kv(header.first, header.second, line, ':')) { LOG_ERRO("Invalid part header line: %s", line.c_str()); return false; } @@ -199,7 +198,7 @@ bool RequestParser::ParsePartHeaders(bool* need_more_data) { header.second.c_str()); // Parse Content-Disposition. - if (boost::iequals(header.first, headers::kContentDisposition)) { + if (iequals(header.first, headers::kContentDisposition)) { ContentDisposition content_disposition(header.second); if (!content_disposition.valid()) { LOG_ERRO("Invalid content-disposition header: %s", diff --git a/webcc/response_parser.cc b/webcc/response_parser.cc index bbc3966..7560b81 100644 --- a/webcc/response_parser.cc +++ b/webcc/response_parser.cc @@ -1,9 +1,8 @@ #include "webcc/response_parser.h" -#include "boost/algorithm/string.hpp" - #include "webcc/logger.h" #include "webcc/response.h" +#include "webcc/string.h" namespace webcc { @@ -54,7 +53,7 @@ bool ResponseParser::ParseStartLine(const std::string& line) { return false; } - if (!boost::starts_with(parts[0], "HTTP/1.")) { + if (!starts_with(parts[0], "HTTP/1.")) { LOG_ERRO("Invalid HTTP version: %s", parts[0].c_str()); return false; } diff --git a/webcc/router.cc b/webcc/router.cc index d16caea..f728716 100644 --- a/webcc/router.cc +++ b/webcc/router.cc @@ -2,9 +2,8 @@ #include -#include "boost/algorithm/string.hpp" - #include "webcc/logger.h" +#include "webcc/string.h" namespace webcc { @@ -60,7 +59,7 @@ ViewPtr Router::FindView(const std::string& method, const std::string& url, return route.view; } } else { - if (boost::iequals(route.url, url)) { + if (iequals(route.url, url)) { return route.view; } } @@ -88,7 +87,7 @@ bool Router::MatchView(const std::string& method, const std::string& url, return true; } } else { - if (boost::iequals(route.url, url)) { + if (iequals(route.url, url)) { *stream = route.view->Stream(method); return true; } diff --git a/webcc/string.cc b/webcc/string.cc index 9e6f72c..cac769a 100644 --- a/webcc/string.cc +++ b/webcc/string.cc @@ -3,10 +3,9 @@ #include namespace webcc { -namespace string { -// See: https://stackoverflow.com/a/24586587 -std::string RandomString(std::size_t length) { +// Ref: https://stackoverflow.com/a/24586587 +std::string random_string(std::size_t length) { static const char chrs[] = "0123456789" "abcdefghijklmnopqrstuvwxyz" @@ -26,15 +25,31 @@ std::string RandomString(std::size_t length) { return s; } -bool EqualsNoCase(const std::string& str1, const std::string& str2) { - if (str1.size() != str2.size()) { +bool to_size_t(const std::string& str, int base, std::size_t* size) { + try { + *size = static_cast(std::stoul(str, 0, base)); + } catch (const std::exception&) { return false; } + return true; +} + +bool split_kv(std::string& key, std::string& value, const std::string& str, + char delim, bool trim_spaces) { + std::size_t pos = str.find(delim); + if (pos == std::string::npos) { + return false; + } + + key = str.substr(0, pos); + value = str.substr(pos + 1); + + if (trim_spaces) { + trim(key); + trim(value); + } - return std::equal(str1.begin(), str1.end(), str2.begin(), [](int c1, int c2) { - return std::toupper(c1) == std::toupper(c2); - }); + return true; } -} // namespace string } // namespace webcc diff --git a/webcc/string.h b/webcc/string.h index 1b9a8ce..b78d176 100644 --- a/webcc/string.h +++ b/webcc/string.h @@ -7,22 +7,87 @@ #include namespace webcc { -namespace string { // Get a randomly generated string with the given length. -std::string RandomString(std::size_t length); +std::string random_string(std::size_t length); -// TODO: What about std::wstring? -bool EqualsNoCase(const std::string& str1, const std::string& str2); +// Convert string to size_t. +bool to_size_t(const std::string& str, int base, std::size_t* size); +inline std::string toupper(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { return std::toupper(c); }); + return s; +} + +inline std::string tolower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { return std::tolower(c); }); + return s; +} + +inline bool iequals(const std::string& str1, const std::string& str2) { + if (str1.size() != str2.size()) { + return false; + } + + return std::equal(str1.begin(), str1.end(), str2.begin(), [](int c1, int c2) { + return std::toupper(c1) == std::toupper(c2); + }); +} + +inline bool starts_with(const std::string& str, const std::string& sub) { + if (sub.empty()) { + return false; + } + return str.find(sub) == 0; +} + +inline std::string& ltrim(std::string& str, const std::string& chars = "\t ") { + str.erase(0, str.find_first_not_of(chars)); + return str; +} + +inline std::string& rtrim(std::string& str, const std::string& chars = "\t ") { + str.erase(str.find_last_not_of(chars) + 1); + return str; +} + +inline std::string& trim(std::string& str, const std::string& chars = "\t ") { + return ltrim(rtrim(str, chars), chars); +} + +// \param compress_token Same as boost::token_compress_on, especially useful +// when the delimeter is space. template -void Split(const std::string& str, Container& cont) { - std::istringstream iss(str); - std::copy(std::istream_iterator(iss), - std::istream_iterator(), std::back_inserter(cont)); +void split(Container& cont, const std::string& str, char delim = ' ', + bool compress_token = false) { + std::size_t i = 0; + std::size_t p = 0; + + i = str.find(delim); + + while (i != std::string::npos) { + cont.push_back(str.substr(p, i - p)); + p = i + 1; + + if (compress_token) { + while (str[p] == delim) { + ++p; + } + } + + i = str.find(delim, p); + } + + cont.push_back(str.substr(p, i - p)); } -} // namespace string +// Split a key-value string. +// E.g., split "Connection: Keep-Alive". +bool split_kv(std::string& key, std::string& value, const std::string& str, + char delim, bool trim_spaces = true); + } // namespace webcc #endif // WEBCC_STRING_H_ diff --git a/webcc/url.cc b/webcc/url.cc index 2feb1a0..86daf2f 100644 --- a/webcc/url.cc +++ b/webcc/url.cc @@ -4,8 +4,7 @@ #include #include -#include "boost/algorithm/string/trim.hpp" - +#include "webcc/string.h" #include "webcc/utility.h" namespace webcc { @@ -238,7 +237,8 @@ void Url::AppendQuery(const std::string& key, const std::string& value, } void Url::Parse(const std::string& str) { - std::string tmp = boost::trim_left_copy(str); + std::string tmp = str; + ltrim(tmp); std::size_t p = std::string::npos; @@ -309,7 +309,7 @@ UrlQuery::UrlQuery(const std::string& encoded_str) { std::string key; std::string value; - if (utility::SplitKV(kv, '=', &key, &value, false)) { + if (split_kv(key, value, kv, '=', false)) { parameters_.push_back({ DecodeUnsafe(key), DecodeUnsafe(value) }); } } diff --git a/webcc/url.h b/webcc/url.h index a8e7a54..3b70fc2 100644 --- a/webcc/url.h +++ b/webcc/url.h @@ -27,34 +27,9 @@ public: explicit Url(const std::string& str, bool encode = false); -#if WEBCC_DEFAULT_MOVE_COPY_ASSIGN - Url(Url&&) = default; Url& operator=(Url&&) = default; -#else - - Url(Url&& rhs) - : scheme_(std::move(rhs.scheme_)), - host_(std::move(rhs.host_)), - port_(std::move(rhs.port_)), - path_(std::move(rhs.path_)), - query_(std::move(rhs.query_)) { - } - - Url& operator=(Url&& rhs) { - if (&rhs != this) { - scheme_ = std::move(rhs.scheme_); - host_ = std::move(rhs.host_); - port_ = std::move(rhs.port_); - path_ = std::move(rhs.path_); - query_ = std::move(rhs.query_); - } - return *this; - } - -#endif // WEBCC_DEFAULT_MOVE_COPY_ASSIGN - const std::string& scheme() const { return scheme_; } diff --git a/webcc/utility.cc b/webcc/utility.cc index b0da0f8..ca3cd66 100644 --- a/webcc/utility.cc +++ b/webcc/utility.cc @@ -7,22 +7,12 @@ #include // for put_time #include -#include "boost/algorithm/string.hpp" -#include "boost/uuid/random_generator.hpp" -#include "boost/uuid/uuid_io.hpp" - +#include "webcc/string.h" #include "webcc/version.h" namespace webcc { namespace utility { -std::string RandomUuid() { - boost::uuids::uuid u = boost::uuids::random_generator()(); - std::stringstream ss; - ss << u; - return ss.str(); -} - const std::string& UserAgent() { static auto s_user_agent = std::string("Webcc/") + WEBCC_VERSION; return s_user_agent; @@ -35,33 +25,6 @@ std::string GetTimestamp() { return ss.str(); } -bool SplitKV(const std::string& str, char delimiter, std::string* key, - std::string* value, bool trim) { - std::size_t pos = str.find(delimiter); - if (pos == std::string::npos) { - return false; - } - - *key = str.substr(0, pos); - *value = str.substr(pos + 1); - - if (trim) { - boost::trim(*key); - boost::trim(*value); - } - - return true; -} - -bool ToSize(const std::string& str, int base, std::size_t* size) { - try { - *size = static_cast(std::stoul(str, 0, base)); - } catch (const std::exception&) { - return false; - } - return true; -} - std::size_t TellSize(const std::filesystem::path& path) { // Flag "ate": seek to the end of stream immediately after open. std::ifstream stream{ path, std::ios::binary | std::ios::ate }; @@ -91,7 +54,7 @@ bool ReadFile(const std::filesystem::path& path, std::string* output) { void DumpByLine(const std::string& data, std::ostream& os, const std::string& prefix) { std::vector lines; - boost::split(lines, data, boost::is_any_of("\n")); + split(lines, data, '\n'); std::size_t size = 0; diff --git a/webcc/utility.h b/webcc/utility.h index ecd181f..d0c7825 100644 --- a/webcc/utility.h +++ b/webcc/utility.h @@ -15,9 +15,6 @@ class path; namespace webcc { namespace utility { -// Get a randomly generated UUID. -std::string RandomUuid(); - // Get default user agent for HTTP headers. const std::string& UserAgent(); @@ -26,14 +23,6 @@ const std::string& UserAgent(); // See: https://tools.ietf.org/html/rfc7231#section-7.1.1.2 std::string GetTimestamp(); -// Split a key-value string. -// E.g., split "Connection: Keep-Alive". -bool SplitKV(const std::string& str, char delimiter, std::string* key, - std::string* value, bool trim = true); - -// Convert string to size_t. -bool ToSize(const std::string& str, int base, std::size_t* size); - // Tell the size in bytes of the given file. // Return kInvalidLength (-1) on failure. std::size_t TellSize(const std::filesystem::path& path);