From ec63c4345ea9a21bd21bf5588d462901282f8d2e Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 7 Jun 2017 18:36:53 +0200 Subject: [PATCH] Cleanup of #127: using standalone asio. --- .travis.yml | 6 +- client_http.hpp | 199 +++++++++++++++++++------------------------ client_https.hpp | 59 +++++++------ http_examples.cpp | 2 +- https_examples.cpp | 2 +- server_http.hpp | 197 +++++++++++++++++++----------------------- server_https.hpp | 32 +++---- tests/parse_test.cpp | 6 +- 8 files changed, 238 insertions(+), 265 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8187122..2b6b746 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ services: script: - sudo docker run -it -v "$PWD:/repository" debian:testing sh -c " apt-get update && - apt-get install --yes cmake make g\+\+ clang perl libssl-dev libboost-thread-dev libboost-regex-dev libboost-date-time-dev libboost-filesystem-dev && + apt-get install --yes cmake make g\+\+ clang perl libssl-dev libboost-thread-dev libboost-regex-dev libboost-date-time-dev libboost-filesystem-dev libasio-dev && cd /repository && mkdir build && cd build && scan-build cmake -DCMAKE_CXX_FLAGS=-Werror .. && scan-build --status-bugs make && @@ -16,5 +16,9 @@ script: rm -r * && CXX=g++ cmake -DCMAKE_CXX_FLAGS=-Werror .. && make && + CTEST_OUTPUT_ON_FAILURE=1 make test && + rm -r * && + CXX=g++ cmake -DCMAKE_CXX_FLAGS=\"-Werror -DUSE_STANDALONE_ASIO\" .. && + make && CTEST_OUTPUT_ON_FAILURE=1 make test " diff --git a/client_http.hpp b/client_http.hpp index c8b17cf..e5e2d46 100644 --- a/client_http.hpp +++ b/client_http.hpp @@ -7,79 +7,58 @@ #include #include -#ifdef NO_BOOST +#ifdef USE_STANDALONE_ASIO #include #include #include #include -namespace asio_ns = asio; -namespace error_ns = std; -namespace merror_ns = std; -# ifndef CASE_INSENSITIVE_EQUALS_AND_HASH -# define CASE_INSENSITIVE_EQUALS_AND_HASH -class case_insensitive_equals { -public: - bool operator()(const std::string &Left, const std::string &Right) const { - return Left.size() == Right.size() - && std::equal ( Left.begin() , Left.end() , Right.begin() , - []( char a , char b ) { - return tolower(a) == tolower(b); - }); - } -}; -bool IEQUALS(const std::string& Left, const std::string& Right) -{ - return Left.size() == Right.size() - && std::equal ( Left.begin() , Left.end() , Right.begin() , []( char a , char b ) { - return tolower(a) == tolower(b); - }); +namespace SimpleWeb { + using error_code = std::error_code; + using errc = std::errc; + using system_error = std::system_error; } -class case_insensitive_hash { -public: - size_t operator()(const std::string& Keyval) const - { - //You might need a better hash function than this - size_t h = 0; - std::for_each( Keyval.begin() , Keyval.end() , [&](char c ) { - h += tolower(c); - }); - return h; - } -}; -# endif - #else #include #include #include #include -namespace asio_ns = boost::asio; -namespace error_ns = boost::system; -namespace merror_ns = boost::system::errc; +namespace SimpleWeb { + namespace asio = boost::asio; + using error_code = boost::system::error_code; + namespace errc = boost::system::errc; + using system_error = boost::system::system_error; +} +#endif # ifndef CASE_INSENSITIVE_EQUALS_AND_HASH # define CASE_INSENSITIVE_EQUALS_AND_HASH -# define IEQUALS boost::iequals -//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html -class case_insensitive_equals { -public: - bool operator()(const std::string &key1, const std::string &key2) const { - return boost::algorithm::iequals(key1, key2); - } -}; -class case_insensitive_hash { -public: - size_t operator()(const std::string &key) const { - std::size_t seed=0; - for(auto &c: key) - boost::hash_combine(seed, std::tolower(c)); - return seed; - } -}; +namespace SimpleWeb { + bool iequals(const std::string &str1, const std::string &str2) { + return str1.size() == str2.size() && + std::equal(str1.begin(), str1.end(), str2.begin(), [](char a, char b) { + return tolower(a) == tolower(b); + }); + } + class case_insensitive_equals { + public: + bool operator()(const std::string &str1, const std::string &str2) const { + return iequals(str1, str2); + } + }; + // Based on https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x/2595226#2595226 + class case_insensitive_hash { + public: + size_t operator()(const std::string &str) const { + size_t h = 0; + std::hash hash; + for (auto c : str) + h ^= hash(tolower(c)) + 0x9e3779b9 + (h << 6) + (h >> 2); + return h; + } + }; +} # endif -#endif - namespace SimpleWeb { template class Client; @@ -100,7 +79,7 @@ namespace SimpleWeb { std::unordered_multimap header; private: - asio_ns::streambuf content_buffer; + asio::streambuf content_buffer; Response(): content(&content_buffer) {} }; @@ -126,10 +105,10 @@ namespace SimpleWeb { auto corrected_path=path; if(corrected_path=="") corrected_path="/"; - if(!config.proxy_server.empty() && std::is_same::value) + if(!config.proxy_server.empty() && std::is_same::value) corrected_path="http://"+host+':'+std::to_string(port)+corrected_path; - asio_ns::streambuf write_buffer; + asio::streambuf write_buffer; std::ostream write_stream(&write_buffer); write_stream << request_type << " " << corrected_path << " HTTP/1.1\r\n"; write_stream << "Host: " << host << "\r\n"; @@ -143,21 +122,21 @@ namespace SimpleWeb { connect(); auto timer=get_timeout_timer(); - asio_ns::async_write(*socket, write_buffer, - [this, &content, timer](const error_ns::error_code &ec, size_t /*bytes_transferred*/) { + asio::async_write(*socket, write_buffer, + [this, &content, timer](const error_code &ec, size_t /*bytes_transferred*/) { if(timer) timer->cancel(); if(!ec) { if(!content.empty()) { auto timer=get_timeout_timer(); - asio_ns::async_write(*socket, asio_ns::buffer(content.data(), content.size()), - [this, timer](const error_ns::error_code &ec, size_t /*bytes_transferred*/) { + asio::async_write(*socket, asio::buffer(content.data(), content.size()), + [this, timer](const error_code &ec, size_t /*bytes_transferred*/) { if(timer) timer->cancel(); if(ec) { std::lock_guard lock(socket_mutex); this->socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); } @@ -165,7 +144,7 @@ namespace SimpleWeb { else { std::lock_guard lock(socket_mutex); socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); io_service.reset(); @@ -179,14 +158,14 @@ namespace SimpleWeb { auto corrected_path=path; if(corrected_path=="") corrected_path="/"; - if(!config.proxy_server.empty() && std::is_same::value) + if(!config.proxy_server.empty() && std::is_same::value) corrected_path="http://"+host+':'+std::to_string(port)+corrected_path; content.seekp(0, std::ios::end); auto content_length=content.tellp(); content.seekp(0, std::ios::beg); - asio_ns::streambuf write_buffer; + asio::streambuf write_buffer; std::ostream write_stream(&write_buffer); write_stream << request_type << " " << corrected_path << " HTTP/1.1\r\n"; write_stream << "Host: " << host << "\r\n"; @@ -202,14 +181,14 @@ namespace SimpleWeb { connect(); auto timer=get_timeout_timer(); - asio_ns::async_write(*socket, write_buffer, - [this, timer](const error_ns::error_code &ec, size_t /*bytes_transferred*/) { + asio::async_write(*socket, write_buffer, + [this, timer](const error_code &ec, size_t /*bytes_transferred*/) { if(timer) timer->cancel(); if(ec) { std::lock_guard lock(socket_mutex); socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); io_service.reset(); @@ -221,15 +200,15 @@ namespace SimpleWeb { void close() { std::lock_guard lock(socket_mutex); if(socket) { - error_ns::error_code ec; - socket->lowest_layer().shutdown(asio_ns::ip::tcp::socket::shutdown_both, ec); + error_code ec; + socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec); socket->lowest_layer().close(); } } protected: - asio_ns::io_service io_service; - asio_ns::ip::tcp::resolver resolver; + asio::io_service io_service; + asio::ip::tcp::resolver resolver; std::unique_ptr socket; std::mutex socket_mutex; @@ -259,15 +238,15 @@ namespace SimpleWeb { virtual void connect()=0; - std::shared_ptr get_timeout_timer(size_t timeout=0) { + std::shared_ptr get_timeout_timer(size_t timeout=0) { if(timeout==0) timeout=config.timeout; if(timeout==0) return nullptr; - auto timer=std::make_shared(io_service); + auto timer=std::make_shared(io_service); timer->expires_from_now(boost::posix_time::seconds(timeout)); - timer->async_wait([this](const error_ns::error_code& ec) { + timer->async_wait([this](const error_code& ec) { if(!ec) { close(); } @@ -304,11 +283,11 @@ namespace SimpleWeb { std::shared_ptr request_read() { std::shared_ptr response(new Response()); - asio_ns::streambuf chunked_streambuf; + asio::streambuf chunked_streambuf; auto timer=get_timeout_timer(); - asio_ns::async_read_until(*socket, response->content_buffer, "\r\n\r\n", - [this, &response, &chunked_streambuf, timer](const error_ns::error_code& ec, size_t bytes_transferred) { + asio::async_read_until(*socket, response->content_buffer, "\r\n\r\n", + [this, &response, &chunked_streambuf, timer](const error_code& ec, size_t bytes_transferred) { if(timer) timer->cancel(); if(!ec) { @@ -321,15 +300,15 @@ namespace SimpleWeb { auto content_length=stoull(header_it->second); if(content_length>num_additional_bytes) { auto timer=get_timeout_timer(); - asio_ns::async_read(*socket, response->content_buffer, - asio_ns::transfer_exactly(content_length-num_additional_bytes), - [this, timer](const error_ns::error_code& ec, size_t /*bytes_transferred*/) { + asio::async_read(*socket, response->content_buffer, + asio::transfer_exactly(content_length-num_additional_bytes), + [this, timer](const error_code& ec, size_t /*bytes_transferred*/) { if(timer) timer->cancel(); if(ec) { std::lock_guard lock(socket_mutex); this->socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); } @@ -339,15 +318,15 @@ namespace SimpleWeb { } else if(response->http_version<"1.1" || ((header_it=response->header.find("Connection"))!=response->header.end() && header_it->second=="close")) { auto timer=get_timeout_timer(); - asio_ns::async_read(*socket, response->content_buffer, - [this, timer](const error_ns::error_code& ec, size_t /*bytes_transferred*/) { + asio::async_read(*socket, response->content_buffer, + [this, timer](const error_code& ec, size_t /*bytes_transferred*/) { if(timer) timer->cancel(); if(ec) { std::lock_guard lock(socket_mutex); this->socket=nullptr; - if(ec!=asio_ns::error::eof) - throw error_ns::system_error(ec); + if(ec!=asio::error::eof) + throw system_error(ec); } }); } @@ -355,7 +334,7 @@ namespace SimpleWeb { else { std::lock_guard lock(socket_mutex); socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); io_service.reset(); @@ -364,10 +343,10 @@ namespace SimpleWeb { return response; } - void request_read_chunked(const std::shared_ptr &response, asio_ns::streambuf &streambuf) { + void request_read_chunked(const std::shared_ptr &response, asio::streambuf &streambuf) { auto timer=get_timeout_timer(); - asio_ns::async_read_until(*socket, response->content_buffer, "\r\n", - [this, &response, &streambuf, timer](const error_ns::error_code& ec, size_t bytes_transferred) { + asio::async_read_until(*socket, response->content_buffer, "\r\n", + [this, &response, &streambuf, timer](const error_code& ec, size_t bytes_transferred) { if(timer) timer->cancel(); if(!ec) { @@ -401,9 +380,9 @@ namespace SimpleWeb { if((2+length)>num_additional_bytes) { auto timer=get_timeout_timer(); - asio_ns::async_read(*socket, response->content_buffer, - asio_ns::transfer_exactly(2+length-num_additional_bytes), - [this, post_process, timer](const error_ns::error_code& ec, size_t /*bytes_transferred*/) { + asio::async_read(*socket, response->content_buffer, + asio::transfer_exactly(2+length-num_additional_bytes), + [this, post_process, timer](const error_code& ec, size_t /*bytes_transferred*/) { if(timer) timer->cancel(); if(!ec) { @@ -412,7 +391,7 @@ namespace SimpleWeb { else { std::lock_guard lock(socket_mutex); this->socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); } @@ -422,7 +401,7 @@ namespace SimpleWeb { else { std::lock_guard lock(socket_mutex); socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); } @@ -431,7 +410,7 @@ namespace SimpleWeb { template class Client : public ClientBase {}; - typedef asio_ns::ip::tcp::socket HTTP; + typedef asio::ip::tcp::socket HTTP; template<> class Client : public ClientBase { @@ -441,15 +420,15 @@ namespace SimpleWeb { protected: void connect() { if(!socket || !socket->is_open()) { - std::unique_ptr query; + std::unique_ptr query; if(config.proxy_server.empty()) - query=std::unique_ptr(new asio_ns::ip::tcp::resolver::query(host, std::to_string(port))); + query=std::unique_ptr(new asio::ip::tcp::resolver::query(host, std::to_string(port))); else { auto proxy_host_port=parse_host_port(config.proxy_server, 8080); - query=std::unique_ptr(new asio_ns::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second))); + query=std::unique_ptr(new asio::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second))); } - resolver.async_resolve(*query, [this](const error_ns::error_code &ec, - asio_ns::ip::tcp::resolver::iterator it){ + resolver.async_resolve(*query, [this](const error_code &ec, + asio::ip::tcp::resolver::iterator it){ if(!ec) { { std::lock_guard lock(socket_mutex); @@ -457,25 +436,25 @@ namespace SimpleWeb { } auto timer=get_timeout_timer(config.timeout_connect); - asio_ns::async_connect(*socket, it, [this, timer] - (const error_ns::error_code &ec, asio_ns::ip::tcp::resolver::iterator /*it*/){ + asio::async_connect(*socket, it, [this, timer] + (const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/){ if(timer) timer->cancel(); if(!ec) { - asio_ns::ip::tcp::no_delay option(true); + asio::ip::tcp::no_delay option(true); this->socket->set_option(option); } else { std::lock_guard lock(socket_mutex); this->socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); } else { std::lock_guard lock(socket_mutex); socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); io_service.reset(); diff --git a/client_https.hpp b/client_https.hpp index 487a24e..96053ed 100644 --- a/client_https.hpp +++ b/client_https.hpp @@ -2,10 +2,15 @@ #define CLIENT_HTTPS_HPP #include "client_http.hpp" + +#ifdef USE_STANDALONE_ASIO +#include +#else #include +#endif namespace SimpleWeb { - typedef asio_ns::ssl::stream HTTPS; + typedef asio::ssl::stream HTTPS; template<> class Client : public ClientBase { @@ -13,14 +18,14 @@ namespace SimpleWeb { Client(const std::string& server_port_path, bool verify_certificate=true, const std::string& cert_file=std::string(), const std::string& private_key_file=std::string(), const std::string& verify_file=std::string()) : - ClientBase::ClientBase(server_port_path, 443), context(asio_ns::ssl::context::tlsv12) { + ClientBase::ClientBase(server_port_path, 443), context(asio::ssl::context::tlsv12) { if(cert_file.size()>0 && private_key_file.size()>0) { context.use_certificate_chain_file(cert_file); - context.use_private_key_file(private_key_file, asio_ns::ssl::context::pem); + context.use_private_key_file(private_key_file, asio::ssl::context::pem); } if(verify_certificate) - context.set_verify_callback(asio_ns::ssl::rfc2818_verification(host)); + context.set_verify_callback(asio::ssl::rfc2818_verification(host)); if(verify_file.size()>0) context.load_verify_file(verify_file); @@ -28,25 +33,25 @@ namespace SimpleWeb { context.set_default_verify_paths(); if(verify_file.size()>0 || verify_certificate) - context.set_verify_mode(asio_ns::ssl::verify_peer); + context.set_verify_mode(asio::ssl::verify_peer); else - context.set_verify_mode(asio_ns::ssl::verify_none); + context.set_verify_mode(asio::ssl::verify_none); } protected: - asio_ns::ssl::context context; + asio::ssl::context context; void connect() { if(!socket || !socket->lowest_layer().is_open()) { - std::unique_ptr query; + std::unique_ptr query; if(config.proxy_server.empty()) - query=std::unique_ptr(new asio_ns::ip::tcp::resolver::query(host, std::to_string(port))); + query=std::unique_ptr(new asio::ip::tcp::resolver::query(host, std::to_string(port))); else { auto proxy_host_port=parse_host_port(config.proxy_server, 8080); - query=std::unique_ptr(new asio_ns::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second))); + query=std::unique_ptr(new asio::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second))); } resolver.async_resolve(*query, [this] - (const error_ns::error_code &ec, asio_ns::ip::tcp::resolver::iterator it){ + (const error_code &ec, asio::ip::tcp::resolver::iterator it){ if(!ec) { { std::lock_guard lock(socket_mutex); @@ -54,44 +59,44 @@ namespace SimpleWeb { } auto timer=get_timeout_timer(config.timeout_connect); - asio_ns::async_connect(socket->lowest_layer(), it, [this, timer] - (const error_ns::error_code &ec, asio_ns::ip::tcp::resolver::iterator /*it*/){ + asio::async_connect(socket->lowest_layer(), it, [this, timer] + (const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/){ if(timer) timer->cancel(); if(!ec) { - asio_ns::ip::tcp::no_delay option(true); + asio::ip::tcp::no_delay option(true); this->socket->lowest_layer().set_option(option); } else { std::lock_guard lock(socket_mutex); this->socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); } else { std::lock_guard lock(socket_mutex); socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); io_service.reset(); io_service.run(); if(!config.proxy_server.empty()) { - asio_ns::streambuf write_buffer; + asio::streambuf write_buffer; std::ostream write_stream(&write_buffer); auto host_port=host+':'+std::to_string(port); write_stream << "CONNECT "+host_port+" HTTP/1.1\r\n" << "Host: " << host_port << "\r\n\r\n"; auto timer=get_timeout_timer(); - asio_ns::async_write(socket->next_layer(), write_buffer, - [this, timer](const error_ns::error_code &ec, size_t /*bytes_transferred*/) { + asio::async_write(socket->next_layer(), write_buffer, + [this, timer](const error_code &ec, size_t /*bytes_transferred*/) { if(timer) timer->cancel(); if(ec) { std::lock_guard lock(socket_mutex); socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); io_service.reset(); @@ -99,14 +104,14 @@ namespace SimpleWeb { std::shared_ptr response(new Response()); timer=get_timeout_timer(); - asio_ns::async_read_until(socket->next_layer(), response->content_buffer, "\r\n\r\n", - [this, timer](const error_ns::error_code& ec, size_t /*bytes_transferred*/) { + asio::async_read_until(socket->next_layer(), response->content_buffer, "\r\n\r\n", + [this, timer](const error_code& ec, size_t /*bytes_transferred*/) { if(timer) timer->cancel(); if(ec) { std::lock_guard lock(socket_mutex); socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); io_service.reset(); @@ -115,19 +120,19 @@ namespace SimpleWeb { if (response->status_code.empty() || response->status_code.compare(0, 3, "200") != 0) { std::lock_guard lock(socket_mutex); socket=nullptr; - throw merror_ns::make_error_code(error_ns::errc::permission_denied); + throw make_error_code(errc::permission_denied); } } auto timer=get_timeout_timer(); - this->socket->async_handshake(asio_ns::ssl::stream_base::client, - [this, timer](const error_ns::error_code& ec) { + this->socket->async_handshake(asio::ssl::stream_base::client, + [this, timer](const error_code& ec) { if(timer) timer->cancel(); if(ec) { std::lock_guard lock(socket_mutex); socket=nullptr; - throw error_ns::system_error(ec); + throw system_error(ec); } }); io_service.reset(); diff --git a/http_examples.cpp b/http_examples.cpp index 539f271..cfd2e58 100644 --- a/http_examples.cpp +++ b/http_examples.cpp @@ -196,7 +196,7 @@ void default_resource_send(const HttpServer &server, const shared_ptrread(&buffer[0], buffer.size()).gcount())>0) { response->write(&buffer[0], read_length); if(read_length==static_cast(buffer.size())) { - server.send(response, [&server, response, ifs](const error_ns::error_code &ec) { + server.send(response, [&server, response, ifs](const SimpleWeb::error_code &ec) { if(!ec) default_resource_send(server, response, ifs); else diff --git a/https_examples.cpp b/https_examples.cpp index 12aad5b..ec7a0b7 100644 --- a/https_examples.cpp +++ b/https_examples.cpp @@ -193,7 +193,7 @@ void default_resource_send(const HttpsServer &server, const shared_ptrread(&buffer[0], buffer.size()).gcount())>0) { response->write(&buffer[0], read_length); if(read_length==static_cast(buffer.size())) { - server.send(response, [&server, response, ifs](const error_ns::error_code &ec) { + server.send(response, [&server, response, ifs](const SimpleWeb::error_code &ec) { if(!ec) default_resource_send(server, response, ifs); else diff --git a/server_http.hpp b/server_http.hpp index acf67ce..3d027ca 100644 --- a/server_http.hpp +++ b/server_http.hpp @@ -8,83 +8,66 @@ #include #include -#ifdef NO_BOOST +#ifdef USE_STANDALONE_ASIO #include #include -#include -namespace asio_ns = asio; -namespace error_ns = std; -namespace merror_ns = std; -# ifndef CASE_INSENSITIVE_EQUALS_AND_HASH -# define CASE_INSENSITIVE_EQUALS_AND_HASH -class case_insensitive_equals { -public: - bool operator()(const std::string &Left, const std::string &Right) const { - return Left.size() == Right.size() - && std::equal ( Left.begin() , Left.end() , Right.begin() , - []( char a , char b ) { - return tolower(a) == tolower(b); - }); - } -}; -bool IEQUALS(const std::string& Left, const std::string& Right) -{ - return Left.size() == Right.size() - && std::equal ( Left.begin() , Left.end() , Right.begin() , []( char a , char b ) { - return tolower(a) == tolower(b); - }); +namespace SimpleWeb { + using error_code = std::error_code; + using errc = std::errc; + error_code (&make_error_code)(errc) = std::make_error_code; } -class case_insensitive_hash { -public: - size_t operator()(const std::string& Keyval) const - { - //You might need a better hash function than this - size_t h = 0; - std::for_each( Keyval.begin() , Keyval.end() , [&](char c ) { - h += tolower(c); - }); - return h; - } -}; -# endif - #else #include #include #include -namespace asio_ns = boost::asio; -namespace error_ns = boost::system; -namespace merror_ns = boost::system::errc; +namespace SimpleWeb { + namespace asio = boost::asio; + using error_code = boost::system::error_code; + namespace errc = boost::system::errc; + error_code (&make_error_code)(boost::system::errc::errc_t) = boost::system::errc::make_error_code; +} +#endif # ifndef CASE_INSENSITIVE_EQUALS_AND_HASH # define CASE_INSENSITIVE_EQUALS_AND_HASH -# define IEQUALS boost::iequals -//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html -class case_insensitive_equals { -public: - bool operator()(const std::string &key1, const std::string &key2) const { - return boost::algorithm::iequals(key1, key2); - } -}; -class case_insensitive_hash { -public: - size_t operator()(const std::string &key) const { - std::size_t seed=0; - for(auto &c: key) - boost::hash_combine(seed, std::tolower(c)); - return seed; - } -}; +namespace SimpleWeb { + bool iequals(const std::string &str1, const std::string &str2) { + return str1.size() == str2.size() && + std::equal(str1.begin(), str1.end(), str2.begin(), [](char a, char b) { + return tolower(a) == tolower(b); + }); + } + class case_insensitive_equals { + public: + bool operator()(const std::string &str1, const std::string &str2) const { + return iequals(str1, str2); + } + }; + // Based on https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x/2595226#2595226 + class case_insensitive_hash { + public: + size_t operator()(const std::string &str) const { + size_t h = 0; + std::hash hash; + for (auto c : str) + h ^= hash(tolower(c)) + 0x9e3779b9 + (h << 6) + (h >> 2); + return h; + } + }; +} # endif -#endif // Late 2017 TODO: remove the following checks and always use std::regex #ifdef USE_BOOST_REGEX #include -namespace regex_ns = boost; +namespace SimpleWeb { + namespace regex = boost; +} #else #include -namespace regex_ns = std; +namespace SimpleWeb { + namespace regex = std; +} #endif // TODO when switching to c++14, use [[deprecated]] instead @@ -110,7 +93,7 @@ namespace SimpleWeb { class Response : public std::ostream { friend class ServerBase; - asio_ns::streambuf streambuf; + asio::streambuf streambuf; std::shared_ptr socket; @@ -140,8 +123,8 @@ namespace SimpleWeb { return ss.str(); } private: - asio_ns::streambuf &streambuf; - Content(asio_ns::streambuf &streambuf): std::istream(&streambuf), streambuf(streambuf) {} + asio::streambuf &streambuf; + Content(asio::streambuf &streambuf): std::istream(&streambuf), streambuf(streambuf) {} }; class Request { @@ -154,7 +137,7 @@ namespace SimpleWeb { std::unordered_multimap header; - regex_ns::smatch path_match; + regex::smatch path_match; std::string remote_endpoint_address; unsigned short remote_endpoint_port; @@ -165,10 +148,10 @@ namespace SimpleWeb { auto qs_start_pos = path.find('?'); if (qs_start_pos != std::string::npos && qs_start_pos + 1 < path.size()) { ++qs_start_pos; - static regex_ns::regex pattern("([\\w+%]+)=?([^&]*)"); + static regex::regex pattern("([\\w+%]+)=?([^&]*)"); int submatches[] = {1, 2}; - auto it_begin = regex_ns::sregex_token_iterator(path.begin() + qs_start_pos, path.end(), pattern, submatches); - auto it_end = regex_ns::sregex_token_iterator(); + auto it_begin = regex::sregex_token_iterator(path.begin() + qs_start_pos, path.end(), pattern, submatches); + auto it_end = regex::sregex_token_iterator(); for (auto it = it_begin; it != it_end; ++it) { auto submatch1=it->str(); auto submatch2=(++it)->str(); @@ -197,7 +180,7 @@ namespace SimpleWeb { catch(...) {} } - asio_ns::streambuf streambuf; + asio::streambuf streambuf; }; class Config { @@ -223,11 +206,11 @@ namespace SimpleWeb { Config config; private: - class regex_orderable : public regex_ns::regex { + class regex_orderable : public regex::regex { std::string str; public: - regex_orderable(const char *regex_cstr) : regex_ns::regex(regex_cstr), str(regex_cstr) {} - regex_orderable(const std::string ®ex_str) : regex_ns::regex(regex_str), str(regex_str) {} + regex_orderable(const char *regex_cstr) : regex::regex(regex_cstr), str(regex_cstr) {} + regex_orderable(const std::string ®ex_str) : regex::regex(regex_str), str(regex_str) {} bool operator<(const regex_orderable &rhs) const { return str::Response>, std::shared_ptr::Request>)> > default_resource; - std::function::Request>, const error_ns::error_code&)> on_error; + std::function::Request>, const error_code&)> on_error; std::function socket, std::shared_ptr::Request>)> on_upgrade; virtual void start() { if(!io_service) - io_service=std::make_shared(); + io_service=std::make_shared(); if(io_service->stopped()) io_service->reset(); - asio_ns::ip::tcp::endpoint endpoint; + asio::ip::tcp::endpoint endpoint; if(config.address.size()>0) - endpoint=asio_ns::ip::tcp::endpoint(asio_ns::ip::address::from_string(config.address), config.port); + endpoint=asio::ip::tcp::endpoint(asio::ip::address::from_string(config.address), config.port); else - endpoint=asio_ns::ip::tcp::endpoint(asio_ns::ip::tcp::v4(), config.port); + endpoint=asio::ip::tcp::endpoint(asio::ip::tcp::v4(), config.port); if(!acceptor) - acceptor=std::unique_ptr(new asio_ns::ip::tcp::acceptor(*io_service)); + acceptor=std::unique_ptr(new asio::ip::tcp::acceptor(*io_service)); acceptor->open(endpoint.protocol()); - acceptor->set_option(asio_ns::socket_base::reuse_address(config.reuse_address)); + acceptor->set_option(asio::socket_base::reuse_address(config.reuse_address)); acceptor->bind(endpoint); acceptor->listen(); @@ -291,34 +274,34 @@ namespace SimpleWeb { } ///Use this function if you need to recursively send parts of a longer message - void send(const std::shared_ptr &response, const std::function& callback=nullptr) const { - asio_ns::async_write(*response->socket, response->streambuf, [this, response, callback](const error_ns::error_code& ec, size_t /*bytes_transferred*/) { + void send(const std::shared_ptr &response, const std::function& callback=nullptr) const { + asio::async_write(*response->socket, response->streambuf, [this, response, callback](const error_code& ec, size_t /*bytes_transferred*/) { if(callback) callback(ec); }); } - /// If you have your own asio_ns::io_service, store its pointer here before running start(). + /// If you have your own asio::io_service, store its pointer here before running start(). /// You might also want to set config.thread_pool_size to 0. - std::shared_ptr io_service; + std::shared_ptr io_service; protected: - std::unique_ptr acceptor; + std::unique_ptr acceptor; std::vector threads; ServerBase(unsigned short port) : config(port) {} virtual void accept()=0; - std::shared_ptr get_timeout_timer(const std::shared_ptr &socket, long seconds) { + std::shared_ptr get_timeout_timer(const std::shared_ptr &socket, long seconds) { if(seconds==0) return nullptr; - auto timer=std::make_shared(*io_service); + auto timer=std::make_shared(*io_service); timer->expires_from_now(boost::posix_time::seconds(seconds)); - timer->async_wait([socket](const error_ns::error_code& ec){ + timer->async_wait([socket](const error_code& ec){ if(!ec) { - error_ns::error_code ec; - socket->lowest_layer().shutdown(asio_ns::ip::tcp::socket::shutdown_both, ec); + error_code ec; + socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec); socket->lowest_layer().close(); } }); @@ -330,11 +313,11 @@ namespace SimpleWeb { //shared_ptr is used to pass temporary objects to the asynchronous functions std::shared_ptr request(new Request(*socket)); - //Set timeout on the following asio_ns::async-read or write function + //Set timeout on the following asio::async-read or write function auto timer=this->get_timeout_timer(socket, config.timeout_request); - asio_ns::async_read_until(*socket, request->streambuf, "\r\n\r\n", - [this, socket, request, timer](const error_ns::error_code& ec, size_t bytes_transferred) { + asio::async_read_until(*socket, request->streambuf, "\r\n\r\n", + [this, socket, request, timer](const error_code& ec, size_t bytes_transferred) { if(timer) timer->cancel(); if(!ec) { @@ -356,16 +339,16 @@ namespace SimpleWeb { } catch(const std::exception &e) { if(on_error) - on_error(request, merror_ns::make_error_code(error_ns::errc::protocol_error)); + on_error(request, make_error_code(errc::protocol_error)); return; } if(content_length>num_additional_bytes) { - //Set timeout on the following asio_ns::async-read or write function + //Set timeout on the following asio::async-read or write function auto timer=this->get_timeout_timer(socket, config.timeout_content); - asio_ns::async_read(*socket, request->streambuf, - asio_ns::transfer_exactly(content_length-num_additional_bytes), + asio::async_read(*socket, request->streambuf, + asio::transfer_exactly(content_length-num_additional_bytes), [this, socket, request, timer] - (const error_ns::error_code& ec, size_t /*bytes_transferred*/) { + (const error_code& ec, size_t /*bytes_transferred*/) { if(timer) timer->cancel(); if(!ec) @@ -439,8 +422,8 @@ namespace SimpleWeb { for(auto ®ex_method: resource) { auto it=regex_method.second.find(request->method); if(it!=regex_method.second.end()) { - regex_ns::smatch sm_res; - if(regex_ns::regex_match(request->path, sm_res, regex_method.first)) { + regex::smatch sm_res; + if(regex::regex_match(request->path, sm_res, regex_method.first)) { request->path_match=std::move(sm_res); write_response(socket, request, it->second); return; @@ -456,12 +439,12 @@ namespace SimpleWeb { void write_response(const std::shared_ptr &socket, const std::shared_ptr &request, std::function::Response>, std::shared_ptr::Request>)>& resource_function) { - //Set timeout on the following asio_ns::async-read or write function + //Set timeout on the following asio::async-read or write function auto timer=this->get_timeout_timer(socket, config.timeout_content); auto response=std::shared_ptr(new Response(socket), [this, request, timer](Response *response_ptr) { auto response=std::shared_ptr(response_ptr); - this->send(response, [this, response, request, timer](const error_ns::error_code& ec) { + this->send(response, [this, response, request, timer](const error_code& ec) { if(timer) timer->cancel(); if(!ec) { @@ -470,9 +453,9 @@ namespace SimpleWeb { auto range=request->header.equal_range("Connection"); for(auto it=range.first;it!=range.second;it++) { - if(IEQUALS(it->second, "close")) { + if(iequals(it->second, "close")) { return; - } else if (IEQUALS(it->second, "keep-alive")) { + } else if (iequals(it->second, "keep-alive")) { this->read_request_and_content(response->socket); return; } @@ -490,7 +473,7 @@ namespace SimpleWeb { } catch(const std::exception &e) { if(on_error) - on_error(request, merror_ns::make_error_code(error_ns::errc::operation_canceled)); + on_error(request, make_error_code(errc::operation_canceled)); return; } } @@ -499,7 +482,7 @@ namespace SimpleWeb { template class Server : public ServerBase {}; - typedef asio_ns::ip::tcp::socket HTTP; + typedef asio::ip::tcp::socket HTTP; template<> class Server : public ServerBase { @@ -520,13 +503,13 @@ namespace SimpleWeb { //Shared_ptr is used to pass temporary objects to the asynchronous functions auto socket=std::make_shared(*io_service); - acceptor->async_accept(*socket, [this, socket](const error_ns::error_code& ec){ + acceptor->async_accept(*socket, [this, socket](const error_code& ec){ //Immediately start accepting a new connection (if io_service hasn't been stopped) - if (ec != asio_ns::error::operation_aborted) + if (ec != asio::error::operation_aborted) accept(); if(!ec) { - asio_ns::ip::tcp::no_delay option(true); + asio::ip::tcp::no_delay option(true); socket->set_option(option); this->read_request_and_content(socket); diff --git a/server_https.hpp b/server_https.hpp index a698616..28ac039 100644 --- a/server_https.hpp +++ b/server_https.hpp @@ -2,16 +2,18 @@ #define SERVER_HTTPS_HPP #include "server_http.hpp" -#ifdef NO_BOOST -# include + +#ifdef USE_STANDALONE_ASIO +#include #else -# include +#include #endif + #include #include namespace SimpleWeb { - typedef asio_ns::ssl::stream HTTPS; + typedef asio::ssl::stream HTTPS; template<> class Server : public ServerBase { @@ -29,14 +31,14 @@ namespace SimpleWeb { } Server(const std::string& cert_file, const std::string& private_key_file, const std::string& verify_file=std::string()): - ServerBase::ServerBase(443), context(asio_ns::ssl::context::tlsv12) { + ServerBase::ServerBase(443), context(asio::ssl::context::tlsv12) { context.use_certificate_chain_file(cert_file); - context.use_private_key_file(private_key_file, asio_ns::ssl::context::pem); + context.use_private_key_file(private_key_file, asio::ssl::context::pem); if(verify_file.size()>0) { context.load_verify_file(verify_file); - context.set_verify_mode(asio_ns::ssl::verify_peer | asio_ns::ssl::verify_fail_if_no_peer_cert | - asio_ns::ssl::verify_client_once); + context.set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert | + asio::ssl::verify_client_once); set_session_id_context=true; } } @@ -53,27 +55,27 @@ namespace SimpleWeb { } protected: - asio_ns::ssl::context context; + asio::ssl::context context; void accept() { //Create new socket for this connection //Shared_ptr is used to pass temporary objects to the asynchronous functions auto socket=std::make_shared(*io_service, context); - acceptor->async_accept((*socket).lowest_layer(), [this, socket](const error_ns::error_code& ec) { + acceptor->async_accept((*socket).lowest_layer(), [this, socket](const error_code& ec) { //Immediately start accepting a new connection (if io_service hasn't been stopped) - if (ec != asio_ns::error::operation_aborted) + if (ec != asio::error::operation_aborted) accept(); if(!ec) { - asio_ns::ip::tcp::no_delay option(true); + asio::ip::tcp::no_delay option(true); socket->lowest_layer().set_option(option); - //Set timeout on the following asio_ns::ssl::stream::async_handshake + //Set timeout on the following asio::ssl::stream::async_handshake auto timer=get_timeout_timer(socket, config.timeout_request); - socket->async_handshake(asio_ns::ssl::stream_base::server, [this, socket, timer] - (const error_ns::error_code& ec) { + socket->async_handshake(asio::ssl::stream_base::server, [this, socket, timer] + (const error_code& ec) { if(timer) timer->cancel(); if(!ec) diff --git a/tests/parse_test.cpp b/tests/parse_test.cpp index 76ff348..436b7e7 100644 --- a/tests/parse_test.cpp +++ b/tests/parse_test.cpp @@ -106,7 +106,7 @@ public: int main() { ServerTest serverTest; - serverTest.io_service=std::make_shared(); + serverTest.io_service=std::make_shared(); serverTest.parse_request_test(); @@ -119,8 +119,8 @@ int main() { clientTest2.parse_response_header_test(); - boost::asio::io_service io_service; - boost::asio::ip::tcp::socket socket(io_service); + asio::io_service io_service; + asio::ip::tcp::socket socket(io_service); SimpleWeb::Server::Request request(socket); { request.path = "/?";