diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d39c93..e0c44e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,7 @@ endif() # include boost headers include_directories(${Boost_INCLUDE_DIRS}) include_directories("ext/mstch/include") +include_directories("ext/crow") # add ext/ subfolder add_subdirectory(ext/) diff --git a/ext/crow/crow.h b/ext/crow/crow.h old mode 100755 new mode 100644 index 00209c7..9a0c22e --- a/ext/crow/crow.h +++ b/ext/crow/crow.h @@ -1,229 +1,23 @@ #pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "settings.h" -#include "logging.h" -#include "utility.h" -#include "routing.h" -#include "middleware_context.h" -#include "http_request.h" -#include "http_server.h" - - -#ifdef CROW_MSVC_WORKAROUND -#define CROW_ROUTE(app, url) app.route_dynamic(url) -#else -#define CROW_ROUTE(app, url) app.route(url) -#endif - -namespace crow -{ -#ifdef CROW_ENABLE_SSL - using ssl_context_t = boost::asio::ssl::context; -#endif - template - class Crow - { - public: - using self_t = Crow; - using server_t = Server; -#ifdef CROW_ENABLE_SSL - using ssl_server_t = Server; -#endif - Crow() - { - } - - template - void handle_upgrade(const request& req, response& res, Adaptor&& adaptor) - { - router_.handle_upgrade(req, res, adaptor); - } - - void handle(const request& req, response& res) - { - router_.handle(req, res); - } - - DynamicRule& route_dynamic(std::string&& rule) - { - return router_.new_rule_dynamic(std::move(rule)); - } - - template - auto route(std::string&& rule) - -> typename std::result_of)(Router, std::string&&)>::type - { - return router_.new_rule_tagged(std::move(rule)); - } - - self_t& port(std::uint16_t port) - { - port_ = port; - return *this; - } - - self_t& bindaddr(std::string bindaddr) - { - bindaddr_ = bindaddr; - return *this; - } - - self_t& multithreaded() - { - return concurrency(std::thread::hardware_concurrency()); - } - - self_t& concurrency(std::uint16_t concurrency) - { - if (concurrency < 1) - concurrency = 1; - concurrency_ = concurrency; - return *this; - } - - void validate() - { - router_.validate(); - } - - void run() - { - validate(); -#ifdef CROW_ENABLE_SSL - if (use_ssl_) - { - ssl_server_ = std::move(std::unique_ptr(new ssl_server_t(this, bindaddr_, port_, &middlewares_, concurrency_, &ssl_context_))); - ssl_server_->run(); - } - else -#endif - { - server_ = std::move(std::unique_ptr(new server_t(this, bindaddr_, port_, &middlewares_, concurrency_, nullptr))); - server_->run(); - } - } - - void stop() - { -#ifdef CROW_ENABLE_SSL - if (use_ssl_) - { - ssl_server_->stop(); - } - else -#endif - { - server_->stop(); - } - } - - void debug_print() - { - CROW_LOG_DEBUG << "Routing:"; - router_.debug_print(); - } - -#ifdef CROW_ENABLE_SSL - self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename) - { - use_ssl_ = true; - ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); - ssl_context_.use_certificate_file(crt_filename, ssl_context_t::pem); - ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem); - ssl_context_.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_file(const std::string& pem_filename) - { - use_ssl_ = true; - ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); - ssl_context_.load_verify_file(pem_filename); - ssl_context_.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) - { - use_ssl_ = true; - ssl_context_ = std::move(ctx); - return *this; - } - - - bool use_ssl_{false}; - ssl_context_t ssl_context_{boost::asio::ssl::context::sslv23}; - -#else - template - self_t& ssl_file(T&&, Remain&&...) - { - // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined. - static_assert( - // make static_assert dependent to T; always false - std::is_base_of::value, - "Define CROW_ENABLE_SSL to enable ssl support."); - return *this; - } - - template - self_t& ssl(T&&) - { - // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined. - static_assert( - // make static_assert dependent to T; always false - std::is_base_of::value, - "Define CROW_ENABLE_SSL to enable ssl support."); - return *this; - } -#endif - - // middleware - using context_t = detail::context; - template - typename T::context& get_context(const request& req) - { - static_assert(black_magic::contains::value, "App doesn't have the specified middleware type."); - auto& ctx = *reinterpret_cast(req.middleware_context); - return ctx.template get(); - } - - template - T& get_middleware() - { - return utility::get_element_by_type(middlewares_); - } - - private: - uint16_t port_ = 80; - uint16_t concurrency_ = 1; - std::string bindaddr_ = "0.0.0.0"; - Router router_; - - std::tuple middlewares_; - -#ifdef CROW_ENABLE_SSL - std::unique_ptr ssl_server_; -#endif - std::unique_ptr server_; - }; - template - using App = Crow; - using SimpleApp = Crow<>; -} +#include "crow/query_string.h" +#include "crow/http_parser_merged.h" +#include "crow/ci_map.h" +#include "crow/TinySHA1.hpp" +#include "crow/settings.h" +#include "crow/socket_adaptors.h" +#include "crow/json.h" +#include "crow/mustache.h" +#include "crow/logging.h" +#include "crow/dumb_timer_queue.h" +#include "crow/utility.h" +#include "crow/common.h" +#include "crow/http_request.h" +#include "crow/websocket.h" +#include "crow/parser.h" +#include "crow/http_response.h" +#include "crow/middleware.h" +#include "crow/routing.h" +#include "crow/middleware_context.h" +#include "crow/http_connection.h" +#include "crow/http_server.h" +#include "crow/app.h" diff --git a/ext/crow/TinySHA1.hpp b/ext/crow/crow/TinySHA1.hpp old mode 100755 new mode 100644 similarity index 100% rename from ext/crow/TinySHA1.hpp rename to ext/crow/crow/TinySHA1.hpp diff --git a/ext/crow/crow/app.h b/ext/crow/crow/app.h new file mode 100644 index 0000000..b454bf0 --- /dev/null +++ b/ext/crow/crow/app.h @@ -0,0 +1,248 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crow/settings.h" +#include "crow/logging.h" +#include "crow/utility.h" +#include "crow/routing.h" +#include "crow/middleware_context.h" +#include "crow/http_request.h" +#include "crow/http_server.h" + + +#ifdef CROW_MSVC_WORKAROUND +#define CROW_ROUTE(app, url) app.route_dynamic(url) +#else +#define CROW_ROUTE(app, url) app.route(url) +#endif + +namespace crow +{ +#ifdef CROW_ENABLE_SSL + using ssl_context_t = boost::asio::ssl::context; +#endif + template + class Crow + { + public: + using self_t = Crow; + using server_t = Server; +#ifdef CROW_ENABLE_SSL + using ssl_server_t = Server; +#endif + Crow() + { + } + + template + void handle_upgrade(const request& req, response& res, Adaptor&& adaptor) + { + router_.handle_upgrade(req, res, adaptor); + } + + void handle(const request& req, response& res) + { + router_.handle(req, res); + } + + DynamicRule& route_dynamic(std::string&& rule) + { + return router_.new_rule_dynamic(std::move(rule)); + } + + template + auto route(std::string&& rule) + -> typename std::result_of)(Router, std::string&&)>::type + { + return router_.new_rule_tagged(std::move(rule)); + } + + self_t& port(std::uint16_t port) + { + port_ = port; + return *this; + } + + self_t& bindaddr(std::string bindaddr) + { + bindaddr_ = bindaddr; + return *this; + } + + self_t& multithreaded() + { + return concurrency(std::thread::hardware_concurrency()); + } + + self_t& concurrency(std::uint16_t concurrency) + { + if (concurrency < 1) + concurrency = 1; + concurrency_ = concurrency; + return *this; + } + + void validate() + { + router_.validate(); + } + + void run() + { + validate(); +#ifdef CROW_ENABLE_SSL + if (use_ssl_) + { + ssl_server_ = std::move(std::unique_ptr(new ssl_server_t(this, bindaddr_, port_, &middlewares_, concurrency_, &ssl_context_))); + ssl_server_->set_tick_function(tick_interval_, tick_function_); + ssl_server_->run(); + } + else +#endif + { + server_ = std::move(std::unique_ptr(new server_t(this, bindaddr_, port_, &middlewares_, concurrency_, nullptr))); + server_->set_tick_function(tick_interval_, tick_function_); + server_->run(); + } + } + + void stop() + { +#ifdef CROW_ENABLE_SSL + if (use_ssl_) + { + ssl_server_->stop(); + } + else +#endif + { + server_->stop(); + } + } + + void debug_print() + { + CROW_LOG_DEBUG << "Routing:"; + router_.debug_print(); + } + + self_t& loglevel(crow::LogLevel level) + { + crow::logger::setLogLevel(level); + return *this; + } + +#ifdef CROW_ENABLE_SSL + self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename) + { + use_ssl_ = true; + ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); + ssl_context_.use_certificate_file(crt_filename, ssl_context_t::pem); + ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem); + ssl_context_.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_file(const std::string& pem_filename) + { + use_ssl_ = true; + ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); + ssl_context_.load_verify_file(pem_filename); + ssl_context_.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) + { + use_ssl_ = true; + ssl_context_ = std::move(ctx); + return *this; + } + + + bool use_ssl_{false}; + ssl_context_t ssl_context_{boost::asio::ssl::context::sslv23}; + +#else + template + self_t& ssl_file(T&&, Remain&&...) + { + // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined. + static_assert( + // make static_assert dependent to T; always false + std::is_base_of::value, + "Define CROW_ENABLE_SSL to enable ssl support."); + return *this; + } + + template + self_t& ssl(T&&) + { + // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined. + static_assert( + // make static_assert dependent to T; always false + std::is_base_of::value, + "Define CROW_ENABLE_SSL to enable ssl support."); + return *this; + } +#endif + + // middleware + using context_t = detail::context; + template + typename T::context& get_context(const request& req) + { + static_assert(black_magic::contains::value, "App doesn't have the specified middleware type."); + auto& ctx = *reinterpret_cast(req.middleware_context); + return ctx.template get(); + } + + template + T& get_middleware() + { + return utility::get_element_by_type(middlewares_); + } + + template + self_t& tick(Duration d, Func f) { + tick_interval_ = std::chrono::duration_cast(d); + tick_function_ = f; + return *this; + } + + private: + uint16_t port_ = 80; + uint16_t concurrency_ = 1; + std::string bindaddr_ = "0.0.0.0"; + Router router_; + + std::chrono::milliseconds tick_interval_; + std::function tick_function_; + + std::tuple middlewares_; + +#ifdef CROW_ENABLE_SSL + std::unique_ptr ssl_server_; +#endif + std::unique_ptr server_; + }; + template + using App = Crow; + using SimpleApp = Crow<>; +} diff --git a/ext/crow/ci_map.h b/ext/crow/crow/ci_map.h similarity index 100% rename from ext/crow/ci_map.h rename to ext/crow/crow/ci_map.h diff --git a/ext/crow/common.h b/ext/crow/crow/common.h old mode 100755 new mode 100644 similarity index 94% rename from ext/crow/common.h rename to ext/crow/crow/common.h index 8203378..ac6e789 --- a/ext/crow/common.h +++ b/ext/crow/crow/common.h @@ -4,7 +4,7 @@ #include #include #include -#include "utility.h" +#include "crow/utility.h" namespace crow { @@ -19,6 +19,7 @@ namespace crow CONNECT, OPTIONS, TRACE, + PATCH = 24, #endif Delete = 0, @@ -29,6 +30,7 @@ namespace crow Connect, Options, Trace, + Patch = 24, }; inline std::string method_name(HTTPMethod method) @@ -51,6 +53,8 @@ namespace crow return "OPTIONS"; case HTTPMethod::Trace: return "TRACE"; + case HTTPMethod::Patch: + return "PATCH"; } return "invalid"; } @@ -132,6 +136,7 @@ constexpr crow::HTTPMethod operator "" _method(const char* str, size_t /*len*/) crow::black_magic::is_equ_p(str, "OPTIONS", 7) ? crow::HTTPMethod::Options : crow::black_magic::is_equ_p(str, "CONNECT", 7) ? crow::HTTPMethod::Connect : crow::black_magic::is_equ_p(str, "TRACE", 5) ? crow::HTTPMethod::Trace : + crow::black_magic::is_equ_p(str, "PATCH", 5) ? crow::HTTPMethod::Patch : throw std::runtime_error("invalid http method"); } #endif diff --git a/ext/crow/dumb_timer_queue.h b/ext/crow/crow/dumb_timer_queue.h similarity index 98% rename from ext/crow/dumb_timer_queue.h rename to ext/crow/crow/dumb_timer_queue.h index 6b690bb..fd51518 100644 --- a/ext/crow/dumb_timer_queue.h +++ b/ext/crow/crow/dumb_timer_queue.h @@ -6,7 +6,7 @@ #include #include -#include "logging.h" +#include "crow/logging.h" namespace crow { diff --git a/ext/crow/http_connection.h b/ext/crow/crow/http_connection.h old mode 100755 new mode 100644 similarity index 96% rename from ext/crow/http_connection.h rename to ext/crow/crow/http_connection.h index 5517521..3d214f3 --- a/ext/crow/http_connection.h +++ b/ext/crow/crow/http_connection.h @@ -7,15 +7,15 @@ #include #include -#include "http_parser_merged.h" +#include "crow/http_parser_merged.h" -#include "parser.h" -#include "http_response.h" -#include "logging.h" -#include "settings.h" -#include "dumb_timer_queue.h" -#include "middleware_context.h" -#include "socket_adaptors.h" +#include "crow/parser.h" +#include "crow/http_response.h" +#include "crow/logging.h" +#include "crow/settings.h" +#include "crow/dumb_timer_queue.h" +#include "crow/middleware_context.h" +#include "crow/socket_adaptors.h" namespace crow { @@ -176,7 +176,7 @@ namespace crow } #ifdef CROW_ENABLE_DEBUG - static int connectionCount; + static std::atomic connectionCount; #endif template class Connection @@ -374,6 +374,9 @@ namespace crow {401, "HTTP/1.1 401 Unauthorized\r\n"}, {403, "HTTP/1.1 403 Forbidden\r\n"}, {404, "HTTP/1.1 404 Not Found\r\n"}, + {413, "HTTP/1.1 413 Payload Too Large\r\n"}, + {422, "HTTP/1.1 422 Unprocessable Entity\r\n"}, + {429, "HTTP/1.1 429 Too Many Requests\r\n"}, {500, "HTTP/1.1 500 Internal Server Error\r\n"}, {501, "HTTP/1.1 501 Not Implemented\r\n"}, @@ -467,7 +470,7 @@ namespace crow if (!ec) { bool ret = parser_.feed(buffer_.data(), bytes_transferred); - if (ret && adaptor_.is_open() && !close_connection_) + if (ret && adaptor_.is_open()) { error_while_reading = false; } @@ -482,6 +485,14 @@ namespace crow CROW_LOG_DEBUG << this << " from read(1)"; check_destroy(); } + else if (close_connection_) + { + cancel_deadline_timer(); + parser_.done(); + is_reading = false; + check_destroy(); + // adaptor will close after write + } else if (!need_to_call_after_handlers_) { start_deadline(); diff --git a/ext/crow/http_parser_merged.h b/ext/crow/crow/http_parser_merged.h old mode 100755 new mode 100644 similarity index 100% rename from ext/crow/http_parser_merged.h rename to ext/crow/crow/http_parser_merged.h diff --git a/ext/crow/http_request.h b/ext/crow/crow/http_request.h old mode 100755 new mode 100644 similarity index 95% rename from ext/crow/http_request.h rename to ext/crow/crow/http_request.h index 535a1fd..a70c299 --- a/ext/crow/http_request.h +++ b/ext/crow/crow/http_request.h @@ -1,10 +1,11 @@ #pragma once -#include "common.h" -#include "ci_map.h" -#include "query_string.h" #include +#include "crow/common.h" +#include "crow/ci_map.h" +#include "crow/query_string.h" + namespace crow { template diff --git a/ext/crow/http_response.h b/ext/crow/crow/http_response.h old mode 100755 new mode 100644 similarity index 93% rename from ext/crow/http_response.h rename to ext/crow/crow/http_response.h index 23f312a..7940174 --- a/ext/crow/http_response.h +++ b/ext/crow/crow/http_response.h @@ -1,9 +1,10 @@ #pragma once #include #include -#include "json.h" -#include "http_request.h" -#include "ci_map.h" + +#include "crow/json.h" +#include "crow/http_request.h" +#include "crow/ci_map.h" namespace crow { @@ -85,6 +86,12 @@ namespace crow completed_ = false; } + void redirect(const std::string& location) + { + code = 301; + set_header("Location", location); + } + void write(const std::string& body_part) { body += body_part; diff --git a/ext/crow/http_server.h b/ext/crow/crow/http_server.h old mode 100755 new mode 100644 similarity index 74% rename from ext/crow/http_server.h rename to ext/crow/crow/http_server.h index 86943ee..6aa070e --- a/ext/crow/http_server.h +++ b/ext/crow/crow/http_server.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #ifdef CROW_ENABLE_SSL @@ -12,9 +13,9 @@ #include -#include "http_connection.h" -#include "logging.h" -#include "dumb_timer_queue.h" +#include "crow/http_connection.h" +#include "crow/logging.h" +#include "crow/dumb_timer_queue.h" namespace crow { @@ -28,6 +29,7 @@ namespace crow Server(Handler* handler, std::string bindaddr, uint16_t port, std::tuple* middlewares = nullptr, uint16_t concurrency = 1, typename Adaptor::context* adaptor_ctx = nullptr) : acceptor_(io_service_, tcp::endpoint(boost::asio::ip::address::from_string(bindaddr), port)), signals_(io_service_, SIGINT, SIGTERM), + tick_timer_(io_service_), handler_(handler), concurrency_(concurrency), port_(port), @@ -37,6 +39,24 @@ namespace crow { } + void set_tick_function(std::chrono::milliseconds d, std::function f) + { + tick_interval_ = d; + tick_function_ = f; + } + + void on_tick() + { + tick_function_(); + tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count())); + tick_timer_.async_wait([this](const boost::system::error_code& ec) + { + if (ec) + return; + on_tick(); + }); + } + void run() { if (concurrency_ < 0) @@ -101,15 +121,36 @@ namespace crow timer.async_wait(handler); init_count ++; - try + while(1) { - io_service_pool_[i]->run(); - } catch(std::exception& e) - { - CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what(); + try + { + if (io_service_pool_[i]->run() == 0) + { + // when io_service.run returns 0, there are no more works to do. + break; + } + } catch(std::exception& e) + { + CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what(); + } } })); - CROW_LOG_INFO << server_name_ << " server is running, local port " << port_; + + if (tick_function_ && tick_interval_.count() > 0) + { + tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count())); + tick_timer_.async_wait([this](const boost::system::error_code& ec) + { + if (ec) + return; + on_tick(); + }); + } + + CROW_LOG_INFO << server_name_ << " server is running at " << bindaddr_ <<":" << port_ + << " using " << concurrency_ << " threads"; + CROW_LOG_INFO << "Call `app.loglevel(crow::LogLevel::Warning)` to hide Info level logs."; signals_.async_wait( [&](const boost::system::error_code& /*error*/, int /*signal_number*/){ @@ -161,6 +202,10 @@ namespace crow p->start(); }); } + else + { + delete p; + } do_accept(); }); } @@ -172,6 +217,7 @@ namespace crow std::vector> get_cached_date_str_pool_; tcp::acceptor acceptor_; boost::asio::signal_set signals_; + boost::asio::deadline_timer tick_timer_; Handler* handler_; uint16_t concurrency_{1}; @@ -180,6 +226,9 @@ namespace crow std::string bindaddr_; unsigned int roundrobin_index_{}; + std::chrono::milliseconds tick_interval_; + std::function tick_function_; + std::tuple* middlewares_; #ifdef CROW_ENABLE_SSL diff --git a/ext/crow/json.h b/ext/crow/crow/json.h old mode 100755 new mode 100644 similarity index 97% rename from ext/crow/json.h rename to ext/crow/crow/json.h index 9f06244..d8dbace --- a/ext/crow/json.h +++ b/ext/crow/crow/json.h @@ -12,6 +12,8 @@ #include #include +#include "crow/settings.h" + #if defined(__GNUG__) || defined(__clang__) #define crow_json_likely(x) __builtin_expect(x, 1) #define crow_json_unlikely(x) __builtin_expect(x, 0) @@ -1262,6 +1264,23 @@ namespace crow return *this; } + wvalue& operator=(std::vector&& v) + { + if (t_ != type::List) + reset(); + t_ = type::List; + if (!l) + l = std::unique_ptr>(new std::vector{}); + l->clear(); + l->resize(v.size()); + size_t idx = 0; + for(auto& x:v) + { + (*l)[idx++] = std::move(x); + } + return *this; + } + template wvalue& operator=(const std::vector& v) { @@ -1314,6 +1333,18 @@ namespace crow return (*o)[str]; } + std::vector keys() const + { + if (t_ != type::Object) + return {}; + std::vector result; + for (auto& kv:*o) + { + result.push_back(kv.first); + } + return result; + } + size_t estimate_length() const { switch(t_) @@ -1354,7 +1385,6 @@ namespace crow return 1; } - friend void dump_internal(const wvalue& v, std::string& out); friend std::string dump(const wvalue& v); }; @@ -1375,7 +1405,12 @@ namespace crow case type::Number: { char outbuf[128]; +#ifdef _MSC_VER + sprintf_s(outbuf, 128, "%g", v.d); +#else sprintf(outbuf, "%g", v.d); +#endif + out += outbuf; } break; diff --git a/ext/crow/logging.h b/ext/crow/crow/logging.h old mode 100755 new mode 100644 similarity index 99% rename from ext/crow/logging.h rename to ext/crow/crow/logging.h index 13cdad2..9c8bfbb --- a/ext/crow/logging.h +++ b/ext/crow/crow/logging.h @@ -7,7 +7,7 @@ #include #include -#include "settings.h" +#include "crow/settings.h" namespace crow { diff --git a/ext/crow/middleware.h b/ext/crow/crow/middleware.h old mode 100755 new mode 100644 similarity index 52% rename from ext/crow/middleware.h rename to ext/crow/crow/middleware.h index 534a87a..3858018 --- a/ext/crow/middleware.h +++ b/ext/crow/crow/middleware.h @@ -1,7 +1,7 @@ #pragma once #include -#include "http_request.h" -#include "http_response.h" +#include "crow/http_request.h" +#include "crow/http_response.h" namespace crow { @@ -35,10 +35,11 @@ namespace crow std::unordered_map jar; std::unordered_map cookies_to_add; - std::string get_cookie(const std::string& key) + std::string get_cookie(const std::string& key) const { - if (jar.count(key)) - return jar[key]; + auto cookie = jar.find(key); + if (cookie != jar.end()) + return cookie->second; return {}; } @@ -73,69 +74,22 @@ namespace crow if (pos == cookies.size()) break; - std::string value; + size_t pos_semicolon = cookies.find(';', pos); + std::string value = cookies.substr(pos, pos_semicolon-pos); - if (cookies[pos] == '"') + boost::trim(value); + if (value[0] == '"' && value[value.size()-1] == '"') { - int dquote_meet_count = 0; - pos ++; - size_t pos_dquote = pos-1; - do - { - pos_dquote = cookies.find('"', pos_dquote+1); - dquote_meet_count ++; - } while(pos_dquote < cookies.size() && cookies[pos_dquote-1] == '\\'); - if (pos_dquote == cookies.npos) - break; - - if (dquote_meet_count == 1) - value = cookies.substr(pos, pos_dquote - pos); - else - { - value.clear(); - value.reserve(pos_dquote-pos); - for(size_t p = pos; p < pos_dquote; p++) - { - // FIXME minimal escaping - if (cookies[p] == '\\' && p + 1 < pos_dquote) - { - p++; - if (cookies[p] == '\\' || cookies[p] == '"') - value += cookies[p]; - else - { - value += '\\'; - value += cookies[p]; - } - } - else - value += cookies[p]; - } - } - - ctx.jar.emplace(std::move(name), std::move(value)); - pos = cookies.find(";", pos_dquote+1); - if (pos == cookies.npos) - break; - pos++; - while(pos < cookies.size() && cookies[pos] == ' ') pos++; - if (pos == cookies.size()) - break; - } - else - { - size_t pos_semicolon = cookies.find(';', pos); - value = cookies.substr(pos, pos_semicolon - pos); - boost::trim(value); - ctx.jar.emplace(std::move(name), std::move(value)); - pos = pos_semicolon; - if (pos == cookies.npos) - break; - pos ++; - while(pos < cookies.size() && cookies[pos] == ' ') pos++; - if (pos == cookies.size()) - break; + value = value.substr(1, value.size()-2); } + + ctx.jar.emplace(std::move(name), std::move(value)); + + pos = pos_semicolon; + if (pos == cookies.npos) + break; + pos++; + while(pos < cookies.size() && cookies[pos] == ' ') pos++; } } @@ -143,7 +97,10 @@ namespace crow { for(auto& cookie:ctx.cookies_to_add) { - res.add_header("Set-Cookie", cookie.first + "=" + cookie.second); + if (cookie.second.empty()) + res.add_header("Set-Cookie", cookie.first + "=\"\""); + else + res.add_header("Set-Cookie", cookie.first + "=" + cookie.second); } } }; diff --git a/ext/crow/middleware_context.h b/ext/crow/crow/middleware_context.h similarity index 96% rename from ext/crow/middleware_context.h rename to ext/crow/crow/middleware_context.h index daaaa5c..6885c13 100644 --- a/ext/crow/middleware_context.h +++ b/ext/crow/crow/middleware_context.h @@ -1,8 +1,8 @@ #pragma once -#include "utility.h" -#include "http_request.h" -#include "http_response.h" +#include "crow/utility.h" +#include "crow/http_request.h" +#include "crow/http_response.h" namespace crow { diff --git a/ext/crow/mustache.h b/ext/crow/crow/mustache.h old mode 100755 new mode 100644 similarity index 99% rename from ext/crow/mustache.h rename to ext/crow/crow/mustache.h index 279f356..bfb9f14 --- a/ext/crow/mustache.h +++ b/ext/crow/crow/mustache.h @@ -4,7 +4,7 @@ #include #include #include -#include "json.h" +#include "crow/json.h" namespace crow { namespace mustache @@ -555,6 +555,11 @@ namespace crow detail::get_loader_ref() = std::move(loader); } + inline std::string load_text(const std::string& filename) + { + return detail::get_loader_ref()(filename); + } + inline template_t load(const std::string& filename) { return compile(detail::get_loader_ref()(filename)); diff --git a/ext/crow/parser.h b/ext/crow/crow/parser.h old mode 100755 new mode 100644 similarity index 98% rename from ext/crow/parser.h rename to ext/crow/crow/parser.h index b621850..61f5aee --- a/ext/crow/parser.h +++ b/ext/crow/crow/parser.h @@ -3,11 +3,10 @@ #include #include #include -#include #include -#include "http_parser_merged.h" -#include "http_request.h" +#include "crow/http_parser_merged.h" +#include "crow/http_request.h" namespace crow { diff --git a/ext/crow/query_string.h b/ext/crow/crow/query_string.h similarity index 81% rename from ext/crow/query_string.h rename to ext/crow/crow/query_string.h index 03e5cfd..ee61e30 100644 --- a/ext/crow/query_string.h +++ b/ext/crow/crow/query_string.h @@ -4,8 +4,12 @@ #include #include #include +#include #include +#include +namespace crow +{ // ---------------------------------------------------------------------------- // qs_parse (modified) // https://github.com/bartgrantham/qs_parse @@ -195,6 +199,48 @@ inline char * qs_k2v(const char * key, char * const * qs_kv, int qs_kv_size, int return NULL; } +inline boost::optional> qs_dict_name2kv(const char * dict_name, char * const * qs_kv, int qs_kv_size, int nth = 0) +{ + int i; + size_t name_len, skip_to_eq, skip_to_brace_open, skip_to_brace_close; + + name_len = strlen(dict_name); + +#ifdef _qsSORTING +// TODO: binary search for key in the sorted qs_kv +#else // _qsSORTING + for(i=0; i 0 && + skip_to_brace_close > 0 && + nth == 0 ) + { + auto key = std::string(qs_kv[i] + skip_to_brace_open, skip_to_brace_close - skip_to_brace_open); + auto value = std::string(qs_kv[i] + skip_to_eq); + return boost::make_optional(std::make_pair(key, value)); + } + else + { + --nth; + } + } + } +#endif // _qsSORTING + + return boost::none; +} + inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len) { @@ -220,8 +266,12 @@ inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t { qs++; i = strcspn(qs, "&=#"); - strncpy(val, qs, (val_len-1)<(i+1) ? (val_len-1) : (i+1)); - qs_decode(val); +#ifdef _MSC_VER + strncpy_s(val, val_len, qs, (val_len - 1)<(i + 1) ? (val_len - 1) : (i + 1)); +#else + strncpy(val, qs, (val_len - 1)<(i + 1) ? (val_len - 1) : (i + 1)); +#endif + qs_decode(val); } else { @@ -231,6 +281,7 @@ inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t return val; } +} // ---------------------------------------------------------------------------- @@ -333,6 +384,20 @@ namespace crow return ret; } + std::unordered_map get_dict (const std::string& name) const + { + std::unordered_map ret; + + int count = 0; + while(1) + { + if (auto element = qs_dict_name2kv(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++)) + ret.insert(*element); + else + break; + } + return ret; + } private: std::string url_; diff --git a/ext/crow/routing.h b/ext/crow/crow/routing.h old mode 100755 new mode 100644 similarity index 96% rename from ext/crow/routing.h rename to ext/crow/crow/routing.h index ed84334..cdfa480 --- a/ext/crow/routing.h +++ b/ext/crow/crow/routing.h @@ -8,12 +8,12 @@ #include #include -#include "common.h" -#include "http_response.h" -#include "http_request.h" -#include "utility.h" -#include "logging.h" -#include "websocket.h" +#include "crow/common.h" +#include "crow/http_response.h" +#include "crow/http_request.h" +#include "crow/utility.h" +#include "crow/logging.h" +#include "crow/websocket.h" namespace crow { @@ -156,7 +156,7 @@ namespace crow struct Wrapped { template - void set2(Func f, typename std::enable_if< + void set_(Func f, typename std::enable_if< !std::is_same>::type, const request&>::value , int>::type = 0) { @@ -190,7 +190,7 @@ namespace crow }; template - void set2(Func f, typename std::enable_if< + void set_(Func f, typename std::enable_if< std::is_same>::type, const request&>::value && !std::is_same>::type, response&>::value , int>::type = 0) @@ -205,7 +205,7 @@ namespace crow } template - void set2(Func f, typename std::enable_if< + void set_(Func f, typename std::enable_if< std::is_same>::type, const request&>::value && std::is_same>::type, response&>::value , int>::type = 0) @@ -276,12 +276,12 @@ namespace crow void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override { - new crow::websocket::Connection(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_); + new crow::websocket::Connection(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_); } #ifdef CROW_ENABLE_SSL void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override { - new crow::websocket::Connection(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_); + new crow::websocket::Connection(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_); } #endif @@ -313,11 +313,19 @@ namespace crow return *this; } + template + self_t& onaccept(Func f) + { + accept_handler_ = f; + return *this; + } + protected: std::function open_handler_; std::function message_handler_; std::function close_handler_; std::function error_handler_; + std::function accept_handler_; }; template @@ -394,7 +402,7 @@ namespace crow #else template #endif - std::function + std::function wrap(Func f, black_magic::seq) { #ifdef CROW_MSVC_WORKAROUND @@ -403,14 +411,16 @@ namespace crow using function_t = utility::function_traits; #endif if (!black_magic::is_parameter_tag_compatible( - black_magic::get_parameter_tag_runtime(rule_.c_str()), + black_magic::get_parameter_tag_runtime(rule_.c_str()), black_magic::compute_parameter_tag_from_args_list< typename function_t::template arg...>::value)) { throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_); } auto ret = detail::routing_handler_call_helper::Wrapped...>(); - ret.template set2...>(std::move(f)); + ret.template set_< + typename function_t::template arg... + >(std::move(f)); return ret; } @@ -454,10 +464,16 @@ namespace crow static_assert(!std::is_same()...))>::value, "Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue"); - handler_ = [f = std::move(f)](const request&, response& res, Args ... args){ + handler_ = ( +#ifdef CROW_CAN_USE_CPP14 + [f = std::move(f)] +#else + [f] +#endif + (const request&, response& res, Args ... args){ res = response(f(args...)); res.end(); - }; + }); } template @@ -473,10 +489,16 @@ namespace crow static_assert(!std::is_same(), std::declval()...))>::value, "Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue"); - handler_ = [f = std::move(f)](const crow::request& req, crow::response& res, Args ... args){ + handler_ = ( +#ifdef CROW_CAN_USE_CPP14 + [f = std::move(f)] +#else + [f] +#endif + (const crow::request& req, crow::response& res, Args ... args){ res = response(f(req, args...)); res.end(); - }; + }); } template @@ -981,7 +1003,6 @@ public: auto found = trie_.find(req.url); unsigned rule_index = found.first; - CROW_LOG_DEBUG << "???" << rule_index; if (!rule_index) { diff --git a/ext/crow/settings.h b/ext/crow/crow/settings.h similarity index 76% rename from ext/crow/settings.h rename to ext/crow/crow/settings.h index fc9392c..bd6259c 100644 --- a/ext/crow/settings.h +++ b/ext/crow/crow/settings.h @@ -3,21 +3,21 @@ // TODO - replace with runtime config. libucl? /* #ifdef - enables debug mode */ -#define CROW_ENABLE_DEBUG +//#define CROW_ENABLE_DEBUG /* #ifdef - enables logging */ #define CROW_ENABLE_LOGGING -/* #ifdef - enables SSL */ +/* #ifdef - enables ssl */ //#define CROW_ENABLE_SSL /* #define - specifies log level */ /* - DEBUG = 0 - INFO = 1 - WARNING = 2 - ERROR = 3 - CRITICAL = 4 + Debug = 0 + Info = 1 + Warning = 2 + Error = 3 + Critical = 4 default to INFO */ diff --git a/ext/crow/socket_adaptors.h b/ext/crow/crow/socket_adaptors.h old mode 100755 new mode 100644 similarity index 98% rename from ext/crow/socket_adaptors.h rename to ext/crow/crow/socket_adaptors.h index 634bd4b..eebd50f --- a/ext/crow/socket_adaptors.h +++ b/ext/crow/crow/socket_adaptors.h @@ -3,7 +3,7 @@ #ifdef CROW_ENABLE_SSL #include #endif -#include "settings.h" +#include "crow/settings.h" namespace crow { using namespace boost; diff --git a/ext/crow/utility.h b/ext/crow/crow/utility.h old mode 100755 new mode 100644 similarity index 98% rename from ext/crow/utility.h rename to ext/crow/crow/utility.h index b714d55..43503e8 --- a/ext/crow/utility.h +++ b/ext/crow/crow/utility.h @@ -8,6 +8,8 @@ #include #include +#include "crow/settings.h" + namespace crow { namespace black_magic @@ -500,7 +502,7 @@ template using arg = typename std::tuple_element>::type; }; - std::string base64encode(const char* data, size_t size, const char* key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") + inline static std::string base64encode(const char* data, size_t size, const char* key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") { std::string ret; ret.resize((size+2) / 3 * 4); @@ -536,7 +538,7 @@ template return ret; } - std::string base64encode_urlsafe(const char* data, size_t size) + inline static std::string base64encode_urlsafe(const char* data, size_t size) { return base64encode(data, size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"); } diff --git a/ext/crow/websocket.h b/ext/crow/crow/websocket.h old mode 100755 new mode 100644 similarity index 91% rename from ext/crow/websocket.h rename to ext/crow/crow/websocket.h index 5299c1a..ee0885f --- a/ext/crow/websocket.h +++ b/ext/crow/crow/websocket.h @@ -1,7 +1,9 @@ #pragma once -#include "socket_adaptors.h" -#include "http_request.h" -#include "TinySHA1.hpp" +#include +#include +#include "crow/socket_adaptors.h" +#include "crow/http_request.h" +#include "crow/TinySHA1.hpp" namespace crow { @@ -22,6 +24,12 @@ namespace crow virtual void send_text(const std::string& msg) = 0; virtual void close(const std::string& msg = "quit") = 0; virtual ~connection(){} + + void userdata(void* u) { userdata_ = u; } + void* userdata() { return userdata_; } + + private: + void* userdata_; }; template @@ -32,15 +40,28 @@ namespace crow std::function open_handler, std::function message_handler, std::function close_handler, - std::function error_handler) + std::function error_handler, + std::function accept_handler) : adaptor_(std::move(adaptor)), open_handler_(std::move(open_handler)), message_handler_(std::move(message_handler)), close_handler_(std::move(close_handler)), error_handler_(std::move(error_handler)) + , accept_handler_(std::move(accept_handler)) { - if (req.get_header_value("upgrade") != "websocket") + if (!boost::iequals(req.get_header_value("upgrade"), "websocket")) { adaptor.close(); delete this; return; } + + if (accept_handler_) + { + if (!accept_handler_(req)) + { + adaptor.close(); + delete this; + return; + } + } + // Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== // Sec-WebSocket-Version: 13 std::string magic = req.get_header_value("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; @@ -125,13 +146,13 @@ namespace crow else if (size < 0x10000) { buf[1] += 126; - *(uint16_t*)(buf+2) = (uint16_t)size; + *(uint16_t*)(buf+2) = htons((uint16_t)size); return {buf, buf+4}; } else { buf[1] += 127; - *(uint64_t*)(buf+2) = (uint64_t)size; + *(uint64_t*)(buf+2) = ((1==htonl(1)) ? (uint64_t)size : ((uint64_t)htonl((size) & 0xFFFFFFFF) << 32) | htonl((size) >> 32)); return {buf, buf+10}; } } @@ -162,7 +183,12 @@ namespace crow { //boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&mini_header_, 1), adaptor_.socket().async_read_some(boost::asio::buffer(&mini_header_, 2), - [this](const boost::system::error_code& ec, std::size_t bytes_transferred) + [this](const boost::system::error_code& ec, std::size_t +#ifdef CROW_ENABLE_DEBUG + bytes_transferred +#endif + ) + { is_reading = false; mini_header_ = htons(mini_header_); @@ -205,11 +231,17 @@ namespace crow case WebSocketReadState::Len16: { remaining_length_ = 0; - boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length_, 2), - [this](const boost::system::error_code& ec, std::size_t bytes_transferred) + uint16_t remaining_length16_ = 0; + boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length16_, 2), + [this,&remaining_length16_](const boost::system::error_code& ec, std::size_t +#ifdef CROW_ENABLE_DEBUG + bytes_transferred +#endif + ) { is_reading = false; - remaining_length_ = ntohs(*(uint16_t*)&remaining_length_); + remaining_length16_ = ntohs(remaining_length16_); + remaining_length_ = remaining_length16_; #ifdef CROW_ENABLE_DEBUG if (!ec && bytes_transferred != 2) { @@ -236,7 +268,11 @@ namespace crow case WebSocketReadState::Len64: { boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length_, 8), - [this](const boost::system::error_code& ec, std::size_t bytes_transferred) + [this](const boost::system::error_code& ec, std::size_t +#ifdef CROW_ENABLE_DEBUG + bytes_transferred +#endif + ) { is_reading = false; remaining_length_ = ((1==ntohl(1)) ? (remaining_length_) : ((uint64_t)ntohl((remaining_length_) & 0xFFFFFFFF) << 32) | ntohl((remaining_length_) >> 32)); @@ -265,7 +301,11 @@ namespace crow break; case WebSocketReadState::Mask: boost::asio::async_read(adaptor_.socket(), boost::asio::buffer((char*)&mask_, 4), - [this](const boost::system::error_code& ec, std::size_t bytes_transferred) + [this](const boost::system::error_code& ec, std::size_t +#ifdef CROW_ENABLE_DEBUG + bytes_transferred +#endif + ) { is_reading = false; #ifdef CROW_ENABLE_DEBUG @@ -477,6 +517,7 @@ namespace crow std::function message_handler_; std::function close_handler_; std::function error_handler_; + std::function accept_handler_; }; } } diff --git a/main.cpp b/main.cpp index 4d7640f..1f428dc 100644 --- a/main.cpp +++ b/main.cpp @@ -285,12 +285,14 @@ main(int ac, const char* av[]) }); CROW_ROUTE(app, "/tx//") - ([&](string tx_hash, uint16_t with_ring_signatures) { + ([&](string tx_hash, uint16_t with_ring_signatures) + { return xmrblocks.show_tx(tx_hash, with_ring_signatures); }); CROW_ROUTE(app, "/myoutputs").methods("POST"_method) - ([&](const crow::request& req) { + ([&](const crow::request& req) + { map post_body = xmreg::parse_crow_post_data(req.body); @@ -319,7 +321,8 @@ main(int ac, const char* av[]) CROW_ROUTE(app, "/myoutputs///") ([&](const crow::request& req, string tx_hash, - string xmr_address, string viewkey) { + string xmr_address, string viewkey) + { string domain = get_domain(req); diff --git a/src/page.h b/src/page.h index fee3651..5796be3 100644 --- a/src/page.h +++ b/src/page.h @@ -20,7 +20,7 @@ #include "CurrentBlockchainStatus.h" #include "MempoolStatus.h" -#include "../ext/crow/http_request.h" +#include "../ext/crow/crow.h" #include "../ext/vpetrigocaches/cache.hpp" #include "../ext/vpetrigocaches/lru_cache_policy.hpp"