diff options
Diffstat (limited to 'crow/include')
-rw-r--r-- | crow/include/crow.h | 3 | ||||
-rw-r--r-- | crow/include/crow/app.h | 389 | ||||
-rw-r--r-- | crow/include/crow/common.h | 195 | ||||
-rw-r--r-- | crow/include/crow/http_connection.h | 1019 | ||||
-rw-r--r-- | crow/include/crow/http_request.h | 98 | ||||
-rw-r--r-- | crow/include/crow/http_response.h | 218 | ||||
-rw-r--r-- | crow/include/crow/http_server.h | 294 | ||||
-rw-r--r-- | crow/include/crow/logging.h | 202 | ||||
-rw-r--r-- | crow/include/crow/middleware_context.h | 83 | ||||
-rw-r--r-- | crow/include/crow/query_string.h | 563 | ||||
-rw-r--r-- | crow/include/crow/routing.h | 1977 | ||||
-rw-r--r-- | crow/include/crow/socket_adaptors.h | 276 | ||||
-rw-r--r-- | crow/include/crow/timer_queue.h | 115 | ||||
-rw-r--r-- | crow/include/crow/utility.h | 919 | ||||
-rw-r--r-- | crow/include/crow/websocket.h | 398 |
15 files changed, 3728 insertions, 3021 deletions
diff --git a/crow/include/crow.h b/crow/include/crow.h index edc0d7f924..7e31cf335c 100644 --- a/crow/include/crow.h +++ b/crow/include/crow.h @@ -1,4 +1,6 @@ #pragma once +#include "boost/beast/core.hpp" + #include "crow/app.h" #include "crow/common.h" #include "crow/http_connection.h" @@ -13,4 +15,3 @@ #include "crow/timer_queue.h" #include "crow/utility.h" #include "crow/websocket.h" -#include "boost/beast/core.hpp" diff --git a/crow/include/crow/app.h b/crow/include/crow/app.h index 78d99df0e1..7051ec869f 100644 --- a/crow/include/crow/app.h +++ b/crow/include/crow/app.h @@ -7,6 +7,7 @@ #include <memory> #include <string> #include <utility> + #include "crow/http_request.h" #include "crow/http_server.h" #include "crow/logging.h" @@ -14,198 +15,236 @@ #include "crow/routing.h" #include "crow/utility.h" -#define BMCWEB_ROUTE(app, url) \ - app.template route<crow::black_magic::get_parameter_tag(url)>(url) +#define BMCWEB_ROUTE(app, url) \ + app.template route<crow::black_magic::get_parameter_tag(url)>(url) -namespace crow { +namespace crow +{ #ifdef BMCWEB_ENABLE_SSL using ssl_context_t = boost::asio::ssl::context; #endif -template <typename... Middlewares> -class Crow { - public: - using self_t = Crow; - using server_t = Server<Crow, SocketAdaptor, Middlewares...>; +template <typename... Middlewares> class Crow +{ + public: + using self_t = Crow; + using server_t = Server<Crow, SocketAdaptor, Middlewares...>; #ifdef BMCWEB_ENABLE_SSL - using ssl_server_t = Server<Crow, SSLAdaptor, Middlewares...>; + using ssl_server_t = Server<Crow, SSLAdaptor, Middlewares...>; #endif - explicit Crow(std::shared_ptr<boost::asio::io_service> io = - std::make_shared<boost::asio::io_service>()) - : io(std::move(io)) {} - ~Crow() { this->stop(); } - - template <typename Adaptor> - void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor) { - router.handleUpgrade(req, res, adaptor); - } - - void handle(const Request& req, Response& res) { router.handle(req, res); } - - DynamicRule& routeDynamic(std::string&& rule) { - return router.newRuleDynamic(rule); - } - - template <uint64_t Tag> - auto route(std::string&& rule) -> typename std::result_of< - decltype (&Router::newRuleTagged<Tag>)(Router, std::string&&)>::type { - return router.newRuleTagged<Tag>(std::move(rule)); - } - - self_t& socket(int existing_socket) { - socketFd = existing_socket; - return *this; - } - - self_t& port(std::uint16_t port) { - portUint = port; - return *this; - } - - self_t& bindaddr(std::string bindaddr) { - bindaddrStr = bindaddr; - return *this; - } - - void validate() { router.validate(); } - - void run() { - validate(); + explicit Crow(std::shared_ptr<boost::asio::io_service> io = + std::make_shared<boost::asio::io_service>()) : + io(std::move(io)) + { + } + ~Crow() + { + this->stop(); + } + + template <typename Adaptor> + void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor) + { + router.handleUpgrade(req, res, adaptor); + } + + void handle(const Request& req, Response& res) + { + router.handle(req, res); + } + + DynamicRule& routeDynamic(std::string&& rule) + { + return router.newRuleDynamic(rule); + } + + template <uint64_t Tag> + auto route(std::string&& rule) -> typename std::result_of< + decltype (&Router::newRuleTagged<Tag>)(Router, std::string&&)>::type + { + return router.newRuleTagged<Tag>(std::move(rule)); + } + + self_t& socket(int existing_socket) + { + socketFd = existing_socket; + return *this; + } + + self_t& port(std::uint16_t port) + { + portUint = port; + return *this; + } + + self_t& bindaddr(std::string bindaddr) + { + bindaddrStr = bindaddr; + return *this; + } + + void validate() + { + router.validate(); + } + + void run() + { + validate(); #ifdef BMCWEB_ENABLE_SSL - if (useSsl) { - if (-1 == socketFd) { - sslServer = std::move(std::make_unique<ssl_server_t>( - this, bindaddrStr, portUint, &middlewares, &sslContext, io)); - } else { - sslServer = std::move(std::make_unique<ssl_server_t>( - this, socketFd, &middlewares, &sslContext, io)); - } - sslServer->setTickFunction(tickInterval, tickFunction); - sslServer->run(); - } else + if (useSsl) + { + if (-1 == socketFd) + { + sslServer = std::move(std::make_unique<ssl_server_t>( + this, bindaddrStr, portUint, &middlewares, &sslContext, + io)); + } + else + { + sslServer = std::move(std::make_unique<ssl_server_t>( + this, socketFd, &middlewares, &sslContext, io)); + } + sslServer->setTickFunction(tickInterval, tickFunction); + sslServer->run(); + } + else #endif + { + if (-1 == socketFd) + { + server = std::move(std::make_unique<server_t>( + this, bindaddrStr, portUint, &middlewares, nullptr, io)); + } + else + { + server = std::move(std::make_unique<server_t>( + this, socketFd, &middlewares, nullptr, io)); + } + server->setTickFunction(tickInterval, tickFunction); + server->run(); + } + } + + void stop() { - if (-1 == socketFd) { - server = std::move(std::make_unique<server_t>( - this, bindaddrStr, portUint, &middlewares, nullptr, io)); - } else { - server = std::move(std::make_unique<server_t>( - this, socketFd, &middlewares, nullptr, io)); - } - server->setTickFunction(tickInterval, tickFunction); - server->run(); - } - } - - void stop() { io->stop(); } - - void debugPrint() { - BMCWEB_LOG_DEBUG << "Routing:"; - router.debugPrint(); - } - - std::vector<const std::string*> getRoutes() { - // TODO(ed) Should this be /? - const std::string root(""); - return router.getRoutes(root); - } - std::vector<const std::string*> getRoutes(const std::string& parent) { - return router.getRoutes(parent); - } + io->stop(); + } + + void debugPrint() + { + BMCWEB_LOG_DEBUG << "Routing:"; + router.debugPrint(); + } + + std::vector<const std::string*> getRoutes() + { + // TODO(ed) Should this be /? + const std::string root(""); + return router.getRoutes(root); + } + std::vector<const std::string*> getRoutes(const std::string& parent) + { + return router.getRoutes(parent); + } #ifdef BMCWEB_ENABLE_SSL - self_t& sslFile(const std::string& crt_filename, - const std::string& key_filename) { - useSsl = true; - sslContext.set_verify_mode(boost::asio::ssl::verify_peer); - sslContext.use_certificate_file(crt_filename, ssl_context_t::pem); - sslContext.use_private_key_file(key_filename, ssl_context_t::pem); - sslContext.set_options(boost::asio::ssl::context::default_workarounds | - boost::asio::ssl::context::no_sslv2 | - boost::asio::ssl::context::no_sslv3); - return *this; - } - - self_t& sslFile(const std::string& pem_filename) { - useSsl = true; - sslContext.set_verify_mode(boost::asio::ssl::verify_peer); - sslContext.load_verify_file(pem_filename); - sslContext.set_options(boost::asio::ssl::context::default_workarounds | - boost::asio::ssl::context::no_sslv2 | - boost::asio::ssl::context::no_sslv3); - return *this; - } - - self_t& ssl(boost::asio::ssl::context&& ctx) { - useSsl = true; - sslContext = std::move(ctx); - return *this; - } - - bool useSsl{false}; - ssl_context_t sslContext{boost::asio::ssl::context::sslv23}; + self_t& sslFile(const std::string& crt_filename, + const std::string& key_filename) + { + useSsl = true; + sslContext.set_verify_mode(boost::asio::ssl::verify_peer); + sslContext.use_certificate_file(crt_filename, ssl_context_t::pem); + sslContext.use_private_key_file(key_filename, ssl_context_t::pem); + sslContext.set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::no_sslv3); + return *this; + } + + self_t& sslFile(const std::string& pem_filename) + { + useSsl = true; + sslContext.set_verify_mode(boost::asio::ssl::verify_peer); + sslContext.load_verify_file(pem_filename); + sslContext.set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::no_sslv3); + return *this; + } + + self_t& ssl(boost::asio::ssl::context&& ctx) + { + useSsl = true; + sslContext = std::move(ctx); + return *this; + } + + bool useSsl{false}; + ssl_context_t sslContext{boost::asio::ssl::context::sslv23}; #else - template <typename T, typename... Remain> - self_t& ssl_file(T&&, Remain&&...) { - // We can't call .ssl() member function unless BMCWEB_ENABLE_SSL is defined. - static_assert( - // make static_assert dependent to T; always false - std::is_base_of<T, void>::value, - "Define BMCWEB_ENABLE_SSL to enable ssl support."); - return *this; - } - - template <typename T> - self_t& ssl(T&&) { - // We can't call .ssl() member function unless BMCWEB_ENABLE_SSL is defined. - static_assert( - // make static_assert dependent to T; always false - std::is_base_of<T, void>::value, - "Define BMCWEB_ENABLE_SSL to enable ssl support."); - return *this; - } + template <typename T, typename... Remain> self_t& ssl_file(T&&, Remain&&...) + { + // We can't call .ssl() member function unless BMCWEB_ENABLE_SSL is + // defined. + static_assert( + // make static_assert dependent to T; always false + std::is_base_of<T, void>::value, + "Define BMCWEB_ENABLE_SSL to enable ssl support."); + return *this; + } + + template <typename T> self_t& ssl(T&&) + { + // We can't call .ssl() member function unless BMCWEB_ENABLE_SSL is + // defined. + static_assert( + // make static_assert dependent to T; always false + std::is_base_of<T, void>::value, + "Define BMCWEB_ENABLE_SSL to enable ssl support."); + return *this; + } #endif - // middleware - using context_t = detail::Context<Middlewares...>; - template <typename T> - typename T::Context& getContext(const Request& req) { - static_assert(black_magic::Contains<T, Middlewares...>::value, - "App doesn't have the specified middleware type."); - auto& ctx = *reinterpret_cast<context_t*>(req.middlewareContext); - return ctx.template get<T>(); - } - - template <typename T> - T& getMiddleware() { - return utility::getElementByType<T, Middlewares...>(middlewares); - } - - template <typename Duration, typename Func> - self_t& tick(Duration d, Func f) { - tickInterval = std::chrono::duration_cast<std::chrono::milliseconds>(d); - tickFunction = f; - return *this; - } - - private: - std::shared_ptr<asio::io_service> io; - uint16_t portUint = 80; - std::string bindaddrStr = "::"; - int socketFd = -1; - Router router; - - std::chrono::milliseconds tickInterval{}; - std::function<void()> tickFunction; - - std::tuple<Middlewares...> middlewares; + // middleware + using context_t = detail::Context<Middlewares...>; + template <typename T> typename T::Context& getContext(const Request& req) + { + static_assert(black_magic::Contains<T, Middlewares...>::value, + "App doesn't have the specified middleware type."); + auto& ctx = *reinterpret_cast<context_t*>(req.middlewareContext); + return ctx.template get<T>(); + } + + template <typename T> T& getMiddleware() + { + return utility::getElementByType<T, Middlewares...>(middlewares); + } + + template <typename Duration, typename Func> self_t& tick(Duration d, Func f) + { + tickInterval = std::chrono::duration_cast<std::chrono::milliseconds>(d); + tickFunction = f; + return *this; + } + + private: + std::shared_ptr<asio::io_service> io; + uint16_t portUint = 80; + std::string bindaddrStr = "::"; + int socketFd = -1; + Router router; + + std::chrono::milliseconds tickInterval{}; + std::function<void()> tickFunction; + + std::tuple<Middlewares...> middlewares; #ifdef BMCWEB_ENABLE_SSL - std::unique_ptr<ssl_server_t> sslServer; + std::unique_ptr<ssl_server_t> sslServer; #endif - std::unique_ptr<server_t> server; + std::unique_ptr<server_t> server; }; -template <typename... Middlewares> -using App = Crow<Middlewares...>; +template <typename... Middlewares> using App = Crow<Middlewares...>; using SimpleApp = Crow<>; -} // namespace crow +} // namespace crow diff --git a/crow/include/crow/common.h b/crow/include/crow/common.h index e155da6da9..b3e424f994 100644 --- a/crow/include/crow/common.h +++ b/crow/include/crow/common.h @@ -1,127 +1,140 @@ #pragma once +#include <boost/beast/http/verb.hpp> #include <iostream> #include <stdexcept> #include <string> #include <vector> + #include "crow/utility.h" -#include <boost/beast/http/verb.hpp> -namespace crow { -enum class HTTPMethod { +namespace crow +{ +enum class HTTPMethod +{ #ifndef DELETE - DELETE = 0, - GET, - HEAD, - POST, - PUT, - CONNECT, - OPTIONS, - TRACE, - PATCH = 24, // see http_parser_merged.h line 118 for why it is 24 + DELETE = 0, + GET, + HEAD, + POST, + PUT, + CONNECT, + OPTIONS, + TRACE, + PATCH = 24, // see http_parser_merged.h line 118 for why it is 24 #endif - Delete = 0, - Get, - Head, - Post, - Put, - Connect, - Options, - Trace, - Patch = 24, + Delete = 0, + Get, + Head, + Post, + Put, + Connect, + Options, + Trace, + Patch = 24, }; -inline std::string methodName(boost::beast::http::verb method) { - switch (method) { - case boost::beast::http::verb::delete_: - return "DELETE"; - case boost::beast::http::verb::get: - return "GET"; - case boost::beast::http::verb::head: - return "HEAD"; - case boost::beast::http::verb::post: - return "POST"; - case boost::beast::http::verb::put: - return "PUT"; - case boost::beast::http::verb::connect: - return "CONNECT"; - case boost::beast::http::verb::options: - return "OPTIONS"; - case boost::beast::http::verb::trace: - return "TRACE"; - case boost::beast::http::verb::patch: - return "PATCH"; - } - return "invalid"; +inline std::string methodName(boost::beast::http::verb method) +{ + switch (method) + { + case boost::beast::http::verb::delete_: + return "DELETE"; + case boost::beast::http::verb::get: + return "GET"; + case boost::beast::http::verb::head: + return "HEAD"; + case boost::beast::http::verb::post: + return "POST"; + case boost::beast::http::verb::put: + return "PUT"; + case boost::beast::http::verb::connect: + return "CONNECT"; + case boost::beast::http::verb::options: + return "OPTIONS"; + case boost::beast::http::verb::trace: + return "TRACE"; + case boost::beast::http::verb::patch: + return "PATCH"; + } + return "invalid"; } -enum class ParamType { - INT, - UINT, - DOUBLE, - STRING, - PATH, +enum class ParamType +{ + INT, + UINT, + DOUBLE, + STRING, + PATH, - MAX + MAX }; -struct RoutingParams { - std::vector<int64_t> intParams; - std::vector<uint64_t> uintParams; - std::vector<double> doubleParams; - std::vector<std::string> stringParams; +struct RoutingParams +{ + std::vector<int64_t> intParams; + std::vector<uint64_t> uintParams; + std::vector<double> doubleParams; + std::vector<std::string> stringParams; - void debugPrint() const { - std::cerr << "RoutingParams" << std::endl; - for (auto i : intParams) { - std::cerr << i << ", "; - } - std::cerr << std::endl; - for (auto i : uintParams) { - std::cerr << i << ", "; + void debugPrint() const + { + std::cerr << "RoutingParams" << std::endl; + for (auto i : intParams) + { + std::cerr << i << ", "; + } + std::cerr << std::endl; + for (auto i : uintParams) + { + std::cerr << i << ", "; + } + std::cerr << std::endl; + for (auto i : doubleParams) + { + std::cerr << i << ", "; + } + std::cerr << std::endl; + for (auto& i : stringParams) + { + std::cerr << i << ", "; + } + std::cerr << std::endl; } - std::cerr << std::endl; - for (auto i : doubleParams) { - std::cerr << i << ", "; - } - std::cerr << std::endl; - for (auto& i : stringParams) { - std::cerr << i << ", "; - } - std::cerr << std::endl; - } - template <typename T> - T get(unsigned) const; + template <typename T> T get(unsigned) const; }; -template <> -inline int64_t RoutingParams::get<int64_t>(unsigned index) const { - return intParams[index]; +template <> inline int64_t RoutingParams::get<int64_t>(unsigned index) const +{ + return intParams[index]; } -template <> -inline uint64_t RoutingParams::get<uint64_t>(unsigned index) const { - return uintParams[index]; +template <> inline uint64_t RoutingParams::get<uint64_t>(unsigned index) const +{ + return uintParams[index]; } -template <> -inline double RoutingParams::get<double>(unsigned index) const { - return doubleParams[index]; +template <> inline double RoutingParams::get<double>(unsigned index) const +{ + return doubleParams[index]; } template <> -inline std::string RoutingParams::get<std::string>(unsigned index) const { - return stringParams[index]; +inline std::string RoutingParams::get<std::string>(unsigned index) const +{ + return stringParams[index]; } -} // namespace crow +} // namespace crow constexpr boost::beast::http::verb operator"" _method(const char* str, - size_t /*len*/) { - using verb = boost::beast::http::verb; - // clang-format off + size_t /*len*/) +{ + using verb = boost::beast::http::verb; + // clang-format off return crow::black_magic::isEquP(str, "GET", 3) ? verb::get : crow::black_magic::isEquP(str, "DELETE", 6) ? verb::delete_ : @@ -134,5 +147,5 @@ constexpr boost::beast::http::verb operator"" _method(const char* str, crow::black_magic::isEquP(str, "PATCH", 5) ? verb::patch : crow::black_magic::isEquP(str, "PURGE", 5) ? verb::purge : throw std::runtime_error("invalid http method"); - // clang-format on + // clang-format on }
\ No newline at end of file diff --git a/crow/include/crow/http_connection.h b/crow/include/crow/http_connection.h index 5f2f719a9b..6d62c85bc4 100644 --- a/crow/include/crow/http_connection.h +++ b/crow/include/crow/http_connection.h @@ -1,249 +1,279 @@ #pragma once +#include "http_utility.hpp" + #include <array> #include <atomic> +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/asio.hpp> +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/websocket.hpp> #include <chrono> #include <regex> #include <vector> -#include "http_utility.hpp" + #include "crow/http_response.h" #include "crow/logging.h" #include "crow/middleware_context.h" #include "crow/socket_adaptors.h" #include "crow/timer_queue.h" -#include <boost/algorithm/string.hpp> -#include <boost/algorithm/string/predicate.hpp> -#include <boost/asio.hpp> -#include <boost/beast/core.hpp> -#include <boost/beast/http.hpp> -#include <boost/beast/websocket.hpp> #ifdef BMCWEB_ENABLE_SSL #include <boost/asio/ssl.hpp> #endif -namespace crow { - -inline void escapeHtml(std::string& data) { - std::string buffer; - // less than 5% of characters should be larger, so reserve a buffer of the - // right size - buffer.reserve(data.size() * 1.05); - for (size_t pos = 0; pos != data.size(); ++pos) { - switch (data[pos]) { - case '&': - buffer.append("&"); - break; - case '\"': - buffer.append("""); - break; - case '\'': - buffer.append("'"); - break; - case '<': - buffer.append("<"); - break; - case '>': - buffer.append(">"); - break; - default: - buffer.append(&data[pos], 1); - break; +namespace crow +{ + +inline void escapeHtml(std::string& data) +{ + std::string buffer; + // less than 5% of characters should be larger, so reserve a buffer of the + // right size + buffer.reserve(data.size() * 1.05); + for (size_t pos = 0; pos != data.size(); ++pos) + { + switch (data[pos]) + { + case '&': + buffer.append("&"); + break; + case '\"': + buffer.append("""); + break; + case '\'': + buffer.append("'"); + break; + case '<': + buffer.append("<"); + break; + case '>': + buffer.append(">"); + break; + default: + buffer.append(&data[pos], 1); + break; + } } - } - data.swap(buffer); + data.swap(buffer); } -inline void convertToLinks(std::string& s) { - const static std::regex r{ - "("@odata\\.((id)|(Context))"[ \\n]*:[ " - "\\n]*)("((?!").*)")"}; - s = std::regex_replace(s, r, "$1<a href=\"$6\">$5</a>"); +inline void convertToLinks(std::string& s) +{ + const static std::regex r{"("@odata\\.((id)|(Context))"[ \\n]*:[ " + "\\n]*)("((?!").*)")"}; + s = std::regex_replace(s, r, "$1<a href=\"$6\">$5</a>"); } -inline void prettyPrintJson(crow::Response& res) { - std::string value = res.jsonValue.dump(4); - escapeHtml(value); - convertToLinks(value); - res.body() = - "<html>\n" - "<head>\n" - "<title>Redfish API</title>\n" - "<link rel=\"stylesheet\" type=\"text/css\" " - "href=\"/styles/default.css\">\n" - "<script src=\"/highlight.pack.js\"></script>" - "<script>hljs.initHighlightingOnLoad();</script>" - "</head>\n" - "<body>\n" - "<div style=\"max-width: 576px;margin:0 auto;\">\n" - "<img src=\"/DMTF_Redfish_logo_2017.svg\" alt=\"redfish\" " - "height=\"406px\" " - "width=\"576px\">\n" - "<br>\n" - "<pre>\n" - "<code class=\"json\">" + - value + - "</code>\n" - "</pre>\n" - "</div>\n" - "</body>\n" - "</html>\n"; +inline void prettyPrintJson(crow::Response& res) +{ + std::string value = res.jsonValue.dump(4); + escapeHtml(value); + convertToLinks(value); + res.body() = "<html>\n" + "<head>\n" + "<title>Redfish API</title>\n" + "<link rel=\"stylesheet\" type=\"text/css\" " + "href=\"/styles/default.css\">\n" + "<script src=\"/highlight.pack.js\"></script>" + "<script>hljs.initHighlightingOnLoad();</script>" + "</head>\n" + "<body>\n" + "<div style=\"max-width: 576px;margin:0 auto;\">\n" + "<img src=\"/DMTF_Redfish_logo_2017.svg\" alt=\"redfish\" " + "height=\"406px\" " + "width=\"576px\">\n" + "<br>\n" + "<pre>\n" + "<code class=\"json\">" + + value + + "</code>\n" + "</pre>\n" + "</div>\n" + "</body>\n" + "</html>\n"; } using namespace boost; using tcp = asio::ip::tcp; -namespace detail { -template <typename MW> -struct CheckBeforeHandleArity3Const { - template <typename T, void (T::*)(Request&, Response&, typename MW::Context&) - const = &T::beforeHandle> - struct Get {}; +namespace detail +{ +template <typename MW> struct CheckBeforeHandleArity3Const +{ + template <typename T, + void (T::*)(Request&, Response&, typename MW::Context&) const = + &T::beforeHandle> + struct Get + { + }; }; -template <typename MW> -struct CheckBeforeHandleArity3 { - template <typename T, void (T::*)(Request&, Response&, - typename MW::Context&) = &T::beforeHandle> - struct Get {}; +template <typename MW> struct CheckBeforeHandleArity3 +{ + template <typename T, void (T::*)(Request&, Response&, + typename MW::Context&) = &T::beforeHandle> + struct Get + { + }; }; -template <typename MW> -struct CheckAfterHandleArity3Const { - template <typename T, void (T::*)(Request&, Response&, typename MW::Context&) - const = &T::afterHandle> - struct Get {}; +template <typename MW> struct CheckAfterHandleArity3Const +{ + template <typename T, + void (T::*)(Request&, Response&, typename MW::Context&) const = + &T::afterHandle> + struct Get + { + }; }; -template <typename MW> -struct CheckAfterHandleArity3 { - template <typename T, void (T::*)(Request&, Response&, - typename MW::Context&) = &T::afterHandle> - struct Get {}; +template <typename MW> struct CheckAfterHandleArity3 +{ + template <typename T, void (T::*)(Request&, Response&, + typename MW::Context&) = &T::afterHandle> + struct Get + { + }; }; -template <typename T> -struct IsBeforeHandleArity3Impl { - template <typename C> - static std::true_type f( - typename CheckBeforeHandleArity3Const<T>::template Get<C>*); +template <typename T> struct IsBeforeHandleArity3Impl +{ + template <typename C> + static std::true_type + f(typename CheckBeforeHandleArity3Const<T>::template Get<C>*); - template <typename C> - static std::true_type f( - typename CheckBeforeHandleArity3<T>::template Get<C>*); + template <typename C> + static std::true_type + f(typename CheckBeforeHandleArity3<T>::template Get<C>*); - template <typename C> - static std::false_type f(...); + template <typename C> static std::false_type f(...); - public: - static const bool value = decltype(f<T>(nullptr))::value; + public: + static const bool value = decltype(f<T>(nullptr))::value; }; -template <typename T> -struct IsAfterHandleArity3Impl { - template <typename C> - static std::true_type f( - typename CheckAfterHandleArity3Const<T>::template Get<C>*); +template <typename T> struct IsAfterHandleArity3Impl +{ + template <typename C> + static std::true_type + f(typename CheckAfterHandleArity3Const<T>::template Get<C>*); - template <typename C> - static std::true_type f(typename CheckAfterHandleArity3<T>::template Get<C>*); + template <typename C> + static std::true_type + f(typename CheckAfterHandleArity3<T>::template Get<C>*); - template <typename C> - static std::false_type f(...); + template <typename C> static std::false_type f(...); - public: - static const bool value = decltype(f<T>(nullptr))::value; + public: + static const bool value = decltype(f<T>(nullptr))::value; }; template <typename MW, typename Context, typename ParentContext> typename std::enable_if<!IsBeforeHandleArity3Impl<MW>::value>::type -beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, - ParentContext& /*parent_ctx*/) { - mw.beforeHandle(req, res, ctx.template get<MW>(), ctx); + beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, + ParentContext& /*parent_ctx*/) +{ + mw.beforeHandle(req, res, ctx.template get<MW>(), ctx); } template <typename MW, typename Context, typename ParentContext> typename std::enable_if<IsBeforeHandleArity3Impl<MW>::value>::type -beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, - ParentContext& /*parent_ctx*/) { - mw.beforeHandle(req, res, ctx.template get<MW>()); + beforeHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, + ParentContext& /*parent_ctx*/) +{ + mw.beforeHandle(req, res, ctx.template get<MW>()); } template <typename MW, typename Context, typename ParentContext> typename std::enable_if<!IsAfterHandleArity3Impl<MW>::value>::type -afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, - ParentContext& /*parent_ctx*/) { - mw.afterHandle(req, res, ctx.template get<MW>(), ctx); + afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, + ParentContext& /*parent_ctx*/) +{ + mw.afterHandle(req, res, ctx.template get<MW>(), ctx); } template <typename MW, typename Context, typename ParentContext> typename std::enable_if<IsAfterHandleArity3Impl<MW>::value>::type -afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, - ParentContext& /*parent_ctx*/) { - mw.afterHandle(req, res, ctx.template get<MW>()); + afterHandlerCall(MW& mw, Request& req, Response& res, Context& ctx, + ParentContext& /*parent_ctx*/) +{ + mw.afterHandle(req, res, ctx.template get<MW>()); } template <int N, typename Context, typename Container, typename CurrentMW, typename... Middlewares> bool middlewareCallHelper(Container& middlewares, Request& req, Response& res, - Context& ctx) { - using parent_context_t = typename Context::template partial<N - 1>; - beforeHandlerCall<CurrentMW, Context, parent_context_t>( - std::get<N>(middlewares), req, res, ctx, - static_cast<parent_context_t&>(ctx)); - - if (res.isCompleted()) { - afterHandlerCall<CurrentMW, Context, parent_context_t>( + Context& ctx) +{ + using parent_context_t = typename Context::template partial<N - 1>; + beforeHandlerCall<CurrentMW, Context, parent_context_t>( std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); - return true; - } - if (middlewareCallHelper<N + 1, Context, Container, Middlewares...>( - middlewares, req, res, ctx)) { - afterHandlerCall<CurrentMW, Context, parent_context_t>( - std::get<N>(middlewares), req, res, ctx, - static_cast<parent_context_t&>(ctx)); - return true; - } + if (res.isCompleted()) + { + afterHandlerCall<CurrentMW, Context, parent_context_t>( + std::get<N>(middlewares), req, res, ctx, + static_cast<parent_context_t&>(ctx)); + return true; + } + + if (middlewareCallHelper<N + 1, Context, Container, Middlewares...>( + middlewares, req, res, ctx)) + { + afterHandlerCall<CurrentMW, Context, parent_context_t>( + std::get<N>(middlewares), req, res, ctx, + static_cast<parent_context_t&>(ctx)); + return true; + } - return false; + return false; } template <int N, typename Context, typename Container> bool middlewareCallHelper(Container& /*middlewares*/, Request& /*req*/, - Response& /*res*/, Context& /*ctx*/) { - return false; + Response& /*res*/, Context& /*ctx*/) +{ + return false; } template <int N, typename Context, typename Container> -typename std::enable_if<(N < 0)>::type afterHandlersCallHelper( - Container& /*middlewares*/, Context& /*Context*/, Request& /*req*/, - Response& /*res*/) {} +typename std::enable_if<(N < 0)>::type + afterHandlersCallHelper(Container& /*middlewares*/, Context& /*Context*/, + Request& /*req*/, Response& /*res*/) +{ +} template <int N, typename Context, typename Container> -typename std::enable_if<(N == 0)>::type afterHandlersCallHelper( - Container& middlewares, Context& ctx, Request& req, Response& res) { - using parent_context_t = typename Context::template partial<N - 1>; - using CurrentMW = typename std::tuple_element< - N, typename std::remove_reference<Container>::type>::type; - afterHandlerCall<CurrentMW, Context, parent_context_t>( - std::get<N>(middlewares), req, res, ctx, - static_cast<parent_context_t&>(ctx)); +typename std::enable_if<(N == 0)>::type + afterHandlersCallHelper(Container& middlewares, Context& ctx, Request& req, + Response& res) +{ + using parent_context_t = typename Context::template partial<N - 1>; + using CurrentMW = typename std::tuple_element< + N, typename std::remove_reference<Container>::type>::type; + afterHandlerCall<CurrentMW, Context, parent_context_t>( + std::get<N>(middlewares), req, res, ctx, + static_cast<parent_context_t&>(ctx)); } template <int N, typename Context, typename Container> -typename std::enable_if<(N > 0)>::type afterHandlersCallHelper( - Container& middlewares, Context& ctx, Request& req, Response& res) { - using parent_context_t = typename Context::template partial<N - 1>; - using CurrentMW = typename std::tuple_element< - N, typename std::remove_reference<Container>::type>::type; - afterHandlerCall<CurrentMW, Context, parent_context_t>( - std::get<N>(middlewares), req, res, ctx, - static_cast<parent_context_t&>(ctx)); - afterHandlersCallHelper<N - 1, Context, Container>(middlewares, ctx, req, - res); +typename std::enable_if<(N > 0)>::type + afterHandlersCallHelper(Container& middlewares, Context& ctx, Request& req, + Response& res) +{ + using parent_context_t = typename Context::template partial<N - 1>; + using CurrentMW = typename std::tuple_element< + N, typename std::remove_reference<Container>::type>::type; + afterHandlerCall<CurrentMW, Context, parent_context_t>( + std::get<N>(middlewares), req, res, ctx, + static_cast<parent_context_t&>(ctx)); + afterHandlersCallHelper<N - 1, Context, Container>(middlewares, ctx, req, + res); } -} // namespace detail +} // namespace detail #ifdef BMCWEB_ENABLE_DEBUG static std::atomic<int> connectionCount; @@ -253,331 +283,382 @@ static std::atomic<int> connectionCount; constexpr unsigned int httpReqBodyLimit = 1024 * 1024 * 30; template <typename Adaptor, typename Handler, typename... Middlewares> -class Connection { - public: - Connection(boost::asio::io_service& ioService, Handler* handler, - const std::string& server_name, - std::tuple<Middlewares...>* middlewares, - std::function<std::string()>& get_cached_date_str_f, - detail::TimerQueue& timerQueue, - typename Adaptor::context* adaptorCtx) - : adaptor(ioService, adaptorCtx), - handler(handler), - serverName(server_name), - middlewares(middlewares), - getCachedDateStr(get_cached_date_str_f), - timerQueue(timerQueue) { - parser.emplace(std::piecewise_construct, std::make_tuple()); - // Temporarily changed to 30MB; Need to modify uploading/authentication - // mechanism - parser->body_limit(httpReqBodyLimit); - req.emplace(parser->get()); +class Connection +{ + public: + Connection(boost::asio::io_service& ioService, Handler* handler, + const std::string& server_name, + std::tuple<Middlewares...>* middlewares, + std::function<std::string()>& get_cached_date_str_f, + detail::TimerQueue& timerQueue, + typename Adaptor::context* adaptorCtx) : + adaptor(ioService, adaptorCtx), + handler(handler), serverName(server_name), middlewares(middlewares), + getCachedDateStr(get_cached_date_str_f), timerQueue(timerQueue) + { + parser.emplace(std::piecewise_construct, std::make_tuple()); + // Temporarily changed to 30MB; Need to modify uploading/authentication + // mechanism + parser->body_limit(httpReqBodyLimit); + req.emplace(parser->get()); #ifdef BMCWEB_ENABLE_DEBUG - connectionCount++; - BMCWEB_LOG_DEBUG << this << " Connection open, total " << connectionCount; + connectionCount++; + BMCWEB_LOG_DEBUG << this << " Connection open, total " + << connectionCount; #endif - } + } - ~Connection() { - res.completeRequestHandler = nullptr; - cancelDeadlineTimer(); + ~Connection() + { + res.completeRequestHandler = nullptr; + cancelDeadlineTimer(); #ifdef BMCWEB_ENABLE_DEBUG - connectionCount--; - BMCWEB_LOG_DEBUG << this << " Connection closed, total " << connectionCount; + connectionCount--; + BMCWEB_LOG_DEBUG << this << " Connection closed, total " + << connectionCount; #endif - } - - decltype(std::declval<Adaptor>().rawSocket()) & socket() { - return adaptor.rawSocket(); - } - - void start() { - adaptor.start([this](const boost::system::error_code& ec) { - if (!ec) { - startDeadline(); - - doReadHeaders(); - } else { - checkDestroy(); - } - }); - } - - void handle() { - cancelDeadlineTimer(); - bool isInvalidRequest = false; - const boost::string_view connection = - req->getHeaderValue(boost::beast::http::field::connection); - - // Check for HTTP version 1.1. - if (req->version() == 11) { - if (req->getHeaderValue(boost::beast::http::field::host).empty()) { - isInvalidRequest = true; - res = Response(boost::beast::http::status::bad_request); - } } - BMCWEB_LOG_INFO << "Request: " << adaptor.remoteEndpoint() << " " << this - << " HTTP/" << req->version() / 10 << "." - << req->version() % 10 << ' ' << req->methodString() << " " - << req->target(); - - needToCallAfterHandlers = false; - - if (!isInvalidRequest) { - res.completeRequestHandler = [] {}; - res.isAliveHelper = [this]() -> bool { return adaptor.isOpen(); }; - - ctx = detail::Context<Middlewares...>(); - req->middlewareContext = (void*)&ctx; - req->ioService = &adaptor.getIoService(); - detail::middlewareCallHelper<0, decltype(ctx), decltype(*middlewares), - Middlewares...>(*middlewares, *req, res, - ctx); - - if (!res.completed) { - if (req->isUpgrade() && - boost::iequals( - req->getHeaderValue(boost::beast::http::field::upgrade), - "websocket")) { - handler->handleUpgrade(*req, res, std::move(adaptor)); - return; + decltype(std::declval<Adaptor>().rawSocket())& socket() + { + return adaptor.rawSocket(); + } + + void start() + { + adaptor.start([this](const boost::system::error_code& ec) { + if (!ec) + { + startDeadline(); + + doReadHeaders(); + } + else + { + checkDestroy(); + } + }); + } + + void handle() + { + cancelDeadlineTimer(); + bool isInvalidRequest = false; + const boost::string_view connection = + req->getHeaderValue(boost::beast::http::field::connection); + + // Check for HTTP version 1.1. + if (req->version() == 11) + { + if (req->getHeaderValue(boost::beast::http::field::host).empty()) + { + isInvalidRequest = true; + res = Response(boost::beast::http::status::bad_request); + } } - res.completeRequestHandler = [this] { this->completeRequest(); }; - needToCallAfterHandlers = true; - handler->handle(*req, res); - if (req->keepAlive()) { - res.addHeader("connection", "Keep-Alive"); + + BMCWEB_LOG_INFO << "Request: " << adaptor.remoteEndpoint() << " " + << this << " HTTP/" << req->version() / 10 << "." + << req->version() % 10 << ' ' << req->methodString() + << " " << req->target(); + + needToCallAfterHandlers = false; + + if (!isInvalidRequest) + { + res.completeRequestHandler = [] {}; + res.isAliveHelper = [this]() -> bool { return adaptor.isOpen(); }; + + ctx = detail::Context<Middlewares...>(); + req->middlewareContext = (void*)&ctx; + req->ioService = &adaptor.getIoService(); + detail::middlewareCallHelper< + 0, decltype(ctx), decltype(*middlewares), Middlewares...>( + *middlewares, *req, res, ctx); + + if (!res.completed) + { + if (req->isUpgrade() && + boost::iequals( + req->getHeaderValue(boost::beast::http::field::upgrade), + "websocket")) + { + handler->handleUpgrade(*req, res, std::move(adaptor)); + return; + } + res.completeRequestHandler = [this] { + this->completeRequest(); + }; + needToCallAfterHandlers = true; + handler->handle(*req, res); + if (req->keepAlive()) + { + res.addHeader("connection", "Keep-Alive"); + } + } + else + { + completeRequest(); + } + } + else + { + completeRequest(); } - } else { - completeRequest(); - } - } else { - completeRequest(); } - } - void completeRequest() { - BMCWEB_LOG_INFO << "Response: " << this << ' ' << req->url << ' ' - << res.resultInt() << " keepalive=" << req->keepAlive(); + void completeRequest() + { + BMCWEB_LOG_INFO << "Response: " << this << ' ' << req->url << ' ' + << res.resultInt() << " keepalive=" << req->keepAlive(); + + if (needToCallAfterHandlers) + { + needToCallAfterHandlers = false; - if (needToCallAfterHandlers) { - needToCallAfterHandlers = false; + // call all afterHandler of middlewares + detail::afterHandlersCallHelper<((int)sizeof...(Middlewares) - 1), + decltype(ctx), + decltype(*middlewares)>( + *middlewares, ctx, *req, res); + } - // call all afterHandler of middlewares - detail::afterHandlersCallHelper<((int)sizeof...(Middlewares) - 1), - decltype(ctx), decltype(*middlewares)>( - *middlewares, ctx, *req, res); + // auto self = this->shared_from_this(); + res.completeRequestHandler = nullptr; + + if (!adaptor.isOpen()) + { + // BMCWEB_LOG_DEBUG << this << " delete (socket is closed) " << + // isReading + // << ' ' << isWriting; + // delete this; + return; + } + if (res.body().empty() && !res.jsonValue.empty()) + { + if (http_helpers::requestPrefersHtml(*req)) + { + prettyPrintJson(res); + } + else + { + res.jsonMode(); + res.body() = res.jsonValue.dump(2); + } + } + + if (res.resultInt() >= 400 && res.body().empty()) + { + res.body() = std::string(res.reason()); + } + res.addHeader(boost::beast::http::field::server, serverName); + res.addHeader(boost::beast::http::field::date, getCachedDateStr()); + + res.keepAlive(req->keepAlive()); + + doWrite(); } - // auto self = this->shared_from_this(); - res.completeRequestHandler = nullptr; + private: + void doReadHeaders() + { + // auto self = this->shared_from_this(); + isReading = true; + BMCWEB_LOG_DEBUG << this << " doReadHeaders"; + + // Clean up any previous Connection. + boost::beast::http::async_read_header( + adaptor.socket(), buffer, *parser, + [this](const boost::system::error_code& ec, + std::size_t bytes_transferred) { + isReading = false; + BMCWEB_LOG_ERROR << this << " async_read_header " + << bytes_transferred << " Bytes"; + bool errorWhileReading = false; + if (ec) + { + errorWhileReading = true; + BMCWEB_LOG_ERROR + << this << " Error while reading: " << ec.message(); + } + else + { + // if the adaptor isn't open anymore, and wasn't handed to a + // websocket, treat as an error + if (!adaptor.isOpen() && !req->isUpgrade()) + { + errorWhileReading = true; + } + } + + if (errorWhileReading) + { + cancelDeadlineTimer(); + adaptor.close(); + BMCWEB_LOG_DEBUG << this << " from read(1)"; + checkDestroy(); + return; + } + + // Compute the url parameters for the request + req->url = req->target(); + std::size_t index = req->url.find("?"); + if (index != boost::string_view::npos) + { + req->url = req->url.substr(0, index - 1); + } + req->urlParams = QueryString(std::string(req->target())); + doRead(); + }); + } - if (!adaptor.isOpen()) { - // BMCWEB_LOG_DEBUG << this << " delete (socket is closed) " << isReading - // << ' ' << isWriting; - // delete this; - return; + void doRead() + { + // auto self = this->shared_from_this(); + isReading = true; + BMCWEB_LOG_DEBUG << this << " doRead"; + + boost::beast::http::async_read( + adaptor.socket(), buffer, *parser, + [this](const boost::system::error_code& ec, + std::size_t bytes_transferred) { + BMCWEB_LOG_ERROR << this << " async_read " << bytes_transferred + << " Bytes"; + isReading = false; + + bool errorWhileReading = false; + if (ec) + { + BMCWEB_LOG_ERROR << "Error while reading: " << ec.message(); + errorWhileReading = true; + } + else + { + if (!adaptor.isOpen()) + { + errorWhileReading = true; + } + } + if (errorWhileReading) + { + cancelDeadlineTimer(); + adaptor.close(); + BMCWEB_LOG_DEBUG << this << " from read(1)"; + checkDestroy(); + return; + } + handle(); + }); } - if (res.body().empty() && !res.jsonValue.empty()) { - if (http_helpers::requestPrefersHtml(*req)) { - prettyPrintJson(res); - } else { - res.jsonMode(); - res.body() = res.jsonValue.dump(2); - } + + void doWrite() + { + // auto self = this->shared_from_this(); + isWriting = true; + BMCWEB_LOG_DEBUG << "Doing Write"; + res.preparePayload(); + serializer.emplace(*res.stringResponse); + boost::beast::http::async_write( + adaptor.socket(), *serializer, + [&](const boost::system::error_code& ec, + std::size_t bytes_transferred) { + isWriting = false; + BMCWEB_LOG_DEBUG << this << " Wrote " << bytes_transferred + << " bytes"; + + if (ec) + { + BMCWEB_LOG_DEBUG << this << " from write(2)"; + checkDestroy(); + return; + } + if (!req->keepAlive()) + { + adaptor.close(); + BMCWEB_LOG_DEBUG << this << " from write(1)"; + checkDestroy(); + return; + } + + serializer.reset(); + BMCWEB_LOG_DEBUG << this << " Clearing response"; + res.clear(); + parser.emplace(std::piecewise_construct, std::make_tuple()); + parser->body_limit(httpReqBodyLimit); // reset body limit for + // newly created parser + buffer.consume(buffer.size()); + + req.emplace(parser->get()); + doReadHeaders(); + }); } - if (res.resultInt() >= 400 && res.body().empty()) { - res.body() = std::string(res.reason()); + void checkDestroy() + { + BMCWEB_LOG_DEBUG << this << " isReading " << isReading << " isWriting " + << isWriting; + if (!isReading && !isWriting) + { + BMCWEB_LOG_DEBUG << this << " delete (idle) "; + delete this; + } } - res.addHeader(boost::beast::http::field::server, serverName); - res.addHeader(boost::beast::http::field::date, getCachedDateStr()); - - res.keepAlive(req->keepAlive()); - - doWrite(); - } - - private: - void doReadHeaders() { - // auto self = this->shared_from_this(); - isReading = true; - BMCWEB_LOG_DEBUG << this << " doReadHeaders"; - - // Clean up any previous Connection. - boost::beast::http::async_read_header( - adaptor.socket(), buffer, *parser, - [this](const boost::system::error_code& ec, - std::size_t bytes_transferred) { - isReading = false; - BMCWEB_LOG_ERROR << this << " async_read_header " << bytes_transferred - << " Bytes"; - bool errorWhileReading = false; - if (ec) { - errorWhileReading = true; - BMCWEB_LOG_ERROR << this - << " Error while reading: " << ec.message(); - } else { - // if the adaptor isn't open anymore, and wasn't handed to a - // websocket, treat as an error - if (!adaptor.isOpen() && !req->isUpgrade()) { - errorWhileReading = true; - } - } - if (errorWhileReading) { - cancelDeadlineTimer(); - adaptor.close(); - BMCWEB_LOG_DEBUG << this << " from read(1)"; - checkDestroy(); - return; - } - - // Compute the url parameters for the request - req->url = req->target(); - std::size_t index = req->url.find("?"); - if (index != boost::string_view::npos) { - req->url = req->url.substr(0, index - 1); - } - req->urlParams = QueryString(std::string(req->target())); - doRead(); - }); - } - - void doRead() { - // auto self = this->shared_from_this(); - isReading = true; - BMCWEB_LOG_DEBUG << this << " doRead"; - - boost::beast::http::async_read( - adaptor.socket(), buffer, *parser, - [this](const boost::system::error_code& ec, - std::size_t bytes_transferred) { - BMCWEB_LOG_ERROR << this << " async_read " << bytes_transferred - << " Bytes"; - isReading = false; - - bool errorWhileReading = false; - if (ec) { - BMCWEB_LOG_ERROR << "Error while reading: " << ec.message(); - errorWhileReading = true; - } else { - if (!adaptor.isOpen()) { - errorWhileReading = true; + void cancelDeadlineTimer() + { + BMCWEB_LOG_DEBUG << this << " timer cancelled: " << &timerQueue << ' ' + << timerCancelKey; + timerQueue.cancel(timerCancelKey); + } + + void startDeadline() + { + cancelDeadlineTimer(); + + timerCancelKey = timerQueue.add([this] { + if (!adaptor.isOpen()) + { + return; } - } - if (errorWhileReading) { - cancelDeadlineTimer(); adaptor.close(); - BMCWEB_LOG_DEBUG << this << " from read(1)"; - checkDestroy(); - return; - } - handle(); - }); - } - - void doWrite() { - // auto self = this->shared_from_this(); - isWriting = true; - BMCWEB_LOG_DEBUG << "Doing Write"; - res.preparePayload(); - serializer.emplace(*res.stringResponse); - boost::beast::http::async_write( - adaptor.socket(), *serializer, - [&](const boost::system::error_code& ec, - std::size_t bytes_transferred) { - isWriting = false; - BMCWEB_LOG_DEBUG << this << " Wrote " << bytes_transferred - << " bytes"; - - if (ec) { - BMCWEB_LOG_DEBUG << this << " from write(2)"; - checkDestroy(); - return; - } - if (!req->keepAlive()) { - adaptor.close(); - BMCWEB_LOG_DEBUG << this << " from write(1)"; - checkDestroy(); - return; - } - - serializer.reset(); - BMCWEB_LOG_DEBUG << this << " Clearing response"; - res.clear(); - parser.emplace(std::piecewise_construct, std::make_tuple()); - parser->body_limit(httpReqBodyLimit); // reset body limit for - // newly created parser - buffer.consume(buffer.size()); - - req.emplace(parser->get()); - doReadHeaders(); }); - } - - void checkDestroy() { - BMCWEB_LOG_DEBUG << this << " isReading " << isReading << " isWriting " - << isWriting; - if (!isReading && !isWriting) { - BMCWEB_LOG_DEBUG << this << " delete (idle) "; - delete this; + BMCWEB_LOG_DEBUG << this << " timer added: " << &timerQueue << ' ' + << timerCancelKey; } - } - - void cancelDeadlineTimer() { - BMCWEB_LOG_DEBUG << this << " timer cancelled: " << &timerQueue << ' ' - << timerCancelKey; - timerQueue.cancel(timerCancelKey); - } - - void startDeadline() { - cancelDeadlineTimer(); - - timerCancelKey = timerQueue.add([this] { - if (!adaptor.isOpen()) { - return; - } - adaptor.close(); - }); - BMCWEB_LOG_DEBUG << this << " timer added: " << &timerQueue << ' ' - << timerCancelKey; - } - - private: - Adaptor adaptor; - Handler* handler; - - // Making this a boost::optional allows it to be efficiently destroyed and - // re-created on Connection reset - boost::optional< - boost::beast::http::request_parser<boost::beast::http::string_body>> - parser; - - boost::beast::flat_buffer buffer{8192}; - - boost::optional< - boost::beast::http::response_serializer<boost::beast::http::string_body>> - serializer; - - boost::optional<crow::Request> req; - crow::Response res; - - const std::string& serverName; - - int timerCancelKey{-1}; - - bool isReading{}; - bool isWriting{}; - bool needToCallAfterHandlers{}; - bool needToStartReadAfterComplete{}; - bool addKeepAlive{}; - - std::tuple<Middlewares...>* middlewares; - detail::Context<Middlewares...> ctx; - - std::function<std::string()>& getCachedDateStr; - detail::TimerQueue& timerQueue; -}; // namespace crow -} // namespace crow + + private: + Adaptor adaptor; + Handler* handler; + + // Making this a boost::optional allows it to be efficiently destroyed and + // re-created on Connection reset + boost::optional< + boost::beast::http::request_parser<boost::beast::http::string_body>> + parser; + + boost::beast::flat_buffer buffer{8192}; + + boost::optional<boost::beast::http::response_serializer< + boost::beast::http::string_body>> + serializer; + + boost::optional<crow::Request> req; + crow::Response res; + + const std::string& serverName; + + int timerCancelKey{-1}; + + bool isReading{}; + bool isWriting{}; + bool needToCallAfterHandlers{}; + bool needToStartReadAfterComplete{}; + bool addKeepAlive{}; + + std::tuple<Middlewares...>* middlewares; + detail::Context<Middlewares...> ctx; + + std::function<std::string()>& getCachedDateStr; + detail::TimerQueue& timerQueue; +}; // namespace crow +} // namespace crow diff --git a/crow/include/crow/http_request.h b/crow/include/crow/http_request.h index 7e95bc6669..0e6dd12932 100644 --- a/crow/include/crow/http_request.h +++ b/crow/include/crow/http_request.h @@ -7,42 +7,66 @@ #include "crow/common.h" #include "crow/query_string.h" -namespace crow { - -struct Request { - boost::string_view url{}; - QueryString urlParams{}; - bool isSecure{false}; - - const std::string& body; - - void* middlewareContext{}; - boost::asio::io_service* ioService{}; - - Request(boost::beast::http::request<boost::beast::http::string_body>& req) - : req(req), body(req.body()) {} - - const boost::beast::http::verb method() const { return req.method(); } - - const boost::string_view getHeaderValue(boost::string_view key) const { - return req[key]; - } - - const boost::string_view getHeaderValue(boost::beast::http::field key) const { - return req[key]; - } - - const boost::string_view methodString() const { return req.method_string(); } - - const boost::string_view target() const { return req.target(); } - - unsigned version() { return req.version(); } - - bool isUpgrade() { return boost::beast::websocket::is_upgrade(req); } - - bool keepAlive() { return req.keep_alive(); } - - boost::beast::http::request<boost::beast::http::string_body>& req; +namespace crow +{ + +struct Request +{ + boost::string_view url{}; + QueryString urlParams{}; + bool isSecure{false}; + + const std::string& body; + + void* middlewareContext{}; + boost::asio::io_service* ioService{}; + + Request(boost::beast::http::request<boost::beast::http::string_body>& req) : + req(req), body(req.body()) + { + } + + const boost::beast::http::verb method() const + { + return req.method(); + } + + const boost::string_view getHeaderValue(boost::string_view key) const + { + return req[key]; + } + + const boost::string_view getHeaderValue(boost::beast::http::field key) const + { + return req[key]; + } + + const boost::string_view methodString() const + { + return req.method_string(); + } + + const boost::string_view target() const + { + return req.target(); + } + + unsigned version() + { + return req.version(); + } + + bool isUpgrade() + { + return boost::beast::websocket::is_upgrade(req); + } + + bool keepAlive() + { + return req.keep_alive(); + } + + boost::beast::http::request<boost::beast::http::string_body>& req; }; -} // namespace crow +} // namespace crow diff --git a/crow/include/crow/http_response.h b/crow/include/crow/http_response.h index 560eef46e8..d66bf47046 100644 --- a/crow/include/crow/http_response.h +++ b/crow/include/crow/http_response.h @@ -1,121 +1,173 @@ #pragma once +#include "nlohmann/json.hpp" + +#include <boost/beast/http.hpp> #include <string> -#include "nlohmann/json.hpp" #include "crow/http_request.h" #include "crow/logging.h" -#include <boost/beast/http.hpp> -namespace crow { +namespace crow +{ template <typename Adaptor, typename Handler, typename... Middlewares> class Connection; -struct Response { - template <typename Adaptor, typename Handler, typename... Middlewares> - friend class crow::Connection; - using response_type = - boost::beast::http::response<boost::beast::http::string_body>; - - boost::optional<response_type> stringResponse; - - nlohmann::json jsonValue; +struct Response +{ + template <typename Adaptor, typename Handler, typename... Middlewares> + friend class crow::Connection; + using response_type = + boost::beast::http::response<boost::beast::http::string_body>; - void addHeader(const boost::string_view key, const boost::string_view value) { - stringResponse->set(key, value); - } + boost::optional<response_type> stringResponse; - void addHeader(boost::beast::http::field key, boost::string_view value) { - stringResponse->set(key, value); - } + nlohmann::json jsonValue; - Response() : stringResponse(response_type{}) {} + void addHeader(const boost::string_view key, const boost::string_view value) + { + stringResponse->set(key, value); + } - explicit Response(boost::beast::http::status code) - : stringResponse(response_type{}) {} + void addHeader(boost::beast::http::field key, boost::string_view value) + { + stringResponse->set(key, value); + } - explicit Response(boost::string_view body_) - : stringResponse(response_type{}) { - stringResponse->body() = std::string(body_); - } + Response() : stringResponse(response_type{}) + { + } - Response(boost::beast::http::status code, boost::string_view s) - : stringResponse(response_type{}) { - stringResponse->result(code); - stringResponse->body() = std::string(s); - } + explicit Response(boost::beast::http::status code) : + stringResponse(response_type{}) + { + } - Response(Response&& r) { - BMCWEB_LOG_DEBUG << "Moving response containers"; - *this = std::move(r); - } + explicit Response(boost::string_view body_) : + stringResponse(response_type{}) + { + stringResponse->body() = std::string(body_); + } - ~Response() { BMCWEB_LOG_DEBUG << this << " Destroying response"; } + Response(boost::beast::http::status code, boost::string_view s) : + stringResponse(response_type{}) + { + stringResponse->result(code); + stringResponse->body() = std::string(s); + } - Response& operator=(const Response& r) = delete; + Response(Response&& r) + { + BMCWEB_LOG_DEBUG << "Moving response containers"; + *this = std::move(r); + } - Response& operator=(Response&& r) noexcept { - BMCWEB_LOG_DEBUG << "Moving response containers"; - stringResponse = std::move(r.stringResponse); - r.stringResponse.emplace(response_type{}); - jsonValue = std::move(r.jsonValue); - completed = r.completed; - return *this; - } + ~Response() + { + BMCWEB_LOG_DEBUG << this << " Destroying response"; + } - void result(boost::beast::http::status v) { stringResponse->result(v); } + Response& operator=(const Response& r) = delete; - boost::beast::http::status result() { return stringResponse->result(); } + Response& operator=(Response&& r) noexcept + { + BMCWEB_LOG_DEBUG << "Moving response containers"; + stringResponse = std::move(r.stringResponse); + r.stringResponse.emplace(response_type{}); + jsonValue = std::move(r.jsonValue); + completed = r.completed; + return *this; + } - unsigned resultInt() { return stringResponse->result_int(); } + void result(boost::beast::http::status v) + { + stringResponse->result(v); + } - boost::string_view reason() { return stringResponse->reason(); } + boost::beast::http::status result() + { + return stringResponse->result(); + } - bool isCompleted() const noexcept { return completed; } + unsigned resultInt() + { + return stringResponse->result_int(); + } - std::string& body() { return stringResponse->body(); } + boost::string_view reason() + { + return stringResponse->reason(); + } - void keepAlive(bool k) { stringResponse->keep_alive(k); } + bool isCompleted() const noexcept + { + return completed; + } - void preparePayload() { stringResponse->prepare_payload(); }; + std::string& body() + { + return stringResponse->body(); + } - void clear() { - BMCWEB_LOG_DEBUG << this << " Clearing response containers"; - stringResponse.emplace(response_type{}); - jsonValue.clear(); - completed = false; - } + void keepAlive(bool k) + { + stringResponse->keep_alive(k); + } - void write(boost::string_view body_part) { - stringResponse->body() += std::string(body_part); - } + void preparePayload() + { + stringResponse->prepare_payload(); + }; + + void clear() + { + BMCWEB_LOG_DEBUG << this << " Clearing response containers"; + stringResponse.emplace(response_type{}); + jsonValue.clear(); + completed = false; + } - void end() { - if (completed) { - BMCWEB_LOG_ERROR << "Response was ended twice"; - return; + void write(boost::string_view body_part) + { + stringResponse->body() += std::string(body_part); } - completed = true; - BMCWEB_LOG_DEBUG << "calling completion handler"; - if (completeRequestHandler) { - BMCWEB_LOG_DEBUG << "completion handler was valid"; - completeRequestHandler(); + + void end() + { + if (completed) + { + BMCWEB_LOG_ERROR << "Response was ended twice"; + return; + } + completed = true; + BMCWEB_LOG_DEBUG << "calling completion handler"; + if (completeRequestHandler) + { + BMCWEB_LOG_DEBUG << "completion handler was valid"; + completeRequestHandler(); + } } - } - void end(boost::string_view body_part) { - write(body_part); - end(); - } + void end(boost::string_view body_part) + { + write(body_part); + end(); + } - bool isAlive() { return isAliveHelper && isAliveHelper(); } + bool isAlive() + { + return isAliveHelper && isAliveHelper(); + } - private: - bool completed{}; - std::function<void()> completeRequestHandler; - std::function<bool()> isAliveHelper; + private: + bool completed{}; + std::function<void()> completeRequestHandler; + std::function<bool()> isAliveHelper; - // In case of a JSON object, set the Content-Type header - void jsonMode() { addHeader("Content-Type", "application/json"); } + // In case of a JSON object, set the Content-Type header + void jsonMode() + { + addHeader("Content-Type", "application/json"); + } }; -} // namespace crow +} // namespace crow diff --git a/crow/include/crow/http_server.h b/crow/include/crow/http_server.h index 96b85dc264..173d0d1ffb 100644 --- a/crow/include/crow/http_server.h +++ b/crow/include/crow/http_server.h @@ -1,183 +1,205 @@ #pragma once #include <atomic> +#include <boost/asio.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> #include <chrono> #include <cstdint> #include <future> #include <memory> #include <utility> #include <vector> + #include "crow/http_connection.h" #include "crow/logging.h" #include "crow/timer_queue.h" -#include <boost/asio.hpp> -#include <boost/date_time/posix_time/posix_time.hpp> #ifdef BMCWEB_ENABLE_SSL #include <boost/asio/ssl.hpp> #endif -namespace crow { +namespace crow +{ using namespace boost; using tcp = asio::ip::tcp; template <typename Handler, typename Adaptor = SocketAdaptor, typename... Middlewares> -class Server { - public: - Server(Handler* handler, std::unique_ptr<tcp::acceptor>&& acceptor, - std::tuple<Middlewares...>* middlewares = nullptr, - typename Adaptor::context* adaptor_ctx = nullptr, - std::shared_ptr<boost::asio::io_service> io = - std::make_shared<boost::asio::io_service>()) - : ioService(std::move(io)), - acceptor(std::move(acceptor)), - signals(*ioService, SIGINT, SIGTERM), - tickTimer(*ioService), - handler(handler), - middlewares(middlewares), - adaptorCtx(adaptor_ctx) {} - - Server(Handler* handler, const std::string& bindaddr, uint16_t port, - std::tuple<Middlewares...>* middlewares = nullptr, - typename Adaptor::context* adaptor_ctx = nullptr, - std::shared_ptr<boost::asio::io_service> io = - std::make_shared<boost::asio::io_service>()) - : Server(handler, +class Server +{ + public: + Server(Handler* handler, std::unique_ptr<tcp::acceptor>&& acceptor, + std::tuple<Middlewares...>* middlewares = nullptr, + typename Adaptor::context* adaptor_ctx = nullptr, + std::shared_ptr<boost::asio::io_service> io = + std::make_shared<boost::asio::io_service>()) : + ioService(std::move(io)), + acceptor(std::move(acceptor)), signals(*ioService, SIGINT, SIGTERM), + tickTimer(*ioService), handler(handler), middlewares(middlewares), + adaptorCtx(adaptor_ctx) + { + } + + Server(Handler* handler, const std::string& bindaddr, uint16_t port, + std::tuple<Middlewares...>* middlewares = nullptr, + typename Adaptor::context* adaptor_ctx = nullptr, + std::shared_ptr<boost::asio::io_service> io = + std::make_shared<boost::asio::io_service>()) : + Server(handler, std::make_unique<tcp::acceptor>( *io, tcp::endpoint( boost::asio::ip::address::from_string(bindaddr), port)), - middlewares, adaptor_ctx, io) {} - - Server(Handler* handler, int existing_socket, - std::tuple<Middlewares...>* middlewares = nullptr, - typename Adaptor::context* adaptor_ctx = nullptr, - std::shared_ptr<boost::asio::io_service> io = - std::make_shared<boost::asio::io_service>()) - : Server(handler, + middlewares, adaptor_ctx, io) + { + } + + Server(Handler* handler, int existing_socket, + std::tuple<Middlewares...>* middlewares = nullptr, + typename Adaptor::context* adaptor_ctx = nullptr, + std::shared_ptr<boost::asio::io_service> io = + std::make_shared<boost::asio::io_service>()) : + Server(handler, std::make_unique<tcp::acceptor>(*io, boost::asio::ip::tcp::v6(), existing_socket), - middlewares, adaptor_ctx, io) {} - - void setTickFunction(std::chrono::milliseconds d, std::function<void()> f) { - tickInterval = d; - tickFunction = f; - } - - void onTick() { - tickFunction(); - tickTimer.expires_from_now( - boost::posix_time::milliseconds(tickInterval.count())); - tickTimer.async_wait([this](const boost::system::error_code& ec) { - if (ec) { - return; - } - onTick(); - }); - } - - void updateDateStr() { - auto lastTimeT = time(0); - tm myTm{}; + middlewares, adaptor_ctx, io) + { + } + + void setTickFunction(std::chrono::milliseconds d, std::function<void()> f) + { + tickInterval = d; + tickFunction = f; + } + + void onTick() + { + tickFunction(); + tickTimer.expires_from_now( + boost::posix_time::milliseconds(tickInterval.count())); + tickTimer.async_wait([this](const boost::system::error_code& ec) { + if (ec) + { + return; + } + onTick(); + }); + } + + void updateDateStr() + { + auto lastTimeT = time(0); + tm myTm{}; #ifdef _MSC_VER - gmtime_s(&my_tm, &last_time_t); + gmtime_s(&my_tm, &last_time_t); #else - gmtime_r(&lastTimeT, &myTm); + gmtime_r(&lastTimeT, &myTm); #endif - dateStr.resize(100); - size_t dateStrSz = - strftime(&dateStr[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &myTm); - dateStr.resize(dateStrSz); - }; - - void run() { - updateDateStr(); - - getCachedDateStr = [this]() -> std::string { - static std::chrono::time_point<std::chrono::steady_clock> lastDateUpdate = - std::chrono::steady_clock::now(); - if (std::chrono::steady_clock::now() - lastDateUpdate >= - std::chrono::seconds(10)) { - lastDateUpdate = std::chrono::steady_clock::now(); - updateDateStr(); - } - return this->dateStr; + dateStr.resize(100); + size_t dateStrSz = + strftime(&dateStr[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &myTm); + dateStr.resize(dateStrSz); }; - boost::asio::deadline_timer timer(*ioService); - timer.expires_from_now(boost::posix_time::seconds(1)); - - std::function<void(const boost::system::error_code& ec)> handler; - handler = [&](const boost::system::error_code& ec) { - if (ec) { - return; - } - timerQueue.process(); - timer.expires_from_now(boost::posix_time::seconds(1)); - timer.async_wait(handler); - }; - timer.async_wait(handler); - - if (tickFunction && tickInterval.count() > 0) { - tickTimer.expires_from_now( - boost::posix_time::milliseconds(tickInterval.count())); - tickTimer.async_wait([this](const boost::system::error_code& ec) { - if (ec) { - return; + void run() + { + updateDateStr(); + + getCachedDateStr = [this]() -> std::string { + static std::chrono::time_point<std::chrono::steady_clock> + lastDateUpdate = std::chrono::steady_clock::now(); + if (std::chrono::steady_clock::now() - lastDateUpdate >= + std::chrono::seconds(10)) + { + lastDateUpdate = std::chrono::steady_clock::now(); + updateDateStr(); + } + return this->dateStr; + }; + + boost::asio::deadline_timer timer(*ioService); + timer.expires_from_now(boost::posix_time::seconds(1)); + + std::function<void(const boost::system::error_code& ec)> handler; + handler = [&](const boost::system::error_code& ec) { + if (ec) + { + return; + } + timerQueue.process(); + timer.expires_from_now(boost::posix_time::seconds(1)); + timer.async_wait(handler); + }; + timer.async_wait(handler); + + if (tickFunction && tickInterval.count() > 0) + { + tickTimer.expires_from_now( + boost::posix_time::milliseconds(tickInterval.count())); + tickTimer.async_wait([this](const boost::system::error_code& ec) { + if (ec) + { + return; + } + onTick(); + }); } - onTick(); - }); - } - BMCWEB_LOG_INFO << serverName << " server is running, local endpoint " - << acceptor->local_endpoint(); + BMCWEB_LOG_INFO << serverName << " server is running, local endpoint " + << acceptor->local_endpoint(); - signals.async_wait([&](const boost::system::error_code& /*error*/, - int /*signal_number*/) { stop(); }); + signals.async_wait([&](const boost::system::error_code& /*error*/, + int /*signal_number*/) { stop(); }); - doAccept(); - } + doAccept(); + } - void stop() { ioService->stop(); } + void stop() + { + ioService->stop(); + } - void doAccept() { - auto p = new Connection<Adaptor, Handler, Middlewares...>( - *ioService, handler, serverName, middlewares, getCachedDateStr, - timerQueue, adaptorCtx); - acceptor->async_accept(p->socket(), - [this, p](boost::system::error_code ec) { - if (!ec) { - this->ioService->post([p] { p->start(); }); - } else { - delete p; - } - doAccept(); - }); - } + void doAccept() + { + auto p = new Connection<Adaptor, Handler, Middlewares...>( + *ioService, handler, serverName, middlewares, getCachedDateStr, + timerQueue, adaptorCtx); + acceptor->async_accept( + p->socket(), [this, p](boost::system::error_code ec) { + if (!ec) + { + this->ioService->post([p] { p->start(); }); + } + else + { + delete p; + } + doAccept(); + }); + } - private: - std::shared_ptr<asio::io_service> ioService; - detail::TimerQueue timerQueue; - std::function<std::string()> getCachedDateStr; - std::unique_ptr<tcp::acceptor> acceptor; - boost::asio::signal_set signals; - boost::asio::deadline_timer tickTimer; + private: + std::shared_ptr<asio::io_service> ioService; + detail::TimerQueue timerQueue; + std::function<std::string()> getCachedDateStr; + std::unique_ptr<tcp::acceptor> acceptor; + boost::asio::signal_set signals; + boost::asio::deadline_timer tickTimer; - std::string dateStr; + std::string dateStr; - Handler* handler; - std::string serverName = "iBMC"; + Handler* handler; + std::string serverName = "iBMC"; - std::chrono::milliseconds tickInterval{}; - std::function<void()> tickFunction; + std::chrono::milliseconds tickInterval{}; + std::function<void()> tickFunction; - std::tuple<Middlewares...>* middlewares; + std::tuple<Middlewares...>* middlewares; #ifdef BMCWEB_ENABLE_SSL - bool useSsl{false}; - boost::asio::ssl::context sslContext{boost::asio::ssl::context::sslv23}; + bool useSsl{false}; + boost::asio::ssl::context sslContext{boost::asio::ssl::context::sslv23}; #endif - typename Adaptor::context* adaptorCtx; -}; // namespace crow -} // namespace crow + typename Adaptor::context* adaptorCtx; +}; // namespace crow +} // namespace crow diff --git a/crow/include/crow/logging.h b/crow/include/crow/logging.h index 4a68df38ce..353a448c95 100644 --- a/crow/include/crow/logging.h +++ b/crow/include/crow/logging.h @@ -7,117 +7,139 @@ #include <sstream> #include <string> -namespace crow { -enum class LogLevel { +namespace crow +{ +enum class LogLevel +{ #ifndef ERROR - DEBUG = 0, - INFO, - WARNING, - ERROR, - CRITICAL, + DEBUG = 0, + INFO, + WARNING, + ERROR, + CRITICAL, #endif - Debug = 0, - Info, - Warning, - Error, - Critical, + Debug = 0, + Info, + Warning, + Error, + Critical, }; -class ILogHandler { - public: - virtual void log(std::string message, LogLevel level) = 0; +class ILogHandler +{ + public: + virtual void log(std::string message, LogLevel level) = 0; }; -class CerrLogHandler : public ILogHandler { - public: - void log(std::string message, LogLevel /*level*/) override { - std::cerr << message; - } +class CerrLogHandler : public ILogHandler +{ + public: + void log(std::string message, LogLevel /*level*/) override + { + std::cerr << message; + } }; -class logger { - private: - // - static std::string timestamp() { - char date[32]; - time_t t = time(0); +class logger +{ + private: + // + static std::string timestamp() + { + char date[32]; + time_t t = time(0); - tm myTm{}; + tm myTm{}; #ifdef _MSC_VER - gmtime_s(&my_tm, &t); + gmtime_s(&my_tm, &t); #else - gmtime_r(&t, &myTm); + gmtime_r(&t, &myTm); #endif - size_t sz = strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", &myTm); - return std::string(date, date + sz); - } + size_t sz = strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", &myTm); + return std::string(date, date + sz); + } - public: - logger(const std::string& prefix, LogLevel level) : level(level) { + public: + logger(const std::string& prefix, LogLevel level) : level(level) + { #ifdef BMCWEB_ENABLE_LOGGING - stringstream << "(" << timestamp() << ") [" << prefix << "] "; + stringstream << "(" << timestamp() << ") [" << prefix << "] "; #endif - } - ~logger() { -#ifdef BMCWEB_ENABLE_LOGGING - if (level >= get_current_log_level()) { - stringstream << std::endl; - getHandlerRef()->log(stringstream.str(), level); } + ~logger() + { +#ifdef BMCWEB_ENABLE_LOGGING + if (level >= get_current_log_level()) + { + stringstream << std::endl; + getHandlerRef()->log(stringstream.str(), level); + } #endif - } + } - // - template <typename T> - logger& operator<<(T const& value) { + // + template <typename T> logger& operator<<(T const& value) + { #ifdef BMCWEB_ENABLE_LOGGING - if (level >= get_current_log_level()) { - stringstream << value; - } + if (level >= get_current_log_level()) + { + stringstream << value; + } #endif - return *this; - } - - // - static void setLogLevel(LogLevel level) { getLogLevelRef() = level; } - - static void setHandler(ILogHandler* handler) { getHandlerRef() = handler; } - - static LogLevel get_current_log_level() { return getLogLevelRef(); } - - private: - // - static LogLevel& getLogLevelRef() { - static auto currentLevel = static_cast<LogLevel>(1); - return currentLevel; - } - static ILogHandler*& getHandlerRef() { - static CerrLogHandler defaultHandler; - static ILogHandler* currentHandler = &defaultHandler; - return currentHandler; - } - - // - std::ostringstream stringstream; - LogLevel level; + return *this; + } + + // + static void setLogLevel(LogLevel level) + { + getLogLevelRef() = level; + } + + static void setHandler(ILogHandler* handler) + { + getHandlerRef() = handler; + } + + static LogLevel get_current_log_level() + { + return getLogLevelRef(); + } + + private: + // + static LogLevel& getLogLevelRef() + { + static auto currentLevel = static_cast<LogLevel>(1); + return currentLevel; + } + static ILogHandler*& getHandlerRef() + { + static CerrLogHandler defaultHandler; + static ILogHandler* currentHandler = &defaultHandler; + return currentHandler; + } + + // + std::ostringstream stringstream; + LogLevel level; }; -} // namespace crow - -#define BMCWEB_LOG_CRITICAL \ - if (crow::logger::get_current_log_level() <= crow::LogLevel::Critical) \ - crow::logger("CRITICAL", crow::LogLevel::Critical) -#define BMCWEB_LOG_ERROR \ - if (crow::logger::get_current_log_level() <= crow::LogLevel::Error) \ - crow::logger("ERROR ", crow::LogLevel::Error) -#define BMCWEB_LOG_WARNING \ - if (crow::logger::get_current_log_level() <= crow::LogLevel::Warning) \ - crow::logger("WARNING ", crow::LogLevel::Warning) -#define BMCWEB_LOG_INFO \ - if (crow::logger::get_current_log_level() <= crow::LogLevel::Info) \ - crow::logger("INFO ", crow::LogLevel::Info) -#define BMCWEB_LOG_DEBUG \ - if (crow::logger::get_current_log_level() <= crow::LogLevel::Debug) \ - crow::logger("DEBUG ", crow::LogLevel::Debug) +} // namespace crow + +#define BMCWEB_LOG_CRITICAL \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Critical) \ + crow::logger("CRITICAL", crow::LogLevel::Critical) +#define BMCWEB_LOG_ERROR \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Error) \ + crow::logger("ERROR ", crow::LogLevel::Error) +#define BMCWEB_LOG_WARNING \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Warning) \ + crow::logger("WARNING ", crow::LogLevel::Warning) +#define BMCWEB_LOG_INFO \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Info) \ + crow::logger("INFO ", crow::LogLevel::Info) +#define BMCWEB_LOG_DEBUG \ + if (crow::logger::get_current_log_level() <= crow::LogLevel::Debug) \ + crow::logger("DEBUG ", crow::LogLevel::Debug) diff --git a/crow/include/crow/middleware_context.h b/crow/include/crow/middleware_context.h index 03309f67c5..f109a44b1f 100644 --- a/crow/include/crow/middleware_context.h +++ b/crow/include/crow/middleware_context.h @@ -4,30 +4,32 @@ #include "crow/http_response.h" #include "crow/utility.h" -namespace crow { -namespace detail { +namespace crow +{ +namespace detail +{ template <typename... Middlewares> struct PartialContext : public black_magic::PopBack<Middlewares...>::template rebind< PartialContext>, - public black_magic::LastElementType<Middlewares...>::type::Context { - using parent_context = typename black_magic::PopBack< - Middlewares...>::template rebind<::crow::detail::PartialContext>; - template <int N> - using partial = typename std::conditional< - N == sizeof...(Middlewares) - 1, PartialContext, - typename parent_context::template partial<N>>::type; - - template <typename T> - typename T::Context& get() { - return static_cast<typename T::Context&>(*this); - } + public black_magic::LastElementType<Middlewares...>::type::Context +{ + using parent_context = typename black_magic::PopBack< + Middlewares...>::template rebind<::crow::detail::PartialContext>; + template <int N> + using partial = typename std::conditional< + N == sizeof...(Middlewares) - 1, PartialContext, + typename parent_context::template partial<N>>::type; + + template <typename T> typename T::Context& get() + { + return static_cast<typename T::Context&>(*this); + } }; -template <> -struct PartialContext<> { - template <int> - using partial = PartialContext; +template <> struct PartialContext<> +{ + template <int> using partial = PartialContext; }; template <int N, typename Context, typename Container, typename CurrentMW, @@ -39,25 +41,28 @@ template <typename... Middlewares> struct Context : private PartialContext<Middlewares...> // struct Context : private Middlewares::context... // simple but less type-safe { - template <int N, typename Context, typename Container> - friend typename std::enable_if<(N == 0)>::type afterHandlersCallHelper( - Container& middlewares, Context& ctx, Request& req, Response& res); - template <int N, typename Context, typename Container> - friend typename std::enable_if<(N > 0)>::type afterHandlersCallHelper( - Container& middlewares, Context& ctx, Request& req, Response& res); - - template <int N, typename Context, typename Container, typename CurrentMW, - typename... Middlewares2> - friend bool middlewareCallHelper(Container& middlewares, Request& req, - Response& res, Context& ctx); - - template <typename T> - typename T::Context& get() { - return static_cast<typename T::Context&>(*this); - } - - template <int N> - using partial = typename PartialContext<Middlewares...>::template partial<N>; + template <int N, typename Context, typename Container> + friend typename std::enable_if<(N == 0)>::type + afterHandlersCallHelper(Container& middlewares, Context& ctx, + Request& req, Response& res); + template <int N, typename Context, typename Container> + friend typename std::enable_if<(N > 0)>::type + afterHandlersCallHelper(Container& middlewares, Context& ctx, + Request& req, Response& res); + + template <int N, typename Context, typename Container, typename CurrentMW, + typename... Middlewares2> + friend bool middlewareCallHelper(Container& middlewares, Request& req, + Response& res, Context& ctx); + + template <typename T> typename T::Context& get() + { + return static_cast<typename T::Context&>(*this); + } + + template <int N> + using partial = + typename PartialContext<Middlewares...>::template partial<N>; }; -} // namespace detail -} // namespace crow +} // namespace detail +} // namespace crow diff --git a/crow/include/crow/query_string.h b/crow/include/crow/query_string.h index 060699477c..67e426acd7 100644 --- a/crow/include/crow/query_string.h +++ b/crow/include/crow/query_string.h @@ -6,7 +6,8 @@ #include <string> #include <vector> -namespace crow { +namespace crow +{ // ---------------------------------------------------------------------------- // qs_parse (modified) // https://github.com/bartgrantham/qs_parse @@ -37,306 +38,376 @@ char* qsScanvalue(const char* key, const char* qs, char* val, size_t val_len); #undef _qsSORTING // isxdigit _is_ available in <ctype.h>, but let's avoid another header instead -#define BMCWEB_QS_ISHEX(x) \ - ((((x) >= '0' && (x) <= '9') || ((x) >= 'A' && (x) <= 'F') || \ - ((x) >= 'a' && (x) <= 'f')) \ - ? 1 \ - : 0) -#define BMCWEB_QS_HEX2DEC(x) \ - (((x) >= '0' && (x) <= '9') \ - ? (x)-48 \ - : ((x) >= 'A' && (x) <= 'F') ? (x)-55 \ - : ((x) >= 'a' && (x) <= 'f') ? (x)-87 : 0) -#define BMCWEB_QS_ISQSCHR(x) \ - ((((x) == '=') || ((x) == '#') || ((x) == '&') || ((x) == '\0')) ? 0 : 1) - -inline int qsStrncmp(const char* s, const char* qs, size_t n) { - int i = 0; - unsigned char u1, u2, unyb, lnyb; - - while (n-- > 0) { - u1 = static_cast<unsigned char>(*s++); - u2 = static_cast<unsigned char>(*qs++); - - if (!BMCWEB_QS_ISQSCHR(u1)) { - u1 = '\0'; - } - if (!BMCWEB_QS_ISQSCHR(u2)) { - u2 = '\0'; +#define BMCWEB_QS_ISHEX(x) \ + ((((x) >= '0' && (x) <= '9') || ((x) >= 'A' && (x) <= 'F') || \ + ((x) >= 'a' && (x) <= 'f')) \ + ? 1 \ + : 0) +#define BMCWEB_QS_HEX2DEC(x) \ + (((x) >= '0' && (x) <= '9') \ + ? (x)-48 \ + : ((x) >= 'A' && (x) <= 'F') \ + ? (x)-55 \ + : ((x) >= 'a' && (x) <= 'f') ? (x)-87 : 0) +#define BMCWEB_QS_ISQSCHR(x) \ + ((((x) == '=') || ((x) == '#') || ((x) == '&') || ((x) == '\0')) ? 0 : 1) + +inline int qsStrncmp(const char* s, const char* qs, size_t n) +{ + int i = 0; + unsigned char u1, u2, unyb, lnyb; + + while (n-- > 0) + { + u1 = static_cast<unsigned char>(*s++); + u2 = static_cast<unsigned char>(*qs++); + + if (!BMCWEB_QS_ISQSCHR(u1)) + { + u1 = '\0'; + } + if (!BMCWEB_QS_ISQSCHR(u2)) + { + u2 = '\0'; + } + + if (u1 == '+') + { + u1 = ' '; + } + if (u1 == '%') // easier/safer than scanf + { + unyb = static_cast<unsigned char>(*s++); + lnyb = static_cast<unsigned char>(*s++); + if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb)) + { + u1 = (BMCWEB_QS_HEX2DEC(unyb) * 16) + BMCWEB_QS_HEX2DEC(lnyb); + } + else + { + u1 = '\0'; + } + } + + if (u2 == '+') + { + u2 = ' '; + } + if (u2 == '%') // easier/safer than scanf + { + unyb = static_cast<unsigned char>(*qs++); + lnyb = static_cast<unsigned char>(*qs++); + if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb)) + { + u2 = (BMCWEB_QS_HEX2DEC(unyb) * 16) + BMCWEB_QS_HEX2DEC(lnyb); + } + else + { + u2 = '\0'; + } + } + + if (u1 != u2) + { + return u1 - u2; + } + if (u1 == '\0') + { + return 0; + } + i++; } - - if (u1 == '+') { - u1 = ' '; + if (BMCWEB_QS_ISQSCHR(*qs)) + { + return -1; } - if (u1 == '%') // easier/safer than scanf + else { - unyb = static_cast<unsigned char>(*s++); - lnyb = static_cast<unsigned char>(*s++); - if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb)) { - u1 = (BMCWEB_QS_HEX2DEC(unyb) * 16) + BMCWEB_QS_HEX2DEC(lnyb); - } else { - u1 = '\0'; - } + return 0; } +} - if (u2 == '+') { - u2 = ' '; - } - if (u2 == '%') // easier/safer than scanf +inline int qsParse(char* qs, char* qs_kv[], int qs_kv_size) +{ + int i, j; + char* substrPtr; + + for (i = 0; i < qs_kv_size; i++) { - unyb = static_cast<unsigned char>(*qs++); - lnyb = static_cast<unsigned char>(*qs++); - if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb)) { - u2 = (BMCWEB_QS_HEX2DEC(unyb) * 16) + BMCWEB_QS_HEX2DEC(lnyb); - } else { - u2 = '\0'; - } + qs_kv[i] = NULL; } - if (u1 != u2) { - return u1 - u2; + // find the beginning of the k/v substrings or the fragment + substrPtr = qs + strcspn(qs, "?#"); + if (substrPtr[0] != '\0') + { + substrPtr++; } - if (u1 == '\0') { - return 0; + else + { + return 0; // no query or fragment } - i++; - } - if (BMCWEB_QS_ISQSCHR(*qs)) { - return -1; - } else { - return 0; - } -} -inline int qsParse(char* qs, char* qs_kv[], int qs_kv_size) { - int i, j; - char* substrPtr; - - for (i = 0; i < qs_kv_size; i++) { - qs_kv[i] = NULL; - } - - // find the beginning of the k/v substrings or the fragment - substrPtr = qs + strcspn(qs, "?#"); - if (substrPtr[0] != '\0') { - substrPtr++; - } else { - return 0; // no query or fragment - } - - i = 0; - while (i < qs_kv_size) { - qs_kv[i] = substrPtr; - j = strcspn(substrPtr, "&"); - if (substrPtr[j] == '\0') { - break; + i = 0; + while (i < qs_kv_size) + { + qs_kv[i] = substrPtr; + j = strcspn(substrPtr, "&"); + if (substrPtr[j] == '\0') + { + break; + } + substrPtr += j + 1; + i++; } - substrPtr += j + 1; - i++; - } - i++; // x &'s -> means x iterations of this loop -> means *x+1* k/v pairs - - // we only decode the values in place, the keys could have '='s in them - // which will hose our ability to distinguish keys from values later - for (j = 0; j < i; j++) { - substrPtr = qs_kv[j] + strcspn(qs_kv[j], "=&#"); - if (substrPtr[0] == '&' || - substrPtr[0] == '\0') { // blank value: skip decoding - substrPtr[0] = '\0'; - } else { - qsDecode(++substrPtr); + i++; // x &'s -> means x iterations of this loop -> means *x+1* k/v pairs + + // we only decode the values in place, the keys could have '='s in them + // which will hose our ability to distinguish keys from values later + for (j = 0; j < i; j++) + { + substrPtr = qs_kv[j] + strcspn(qs_kv[j], "=&#"); + if (substrPtr[0] == '&' || substrPtr[0] == '\0') + { // blank value: skip decoding + substrPtr[0] = '\0'; + } + else + { + qsDecode(++substrPtr); + } } - } #ifdef _qsSORTING // TODO: qsort qs_kv, using qs_strncmp() for the comparison #endif - return i; + return i; } -inline int qsDecode(char* qs) { - int i = 0, j = 0; +inline int qsDecode(char* qs) +{ + int i = 0, j = 0; - while (BMCWEB_QS_ISQSCHR(qs[j])) { - if (qs[j] == '+') { - qs[i] = ' '; - } else if (qs[j] == '%') // easier/safer than scanf + while (BMCWEB_QS_ISQSCHR(qs[j])) { - if (!BMCWEB_QS_ISHEX(qs[j + 1]) || !BMCWEB_QS_ISHEX(qs[j + 2])) { - qs[i] = '\0'; - return i; - } - qs[i] = - (BMCWEB_QS_HEX2DEC(qs[j + 1]) * 16) + BMCWEB_QS_HEX2DEC(qs[j + 2]); - j += 2; - } else { - qs[i] = qs[j]; + if (qs[j] == '+') + { + qs[i] = ' '; + } + else if (qs[j] == '%') // easier/safer than scanf + { + if (!BMCWEB_QS_ISHEX(qs[j + 1]) || !BMCWEB_QS_ISHEX(qs[j + 2])) + { + qs[i] = '\0'; + return i; + } + qs[i] = (BMCWEB_QS_HEX2DEC(qs[j + 1]) * 16) + + BMCWEB_QS_HEX2DEC(qs[j + 2]); + j += 2; + } + else + { + qs[i] = qs[j]; + } + i++; + j++; } - i++; - j++; - } - qs[i] = '\0'; + qs[i] = '\0'; - return i; + return i; } inline char* qsK2v(const char* key, char* const* qs_kv, int qs_kv_size, - int nth = 0) { - int i; - size_t keyLen, skip; + int nth = 0) +{ + int i; + size_t keyLen, skip; - keyLen = strlen(key); + keyLen = strlen(key); #ifdef _qsSORTING // TODO: binary search for key in the sorted qs_kv -#else // _qsSORTING - for (i = 0; i < qs_kv_size; i++) { - // we rely on the unambiguous '=' to find the value in our k/v pair - if (qsStrncmp(key, qs_kv[i], keyLen) == 0) { - skip = strcspn(qs_kv[i], "="); - if (qs_kv[i][skip] == '=') { - skip++; - } - // return (zero-char value) ? ptr to trailing '\0' : ptr to value - if (nth == 0) { - return qs_kv[i] + skip; - } else { - --nth; - } +#else // _qsSORTING + for (i = 0; i < qs_kv_size; i++) + { + // we rely on the unambiguous '=' to find the value in our k/v pair + if (qsStrncmp(key, qs_kv[i], keyLen) == 0) + { + skip = strcspn(qs_kv[i], "="); + if (qs_kv[i][skip] == '=') + { + skip++; + } + // return (zero-char value) ? ptr to trailing '\0' : ptr to value + if (nth == 0) + { + return qs_kv[i] + skip; + } + else + { + --nth; + } + } } - } -#endif // _qsSORTING +#endif // _qsSORTING - return NULL; + return NULL; } inline char* qsScanvalue(const char* key, const char* qs, char* val, - size_t val_len) { - size_t i, keyLen; - const char* tmp; - - // find the beginning of the k/v substrings - if ((tmp = strchr(qs, '?')) != NULL) { - qs = tmp + 1; - } - - keyLen = strlen(key); - while (qs[0] != '#' && qs[0] != '\0') { - if (qsStrncmp(key, qs, keyLen) == 0) { - break; + size_t val_len) +{ + size_t i, keyLen; + const char* tmp; + + // find the beginning of the k/v substrings + if ((tmp = strchr(qs, '?')) != NULL) + { + qs = tmp + 1; } - qs += strcspn(qs, "&") + 1; - } - if (qs[0] == '\0') { - return NULL; - } - - qs += strcspn(qs, "=&#"); - if (qs[0] == '=') { - qs++; - i = strcspn(qs, "&=#"); - strncpy(val, qs, (val_len - 1) < (i + 1) ? (val_len - 1) : (i + 1)); - qsDecode(val); - } else { - if (val_len > 0) { - val[0] = '\0'; + keyLen = strlen(key); + while (qs[0] != '#' && qs[0] != '\0') + { + if (qsStrncmp(key, qs, keyLen) == 0) + { + break; + } + qs += strcspn(qs, "&") + 1; + } + + if (qs[0] == '\0') + { + return NULL; } - } - return val; + qs += strcspn(qs, "=&#"); + if (qs[0] == '=') + { + qs++; + i = strcspn(qs, "&=#"); + strncpy(val, qs, (val_len - 1) < (i + 1) ? (val_len - 1) : (i + 1)); + qsDecode(val); + } + else + { + if (val_len > 0) + { + val[0] = '\0'; + } + } + + return val; } -} // namespace crow +} // namespace crow // ---------------------------------------------------------------------------- -namespace crow { -class QueryString { - public: - static const int maxKeyValuePairsCount = 256; +namespace crow +{ +class QueryString +{ + public: + static const int maxKeyValuePairsCount = 256; - QueryString() = default; + QueryString() = default; - QueryString(const QueryString& qs) : url(qs.url) { - for (auto p : qs.keyValuePairs) { - keyValuePairs.push_back( - const_cast<char*>(p - qs.url.c_str() + url.c_str())); - } - } - - QueryString& operator=(const QueryString& qs) { - url = qs.url; - keyValuePairs.clear(); - for (auto p : qs.keyValuePairs) { - keyValuePairs.push_back( - const_cast<char*>(p - qs.url.c_str() + url.c_str())); + QueryString(const QueryString& qs) : url(qs.url) + { + for (auto p : qs.keyValuePairs) + { + keyValuePairs.push_back( + const_cast<char*>(p - qs.url.c_str() + url.c_str())); + } } - return *this; - } - - QueryString& operator=(QueryString&& qs) { - keyValuePairs = std::move(qs.keyValuePairs); - auto* oldData = const_cast<char*>(qs.url.c_str()); - url = std::move(qs.url); - for (auto& p : keyValuePairs) { - p += const_cast<char*>(url.c_str()) - oldData; + + QueryString& operator=(const QueryString& qs) + { + url = qs.url; + keyValuePairs.clear(); + for (auto p : qs.keyValuePairs) + { + keyValuePairs.push_back( + const_cast<char*>(p - qs.url.c_str() + url.c_str())); + } + return *this; } - return *this; - } - explicit QueryString(std::string url) : url(std::move(url)) { - if (url.empty()) { - return; + QueryString& operator=(QueryString&& qs) + { + keyValuePairs = std::move(qs.keyValuePairs); + auto* oldData = const_cast<char*>(qs.url.c_str()); + url = std::move(qs.url); + for (auto& p : keyValuePairs) + { + p += const_cast<char*>(url.c_str()) - oldData; + } + return *this; } - keyValuePairs.resize(maxKeyValuePairsCount); + explicit QueryString(std::string url) : url(std::move(url)) + { + if (url.empty()) + { + return; + } - int count = qsParse(&url[0], &keyValuePairs[0], maxKeyValuePairsCount); - keyValuePairs.resize(count); - } + keyValuePairs.resize(maxKeyValuePairsCount); - void clear() { - keyValuePairs.clear(); - url.clear(); - } + int count = qsParse(&url[0], &keyValuePairs[0], maxKeyValuePairsCount); + keyValuePairs.resize(count); + } + + void clear() + { + keyValuePairs.clear(); + url.clear(); + } + + friend std::ostream& operator<<(std::ostream& os, const QueryString& qs) + { + os << "[ "; + for (size_t i = 0; i < qs.keyValuePairs.size(); ++i) + { + if (i != 0u) + { + os << ", "; + } + os << qs.keyValuePairs[i]; + } + os << " ]"; + return os; + } - friend std::ostream& operator<<(std::ostream& os, const QueryString& qs) { - os << "[ "; - for (size_t i = 0; i < qs.keyValuePairs.size(); ++i) { - if (i != 0u) { - os << ", "; - } - os << qs.keyValuePairs[i]; + char* get(const std::string& name) const + { + char* ret = + qsK2v(name.c_str(), keyValuePairs.data(), keyValuePairs.size()); + return ret; } - os << " ]"; - return os; - } - - char* get(const std::string& name) const { - char* ret = qsK2v(name.c_str(), keyValuePairs.data(), keyValuePairs.size()); - return ret; - } - - std::vector<char*> getList(const std::string& name) const { - std::vector<char*> ret; - std::string plus = name + "[]"; - char* element = nullptr; - - int count = 0; - while (1) { - element = qsK2v(plus.c_str(), keyValuePairs.data(), keyValuePairs.size(), - count++); - if (element == nullptr) { - break; - } - ret.push_back(element); + + std::vector<char*> getList(const std::string& name) const + { + std::vector<char*> ret; + std::string plus = name + "[]"; + char* element = nullptr; + + int count = 0; + while (1) + { + element = qsK2v(plus.c_str(), keyValuePairs.data(), + keyValuePairs.size(), count++); + if (element == nullptr) + { + break; + } + ret.push_back(element); + } + return ret; } - return ret; - } - private: - std::string url; - std::vector<char*> keyValuePairs; + private: + std::string url; + std::vector<char*> keyValuePairs; }; -} // namespace crow +} // namespace crow diff --git a/crow/include/crow/routing.h b/crow/include/crow/routing.h index ddf307aa63..945f3611d8 100644 --- a/crow/include/crow/routing.h +++ b/crow/include/crow/routing.h @@ -1,5 +1,8 @@ #pragma once +#include "boost/container/flat_map.hpp" + +#include <boost/lexical_cast.hpp> #include <cerrno> #include <cstdint> #include <cstdlib> @@ -8,9 +11,6 @@ #include <tuple> #include <utility> #include <vector> -#include <boost/lexical_cast.hpp> - -#include "boost/container/flat_map.hpp" #include "crow/common.h" #include "crow/http_request.h" @@ -19,969 +19,1174 @@ #include "crow/utility.h" #include "crow/websocket.h" -namespace crow { -class BaseRule { - public: - BaseRule(std::string rule) : rule(std::move(rule)) {} +namespace crow +{ +class BaseRule +{ + public: + BaseRule(std::string rule) : rule(std::move(rule)) + { + } - virtual ~BaseRule() {} + virtual ~BaseRule() + { + } - virtual void validate() = 0; - std::unique_ptr<BaseRule> upgrade() { - if (ruleToUpgrade) return std::move(ruleToUpgrade); - return {}; - } + virtual void validate() = 0; + std::unique_ptr<BaseRule> upgrade() + { + if (ruleToUpgrade) + return std::move(ruleToUpgrade); + return {}; + } - virtual void handle(const Request&, Response&, const RoutingParams&) = 0; - virtual void handleUpgrade(const Request&, Response& res, SocketAdaptor&&) { - res = Response(boost::beast::http::status::not_found); - res.end(); - } + virtual void handle(const Request&, Response&, const RoutingParams&) = 0; + virtual void handleUpgrade(const Request&, Response& res, SocketAdaptor&&) + { + res = Response(boost::beast::http::status::not_found); + res.end(); + } #ifdef BMCWEB_ENABLE_SSL - virtual void handleUpgrade(const Request&, Response& res, SSLAdaptor&&) { - res = Response(boost::beast::http::status::not_found); - res.end(); - } + virtual void handleUpgrade(const Request&, Response& res, SSLAdaptor&&) + { + res = Response(boost::beast::http::status::not_found); + res.end(); + } #endif - uint32_t getMethods() { return methodsBitfield; } + uint32_t getMethods() + { + return methodsBitfield; + } - protected: - uint32_t methodsBitfield{1 << (int)boost::beast::http::verb::get}; + protected: + uint32_t methodsBitfield{1 << (int)boost::beast::http::verb::get}; - std::string rule; - std::string nameStr; + std::string rule; + std::string nameStr; - std::unique_ptr<BaseRule> ruleToUpgrade; + std::unique_ptr<BaseRule> ruleToUpgrade; - friend class Router; - template <typename T> - friend struct RuleParameterTraits; + friend class Router; + template <typename T> friend struct RuleParameterTraits; }; -namespace detail { -namespace routing_handler_call_helper { -template <typename T, int Pos> -struct CallPair { - using type = T; - static const int pos = Pos; +namespace detail +{ +namespace routing_handler_call_helper +{ +template <typename T, int Pos> struct CallPair +{ + using type = T; + static const int pos = Pos; }; -template <typename H1> -struct CallParams { - H1& handler; - const RoutingParams& params; - const Request& req; - Response& res; +template <typename H1> struct CallParams +{ + H1& handler; + const RoutingParams& params; + const Request& req; + Response& res; }; template <typename F, int NInt, int NUint, int NDouble, int NString, typename S1, typename S2> -struct Call {}; +struct Call +{ +}; template <typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2> struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>, - black_magic::S<Args2...>> { - void operator()(F cparams) { - using pushed = typename black_magic::S<Args2...>::template push_back< - CallPair<int64_t, NInt>>; - Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>, - pushed>()(cparams); - } + black_magic::S<Args2...>> +{ + void operator()(F cparams) + { + using pushed = typename black_magic::S<Args2...>::template push_back< + CallPair<int64_t, NInt>>; + Call<F, NInt + 1, NUint, NDouble, NString, black_magic::S<Args1...>, + pushed>()(cparams); + } }; template <typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2> struct Call<F, NInt, NUint, NDouble, NString, - black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>> { - void operator()(F cparams) { - using pushed = typename black_magic::S<Args2...>::template push_back< - CallPair<uint64_t, NUint>>; - Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>, - pushed>()(cparams); - } + black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>> +{ + void operator()(F cparams) + { + using pushed = typename black_magic::S<Args2...>::template push_back< + CallPair<uint64_t, NUint>>; + Call<F, NInt, NUint + 1, NDouble, NString, black_magic::S<Args1...>, + pushed>()(cparams); + } }; template <typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2> struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>, - black_magic::S<Args2...>> { - void operator()(F cparams) { - using pushed = typename black_magic::S<Args2...>::template push_back< - CallPair<double, NDouble>>; - Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>, - pushed>()(cparams); - } + black_magic::S<Args2...>> +{ + void operator()(F cparams) + { + using pushed = typename black_magic::S<Args2...>::template push_back< + CallPair<double, NDouble>>; + Call<F, NInt, NUint, NDouble + 1, NString, black_magic::S<Args1...>, + pushed>()(cparams); + } }; template <typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1, typename... Args2> struct Call<F, NInt, NUint, NDouble, NString, - black_magic::S<std::string, Args1...>, black_magic::S<Args2...>> { - void operator()(F cparams) { - using pushed = typename black_magic::S<Args2...>::template push_back< - CallPair<std::string, NString>>; - Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>, - pushed>()(cparams); - } + black_magic::S<std::string, Args1...>, black_magic::S<Args2...>> +{ + void operator()(F cparams) + { + using pushed = typename black_magic::S<Args2...>::template push_back< + CallPair<std::string, NString>>; + Call<F, NInt, NUint, NDouble, NString + 1, black_magic::S<Args1...>, + pushed>()(cparams); + } }; template <typename F, int NInt, int NUint, int NDouble, int NString, typename... Args1> struct Call<F, NInt, NUint, NDouble, NString, black_magic::S<>, - black_magic::S<Args1...>> { - void operator()(F cparams) { - cparams.handler( - cparams.req, cparams.res, - cparams.params.template get<typename Args1::type>(Args1::pos)...); - } + black_magic::S<Args1...>> +{ + void operator()(F cparams) + { + cparams.handler( + cparams.req, cparams.res, + cparams.params.template get<typename Args1::type>(Args1::pos)...); + } }; -template <typename Func, typename... ArgsWrapped> -struct Wrapped { - template <typename... Args> - void set( - Func f, - typename std::enable_if< - !std::is_same< - typename std::tuple_element<0, std::tuple<Args..., void>>::type, - const Request&>::value, - int>::type = 0) { - handler = ( +template <typename Func, typename... ArgsWrapped> struct Wrapped +{ + template <typename... Args> + void set( + Func f, + typename std::enable_if< + !std::is_same< + typename std::tuple_element<0, std::tuple<Args..., void>>::type, + const Request&>::value, + int>::type = 0) + { + handler = ( #ifdef BMCWEB_CAN_USE_CPP14 - [f = std::move(f)] + [f = std::move(f)] #else - [f] + [f] #endif - (const Request&, Response& res, Args... args) { - res = Response(f(args...)); - res.end(); - }); - } - - template <typename Req, typename... Args> - struct ReqHandlerWrapper { - ReqHandlerWrapper(Func f) : f(std::move(f)) {} - - void operator()(const Request& req, Response& res, Args... args) { - res = Response(f(req, args...)); - res.end(); - } - - Func f; - }; - - template <typename... Args> - void set( - Func f, - typename std::enable_if< - std::is_same< - typename std::tuple_element<0, std::tuple<Args..., void>>::type, - const Request&>::value && - !std::is_same<typename std::tuple_element< - 1, std::tuple<Args..., void, void>>::type, - Response&>::value, - int>::type = 0) { - handler = ReqHandlerWrapper<Args...>(std::move(f)); - /*handler = ( - [f = std::move(f)] - (const Request& req, Response& res, Args... args){ - res = Response(f(req, args...)); - res.end(); - });*/ - } - - template <typename... Args> - void set( - Func f, - typename std::enable_if< - std::is_same< - typename std::tuple_element<0, std::tuple<Args..., void>>::type, - const Request&>::value && - std::is_same<typename std::tuple_element< - 1, std::tuple<Args..., void, void>>::type, - Response&>::value, - int>::type = 0) { - handler = std::move(f); - } - - template <typename... Args> - struct HandlerTypeHelper { - using type = - std::function<void(const crow::Request&, crow::Response&, Args...)>; - using args_type = black_magic::S<typename black_magic::promote_t<Args>...>; - }; - - template <typename... Args> - struct HandlerTypeHelper<const Request&, Args...> { - using type = - std::function<void(const crow::Request&, crow::Response&, Args...)>; - using args_type = black_magic::S<typename black_magic::promote_t<Args>...>; - }; - - template <typename... Args> - struct HandlerTypeHelper<const Request&, Response&, Args...> { - using type = - std::function<void(const crow::Request&, crow::Response&, Args...)>; - using args_type = black_magic::S<typename black_magic::promote_t<Args>...>; - }; - - typename HandlerTypeHelper<ArgsWrapped...>::type handler; - - void operator()(const Request& req, Response& res, - const RoutingParams& params) { - detail::routing_handler_call_helper::Call< - detail::routing_handler_call_helper::CallParams<decltype(handler)>, 0, - 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type, - black_magic::S<>>()( - detail::routing_handler_call_helper::CallParams<decltype(handler)>{ - handler, params, req, res}); - } + (const Request&, Response& res, Args... args) { + res = Response(f(args...)); + res.end(); + }); + } + + template <typename Req, typename... Args> struct ReqHandlerWrapper + { + ReqHandlerWrapper(Func f) : f(std::move(f)) + { + } + + void operator()(const Request& req, Response& res, Args... args) + { + res = Response(f(req, args...)); + res.end(); + } + + Func f; + }; + + template <typename... Args> + void set( + Func f, + typename std::enable_if< + std::is_same< + typename std::tuple_element<0, std::tuple<Args..., void>>::type, + const Request&>::value && + !std::is_same<typename std::tuple_element< + 1, std::tuple<Args..., void, void>>::type, + Response&>::value, + int>::type = 0) + { + handler = ReqHandlerWrapper<Args...>(std::move(f)); + /*handler = ( + [f = std::move(f)] + (const Request& req, Response& res, Args... args){ + res = Response(f(req, args...)); + res.end(); + });*/ + } + + template <typename... Args> + void set( + Func f, + typename std::enable_if< + std::is_same< + typename std::tuple_element<0, std::tuple<Args..., void>>::type, + const Request&>::value && + std::is_same<typename std::tuple_element< + 1, std::tuple<Args..., void, void>>::type, + Response&>::value, + int>::type = 0) + { + handler = std::move(f); + } + + template <typename... Args> struct HandlerTypeHelper + { + using type = + std::function<void(const crow::Request&, crow::Response&, Args...)>; + using args_type = + black_magic::S<typename black_magic::promote_t<Args>...>; + }; + + template <typename... Args> + struct HandlerTypeHelper<const Request&, Args...> + { + using type = + std::function<void(const crow::Request&, crow::Response&, Args...)>; + using args_type = + black_magic::S<typename black_magic::promote_t<Args>...>; + }; + + template <typename... Args> + struct HandlerTypeHelper<const Request&, Response&, Args...> + { + using type = + std::function<void(const crow::Request&, crow::Response&, Args...)>; + using args_type = + black_magic::S<typename black_magic::promote_t<Args>...>; + }; + + typename HandlerTypeHelper<ArgsWrapped...>::type handler; + + void operator()(const Request& req, Response& res, + const RoutingParams& params) + { + detail::routing_handler_call_helper::Call< + detail::routing_handler_call_helper::CallParams<decltype(handler)>, + 0, 0, 0, 0, typename HandlerTypeHelper<ArgsWrapped...>::args_type, + black_magic::S<>>()( + detail::routing_handler_call_helper::CallParams<decltype(handler)>{ + handler, params, req, res}); + } }; -} // namespace routing_handler_call_helper -} // namespace detail +} // namespace routing_handler_call_helper +} // namespace detail -class WebSocketRule : public BaseRule { - using self_t = WebSocketRule; +class WebSocketRule : public BaseRule +{ + using self_t = WebSocketRule; - public: - WebSocketRule(std::string rule) : BaseRule(std::move(rule)) {} + public: + WebSocketRule(std::string rule) : BaseRule(std::move(rule)) + { + } - void validate() override {} + void validate() override + { + } - void handle(const Request&, Response& res, const RoutingParams&) override { - res = Response(boost::beast::http::status::not_found); - res.end(); - } + void handle(const Request&, Response& res, const RoutingParams&) override + { + res = Response(boost::beast::http::status::not_found); + res.end(); + } - void handleUpgrade(const Request& req, Response&, - SocketAdaptor&& adaptor) override { - new crow::websocket::ConnectionImpl<SocketAdaptor>( - req, std::move(adaptor), openHandler, messageHandler, closeHandler, - errorHandler); - } -#ifdef BMCWEB_ENABLE_SSL - void handleUpgrade(const Request& req, Response&, - SSLAdaptor&& adaptor) override { - std::shared_ptr<crow::websocket::ConnectionImpl<SSLAdaptor>> myConnection = - std::make_shared<crow::websocket::ConnectionImpl<SSLAdaptor>>( + void handleUpgrade(const Request& req, Response&, + SocketAdaptor&& adaptor) override + { + new crow::websocket::ConnectionImpl<SocketAdaptor>( req, std::move(adaptor), openHandler, messageHandler, closeHandler, errorHandler); - myConnection->start(); - } + } +#ifdef BMCWEB_ENABLE_SSL + void handleUpgrade(const Request& req, Response&, + SSLAdaptor&& adaptor) override + { + std::shared_ptr<crow::websocket::ConnectionImpl<SSLAdaptor>> + myConnection = + std::make_shared<crow::websocket::ConnectionImpl<SSLAdaptor>>( + req, std::move(adaptor), openHandler, messageHandler, + closeHandler, errorHandler); + myConnection->start(); + } #endif - template <typename Func> - self_t& onopen(Func f) { - openHandler = f; - return *this; - } - - template <typename Func> - self_t& onmessage(Func f) { - messageHandler = f; - return *this; - } - - template <typename Func> - self_t& onclose(Func f) { - closeHandler = f; - return *this; - } - - template <typename Func> - self_t& onerror(Func f) { - errorHandler = f; - return *this; - } - - protected: - std::function<void(crow::websocket::Connection&)> openHandler; - std::function<void(crow::websocket::Connection&, const std::string&, bool)> - messageHandler; - std::function<void(crow::websocket::Connection&, const std::string&)> - closeHandler; - std::function<void(crow::websocket::Connection&)> errorHandler; + template <typename Func> self_t& onopen(Func f) + { + openHandler = f; + return *this; + } + + template <typename Func> self_t& onmessage(Func f) + { + messageHandler = f; + return *this; + } + + template <typename Func> self_t& onclose(Func f) + { + closeHandler = f; + return *this; + } + + template <typename Func> self_t& onerror(Func f) + { + errorHandler = f; + return *this; + } + + protected: + std::function<void(crow::websocket::Connection&)> openHandler; + std::function<void(crow::websocket::Connection&, const std::string&, bool)> + messageHandler; + std::function<void(crow::websocket::Connection&, const std::string&)> + closeHandler; + std::function<void(crow::websocket::Connection&)> errorHandler; }; -template <typename T> -struct RuleParameterTraits { - using self_t = T; - WebSocketRule& websocket() { - auto p = new WebSocketRule(((self_t*)this)->rule); - ((self_t*)this)->ruleToUpgrade.reset(p); - return *p; - } - - self_t& name(std::string name) noexcept { - ((self_t*)this)->nameStr = std::move(name); - return (self_t&)*this; - } - - self_t& methods(boost::beast::http::verb method) { - ((self_t*)this)->methodsBitfield = 1 << (int)method; - return (self_t&)*this; - } - - template <typename... MethodArgs> - self_t& methods(boost::beast::http::verb method, MethodArgs... args_method) { - methods(args_method...); - ((self_t*)this)->methodsBitfield |= 1 << (int)method; - return (self_t&)*this; - } +template <typename T> struct RuleParameterTraits +{ + using self_t = T; + WebSocketRule& websocket() + { + auto p = new WebSocketRule(((self_t*)this)->rule); + ((self_t*)this)->ruleToUpgrade.reset(p); + return *p; + } + + self_t& name(std::string name) noexcept + { + ((self_t*)this)->nameStr = std::move(name); + return (self_t&)*this; + } + + self_t& methods(boost::beast::http::verb method) + { + ((self_t*)this)->methodsBitfield = 1 << (int)method; + return (self_t&)*this; + } + + template <typename... MethodArgs> + self_t& methods(boost::beast::http::verb method, MethodArgs... args_method) + { + methods(args_method...); + ((self_t*)this)->methodsBitfield |= 1 << (int)method; + return (self_t&)*this; + } }; -class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule> { - public: - DynamicRule(std::string rule) : BaseRule(std::move(rule)) {} - - void validate() override { - if (!erasedHandler) { - throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") + - "no handler for url " + rule); - } - } - - void handle(const Request& req, Response& res, - const RoutingParams& params) override { - erasedHandler(req, res, params); - } - - template <typename Func> - void operator()(Func f) { - using function_t = utility::function_traits<Func>; - - erasedHandler = - wrap(std::move(f), black_magic::gen_seq<function_t::arity>()); - } - - // enable_if Arg1 == request && Arg2 == Response - // enable_if Arg1 == request && Arg2 != resposne - // enable_if Arg1 != request - - template <typename Func, unsigned... Indices> - - std::function<void(const Request&, Response&, const RoutingParams&)> wrap( - Func f, black_magic::Seq<Indices...>) { - using function_t = utility::function_traits<Func>; - - if (!black_magic::isParameterTagCompatible( - black_magic::getParameterTagRuntime(rule.c_str()), - black_magic::compute_parameter_tag_from_args_list< - typename function_t::template arg<Indices>...>::value)) { - throw std::runtime_error( - "routeDynamic: Handler type is mismatched with URL parameters: " + - rule); - } - auto ret = detail::routing_handler_call_helper::Wrapped< - Func, typename function_t::template arg<Indices>...>(); - ret.template set<typename function_t::template arg<Indices>...>( - std::move(f)); - return ret; - } - - template <typename Func> - void operator()(std::string name, Func&& f) { - nameStr = std::move(name); - (*this).template operator()<Func>(std::forward(f)); - } - - private: - std::function<void(const Request&, Response&, const RoutingParams&)> - erasedHandler; +class DynamicRule : public BaseRule, public RuleParameterTraits<DynamicRule> +{ + public: + DynamicRule(std::string rule) : BaseRule(std::move(rule)) + { + } + + void validate() override + { + if (!erasedHandler) + { + throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") + + "no handler for url " + rule); + } + } + + void handle(const Request& req, Response& res, + const RoutingParams& params) override + { + erasedHandler(req, res, params); + } + + template <typename Func> void operator()(Func f) + { + using function_t = utility::function_traits<Func>; + + erasedHandler = + wrap(std::move(f), black_magic::gen_seq<function_t::arity>()); + } + + // enable_if Arg1 == request && Arg2 == Response + // enable_if Arg1 == request && Arg2 != resposne + // enable_if Arg1 != request + + template <typename Func, unsigned... Indices> + + std::function<void(const Request&, Response&, const RoutingParams&)> + wrap(Func f, black_magic::Seq<Indices...>) + { + using function_t = utility::function_traits<Func>; + + if (!black_magic::isParameterTagCompatible( + black_magic::getParameterTagRuntime(rule.c_str()), + black_magic::compute_parameter_tag_from_args_list< + typename function_t::template arg<Indices>...>::value)) + { + throw std::runtime_error("routeDynamic: Handler type is mismatched " + "with URL parameters: " + + rule); + } + auto ret = detail::routing_handler_call_helper::Wrapped< + Func, typename function_t::template arg<Indices>...>(); + ret.template set<typename function_t::template arg<Indices>...>( + std::move(f)); + return ret; + } + + template <typename Func> void operator()(std::string name, Func&& f) + { + nameStr = std::move(name); + (*this).template operator()<Func>(std::forward(f)); + } + + private: + std::function<void(const Request&, Response&, const RoutingParams&)> + erasedHandler; }; template <typename... Args> class TaggedRule : public BaseRule, - public RuleParameterTraits<TaggedRule<Args...>> { - public: - using self_t = TaggedRule<Args...>; + public RuleParameterTraits<TaggedRule<Args...>> +{ + public: + using self_t = TaggedRule<Args...>; + + TaggedRule(std::string rule) : BaseRule(std::move(rule)) + { + } - TaggedRule(std::string rule) : BaseRule(std::move(rule)) {} + void validate() override + { + if (!handler) + { + throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") + + "no handler for url " + rule); + } + } - void validate() override { - if (!handler) { - throw std::runtime_error(nameStr + (!nameStr.empty() ? ": " : "") + - "no handler for url " + rule); + template <typename Func> + typename std::enable_if< + black_magic::CallHelper<Func, black_magic::S<Args...>>::value, + void>::type + operator()(Func&& f) + { + static_assert( + black_magic::CallHelper<Func, black_magic::S<Args...>>::value || + black_magic::CallHelper< + Func, black_magic::S<crow::Request, Args...>>::value, + "Handler type is mismatched with URL parameters"); + static_assert( + !std::is_same<void, decltype(f(std::declval<Args>()...))>::value, + "Handler function cannot have void return type; valid return " + "types: " + "string, int, crow::resposne, nlohmann::json"); + + handler = [f = std::move(f)](const Request&, Response& res, + Args... args) { + res = Response(f(args...)); + res.end(); + }; } - } - template <typename Func> - typename std::enable_if< - black_magic::CallHelper<Func, black_magic::S<Args...>>::value, void>::type - operator()(Func&& f) { - static_assert( - black_magic::CallHelper<Func, black_magic::S<Args...>>::value || + template <typename Func> + typename std::enable_if< + !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && black_magic::CallHelper< Func, black_magic::S<crow::Request, Args...>>::value, - "Handler type is mismatched with URL parameters"); - static_assert( - !std::is_same<void, decltype(f(std::declval<Args>()...))>::value, - "Handler function cannot have void return type; valid return types: " - "string, int, crow::resposne, nlohmann::json"); - - handler = [f = std::move(f)](const Request&, Response& res, Args... args) { - res = Response(f(args...)); - res.end(); - }; - } - - template <typename Func> - typename std::enable_if< - !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && - black_magic::CallHelper< - Func, black_magic::S<crow::Request, Args...>>::value, - void>::type - operator()(Func&& f) { - static_assert( - black_magic::CallHelper<Func, black_magic::S<Args...>>::value || - black_magic::CallHelper< + void>::type + operator()(Func&& f) + { + static_assert( + black_magic::CallHelper<Func, black_magic::S<Args...>>::value || + black_magic::CallHelper< + Func, black_magic::S<crow::Request, Args...>>::value, + "Handler type is mismatched with URL parameters"); + static_assert( + !std::is_same<void, decltype(f(std::declval<crow::Request>(), + std::declval<Args>()...))>::value, + "Handler function cannot have void return type; valid return " + "types: " + "string, int, crow::resposne,nlohmann::json"); + + handler = [f = std::move(f)](const crow::Request& req, + crow::Response& res, Args... args) { + res = Response(f(req, args...)); + res.end(); + }; + } + + template <typename Func> + typename std::enable_if< + !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && + !black_magic::CallHelper< Func, black_magic::S<crow::Request, Args...>>::value, - "Handler type is mismatched with URL parameters"); - static_assert( - !std::is_same<void, decltype(f(std::declval<crow::Request>(), - std::declval<Args>()...))>::value, - "Handler function cannot have void return type; valid return types: " - "string, int, crow::resposne,nlohmann::json"); - - handler = [f = std::move(f)](const crow::Request& req, crow::Response& res, - Args... args) { - res = Response(f(req, args...)); - res.end(); - }; - } - - template <typename Func> - typename std::enable_if< - !black_magic::CallHelper<Func, black_magic::S<Args...>>::value && - !black_magic::CallHelper< - Func, black_magic::S<crow::Request, Args...>>::value, - void>::type - operator()(Func&& f) { - static_assert( - black_magic::CallHelper<Func, black_magic::S<Args...>>::value || - black_magic::CallHelper< - Func, black_magic::S<crow::Request, Args...>>::value || - black_magic::CallHelper< - Func, - black_magic::S<crow::Request, crow::Response&, Args...>>::value, - "Handler type is mismatched with URL parameters"); - static_assert( - std::is_same<void, decltype(f(std::declval<crow::Request>(), - std::declval<crow::Response&>(), - std::declval<Args>()...))>::value, - "Handler function with response argument should have void return type"); - - handler = std::move(f); - } - - template <typename Func> - void operator()(std::string name, Func&& f) { - nameStr = std::move(name); - (*this).template operator()<Func>(std::forward(f)); - } - - void handle(const Request& req, Response& res, - const RoutingParams& params) override { - detail::routing_handler_call_helper::Call< - detail::routing_handler_call_helper::CallParams<decltype(handler)>, 0, - 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()( - detail::routing_handler_call_helper::CallParams<decltype(handler)>{ - handler, params, req, res}); - } - - private: - std::function<void(const crow::Request&, crow::Response&, Args...)> handler; + void>::type + operator()(Func&& f) + { + static_assert( + black_magic::CallHelper<Func, black_magic::S<Args...>>::value || + black_magic::CallHelper< + Func, black_magic::S<crow::Request, Args...>>::value || + black_magic::CallHelper< + Func, black_magic::S<crow::Request, crow::Response&, + Args...>>::value, + "Handler type is mismatched with URL parameters"); + static_assert( + std::is_same<void, decltype(f(std::declval<crow::Request>(), + std::declval<crow::Response&>(), + std::declval<Args>()...))>::value, + "Handler function with response argument should have void return " + "type"); + + handler = std::move(f); + } + + template <typename Func> void operator()(std::string name, Func&& f) + { + nameStr = std::move(name); + (*this).template operator()<Func>(std::forward(f)); + } + + void handle(const Request& req, Response& res, + const RoutingParams& params) override + { + detail::routing_handler_call_helper::Call< + detail::routing_handler_call_helper::CallParams<decltype(handler)>, + 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()( + detail::routing_handler_call_helper::CallParams<decltype(handler)>{ + handler, params, req, res}); + } + + private: + std::function<void(const crow::Request&, crow::Response&, Args...)> handler; }; const int ruleSpecialRedirectSlash = 1; -class Trie { - public: - struct Node { - unsigned ruleIndex{}; - std::array<unsigned, (int)ParamType::MAX> paramChildrens{}; - boost::container::flat_map<std::string, unsigned> children; - - bool isSimpleNode() const { - return !ruleIndex && - std::all_of(std::begin(paramChildrens), std::end(paramChildrens), - [](unsigned x) { return !x; }); - } - }; - - Trie() : nodes(1) {} - - private: - void optimizeNode(Node* node) { - for (auto x : node->paramChildrens) { - if (!x) continue; - Node* child = &nodes[x]; - optimizeNode(child); - } - if (node->children.empty()) return; - bool mergeWithChild = true; - for (auto& kv : node->children) { - Node* child = &nodes[kv.second]; - if (!child->isSimpleNode()) { - mergeWithChild = false; - break; - } - } - if (mergeWithChild) { - decltype(node->children) merged; - for (auto& kv : node->children) { - Node* child = &nodes[kv.second]; - for (auto& childKv : child->children) { - merged[kv.first + childKv.first] = childKv.second; - } - } - node->children = std::move(merged); - optimizeNode(node); - } else { - for (auto& kv : node->children) { - Node* child = &nodes[kv.second]; - optimizeNode(child); - } - } - } - - void optimize() { optimizeNode(head()); } - - public: - void validate() { - if (!head()->isSimpleNode()) - throw std::runtime_error("Internal error: Trie header should be simple!"); - optimize(); - } - - void findRouteIndexes(const std::string& req_url, - std::vector<unsigned>& route_indexes, - const Node* node = nullptr, unsigned pos = 0) { - if (node == nullptr) { - node = head(); - } - for (auto& kv : node->children) { - const std::string& fragment = kv.first; - const Node* child = &nodes[kv.second]; - if (pos >= req_url.size()) { - if (child->ruleIndex != 0 && fragment != "/") { - route_indexes.push_back(child->ruleIndex); - } - findRouteIndexes(req_url, route_indexes, child, pos + fragment.size()); - } else { - if (req_url.compare(pos, fragment.size(), fragment) == 0) { - findRouteIndexes(req_url, route_indexes, child, - pos + fragment.size()); - } - } - } - } - - std::pair<unsigned, RoutingParams> find( - const boost::string_view req_url, const Node* node = nullptr, - unsigned pos = 0, RoutingParams* params = nullptr) const { - RoutingParams empty; - if (params == nullptr) params = ∅ - - unsigned found{}; - RoutingParams matchParams; - - if (node == nullptr) node = head(); - if (pos == req_url.size()) return {node->ruleIndex, *params}; - - auto updateFound = [&found, - &matchParams](std::pair<unsigned, RoutingParams>& ret) { - if (ret.first && (!found || found > ret.first)) { - found = ret.first; - matchParams = std::move(ret.second); - } +class Trie +{ + public: + struct Node + { + unsigned ruleIndex{}; + std::array<unsigned, (int)ParamType::MAX> paramChildrens{}; + boost::container::flat_map<std::string, unsigned> children; + + bool isSimpleNode() const + { + return !ruleIndex && std::all_of(std::begin(paramChildrens), + std::end(paramChildrens), + [](unsigned x) { return !x; }); + } }; - if (node->paramChildrens[(int)ParamType::INT]) { - char c = req_url[pos]; - if ((c >= '0' && c <= '9') || c == '+' || c == '-') { - char* eptr; - errno = 0; - long long int value = std::strtoll(req_url.data() + pos, &eptr, 10); - if (errno != ERANGE && eptr != req_url.data() + pos) { - params->intParams.push_back(value); - auto ret = - find(req_url, &nodes[node->paramChildrens[(int)ParamType::INT]], - eptr - req_url.data(), params); - updateFound(ret); - params->intParams.pop_back(); - } - } - } - - if (node->paramChildrens[(int)ParamType::UINT]) { - char c = req_url[pos]; - if ((c >= '0' && c <= '9') || c == '+') { - char* eptr; - errno = 0; - unsigned long long int value = - std::strtoull(req_url.data() + pos, &eptr, 10); - if (errno != ERANGE && eptr != req_url.data() + pos) { - params->uintParams.push_back(value); - auto ret = - find(req_url, &nodes[node->paramChildrens[(int)ParamType::UINT]], - eptr - req_url.data(), params); - updateFound(ret); - params->uintParams.pop_back(); - } - } - } - - if (node->paramChildrens[(int)ParamType::DOUBLE]) { - char c = req_url[pos]; - if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.') { - char* eptr; - errno = 0; - double value = std::strtod(req_url.data() + pos, &eptr); - if (errno != ERANGE && eptr != req_url.data() + pos) { - params->doubleParams.push_back(value); - auto ret = find(req_url, - &nodes[node->paramChildrens[(int)ParamType::DOUBLE]], - eptr - req_url.data(), params); - updateFound(ret); - params->doubleParams.pop_back(); - } - } - } - - if (node->paramChildrens[(int)ParamType::STRING]) { - size_t epos = pos; - for (; epos < req_url.size(); epos++) { - if (req_url[epos] == '/') break; - } - - if (epos != pos) { - params->stringParams.emplace_back(req_url.substr(pos, epos - pos)); - auto ret = - find(req_url, &nodes[node->paramChildrens[(int)ParamType::STRING]], - epos, params); - updateFound(ret); - params->stringParams.pop_back(); - } - } - - if (node->paramChildrens[(int)ParamType::PATH]) { - size_t epos = req_url.size(); - - if (epos != pos) { - params->stringParams.emplace_back(req_url.substr(pos, epos - pos)); - auto ret = - find(req_url, &nodes[node->paramChildrens[(int)ParamType::PATH]], - epos, params); - updateFound(ret); - params->stringParams.pop_back(); - } - } - - for (auto& kv : node->children) { - const std::string& fragment = kv.first; - const Node* child = &nodes[kv.second]; - - if (req_url.compare(pos, fragment.size(), fragment) == 0) { - auto ret = find(req_url, child, pos + fragment.size(), params); - updateFound(ret); - } - } - - return {found, matchParams}; - } - - void add(const std::string& url, unsigned ruleIndex) { - unsigned idx{0}; - - for (unsigned i = 0; i < url.size(); i++) { - char c = url[i]; - if (c == '<') { - static struct ParamTraits { - ParamType type; - std::string name; - } paramTraits[] = { - {ParamType::INT, "<int>"}, {ParamType::UINT, "<uint>"}, - {ParamType::DOUBLE, "<float>"}, {ParamType::DOUBLE, "<double>"}, - {ParamType::STRING, "<str>"}, {ParamType::STRING, "<string>"}, - {ParamType::PATH, "<path>"}, - }; + Trie() : nodes(1) + { + } + + private: + void optimizeNode(Node* node) + { + for (auto x : node->paramChildrens) + { + if (!x) + continue; + Node* child = &nodes[x]; + optimizeNode(child); + } + if (node->children.empty()) + return; + bool mergeWithChild = true; + for (auto& kv : node->children) + { + Node* child = &nodes[kv.second]; + if (!child->isSimpleNode()) + { + mergeWithChild = false; + break; + } + } + if (mergeWithChild) + { + decltype(node->children) merged; + for (auto& kv : node->children) + { + Node* child = &nodes[kv.second]; + for (auto& childKv : child->children) + { + merged[kv.first + childKv.first] = childKv.second; + } + } + node->children = std::move(merged); + optimizeNode(node); + } + else + { + for (auto& kv : node->children) + { + Node* child = &nodes[kv.second]; + optimizeNode(child); + } + } + } - for (auto& x : paramTraits) { - if (url.compare(i, x.name.size(), x.name) == 0) { - if (!nodes[idx].paramChildrens[(int)x.type]) { - auto newNodeIdx = newNode(); - nodes[idx].paramChildrens[(int)x.type] = newNodeIdx; + void optimize() + { + optimizeNode(head()); + } + + public: + void validate() + { + if (!head()->isSimpleNode()) + throw std::runtime_error( + "Internal error: Trie header should be simple!"); + optimize(); + } + + void findRouteIndexes(const std::string& req_url, + std::vector<unsigned>& route_indexes, + const Node* node = nullptr, unsigned pos = 0) + { + if (node == nullptr) + { + node = head(); + } + for (auto& kv : node->children) + { + const std::string& fragment = kv.first; + const Node* child = &nodes[kv.second]; + if (pos >= req_url.size()) + { + if (child->ruleIndex != 0 && fragment != "/") + { + route_indexes.push_back(child->ruleIndex); + } + findRouteIndexes(req_url, route_indexes, child, + pos + fragment.size()); + } + else + { + if (req_url.compare(pos, fragment.size(), fragment) == 0) + { + findRouteIndexes(req_url, route_indexes, child, + pos + fragment.size()); + } + } + } + } + + std::pair<unsigned, RoutingParams> + find(const boost::string_view req_url, const Node* node = nullptr, + unsigned pos = 0, RoutingParams* params = nullptr) const + { + RoutingParams empty; + if (params == nullptr) + params = ∅ + + unsigned found{}; + RoutingParams matchParams; + + if (node == nullptr) + node = head(); + if (pos == req_url.size()) + return {node->ruleIndex, *params}; + + auto updateFound = + [&found, &matchParams](std::pair<unsigned, RoutingParams>& ret) { + if (ret.first && (!found || found > ret.first)) + { + found = ret.first; + matchParams = std::move(ret.second); + } + }; + + if (node->paramChildrens[(int)ParamType::INT]) + { + char c = req_url[pos]; + if ((c >= '0' && c <= '9') || c == '+' || c == '-') + { + char* eptr; + errno = 0; + long long int value = + std::strtoll(req_url.data() + pos, &eptr, 10); + if (errno != ERANGE && eptr != req_url.data() + pos) + { + params->intParams.push_back(value); + auto ret = + find(req_url, + &nodes[node->paramChildrens[(int)ParamType::INT]], + eptr - req_url.data(), params); + updateFound(ret); + params->intParams.pop_back(); + } } - idx = nodes[idx].paramChildrens[(int)x.type]; - i += x.name.size(); - break; - } - } - - i--; - } else { - std::string piece(&c, 1); - if (!nodes[idx].children.count(piece)) { - auto newNodeIdx = newNode(); - nodes[idx].children.emplace(piece, newNodeIdx); - } - idx = nodes[idx].children[piece]; - } - } - if (nodes[idx].ruleIndex) - throw std::runtime_error("handler already exists for " + url); - nodes[idx].ruleIndex = ruleIndex; - } - - private: - void debugNodePrint(Node* n, int level) { - for (int i = 0; i < (int)ParamType::MAX; i++) { - if (n->paramChildrens[i]) { - BMCWEB_LOG_DEBUG << std::string( - 2 * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/; - switch ((ParamType)i) { - case ParamType::INT: - BMCWEB_LOG_DEBUG << "<int>"; - break; - case ParamType::UINT: - BMCWEB_LOG_DEBUG << "<uint>"; - break; - case ParamType::DOUBLE: - BMCWEB_LOG_DEBUG << "<float>"; - break; - case ParamType::STRING: - BMCWEB_LOG_DEBUG << "<str>"; - break; - case ParamType::PATH: - BMCWEB_LOG_DEBUG << "<path>"; - break; - default: - BMCWEB_LOG_DEBUG << "<ERROR>"; - break; - } - - debugNodePrint(&nodes[n->paramChildrens[i]], level + 1); - } - } - for (auto& kv : n->children) { - BMCWEB_LOG_DEBUG << std::string(2 * level, - ' ') /*<< "(" << kv.second << ") "*/ - << kv.first; - debugNodePrint(&nodes[kv.second], level + 1); - } - } - - public: - void debugPrint() { debugNodePrint(head(), 0); } - - private: - const Node* head() const { return &nodes.front(); } - - Node* head() { return &nodes.front(); } - - unsigned newNode() { - nodes.resize(nodes.size() + 1); - return nodes.size() - 1; - } - - std::vector<Node> nodes; + } + + if (node->paramChildrens[(int)ParamType::UINT]) + { + char c = req_url[pos]; + if ((c >= '0' && c <= '9') || c == '+') + { + char* eptr; + errno = 0; + unsigned long long int value = + std::strtoull(req_url.data() + pos, &eptr, 10); + if (errno != ERANGE && eptr != req_url.data() + pos) + { + params->uintParams.push_back(value); + auto ret = + find(req_url, + &nodes[node->paramChildrens[(int)ParamType::UINT]], + eptr - req_url.data(), params); + updateFound(ret); + params->uintParams.pop_back(); + } + } + } + + if (node->paramChildrens[(int)ParamType::DOUBLE]) + { + char c = req_url[pos]; + if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.') + { + char* eptr; + errno = 0; + double value = std::strtod(req_url.data() + pos, &eptr); + if (errno != ERANGE && eptr != req_url.data() + pos) + { + params->doubleParams.push_back(value); + auto ret = find( + req_url, + &nodes[node->paramChildrens[(int)ParamType::DOUBLE]], + eptr - req_url.data(), params); + updateFound(ret); + params->doubleParams.pop_back(); + } + } + } + + if (node->paramChildrens[(int)ParamType::STRING]) + { + size_t epos = pos; + for (; epos < req_url.size(); epos++) + { + if (req_url[epos] == '/') + break; + } + + if (epos != pos) + { + params->stringParams.emplace_back( + req_url.substr(pos, epos - pos)); + auto ret = + find(req_url, + &nodes[node->paramChildrens[(int)ParamType::STRING]], + epos, params); + updateFound(ret); + params->stringParams.pop_back(); + } + } + + if (node->paramChildrens[(int)ParamType::PATH]) + { + size_t epos = req_url.size(); + + if (epos != pos) + { + params->stringParams.emplace_back( + req_url.substr(pos, epos - pos)); + auto ret = find( + req_url, &nodes[node->paramChildrens[(int)ParamType::PATH]], + epos, params); + updateFound(ret); + params->stringParams.pop_back(); + } + } + + for (auto& kv : node->children) + { + const std::string& fragment = kv.first; + const Node* child = &nodes[kv.second]; + + if (req_url.compare(pos, fragment.size(), fragment) == 0) + { + auto ret = find(req_url, child, pos + fragment.size(), params); + updateFound(ret); + } + } + + return {found, matchParams}; + } + + void add(const std::string& url, unsigned ruleIndex) + { + unsigned idx{0}; + + for (unsigned i = 0; i < url.size(); i++) + { + char c = url[i]; + if (c == '<') + { + static struct ParamTraits + { + ParamType type; + std::string name; + } paramTraits[] = { + {ParamType::INT, "<int>"}, + {ParamType::UINT, "<uint>"}, + {ParamType::DOUBLE, "<float>"}, + {ParamType::DOUBLE, "<double>"}, + {ParamType::STRING, "<str>"}, + {ParamType::STRING, "<string>"}, + {ParamType::PATH, "<path>"}, + }; + + for (auto& x : paramTraits) + { + if (url.compare(i, x.name.size(), x.name) == 0) + { + if (!nodes[idx].paramChildrens[(int)x.type]) + { + auto newNodeIdx = newNode(); + nodes[idx].paramChildrens[(int)x.type] = newNodeIdx; + } + idx = nodes[idx].paramChildrens[(int)x.type]; + i += x.name.size(); + break; + } + } + + i--; + } + else + { + std::string piece(&c, 1); + if (!nodes[idx].children.count(piece)) + { + auto newNodeIdx = newNode(); + nodes[idx].children.emplace(piece, newNodeIdx); + } + idx = nodes[idx].children[piece]; + } + } + if (nodes[idx].ruleIndex) + throw std::runtime_error("handler already exists for " + url); + nodes[idx].ruleIndex = ruleIndex; + } + + private: + void debugNodePrint(Node* n, int level) + { + for (int i = 0; i < (int)ParamType::MAX; i++) + { + if (n->paramChildrens[i]) + { + BMCWEB_LOG_DEBUG << std::string( + 2 * level, ' ') /*<< "("<<n->paramChildrens[i]<<") "*/; + switch ((ParamType)i) + { + case ParamType::INT: + BMCWEB_LOG_DEBUG << "<int>"; + break; + case ParamType::UINT: + BMCWEB_LOG_DEBUG << "<uint>"; + break; + case ParamType::DOUBLE: + BMCWEB_LOG_DEBUG << "<float>"; + break; + case ParamType::STRING: + BMCWEB_LOG_DEBUG << "<str>"; + break; + case ParamType::PATH: + BMCWEB_LOG_DEBUG << "<path>"; + break; + default: + BMCWEB_LOG_DEBUG << "<ERROR>"; + break; + } + + debugNodePrint(&nodes[n->paramChildrens[i]], level + 1); + } + } + for (auto& kv : n->children) + { + BMCWEB_LOG_DEBUG + << std::string(2 * level, ' ') /*<< "(" << kv.second << ") "*/ + << kv.first; + debugNodePrint(&nodes[kv.second], level + 1); + } + } + + public: + void debugPrint() + { + debugNodePrint(head(), 0); + } + + private: + const Node* head() const + { + return &nodes.front(); + } + + Node* head() + { + return &nodes.front(); + } + + unsigned newNode() + { + nodes.resize(nodes.size() + 1); + return nodes.size() - 1; + } + + std::vector<Node> nodes; }; -class Router { - public: - Router() : rules(2) {} - - DynamicRule& newRuleDynamic(const std::string& rule) { - std::unique_ptr<DynamicRule> ruleObject = - std::make_unique<DynamicRule>(rule); - DynamicRule* ptr = ruleObject.get(); - internalAddRuleObject(rule, std::move(ruleObject)); - - return *ptr; - } - - template <uint64_t N> - typename black_magic::Arguments<N>::type::template rebind<TaggedRule>& - newRuleTagged(const std::string& rule) { - using RuleT = - typename black_magic::Arguments<N>::type::template rebind<TaggedRule>; - std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); - RuleT* ptr = ruleObject.get(); - - internalAddRuleObject(rule, std::move(ruleObject)); - - return *ptr; - } - - void internalAddRuleObject(const std::string& rule, - std::unique_ptr<BaseRule> ruleObject) { - rules.emplace_back(std::move(ruleObject)); - trie.add(rule, rules.size() - 1); - - // directory case: - // request to `/about' url matches `/about/' rule - if (rule.size() > 2 && rule.back() == '/') { - trie.add(rule.substr(0, rule.size() - 1), rules.size() - 1); - } - } - - void validate() { - trie.validate(); - for (auto& rule : rules) { - if (rule) { - auto upgraded = rule->upgrade(); - if (upgraded) rule = std::move(upgraded); - rule->validate(); - } - } - } - - template <typename Adaptor> - void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor) { - auto found = trie.find(req.url); - unsigned ruleIndex = found.first; - if (!ruleIndex) { - BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url; - res = Response(boost::beast::http::status::not_found); - res.end(); - return; - } - - if (ruleIndex >= rules.size()) - throw std::runtime_error("Trie internal structure corrupted!"); - - if (ruleIndex == ruleSpecialRedirectSlash) { - BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: " - << req.url; - res = Response(boost::beast::http::status::moved_permanently); - - // TODO absolute url building - if (req.getHeaderValue("Host").empty()) { - res.addHeader("Location", std::string(req.url) + "/"); - } else { - res.addHeader( - "Location", - req.isSecure ? "https://" - : "http://" + std::string(req.getHeaderValue("Host")) + - std::string(req.url) + "/"); - } - res.end(); - return; - } - - if ((rules[ruleIndex]->getMethods() & (1 << (uint32_t)req.method())) == 0) { - BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url - << " with " << req.methodString() << "(" - << (uint32_t)req.method() << ") / " - << rules[ruleIndex]->getMethods(); - res = Response(boost::beast::http::status::not_found); - res.end(); - return; - } - - BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule - << "' " << (uint32_t)req.method() << " / " - << rules[ruleIndex]->getMethods(); - - // any uncaught exceptions become 500s - try { - rules[ruleIndex]->handleUpgrade(req, res, std::move(adaptor)); - } catch (std::exception& e) { - BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what(); - res = Response(boost::beast::http::status::internal_server_error); - res.end(); - return; - } catch (...) { - BMCWEB_LOG_ERROR - << "An uncaught exception occurred. The type was unknown " - "so no information was available."; - res = Response(boost::beast::http::status::internal_server_error); - res.end(); - return; - } - } - - void handle(const Request& req, Response& res) { - auto found = trie.find(req.url); - - unsigned ruleIndex = found.first; - - if (!ruleIndex) { - BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url; - res.result(boost::beast::http::status::not_found); - res.end(); - return; - } - - if (ruleIndex >= rules.size()) - throw std::runtime_error("Trie internal structure corrupted!"); - - if (ruleIndex == ruleSpecialRedirectSlash) { - BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: " - << req.url; - res = Response(boost::beast::http::status::moved_permanently); - - // TODO absolute url building - if (req.getHeaderValue("Host").empty()) { - res.addHeader("Location", std::string(req.url) + "/"); - } else { - res.addHeader("Location", (req.isSecure ? "https://" : "http://") + - std::string(req.getHeaderValue("Host")) + - std::string(req.url) + "/"); - } - res.end(); - return; - } - - if ((rules[ruleIndex]->getMethods() & (1 << (uint32_t)req.method())) == 0) { - BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url - << " with " << req.methodString() << "(" - << (uint32_t)req.method() << ") / " - << rules[ruleIndex]->getMethods(); - res = Response(boost::beast::http::status::not_found); - res.end(); - return; - } - - BMCWEB_LOG_DEBUG << "Matched rule '" << rules[ruleIndex]->rule << "' " - << (uint32_t)req.method() << " / " - << rules[ruleIndex]->getMethods(); - - // any uncaught exceptions become 500s - try { - rules[ruleIndex]->handle(req, res, found.second); - } catch (std::exception& e) { - BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what(); - res = Response(boost::beast::http::status::internal_server_error); - res.end(); - return; - } catch (...) { - BMCWEB_LOG_ERROR - << "An uncaught exception occurred. The type was unknown " - "so no information was available."; - res = Response(boost::beast::http::status::internal_server_error); - res.end(); - return; - } - } - - void debugPrint() { trie.debugPrint(); } - - std::vector<const std::string*> getRoutes(const std::string& parent) { - std::vector<unsigned> x; - std::vector<const std::string*> ret; - trie.findRouteIndexes(parent, x); - for (unsigned index : x) { - ret.push_back(&rules[index]->rule); - } - return ret; - } - - private: - std::vector<std::unique_ptr<BaseRule>> rules; - Trie trie; +class Router +{ + public: + Router() : rules(2) + { + } + + DynamicRule& newRuleDynamic(const std::string& rule) + { + std::unique_ptr<DynamicRule> ruleObject = + std::make_unique<DynamicRule>(rule); + DynamicRule* ptr = ruleObject.get(); + internalAddRuleObject(rule, std::move(ruleObject)); + + return *ptr; + } + + template <uint64_t N> + typename black_magic::Arguments<N>::type::template rebind<TaggedRule>& + newRuleTagged(const std::string& rule) + { + using RuleT = typename black_magic::Arguments<N>::type::template rebind< + TaggedRule>; + std::unique_ptr<RuleT> ruleObject = std::make_unique<RuleT>(rule); + RuleT* ptr = ruleObject.get(); + + internalAddRuleObject(rule, std::move(ruleObject)); + + return *ptr; + } + + void internalAddRuleObject(const std::string& rule, + std::unique_ptr<BaseRule> ruleObject) + { + rules.emplace_back(std::move(ruleObject)); + trie.add(rule, rules.size() - 1); + + // directory case: + // request to `/about' url matches `/about/' rule + if (rule.size() > 2 && rule.back() == '/') + { + trie.add(rule.substr(0, rule.size() - 1), rules.size() - 1); + } + } + + void validate() + { + trie.validate(); + for (auto& rule : rules) + { + if (rule) + { + auto upgraded = rule->upgrade(); + if (upgraded) + rule = std::move(upgraded); + rule->validate(); + } + } + } + + template <typename Adaptor> + void handleUpgrade(const Request& req, Response& res, Adaptor&& adaptor) + { + auto found = trie.find(req.url); + unsigned ruleIndex = found.first; + if (!ruleIndex) + { + BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url; + res = Response(boost::beast::http::status::not_found); + res.end(); + return; + } + + if (ruleIndex >= rules.size()) + throw std::runtime_error("Trie internal structure corrupted!"); + + if (ruleIndex == ruleSpecialRedirectSlash) + { + BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: " + << req.url; + res = Response(boost::beast::http::status::moved_permanently); + + // TODO absolute url building + if (req.getHeaderValue("Host").empty()) + { + res.addHeader("Location", std::string(req.url) + "/"); + } + else + { + res.addHeader( + "Location", + req.isSecure + ? "https://" + : "http://" + std::string(req.getHeaderValue("Host")) + + std::string(req.url) + "/"); + } + res.end(); + return; + } + + if ((rules[ruleIndex]->getMethods() & (1 << (uint32_t)req.method())) == + 0) + { + BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url + << " with " << req.methodString() << "(" + << (uint32_t)req.method() << ") / " + << rules[ruleIndex]->getMethods(); + res = Response(boost::beast::http::status::not_found); + res.end(); + return; + } + + BMCWEB_LOG_DEBUG << "Matched rule (upgrade) '" << rules[ruleIndex]->rule + << "' " << (uint32_t)req.method() << " / " + << rules[ruleIndex]->getMethods(); + + // any uncaught exceptions become 500s + try + { + rules[ruleIndex]->handleUpgrade(req, res, std::move(adaptor)); + } + catch (std::exception& e) + { + BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what(); + res = Response(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + catch (...) + { + BMCWEB_LOG_ERROR + << "An uncaught exception occurred. The type was unknown " + "so no information was available."; + res = Response(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + } + + void handle(const Request& req, Response& res) + { + auto found = trie.find(req.url); + + unsigned ruleIndex = found.first; + + if (!ruleIndex) + { + BMCWEB_LOG_DEBUG << "Cannot match rules " << req.url; + res.result(boost::beast::http::status::not_found); + res.end(); + return; + } + + if (ruleIndex >= rules.size()) + throw std::runtime_error("Trie internal structure corrupted!"); + + if (ruleIndex == ruleSpecialRedirectSlash) + { + BMCWEB_LOG_INFO << "Redirecting to a url with trailing slash: " + << req.url; + res = Response(boost::beast::http::status::moved_permanently); + + // TODO absolute url building + if (req.getHeaderValue("Host").empty()) + { + res.addHeader("Location", std::string(req.url) + "/"); + } + else + { + res.addHeader("Location", + (req.isSecure ? "https://" : "http://") + + std::string(req.getHeaderValue("Host")) + + std::string(req.url) + "/"); + } + res.end(); + return; + } + + if ((rules[ruleIndex]->getMethods() & (1 << (uint32_t)req.method())) == + 0) + { + BMCWEB_LOG_DEBUG << "Rule found but method mismatch: " << req.url + << " with " << req.methodString() << "(" + << (uint32_t)req.method() << ") / " + << rules[ruleIndex]->getMethods(); + res = Response(boost::beast::http::status::not_found); + res.end(); + return; + } + + BMCWEB_LOG_DEBUG << "Matched rule '" << rules[ruleIndex]->rule << "' " + << (uint32_t)req.method() << " / " + << rules[ruleIndex]->getMethods(); + + // any uncaught exceptions become 500s + try + { + rules[ruleIndex]->handle(req, res, found.second); + } + catch (std::exception& e) + { + BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what(); + res = Response(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + catch (...) + { + BMCWEB_LOG_ERROR + << "An uncaught exception occurred. The type was unknown " + "so no information was available."; + res = Response(boost::beast::http::status::internal_server_error); + res.end(); + return; + } + } + + void debugPrint() + { + trie.debugPrint(); + } + + std::vector<const std::string*> getRoutes(const std::string& parent) + { + std::vector<unsigned> x; + std::vector<const std::string*> ret; + trie.findRouteIndexes(parent, x); + for (unsigned index : x) + { + ret.push_back(&rules[index]->rule); + } + return ret; + } + + private: + std::vector<std::unique_ptr<BaseRule>> rules; + Trie trie; }; -} // namespace crow +} // namespace crow diff --git a/crow/include/crow/socket_adaptors.h b/crow/include/crow/socket_adaptors.h index 1d43ca2faa..a47697f2a5 100644 --- a/crow/include/crow/socket_adaptors.h +++ b/crow/include/crow/socket_adaptors.h @@ -1,144 +1,200 @@ #pragma once -#include "crow/logging.h" - #include <boost/asio.hpp> #include <boost/lexical_cast.hpp> +#include "crow/logging.h" + #ifdef BMCWEB_ENABLE_SSL #include <boost/asio/ssl.hpp> #endif -namespace crow { +namespace crow +{ using namespace boost; using tcp = asio::ip::tcp; -struct SocketAdaptor { - using streamType = tcp::socket; - using secure = std::false_type; - using context = void; - SocketAdaptor(boost::asio::io_service& ioService, context* /*unused*/) - : socketCls(ioService) {} +struct SocketAdaptor +{ + using streamType = tcp::socket; + using secure = std::false_type; + using context = void; + SocketAdaptor(boost::asio::io_service& ioService, context* /*unused*/) : + socketCls(ioService) + { + } - boost::asio::io_service& getIoService() { return socketCls.get_io_service(); } + boost::asio::io_service& getIoService() + { + return socketCls.get_io_service(); + } - tcp::socket& rawSocket() { return socketCls; } + tcp::socket& rawSocket() + { + return socketCls; + } - tcp::socket& socket() { return socketCls; } + tcp::socket& socket() + { + return socketCls; + } - std::string remoteEndpoint() { - boost::system::error_code ec; - tcp::endpoint ep = socketCls.remote_endpoint(ec); - if (ec) { - return ""; + std::string remoteEndpoint() + { + boost::system::error_code ec; + tcp::endpoint ep = socketCls.remote_endpoint(ec); + if (ec) + { + return ""; + } + return boost::lexical_cast<std::string>(ep); } - return boost::lexical_cast<std::string>(ep); - } - bool isOpen() { return socketCls.is_open(); } + bool isOpen() + { + return socketCls.is_open(); + } - void close() { socketCls.close(); } + void close() + { + socketCls.close(); + } - template <typename F> - void start(F f) { - boost::system::error_code ec; - f(ec); - } + template <typename F> void start(F f) + { + boost::system::error_code ec; + f(ec); + } - tcp::socket socketCls; + tcp::socket socketCls; }; -struct TestSocketAdaptor { - using secure = std::false_type; - using context = void; - TestSocketAdaptor(boost::asio::io_service& ioService, context* /*unused*/) - : socketCls(ioService) {} +struct TestSocketAdaptor +{ + using secure = std::false_type; + using context = void; + TestSocketAdaptor(boost::asio::io_service& ioService, context* /*unused*/) : + socketCls(ioService) + { + } - boost::asio::io_service& getIoService() { return socketCls.get_io_service(); } + boost::asio::io_service& getIoService() + { + return socketCls.get_io_service(); + } - tcp::socket& rawSocket() { return socketCls; } + tcp::socket& rawSocket() + { + return socketCls; + } - tcp::socket& socket() { return socketCls; } + tcp::socket& socket() + { + return socketCls; + } - std::string remoteEndpoint() { return "Testhost"; } + std::string remoteEndpoint() + { + return "Testhost"; + } - bool isOpen() { return socketCls.is_open(); } + bool isOpen() + { + return socketCls.is_open(); + } - void close() { socketCls.close(); } + void close() + { + socketCls.close(); + } - template <typename F> - void start(F f) { - f(boost::system::error_code()); - } + template <typename F> void start(F f) + { + f(boost::system::error_code()); + } - tcp::socket socketCls; + tcp::socket socketCls; }; #ifdef BMCWEB_ENABLE_SSL -struct SSLAdaptor { - using streamType = boost::asio::ssl::stream<tcp::socket>; - using secure = std::true_type; - using context = boost::asio::ssl::context; - using ssl_socket_t = boost::asio::ssl::stream<tcp::socket>; - SSLAdaptor(boost::asio::io_service& ioService, context* ctx) - : sslSocket(new ssl_socket_t(ioService, *ctx)) {} - - boost::asio::ssl::stream<tcp::socket>& socket() { return *sslSocket; } - - tcp::socket::lowest_layer_type& rawSocket() { - return sslSocket->lowest_layer(); - } - - std::string remoteEndpoint() { - boost::system::error_code ec; - tcp::endpoint ep = rawSocket().remote_endpoint(ec); - if (ec) { - return ""; - } - return boost::lexical_cast<std::string>(ep); - } - - bool isOpen() { - /*TODO(ed) this is a bit of a cheat. - There are cases when running a websocket where sslSocket might have - std::move() called on it (to transfer ownership to websocket::Connection) - and be empty. This (and the check on close()) is a cheat to do something - sane in this scenario. the correct fix would likely involve changing the - http parser to return a specific code meaning "has been upgraded" so that - the doRead function knows not to try to close the Connection which would - fail, because the adapter is gone. As is, doRead believes the parse - failed, because isOpen now returns False (which could also mean the client - disconnected during parse) - UPdate: The parser does in fact have an "isUpgrade" method that is - intended for exactly this purpose. Todo is now to make doRead obey the - flag appropriately so this code can be changed back. - */ - if (sslSocket != nullptr) { - return sslSocket->lowest_layer().is_open(); - } - return false; - } - - void close() { - if (sslSocket == nullptr) { - return; - } - boost::system::error_code ec; - - // Shut it down - this->sslSocket->lowest_layer().close(); - } - - boost::asio::io_service& getIoService() { - return rawSocket().get_io_service(); - } - - template <typename F> - void start(F f) { - sslSocket->async_handshake( - boost::asio::ssl::stream_base::server, - [f](const boost::system::error_code& ec) { f(ec); }); - } - - std::unique_ptr<boost::asio::ssl::stream<tcp::socket>> sslSocket; +struct SSLAdaptor +{ + using streamType = boost::asio::ssl::stream<tcp::socket>; + using secure = std::true_type; + using context = boost::asio::ssl::context; + using ssl_socket_t = boost::asio::ssl::stream<tcp::socket>; + SSLAdaptor(boost::asio::io_service& ioService, context* ctx) : + sslSocket(new ssl_socket_t(ioService, *ctx)) + { + } + + boost::asio::ssl::stream<tcp::socket>& socket() + { + return *sslSocket; + } + + tcp::socket::lowest_layer_type& rawSocket() + { + return sslSocket->lowest_layer(); + } + + std::string remoteEndpoint() + { + boost::system::error_code ec; + tcp::endpoint ep = rawSocket().remote_endpoint(ec); + if (ec) + { + return ""; + } + return boost::lexical_cast<std::string>(ep); + } + + bool isOpen() + { + /*TODO(ed) this is a bit of a cheat. + There are cases when running a websocket where sslSocket might have + std::move() called on it (to transfer ownership to + websocket::Connection) and be empty. This (and the check on close()) is + a cheat to do something sane in this scenario. the correct fix would + likely involve changing the http parser to return a specific code + meaning "has been upgraded" so that the doRead function knows not to try + to close the Connection which would fail, because the adapter is gone. + As is, doRead believes the parse failed, because isOpen now returns + False (which could also mean the client disconnected during parse) + UPdate: The parser does in fact have an "isUpgrade" method that is + intended for exactly this purpose. Todo is now to make doRead obey the + flag appropriately so this code can be changed back. + */ + if (sslSocket != nullptr) + { + return sslSocket->lowest_layer().is_open(); + } + return false; + } + + void close() + { + if (sslSocket == nullptr) + { + return; + } + boost::system::error_code ec; + + // Shut it down + this->sslSocket->lowest_layer().close(); + } + + boost::asio::io_service& getIoService() + { + return rawSocket().get_io_service(); + } + + template <typename F> void start(F f) + { + sslSocket->async_handshake( + boost::asio::ssl::stream_base::server, + [f](const boost::system::error_code& ec) { f(ec); }); + } + + std::unique_ptr<boost::asio::ssl::stream<tcp::socket>> sslSocket; }; #endif -} // namespace crow +} // namespace crow diff --git a/crow/include/crow/timer_queue.h b/crow/include/crow/timer_queue.h index f5bb467a1f..bf1e084a00 100644 --- a/crow/include/crow/timer_queue.h +++ b/crow/include/crow/timer_queue.h @@ -1,63 +1,78 @@ #pragma once +#include <boost/circular_buffer.hpp> +#include <boost/circular_buffer/space_optimized.hpp> #include <chrono> #include <functional> + #include "crow/logging.h" -#include <boost/circular_buffer.hpp> -#include <boost/circular_buffer/space_optimized.hpp> -namespace crow { -namespace detail { +namespace crow +{ +namespace detail +{ // fast timer queue for fixed tick value. -class TimerQueue { - public: - TimerQueue() { dq.set_capacity(100); } - - void cancel(int k) { - unsigned int index = static_cast<unsigned int>(k - step); - if (index < dq.size()) { - dq[index].second = nullptr; +class TimerQueue +{ + public: + TimerQueue() + { + dq.set_capacity(100); } - } - - int add(std::function<void()> f) { - dq.push_back( - std::make_pair(std::chrono::steady_clock::now(), std::move(f))); - int ret = step + dq.size() - 1; - - BMCWEB_LOG_DEBUG << "timer add inside: " << this << ' ' << ret; - return ret; - } - - void process() { - auto now = std::chrono::steady_clock::now(); - while (!dq.empty()) { - auto& x = dq.front(); - if (now - x.first < std::chrono::seconds(5)) { - break; - } - if (x.second) { - BMCWEB_LOG_DEBUG << "timer call: " << this << ' ' << step; - // we know that timer handlers are very simple currenty; call here - x.second(); - } - dq.pop_front(); - step++; + + void cancel(int k) + { + unsigned int index = static_cast<unsigned int>(k - step); + if (index < dq.size()) + { + dq[index].second = nullptr; + } + } + + int add(std::function<void()> f) + { + dq.push_back( + std::make_pair(std::chrono::steady_clock::now(), std::move(f))); + int ret = step + dq.size() - 1; + + BMCWEB_LOG_DEBUG << "timer add inside: " << this << ' ' << ret; + return ret; + } + + void process() + { + auto now = std::chrono::steady_clock::now(); + while (!dq.empty()) + { + auto& x = dq.front(); + if (now - x.first < std::chrono::seconds(5)) + { + break; + } + if (x.second) + { + BMCWEB_LOG_DEBUG << "timer call: " << this << ' ' << step; + // we know that timer handlers are very simple currenty; call + // here + x.second(); + } + dq.pop_front(); + step++; + } } - } - private: - using storage_type = - std::pair<std::chrono::time_point<std::chrono::steady_clock>, - std::function<void()>>; + private: + using storage_type = + std::pair<std::chrono::time_point<std::chrono::steady_clock>, + std::function<void()>>; - boost::circular_buffer_space_optimized<storage_type, - std::allocator<storage_type>> - dq{}; + boost::circular_buffer_space_optimized<storage_type, + std::allocator<storage_type>> + dq{}; - // boost::circular_buffer<storage_type> dq{20}; - // std::deque<storage_type> dq{}; - int step{}; + // boost::circular_buffer<storage_type> dq{20}; + // std::deque<storage_type> dq{}; + int step{}; }; -} // namespace detail -} // namespace crow +} // namespace detail +} // namespace crow diff --git a/crow/include/crow/utility.h b/crow/include/crow/utility.h index d2557b0af5..9d34e7113b 100644 --- a/crow/include/crow/utility.h +++ b/crow/include/crow/utility.h @@ -1,105 +1,136 @@ #pragma once +#include "nlohmann/json.hpp" + +#include <boost/utility/string_view.hpp> #include <cstdint> #include <cstring> #include <functional> #include <stdexcept> #include <string> #include <tuple> -#include "nlohmann/json.hpp" -#include <boost/utility/string_view.hpp> -namespace crow { -namespace black_magic { -struct OutOfRange { - OutOfRange(unsigned /*pos*/, unsigned /*length*/) {} +namespace crow +{ +namespace black_magic +{ +struct OutOfRange +{ + OutOfRange(unsigned /*pos*/, unsigned /*length*/) + { + } }; -constexpr unsigned requiresInRange(unsigned i, unsigned len) { - return i >= len ? throw OutOfRange(i, len) : i; +constexpr unsigned requiresInRange(unsigned i, unsigned len) +{ + return i >= len ? throw OutOfRange(i, len) : i; } -class ConstStr { - const char* const beginPtr; - unsigned sizeUint; - - public: - template <unsigned N> - constexpr ConstStr(const char (&arr)[N]) : beginPtr(arr), sizeUint(N - 1) { - static_assert(N >= 1, "not a string literal"); - } - constexpr char operator[](unsigned i) const { - return requiresInRange(i, sizeUint), beginPtr[i]; - } +class ConstStr +{ + const char* const beginPtr; + unsigned sizeUint; + + public: + template <unsigned N> + constexpr ConstStr(const char (&arr)[N]) : beginPtr(arr), sizeUint(N - 1) + { + static_assert(N >= 1, "not a string literal"); + } + constexpr char operator[](unsigned i) const + { + return requiresInRange(i, sizeUint), beginPtr[i]; + } - constexpr operator const char*() const { return beginPtr; } + constexpr operator const char*() const + { + return beginPtr; + } - constexpr const char* begin() const { return beginPtr; } - constexpr const char* end() const { return beginPtr + sizeUint; } + constexpr const char* begin() const + { + return beginPtr; + } + constexpr const char* end() const + { + return beginPtr + sizeUint; + } - constexpr unsigned size() const { return sizeUint; } + constexpr unsigned size() const + { + return sizeUint; + } }; -constexpr unsigned findClosingTag(ConstStr s, unsigned p) { - return s[p] == '>' ? p : findClosingTag(s, p + 1); +constexpr unsigned findClosingTag(ConstStr s, unsigned p) +{ + return s[p] == '>' ? p : findClosingTag(s, p + 1); } -constexpr bool isValid(ConstStr s, unsigned i = 0, int f = 0) { - return i == s.size() - ? f == 0 - : f < 0 || f >= 2 - ? false - : s[i] == '<' ? isValid(s, i + 1, f + 1) - : s[i] == '>' ? isValid(s, i + 1, f - 1) - : isValid(s, i + 1, f); +constexpr bool isValid(ConstStr s, unsigned i = 0, int f = 0) +{ + return i == s.size() + ? f == 0 + : f < 0 || f >= 2 + ? false + : s[i] == '<' ? isValid(s, i + 1, f + 1) + : s[i] == '>' ? isValid(s, i + 1, f - 1) + : isValid(s, i + 1, f); } -constexpr bool isEquP(const char* a, const char* b, unsigned n) { - return *a == 0 && *b == 0 && n == 0 - ? true - : (*a == 0 || *b == 0) - ? false - : n == 0 ? true - : *a != *b ? false : isEquP(a + 1, b + 1, n - 1); +constexpr bool isEquP(const char* a, const char* b, unsigned n) +{ + return *a == 0 && *b == 0 && n == 0 + ? true + : (*a == 0 || *b == 0) + ? false + : n == 0 ? true + : *a != *b ? false : isEquP(a + 1, b + 1, n - 1); } constexpr bool isEquN(ConstStr a, unsigned ai, ConstStr b, unsigned bi, - unsigned n) { - return ai + n > a.size() || bi + n > b.size() - ? false - : n == 0 ? true - : a[ai] != b[bi] ? false - : isEquN(a, ai + 1, b, bi + 1, n - 1); + unsigned n) +{ + return ai + n > a.size() || bi + n > b.size() + ? false + : n == 0 ? true + : a[ai] != b[bi] ? false + : isEquN(a, ai + 1, b, bi + 1, n - 1); } -constexpr bool isInt(ConstStr s, unsigned i) { - return isEquN(s, i, "<int>", 0, 5); +constexpr bool isInt(ConstStr s, unsigned i) +{ + return isEquN(s, i, "<int>", 0, 5); } -constexpr bool isUint(ConstStr s, unsigned i) { - return isEquN(s, i, "<uint>", 0, 6); +constexpr bool isUint(ConstStr s, unsigned i) +{ + return isEquN(s, i, "<uint>", 0, 6); } -constexpr bool isFloat(ConstStr s, unsigned i) { - return isEquN(s, i, "<float>", 0, 7) || isEquN(s, i, "<double>", 0, 8); +constexpr bool isFloat(ConstStr s, unsigned i) +{ + return isEquN(s, i, "<float>", 0, 7) || isEquN(s, i, "<double>", 0, 8); } -constexpr bool isStr(ConstStr s, unsigned i) { - return isEquN(s, i, "<str>", 0, 5) || isEquN(s, i, "<string>", 0, 8); +constexpr bool isStr(ConstStr s, unsigned i) +{ + return isEquN(s, i, "<str>", 0, 5) || isEquN(s, i, "<string>", 0, 8); } -constexpr bool isPath(ConstStr s, unsigned i) { - return isEquN(s, i, "<path>", 0, 6); +constexpr bool isPath(ConstStr s, unsigned i) +{ + return isEquN(s, i, "<path>", 0, 6); } -template <typename T> -struct parameter_tag { - static const int value = 0; -}; -#define BMCWEB_INTERNAL_PARAMETER_TAG(t, i) \ - template <> \ - struct parameter_tag<t> { \ - static const int value = i; \ - } +template <typename T> struct parameter_tag +{ + static const int value = 0; +}; +#define BMCWEB_INTERNAL_PARAMETER_TAG(t, i) \ + template <> struct parameter_tag<t> \ + { \ + static const int value = i; \ + } BMCWEB_INTERNAL_PARAMETER_TAG(int, 1); BMCWEB_INTERNAL_PARAMETER_TAG(char, 1); BMCWEB_INTERNAL_PARAMETER_TAG(short, 1); @@ -113,279 +144,290 @@ BMCWEB_INTERNAL_PARAMETER_TAG(unsigned long long, 2); BMCWEB_INTERNAL_PARAMETER_TAG(double, 3); BMCWEB_INTERNAL_PARAMETER_TAG(std::string, 4); #undef BMCWEB_INTERNAL_PARAMETER_TAG -template <typename... Args> -struct compute_parameter_tag_from_args_list; +template <typename... Args> struct compute_parameter_tag_from_args_list; -template <> -struct compute_parameter_tag_from_args_list<> { - static const int value = 0; +template <> struct compute_parameter_tag_from_args_list<> +{ + static const int value = 0; }; template <typename Arg, typename... Args> -struct compute_parameter_tag_from_args_list<Arg, Args...> { - static const int subValue = - compute_parameter_tag_from_args_list<Args...>::value; - static const int value = - parameter_tag<typename std::decay<Arg>::type>::value - ? subValue * 6 + parameter_tag<typename std::decay<Arg>::type>::value - : subValue; -}; - -static inline bool isParameterTagCompatible(uint64_t a, uint64_t b) { - if (a == 0) { - return b == 0; - } - if (b == 0) { - return a == 0; - } - int sa = a % 6; - int sb = a % 6; - if (sa == 5) { - sa = 4; - } - if (sb == 5) { - sb = 4; - } - if (sa != sb) { - return false; - } - return isParameterTagCompatible(a / 6, b / 6); +struct compute_parameter_tag_from_args_list<Arg, Args...> +{ + static const int subValue = + compute_parameter_tag_from_args_list<Args...>::value; + static const int value = + parameter_tag<typename std::decay<Arg>::type>::value + ? subValue * 6 + + parameter_tag<typename std::decay<Arg>::type>::value + : subValue; +}; + +static inline bool isParameterTagCompatible(uint64_t a, uint64_t b) +{ + if (a == 0) + { + return b == 0; + } + if (b == 0) + { + return a == 0; + } + int sa = a % 6; + int sb = a % 6; + if (sa == 5) + { + sa = 4; + } + if (sb == 5) + { + sb = 4; + } + if (sa != sb) + { + return false; + } + return isParameterTagCompatible(a / 6, b / 6); } -static inline unsigned findClosingTagRuntime(const char* s, unsigned p) { - return s[p] == 0 ? throw std::runtime_error("unmatched tag <") - : s[p] == '>' ? p : findClosingTagRuntime(s, p + 1); +static inline unsigned findClosingTagRuntime(const char* s, unsigned p) +{ + return s[p] == 0 ? throw std::runtime_error("unmatched tag <") + : s[p] == '>' ? p : findClosingTagRuntime(s, p + 1); } -static inline uint64_t getParameterTagRuntime(const char* s, unsigned p = 0) { - return s[p] == 0 - ? 0 - : s[p] == '<' - ? (std::strncmp(s + p, "<int>", 5) == 0 - ? getParameterTagRuntime( - s, findClosingTagRuntime(s, p)) * - 6 + - 1 - : std::strncmp(s + p, "<uint>", 6) == 0 - ? getParameterTagRuntime( - s, findClosingTagRuntime(s, p)) * - 6 + - 2 - : (std::strncmp(s + p, "<float>", 7) == 0 || - std::strncmp(s + p, "<double>", 8) == 0) - ? getParameterTagRuntime( - s, findClosingTagRuntime(s, p)) * - 6 + - 3 - : (std::strncmp(s + p, "<str>", 5) == 0 || - std::strncmp(s + p, "<string>", 8) == - 0) - ? getParameterTagRuntime( - s, - findClosingTagRuntime(s, p)) * - 6 + - 4 - : std::strncmp(s + p, "<path>", - 6) == 0 - ? getParameterTagRuntime( - s, - findClosingTagRuntime( - s, p)) * - 6 + - 5 - : throw std::runtime_error( - "invalid parameter " - "type")) - : getParameterTagRuntime(s, p + 1); +static inline uint64_t getParameterTagRuntime(const char* s, unsigned p = 0) +{ + return s[p] == 0 + ? 0 + : s[p] == '<' + ? (std::strncmp(s + p, "<int>", 5) == 0 + ? getParameterTagRuntime( + s, findClosingTagRuntime(s, p)) * + 6 + + 1 + : std::strncmp(s + p, "<uint>", 6) == 0 + ? getParameterTagRuntime( + s, findClosingTagRuntime(s, p)) * + 6 + + 2 + : (std::strncmp(s + p, "<float>", 7) == 0 || + std::strncmp(s + p, "<double>", 8) == 0) + ? getParameterTagRuntime( + s, findClosingTagRuntime(s, p)) * + 6 + + 3 + : (std::strncmp(s + p, "<str>", 5) == + 0 || + std::strncmp(s + p, "<string>", 8) == + 0) + ? getParameterTagRuntime( + s, findClosingTagRuntime( + s, p)) * + 6 + + 4 + : std::strncmp(s + p, "<path>", + 6) == 0 + ? getParameterTagRuntime( + s, + findClosingTagRuntime( + s, p)) * + 6 + + 5 + : throw std::runtime_error( + "invalid parameter " + "type")) + : getParameterTagRuntime(s, p + 1); } -constexpr uint64_t get_parameter_tag(ConstStr s, unsigned p = 0) { - return p == s.size() - ? 0 - : s[p] == '<' - ? (isInt(s, p) - ? get_parameter_tag(s, findClosingTag(s, p)) * 6 + 1 - : isUint(s, p) - ? get_parameter_tag(s, findClosingTag(s, p)) * - 6 + - 2 - : isFloat(s, p) - ? get_parameter_tag( - s, findClosingTag(s, p)) * - 6 + - 3 - : isStr(s, p) - ? get_parameter_tag( - s, findClosingTag(s, p)) * - 6 + - 4 - : isPath(s, p) - ? get_parameter_tag( - s, - findClosingTag(s, p)) * - 6 + - 5 - : throw std::runtime_error( - "invalid parameter " - "type")) - : get_parameter_tag(s, p + 1); +constexpr uint64_t get_parameter_tag(ConstStr s, unsigned p = 0) +{ + return p == s.size() + ? 0 + : s[p] == '<' + ? (isInt(s, p) + ? get_parameter_tag(s, findClosingTag(s, p)) * 6 + 1 + : isUint(s, p) + ? get_parameter_tag(s, findClosingTag(s, p)) * + 6 + + 2 + : isFloat(s, p) + ? get_parameter_tag( + s, findClosingTag(s, p)) * + 6 + + 3 + : isStr(s, p) + ? get_parameter_tag( + s, findClosingTag(s, p)) * + 6 + + 4 + : isPath(s, p) + ? get_parameter_tag( + s, findClosingTag( + s, p)) * + 6 + + 5 + : throw std::runtime_error( + "invalid parameter " + "type")) + : get_parameter_tag(s, p + 1); } -template <typename... T> -struct S { - template <typename U> - using push = S<U, T...>; - template <typename U> - using push_back = S<T..., U>; - template <template <typename... Args> class U> - using rebind = U<T...>; +template <typename... T> struct S +{ + template <typename U> using push = S<U, T...>; + template <typename U> using push_back = S<T..., U>; + template <template <typename... Args> class U> using rebind = U<T...>; }; -template <typename F, typename Set> -struct CallHelper; -template <typename F, typename... Args> -struct CallHelper<F, S<Args...>> { - template <typename F1, typename... Args1, - typename = decltype(std::declval<F1>()(std::declval<Args1>()...))> - static char __test(int); +template <typename F, typename Set> struct CallHelper; +template <typename F, typename... Args> struct CallHelper<F, S<Args...>> +{ + template <typename F1, typename... Args1, + typename = decltype(std::declval<F1>()(std::declval<Args1>()...))> + static char __test(int); - template <typename...> - static int __test(...); + template <typename...> static int __test(...); - static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char); + static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char); }; -template <int N> -struct SingleTagToType {}; +template <int N> struct SingleTagToType +{ +}; -template <> -struct SingleTagToType<1> { - using type = int64_t; +template <> struct SingleTagToType<1> +{ + using type = int64_t; }; -template <> -struct SingleTagToType<2> { - using type = uint64_t; +template <> struct SingleTagToType<2> +{ + using type = uint64_t; }; -template <> -struct SingleTagToType<3> { - using type = double; +template <> struct SingleTagToType<3> +{ + using type = double; }; -template <> -struct SingleTagToType<4> { - using type = std::string; +template <> struct SingleTagToType<4> +{ + using type = std::string; }; -template <> -struct SingleTagToType<5> { - using type = std::string; +template <> struct SingleTagToType<5> +{ + using type = std::string; }; -template <uint64_t Tag> -struct Arguments { - using subarguments = typename Arguments<Tag / 6>::type; - using type = typename subarguments::template push< - typename SingleTagToType<Tag % 6>::type>; +template <uint64_t Tag> struct Arguments +{ + using subarguments = typename Arguments<Tag / 6>::type; + using type = typename subarguments::template push< + typename SingleTagToType<Tag % 6>::type>; }; -template <> -struct Arguments<0> { - using type = S<>; +template <> struct Arguments<0> +{ + using type = S<>; }; -template <typename... T> -struct LastElementType { - using type = - typename std::tuple_element<sizeof...(T) - 1, std::tuple<T...>>::type; +template <typename... T> struct LastElementType +{ + using type = + typename std::tuple_element<sizeof...(T) - 1, std::tuple<T...>>::type; }; -template <> -struct LastElementType<> {}; +template <> struct LastElementType<> +{ +}; // from // http://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth -template <class T> -using Invoke = typename T::type; +template <class T> using Invoke = typename T::type; -template <unsigned...> -struct Seq { - using type = Seq; +template <unsigned...> struct Seq +{ + using type = Seq; }; -template <class S1, class S2> -struct concat; +template <class S1, class S2> struct concat; template <unsigned... I1, unsigned... I2> -struct concat<Seq<I1...>, Seq<I2...>> : Seq<I1..., (sizeof...(I1) + I2)...> {}; +struct concat<Seq<I1...>, Seq<I2...>> : Seq<I1..., (sizeof...(I1) + I2)...> +{ +}; -template <class S1, class S2> -using Concat = Invoke<concat<S1, S2>>; +template <class S1, class S2> using Concat = Invoke<concat<S1, S2>>; -template <unsigned N> -struct gen_seq; -template <unsigned N> -using GenSeq = Invoke<gen_seq<N>>; +template <unsigned N> struct gen_seq; +template <unsigned N> using GenSeq = Invoke<gen_seq<N>>; -template <unsigned N> -struct gen_seq : Concat<GenSeq<N / 2>, GenSeq<N - N / 2>> {}; +template <unsigned N> struct gen_seq : Concat<GenSeq<N / 2>, GenSeq<N - N / 2>> +{ +}; -template <> -struct gen_seq<0> : Seq<> {}; -template <> -struct gen_seq<1> : Seq<0> {}; +template <> struct gen_seq<0> : Seq<> +{ +}; +template <> struct gen_seq<1> : Seq<0> +{ +}; -template <typename Seq, typename Tuple> -struct PopBackHelper; +template <typename Seq, typename Tuple> struct PopBackHelper; -template <unsigned... N, typename Tuple> -struct PopBackHelper<Seq<N...>, Tuple> { - template <template <typename... Args> class U> - using rebind = U<typename std::tuple_element<N, Tuple>::type...>; +template <unsigned... N, typename Tuple> struct PopBackHelper<Seq<N...>, Tuple> +{ + template <template <typename... Args> class U> + using rebind = U<typename std::tuple_element<N, Tuple>::type...>; }; template <typename... T> -struct PopBack //: public PopBackHelper<typename - // gen_seq<sizeof...(T)-1>::type, std::tuple<T...>> +struct PopBack //: public PopBackHelper<typename + // gen_seq<sizeof...(T)-1>::type, std::tuple<T...>> { - template <template <typename... Args> class U> - using rebind = - typename PopBackHelper<typename gen_seq<sizeof...(T) - 1>::type, - std::tuple<T...>>::template rebind<U>; + template <template <typename... Args> class U> + using rebind = + typename PopBackHelper<typename gen_seq<sizeof...(T) - 1>::type, + std::tuple<T...>>::template rebind<U>; }; -template <> -struct PopBack<> { - template <template <typename... Args> class U> - using rebind = U<>; +template <> struct PopBack<> +{ + template <template <typename... Args> class U> using rebind = U<>; }; // from // http://stackoverflow.com/questions/2118541/check-if-c0x-parameter-pack-contains-a-type -template <typename Tp, typename... List> -struct Contains : std::true_type {}; +template <typename Tp, typename... List> struct Contains : std::true_type +{ +}; template <typename Tp, typename Head, typename... Rest> struct Contains<Tp, Head, Rest...> : std::conditional<std::is_same<Tp, Head>::value, std::true_type, - Contains<Tp, Rest...>>::type {}; + Contains<Tp, Rest...>>::type +{ +}; -template <typename Tp> -struct Contains<Tp> : std::false_type {}; +template <typename Tp> struct Contains<Tp> : std::false_type +{ +}; -template <typename T> -struct EmptyContext {}; +template <typename T> struct EmptyContext +{ +}; -template <typename T> -struct promote { - using type = T; +template <typename T> struct promote +{ + using type = T; }; -#define BMCWEB_INTERNAL_PROMOTE_TYPE(t1, t2) \ - template <> \ - struct promote<t1> { \ - using type = t2; \ - } +#define BMCWEB_INTERNAL_PROMOTE_TYPE(t1, t2) \ + template <> struct promote<t1> \ + { \ + using type = t2; \ + } BMCWEB_INTERNAL_PROMOTE_TYPE(char, int64_t); BMCWEB_INTERNAL_PROMOTE_TYPE(short, int64_t); @@ -400,204 +442,229 @@ BMCWEB_INTERNAL_PROMOTE_TYPE(unsigned long long, uint64_t); BMCWEB_INTERNAL_PROMOTE_TYPE(float, double); #undef BMCWEB_INTERNAL_PROMOTE_TYPE -template <typename T> -using promote_t = typename promote<T>::type; +template <typename T> using promote_t = typename promote<T>::type; -} // namespace black_magic +} // namespace black_magic -namespace detail { +namespace detail +{ template <class T, std::size_t N, class... Args> -struct GetIndexOfElementFromTupleByTypeImpl { - static constexpr auto value = N; +struct GetIndexOfElementFromTupleByTypeImpl +{ + static constexpr auto value = N; }; template <class T, std::size_t N, class... Args> -struct GetIndexOfElementFromTupleByTypeImpl<T, N, T, Args...> { - static constexpr auto value = N; +struct GetIndexOfElementFromTupleByTypeImpl<T, N, T, Args...> +{ + static constexpr auto value = N; }; template <class T, std::size_t N, class U, class... Args> -struct GetIndexOfElementFromTupleByTypeImpl<T, N, U, Args...> { - static constexpr auto value = - GetIndexOfElementFromTupleByTypeImpl<T, N + 1, Args...>::value; +struct GetIndexOfElementFromTupleByTypeImpl<T, N, U, Args...> +{ + static constexpr auto value = + GetIndexOfElementFromTupleByTypeImpl<T, N + 1, Args...>::value; }; -} // namespace detail +} // namespace detail -namespace utility { -template <class T, class... Args> -T& getElementByType(std::tuple<Args...>& t) { - return std::get< - detail::GetIndexOfElementFromTupleByTypeImpl<T, 0, Args...>::value>(t); +namespace utility +{ +template <class T, class... Args> T& getElementByType(std::tuple<Args...>& t) +{ + return std::get< + detail::GetIndexOfElementFromTupleByTypeImpl<T, 0, Args...>::value>(t); } -template <typename T> -struct function_traits; +template <typename T> struct function_traits; template <typename T> -struct function_traits : public function_traits<decltype(&T::operator())> { - using parent_t = function_traits<decltype(&T::operator())>; - static const size_t arity = parent_t::arity; - using result_type = typename parent_t::result_type; - template <size_t i> - using arg = typename parent_t::template arg<i>; +struct function_traits : public function_traits<decltype(&T::operator())> +{ + using parent_t = function_traits<decltype(&T::operator())>; + static const size_t arity = parent_t::arity; + using result_type = typename parent_t::result_type; + template <size_t i> using arg = typename parent_t::template arg<i>; }; template <typename ClassType, typename r, typename... Args> -struct function_traits<r (ClassType::*)(Args...) const> { - static const size_t arity = sizeof...(Args); +struct function_traits<r (ClassType::*)(Args...) const> +{ + static const size_t arity = sizeof...(Args); - using result_type = r; + using result_type = r; - template <size_t i> - using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; + template <size_t i> + using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; }; template <typename ClassType, typename r, typename... Args> -struct function_traits<r (ClassType::*)(Args...)> { - static const size_t arity = sizeof...(Args); +struct function_traits<r (ClassType::*)(Args...)> +{ + static const size_t arity = sizeof...(Args); - using result_type = r; + using result_type = r; - template <size_t i> - using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; + template <size_t i> + using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; }; template <typename r, typename... Args> -struct function_traits<std::function<r(Args...)>> { - static const size_t arity = sizeof...(Args); +struct function_traits<std::function<r(Args...)>> +{ + static const size_t arity = sizeof...(Args); - using result_type = r; + using result_type = r; - template <size_t i> - using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; + template <size_t i> + using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; }; inline static std::string base64encode( const char* data, size_t size, const char* key = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") { - std::string ret; - ret.resize((size + 2) / 3 * 4); - auto it = ret.begin(); - while (size >= 3) { - *it++ = key[(((unsigned char)*data) & 0xFC) >> 2]; - unsigned char h = (((unsigned char)*data++) & 0x03) << 4; - *it++ = key[h | ((((unsigned char)*data) & 0xF0) >> 4)]; - h = (((unsigned char)*data++) & 0x0F) << 2; - *it++ = key[h | ((((unsigned char)*data) & 0xC0) >> 6)]; - *it++ = key[((unsigned char)*data++) & 0x3F]; - - size -= 3; - } - if (size == 1) { - *it++ = key[(((unsigned char)*data) & 0xFC) >> 2]; - unsigned char h = (((unsigned char)*data++) & 0x03) << 4; - *it++ = key[h]; - *it++ = '='; - *it++ = '='; - } else if (size == 2) { - *it++ = key[(((unsigned char)*data) & 0xFC) >> 2]; - unsigned char h = (((unsigned char)*data++) & 0x03) << 4; - *it++ = key[h | ((((unsigned char)*data) & 0xF0) >> 4)]; - h = (((unsigned char)*data++) & 0x0F) << 2; - *it++ = key[h]; - *it++ = '='; - } - return ret; + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") +{ + std::string ret; + ret.resize((size + 2) / 3 * 4); + auto it = ret.begin(); + while (size >= 3) + { + *it++ = key[(((unsigned char)*data) & 0xFC) >> 2]; + unsigned char h = (((unsigned char)*data++) & 0x03) << 4; + *it++ = key[h | ((((unsigned char)*data) & 0xF0) >> 4)]; + h = (((unsigned char)*data++) & 0x0F) << 2; + *it++ = key[h | ((((unsigned char)*data) & 0xC0) >> 6)]; + *it++ = key[((unsigned char)*data++) & 0x3F]; + + size -= 3; + } + if (size == 1) + { + *it++ = key[(((unsigned char)*data) & 0xFC) >> 2]; + unsigned char h = (((unsigned char)*data++) & 0x03) << 4; + *it++ = key[h]; + *it++ = '='; + *it++ = '='; + } + else if (size == 2) + { + *it++ = key[(((unsigned char)*data) & 0xFC) >> 2]; + unsigned char h = (((unsigned char)*data++) & 0x03) << 4; + *it++ = key[h | ((((unsigned char)*data) & 0xF0) >> 4)]; + h = (((unsigned char)*data++) & 0x0F) << 2; + *it++ = key[h]; + *it++ = '='; + } + return ret; } -inline static std::string base64encodeUrlsafe(const char* data, size_t size) { - return base64encode( - data, size, - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"); +inline static std::string base64encodeUrlsafe(const char* data, size_t size) +{ + return base64encode( + data, size, + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"); } // TODO this is temporary and should be deleted once base64 is refactored out of // crow -inline bool base64Decode(const boost::string_view input, std::string& output) { - static const char nop = -1; - // See note on encoding_data[] in above function - static const char decodingData[] = { - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 62, nop, - nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, nop, nop, - nop, nop, nop, nop, nop, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, nop, nop, nop, nop, nop, nop, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, - nop}; - - size_t inputLength = input.size(); - - // allocate space for output string - output.clear(); - output.reserve(((inputLength + 2) / 3) * 4); - - // for each 4-bytes sequence from the input, extract 4 6-bits sequences by - // droping first two bits - // and regenerate into 3 8-bits sequences - - for (size_t i = 0; i < inputLength; i++) { - char base64code0; - char base64code1; - char base64code2 = 0; // initialized to 0 to suppress warnings - char base64code3; - - base64code0 = decodingData[static_cast<int>(input[i])]; // NOLINT - if (base64code0 == nop) { // non base64 character - return false; - } - if (!(++i < inputLength)) { // we need at least two input bytes for first - // byte output - return false; - } - base64code1 = decodingData[static_cast<int>(input[i])]; // NOLINT - if (base64code1 == nop) { // non base64 character - return false; - } - output += - static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3)); - - if (++i < inputLength) { - char c = input[i]; - if (c == '=') { // padding , end of input - return (base64code1 & 0x0f) == 0; - } - base64code2 = decodingData[static_cast<int>(input[i])]; // NOLINT - if (base64code2 == nop) { // non base64 character - return false; - } - output += static_cast<char>(((base64code1 << 4) & 0xf0) | - ((base64code2 >> 2) & 0x0f)); - } - - if (++i < inputLength) { - char c = input[i]; - if (c == '=') { // padding , end of input - return (base64code2 & 0x03) == 0; - } - base64code3 = decodingData[static_cast<int>(input[i])]; // NOLINT - if (base64code3 == nop) { // non base64 character - return false; - } - output += static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3)); +inline bool base64Decode(const boost::string_view input, std::string& output) +{ + static const char nop = -1; + // See note on encoding_data[] in above function + static const char decodingData[] = { + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, + nop, nop, nop, nop}; + + size_t inputLength = input.size(); + + // allocate space for output string + output.clear(); + output.reserve(((inputLength + 2) / 3) * 4); + + // for each 4-bytes sequence from the input, extract 4 6-bits sequences by + // droping first two bits + // and regenerate into 3 8-bits sequences + + for (size_t i = 0; i < inputLength; i++) + { + char base64code0; + char base64code1; + char base64code2 = 0; // initialized to 0 to suppress warnings + char base64code3; + + base64code0 = decodingData[static_cast<int>(input[i])]; // NOLINT + if (base64code0 == nop) + { // non base64 character + return false; + } + if (!(++i < inputLength)) + { // we need at least two input bytes for first + // byte output + return false; + } + base64code1 = decodingData[static_cast<int>(input[i])]; // NOLINT + if (base64code1 == nop) + { // non base64 character + return false; + } + output += + static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3)); + + if (++i < inputLength) + { + char c = input[i]; + if (c == '=') + { // padding , end of input + return (base64code1 & 0x0f) == 0; + } + base64code2 = decodingData[static_cast<int>(input[i])]; // NOLINT + if (base64code2 == nop) + { // non base64 character + return false; + } + output += static_cast<char>(((base64code1 << 4) & 0xf0) | + ((base64code2 >> 2) & 0x0f)); + } + + if (++i < inputLength) + { + char c = input[i]; + if (c == '=') + { // padding , end of input + return (base64code2 & 0x03) == 0; + } + base64code3 = decodingData[static_cast<int>(input[i])]; // NOLINT + if (base64code3 == nop) + { // non base64 character + return false; + } + output += + static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3)); + } } - } - return true; + return true; } -} // namespace utility -} // namespace crow +} // namespace utility +} // namespace crow diff --git a/crow/include/crow/websocket.h b/crow/include/crow/websocket.h index 82c6db8d73..f345223ebc 100644 --- a/crow/include/crow/websocket.h +++ b/crow/include/crow/websocket.h @@ -1,206 +1,240 @@ #pragma once #include <array> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/beast/websocket.hpp> #include <functional> + #include "crow/http_request.h" #include "crow/socket_adaptors.h" -#include <boost/algorithm/string/predicate.hpp> -#include <boost/beast/websocket.hpp> #ifdef BMCWEB_ENABLE_SSL #include <boost/beast/websocket/ssl.hpp> #endif -namespace crow { -namespace websocket { -struct Connection : std::enable_shared_from_this<Connection> { - public: - explicit Connection(const crow::Request& req) - : req(req), userdataPtr(nullptr){}; - - virtual void sendBinary(const boost::beast::string_view msg) = 0; - virtual void sendBinary(std::string&& msg) = 0; - virtual void sendText(const boost::beast::string_view msg) = 0; - virtual void sendText(std::string&& msg) = 0; - virtual void close(const boost::beast::string_view msg = "quit") = 0; - virtual boost::asio::io_service& getIoService() = 0; - virtual ~Connection() = default; - - void userdata(void* u) { userdataPtr = u; } - void* userdata() { return userdataPtr; } +namespace crow +{ +namespace websocket +{ +struct Connection : std::enable_shared_from_this<Connection> +{ + public: + explicit Connection(const crow::Request& req) : + req(req), userdataPtr(nullptr){}; + + virtual void sendBinary(const boost::beast::string_view msg) = 0; + virtual void sendBinary(std::string&& msg) = 0; + virtual void sendText(const boost::beast::string_view msg) = 0; + virtual void sendText(std::string&& msg) = 0; + virtual void close(const boost::beast::string_view msg = "quit") = 0; + virtual boost::asio::io_service& getIoService() = 0; + virtual ~Connection() = default; + + void userdata(void* u) + { + userdataPtr = u; + } + void* userdata() + { + return userdataPtr; + } - crow::Request req; + crow::Request req; - private: - void* userdataPtr; + private: + void* userdataPtr; }; -template <typename Adaptor> -class ConnectionImpl : public Connection { - public: - ConnectionImpl( - const crow::Request& req, Adaptor&& adaptorIn, - std::function<void(Connection&)> open_handler, - std::function<void(Connection&, const std::string&, bool)> - message_handler, - std::function<void(Connection&, const std::string&)> close_handler, - std::function<void(Connection&)> error_handler) - : adaptor(std::move(adaptorIn)), - ws(adaptor.socket()), - Connection(req), +template <typename Adaptor> class ConnectionImpl : public Connection +{ + public: + ConnectionImpl( + const crow::Request& req, Adaptor&& adaptorIn, + std::function<void(Connection&)> open_handler, + std::function<void(Connection&, const std::string&, bool)> + message_handler, + std::function<void(Connection&, const std::string&)> close_handler, + std::function<void(Connection&)> error_handler) : + adaptor(std::move(adaptorIn)), + ws(adaptor.socket()), Connection(req), openHandler(std::move(open_handler)), messageHandler(std::move(message_handler)), closeHandler(std::move(close_handler)), - errorHandler(std::move(error_handler)) { - BMCWEB_LOG_DEBUG << "Creating new connection " << this; - } - - boost::asio::io_service& getIoService() override { - return adaptor.getIoService(); - } - - void start() { - BMCWEB_LOG_DEBUG << "starting connection " << this; - - boost::string_view protocol = - req.getHeaderValue(boost::beast::http::field::sec_websocket_protocol); - - // Perform the websocket upgrade - ws.async_accept_ex( - req.req, - [protocol{std::string(protocol)}]( - boost::beast::websocket::response_type & m) { - if (!protocol.empty()) { - m.insert(boost::beast::http::field::sec_websocket_protocol, - protocol); - } - }, - [ this, self(shared_from_this()) ](boost::system::error_code ec) { - if (ec) { - BMCWEB_LOG_ERROR << "Error in ws.async_accept " << ec; - return; - } - acceptDone(); - }); - } - - void sendBinary(const boost::beast::string_view msg) override { - ws.binary(true); - outBuffer.emplace_back(msg); - doWrite(); - } - - void sendBinary(std::string&& msg) override { - ws.binary(true); - outBuffer.emplace_back(std::move(msg)); - doWrite(); - } - - void sendText(const boost::beast::string_view msg) override { - ws.text(true); - outBuffer.emplace_back(msg); - doWrite(); - } - - void sendText(std::string&& msg) override { - ws.text(true); - outBuffer.emplace_back(std::move(msg)); - doWrite(); - } - - void close(const boost::beast::string_view msg) override { - ws.async_close( - boost::beast::websocket::close_code::normal, - [ this, self(shared_from_this()) ](boost::system::error_code ec) { - if (ec) { - BMCWEB_LOG_ERROR << "Error closing websocket " << ec; - return; - } - adaptor.close(); - }); - } + errorHandler(std::move(error_handler)) + { + BMCWEB_LOG_DEBUG << "Creating new connection " << this; + } - void acceptDone() { - BMCWEB_LOG_DEBUG << "Websocket accepted connection"; + boost::asio::io_service& getIoService() override + { + return adaptor.getIoService(); + } - if (openHandler) { - openHandler(*this); + void start() + { + BMCWEB_LOG_DEBUG << "starting connection " << this; + + boost::string_view protocol = req.getHeaderValue( + boost::beast::http::field::sec_websocket_protocol); + + // Perform the websocket upgrade + ws.async_accept_ex( + req.req, + [protocol{std::string(protocol)}]( + boost::beast::websocket::response_type& m) { + if (!protocol.empty()) + { + m.insert(boost::beast::http::field::sec_websocket_protocol, + protocol); + } + }, + [this, self(shared_from_this())](boost::system::error_code ec) { + if (ec) + { + BMCWEB_LOG_ERROR << "Error in ws.async_accept " << ec; + return; + } + acceptDone(); + }); } - doRead(); - } - - void doRead() { - ws.async_read( - inBuffer, [ this, self(shared_from_this()) ]( - boost::beast::error_code ec, std::size_t bytes_read) { - if (ec) { - if (ec != boost::beast::websocket::error::closed) { - BMCWEB_LOG_ERROR << "doRead error " << ec; - } - if (closeHandler) { - boost::beast::string_view reason = ws.reason().reason; - closeHandler(*this, std::string(reason)); - } - return; - } - if (messageHandler) { - // TODO(Ed) There must be a more direct way to do this conversion, - // but I can't find it at the moment. It should get optimized away - boost::asio::const_buffer cb = - boost::beast::buffers_front(inBuffer.data()); - boost::beast::string_view message( - reinterpret_cast<char const*>(cb.data()), cb.size()); - messageHandler(*this, std::string(message), ws.got_text()); - } - doRead(); - }); - } - - void doWrite() { - // If we're already doing a write, ignore the request, it will be picked up - // when the current write is complete - if (doingWrite) { - return; + + void sendBinary(const boost::beast::string_view msg) override + { + ws.binary(true); + outBuffer.emplace_back(msg); + doWrite(); + } + + void sendBinary(std::string&& msg) override + { + ws.binary(true); + outBuffer.emplace_back(std::move(msg)); + doWrite(); } - if (outBuffer.empty()) { - // Done for now - return; + void sendText(const boost::beast::string_view msg) override + { + ws.text(true); + outBuffer.emplace_back(msg); + doWrite(); } - doingWrite = true; - ws.async_write(boost::asio::buffer(outBuffer.front()), - [ this, self(shared_from_this()) ]( - boost::beast::error_code ec, std::size_t bytes_written) { - doingWrite = false; - outBuffer.erase(outBuffer.begin()); - if (ec == boost::beast::websocket::error::closed) { - // Do nothing here. doRead handler will call the - // closeHandler. - close("Write error"); - return; - } - if (ec) { - BMCWEB_LOG_ERROR << "Error in ws.async_write " << ec; - return; - } - doWrite(); - }); - } - - private: - Adaptor adaptor; - - boost::beast::websocket::stream< - std::add_lvalue_reference_t<typename Adaptor::streamType>> - ws; - - boost::beast::flat_static_buffer<4096> inBuffer; - std::vector<std::string> outBuffer; - bool doingWrite = false; - - std::function<void(Connection&)> openHandler; - std::function<void(Connection&, const std::string&, bool)> messageHandler; - std::function<void(Connection&, const std::string&)> closeHandler; - std::function<void(Connection&)> errorHandler; + + void sendText(std::string&& msg) override + { + ws.text(true); + outBuffer.emplace_back(std::move(msg)); + doWrite(); + } + + void close(const boost::beast::string_view msg) override + { + ws.async_close( + boost::beast::websocket::close_code::normal, + [this, self(shared_from_this())](boost::system::error_code ec) { + if (ec) + { + BMCWEB_LOG_ERROR << "Error closing websocket " << ec; + return; + } + adaptor.close(); + }); + } + + void acceptDone() + { + BMCWEB_LOG_DEBUG << "Websocket accepted connection"; + + if (openHandler) + { + openHandler(*this); + } + doRead(); + } + + void doRead() + { + ws.async_read( + inBuffer, [this, self(shared_from_this())]( + boost::beast::error_code ec, std::size_t bytes_read) { + if (ec) + { + if (ec != boost::beast::websocket::error::closed) + { + BMCWEB_LOG_ERROR << "doRead error " << ec; + } + if (closeHandler) + { + boost::beast::string_view reason = ws.reason().reason; + closeHandler(*this, std::string(reason)); + } + return; + } + if (messageHandler) + { + // TODO(Ed) There must be a more direct way to do this + // conversion, but I can't find it at the moment. It should + // get optimized away + boost::asio::const_buffer cb = + boost::beast::buffers_front(inBuffer.data()); + boost::beast::string_view message( + reinterpret_cast<char const*>(cb.data()), cb.size()); + messageHandler(*this, std::string(message), ws.got_text()); + } + doRead(); + }); + } + + void doWrite() + { + // If we're already doing a write, ignore the request, it will be picked + // up when the current write is complete + if (doingWrite) + { + return; + } + + if (outBuffer.empty()) + { + // Done for now + return; + } + doingWrite = true; + ws.async_write( + boost::asio::buffer(outBuffer.front()), + [this, self(shared_from_this())](boost::beast::error_code ec, + std::size_t bytes_written) { + doingWrite = false; + outBuffer.erase(outBuffer.begin()); + if (ec == boost::beast::websocket::error::closed) + { + // Do nothing here. doRead handler will call the + // closeHandler. + close("Write error"); + return; + } + if (ec) + { + BMCWEB_LOG_ERROR << "Error in ws.async_write " << ec; + return; + } + doWrite(); + }); + } + + private: + Adaptor adaptor; + + boost::beast::websocket::stream< + std::add_lvalue_reference_t<typename Adaptor::streamType>> + ws; + + boost::beast::flat_static_buffer<4096> inBuffer; + std::vector<std::string> outBuffer; + bool doingWrite = false; + + std::function<void(Connection&)> openHandler; + std::function<void(Connection&, const std::string&, bool)> messageHandler; + std::function<void(Connection&, const std::string&)> closeHandler; + std::function<void(Connection&)> errorHandler; }; -} // namespace websocket -} // namespace crow +} // namespace websocket +} // namespace crow |