diff options
Diffstat (limited to 'include/http_connection.h')
-rw-r--r-- | include/http_connection.h | 599 |
1 files changed, 0 insertions, 599 deletions
diff --git a/include/http_connection.h b/include/http_connection.h deleted file mode 100644 index e8eb4b1..0000000 --- a/include/http_connection.h +++ /dev/null @@ -1,599 +0,0 @@ -#pragma once -#include <boost/asio.hpp> -#include <boost/algorithm/string/predicate.hpp> -#include <boost/lexical_cast.hpp> -#include <boost/array.hpp> -#include <atomic> -#include <chrono> -#include <vector> - -#include "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" - -namespace crow -{ - using namespace boost; - using tcp = asio::ip::tcp; - - namespace detail - { - template <typename MW> - struct check_before_handle_arity_3_const - { - template <typename T, - void (T::*)(request&, response&, typename MW::context&) const = &T::before_handle - > - struct get - { }; - }; - - template <typename MW> - struct check_before_handle_arity_3 - { - template <typename T, - void (T::*)(request&, response&, typename MW::context&) = &T::before_handle - > - struct get - { }; - }; - - template <typename MW> - struct check_after_handle_arity_3_const - { - template <typename T, - void (T::*)(request&, response&, typename MW::context&) const = &T::after_handle - > - struct get - { }; - }; - - template <typename MW> - struct check_after_handle_arity_3 - { - template <typename T, - void (T::*)(request&, response&, typename MW::context&) = &T::after_handle - > - struct get - { }; - }; - - template <typename T> - struct is_before_handle_arity_3_impl - { - template <typename C> - static std::true_type f(typename check_before_handle_arity_3_const<T>::template get<C>*); - - template <typename C> - static std::true_type f(typename check_before_handle_arity_3<T>::template get<C>*); - - template <typename C> - static std::false_type f(...); - - public: - static const bool value = decltype(f<T>(nullptr))::value; - }; - - template <typename T> - struct is_after_handle_arity_3_impl - { - template <typename C> - static std::true_type f(typename check_after_handle_arity_3_const<T>::template get<C>*); - - template <typename C> - static std::true_type f(typename check_after_handle_arity_3<T>::template get<C>*); - - template <typename C> - static std::false_type f(...); - - public: - static const bool value = decltype(f<T>(nullptr))::value; - }; - - template <typename MW, typename Context, typename ParentContext> - typename std::enable_if<!is_before_handle_arity_3_impl<MW>::value>::type - before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) - { - mw.before_handle(req, res, ctx.template get<MW>(), ctx); - } - - template <typename MW, typename Context, typename ParentContext> - typename std::enable_if<is_before_handle_arity_3_impl<MW>::value>::type - before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) - { - mw.before_handle(req, res, ctx.template get<MW>()); - } - - template <typename MW, typename Context, typename ParentContext> - typename std::enable_if<!is_after_handle_arity_3_impl<MW>::value>::type - after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) - { - mw.after_handle(req, res, ctx.template get<MW>(), ctx); - } - - template <typename MW, typename Context, typename ParentContext> - typename std::enable_if<is_after_handle_arity_3_impl<MW>::value>::type - after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) - { - mw.after_handle(req, res, ctx.template get<MW>()); - } - - template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares> - bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx) - { - using parent_context_t = typename Context::template partial<N-1>; - before_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); - - if (res.is_completed()) - { - after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); - return true; - } - - if (middleware_call_helper<N+1, Context, Container, Middlewares...>(middlewares, req, res, ctx)) - { - after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); - return true; - } - - return false; - } - - template <int N, typename Context, typename Container> - bool middleware_call_helper(Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/) - { - return false; - } - - template <int N, typename Context, typename Container> - typename std::enable_if<(N<0)>::type - after_handlers_call_helper(Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/) - { - } - - template <int N, typename Context, typename Container> - typename std::enable_if<(N==0)>::type after_handlers_call_helper(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; - after_handler_call<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 after_handlers_call_helper(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; - after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx)); - after_handlers_call_helper<N-1, Context, Container>(middlewares, ctx, req, res); - } - } - -#ifdef CROW_ENABLE_DEBUG - static int connectionCount; -#endif - template <typename Adaptor, typename Handler, typename ... Middlewares> - class Connection - { - public: - Connection( - boost::asio::io_service& io_service, - Handler* handler, - const std::string& server_name, - std::tuple<Middlewares...>* middlewares, - std::function<std::string()>& get_cached_date_str_f, - detail::dumb_timer_queue& timer_queue, - typename Adaptor::context* adaptor_ctx_ - ) - : adaptor_(io_service, adaptor_ctx_), - handler_(handler), - parser_(this), - server_name_(server_name), - middlewares_(middlewares), - get_cached_date_str(get_cached_date_str_f), - timer_queue(timer_queue) - { -#ifdef CROW_ENABLE_DEBUG - connectionCount ++; - CROW_LOG_DEBUG << "Connection open, total " << connectionCount << ", " << this; -#endif - } - - ~Connection() - { - res.complete_request_handler_ = nullptr; - cancel_deadline_timer(); -#ifdef CROW_ENABLE_DEBUG - connectionCount --; - CROW_LOG_DEBUG << "Connection closed, total " << connectionCount << ", " << this; -#endif - } - - decltype(std::declval<Adaptor>().raw_socket())& socket() - { - return adaptor_.raw_socket(); - } - - void start() - { - adaptor_.start([this](const boost::system::error_code& ec) { - if (!ec) - { - start_deadline(); - - do_read(); - } - else - { - check_destroy(); - } - }); - } - - void handle_header() - { - // HTTP 1.1 Expect: 100-continue - if (parser_.check_version(1, 1) && parser_.headers.count("expect") && get_header_value(parser_.headers, "expect") == "100-continue") - { - buffers_.clear(); - static std::string expect_100_continue = "HTTP/1.1 100 Continue\r\n\r\n"; - buffers_.emplace_back(expect_100_continue.data(), expect_100_continue.size()); - do_write(); - } - } - - void handle() - { - cancel_deadline_timer(); - bool is_invalid_request = false; - add_keep_alive_ = false; - - req_ = std::move(parser_.to_request()); - request& req = req_; - - if (parser_.check_version(1, 0)) - { - // HTTP/1.0 - if (req.headers.count("connection")) - { - if (boost::iequals(req.get_header_value("connection"),"Keep-Alive")) - add_keep_alive_ = true; - } - else - close_connection_ = true; - } - else if (parser_.check_version(1, 1)) - { - // HTTP/1.1 - if (req.headers.count("connection")) - { - if (req.get_header_value("connection") == "close") - close_connection_ = true; - else if (boost::iequals(req.get_header_value("connection"),"Keep-Alive")) - add_keep_alive_ = true; - } - if (!req.headers.count("host")) - { - is_invalid_request = true; - res = response(400); - } - if (parser_.is_upgrade()) - { - if (req.get_header_value("upgrade") == "h2c") - { - // TODO HTTP/2 - // currently, ignore upgrade header - } - else - { - close_connection_ = true; - handler_->handle_upgrade(req, res, std::move(adaptor_)); - return; - } - } - } - - CROW_LOG_INFO << "Request: " << boost::lexical_cast<std::string>(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << parser_.http_major << "." << parser_.http_minor << ' ' - << method_name(req.method) << " " << req.url; - - - need_to_call_after_handlers_ = false; - if (!is_invalid_request) - { - res.complete_request_handler_ = []{}; - res.is_alive_helper_ = [this]()->bool{ return adaptor_.is_open(); }; - - ctx_ = detail::context<Middlewares...>(); - req.middleware_context = (void*)&ctx_; - req.io_service = &adaptor_.get_io_service(); - detail::middleware_call_helper<0, decltype(ctx_), decltype(*middlewares_), Middlewares...>(*middlewares_, req, res, ctx_); - - if (!res.completed_) - { - res.complete_request_handler_ = [this]{ this->complete_request(); }; - need_to_call_after_handlers_ = true; - handler_->handle(req, res); - if (add_keep_alive_) - res.set_header("connection", "Keep-Alive"); - } - else - { - complete_request(); - } - } - else - { - complete_request(); - } - } - - void complete_request() - { - CROW_LOG_INFO << "Response: " << this << ' ' << req_.raw_url << ' ' << res.code << ' ' << close_connection_; - - if (need_to_call_after_handlers_) - { - need_to_call_after_handlers_ = false; - - // call all after_handler of middlewares - detail::after_handlers_call_helper< - ((int)sizeof...(Middlewares)-1), - decltype(ctx_), - decltype(*middlewares_)> - (*middlewares_, ctx_, req_, res); - } - - //auto self = this->shared_from_this(); - res.complete_request_handler_ = nullptr; - - if (!adaptor_.is_open()) - { - //CROW_LOG_DEBUG << this << " delete (socket is closed) " << is_reading << ' ' << is_writing; - //delete this; - return; - } - - static std::unordered_map<int, std::string> statusCodes = { - {200, "HTTP/1.1 200 OK\r\n"}, - {201, "HTTP/1.1 201 Created\r\n"}, - {202, "HTTP/1.1 202 Accepted\r\n"}, - {204, "HTTP/1.1 204 No Content\r\n"}, - - {300, "HTTP/1.1 300 Multiple Choices\r\n"}, - {301, "HTTP/1.1 301 Moved Permanently\r\n"}, - {302, "HTTP/1.1 302 Moved Temporarily\r\n"}, - {304, "HTTP/1.1 304 Not Modified\r\n"}, - - {400, "HTTP/1.1 400 Bad Request\r\n"}, - {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"}, - - {500, "HTTP/1.1 500 Internal Server Error\r\n"}, - {501, "HTTP/1.1 501 Not Implemented\r\n"}, - {502, "HTTP/1.1 502 Bad Gateway\r\n"}, - {503, "HTTP/1.1 503 Service Unavailable\r\n"}, - }; - - static std::string seperator = ": "; - static std::string crlf = "\r\n"; - - buffers_.clear(); - buffers_.reserve(4*(res.headers.size()+5)+3); - - if (res.body.empty() && res.json_value.t() == json::type::Object) - { - res.body = json::dump(res.json_value); - } - - if (!statusCodes.count(res.code)) - res.code = 500; - { - auto& status = statusCodes.find(res.code)->second; - buffers_.emplace_back(status.data(), status.size()); - } - - if (res.code >= 400 && res.body.empty()) - res.body = statusCodes[res.code].substr(9); - - for(auto& kv : res.headers) - { - buffers_.emplace_back(kv.first.data(), kv.first.size()); - buffers_.emplace_back(seperator.data(), seperator.size()); - buffers_.emplace_back(kv.second.data(), kv.second.size()); - buffers_.emplace_back(crlf.data(), crlf.size()); - - } - - if (!res.headers.count("content-length")) - { - content_length_ = std::to_string(res.body.size()); - static std::string content_length_tag = "Content-Length: "; - buffers_.emplace_back(content_length_tag.data(), content_length_tag.size()); - buffers_.emplace_back(content_length_.data(), content_length_.size()); - buffers_.emplace_back(crlf.data(), crlf.size()); - } - if (!res.headers.count("server")) - { - static std::string server_tag = "Server: "; - buffers_.emplace_back(server_tag.data(), server_tag.size()); - buffers_.emplace_back(server_name_.data(), server_name_.size()); - buffers_.emplace_back(crlf.data(), crlf.size()); - } - if (!res.headers.count("date")) - { - static std::string date_tag = "Date: "; - date_str_ = get_cached_date_str(); - buffers_.emplace_back(date_tag.data(), date_tag.size()); - buffers_.emplace_back(date_str_.data(), date_str_.size()); - buffers_.emplace_back(crlf.data(), crlf.size()); - } - if (add_keep_alive_) - { - static std::string keep_alive_tag = "Connection: Keep-Alive"; - buffers_.emplace_back(keep_alive_tag.data(), keep_alive_tag.size()); - buffers_.emplace_back(crlf.data(), crlf.size()); - } - - buffers_.emplace_back(crlf.data(), crlf.size()); - res_body_copy_.swap(res.body); - buffers_.emplace_back(res_body_copy_.data(), res_body_copy_.size()); - - do_write(); - - if (need_to_start_read_after_complete_) - { - need_to_start_read_after_complete_ = false; - start_deadline(); - do_read(); - } - } - - private: - void do_read() - { - //auto self = this->shared_from_this(); - is_reading = true; - adaptor_.socket().async_read_some(boost::asio::buffer(buffer_), - [this](const boost::system::error_code& ec, std::size_t bytes_transferred) - { - bool error_while_reading = true; - if (!ec) - { - bool ret = parser_.feed(buffer_.data(), bytes_transferred); - if (ret && adaptor_.is_open()) - { - error_while_reading = false; - } - } - - if (error_while_reading) - { - cancel_deadline_timer(); - parser_.done(); - adaptor_.close(); - is_reading = false; - 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(); - do_read(); - } - else - { - // res will be completed later by user - need_to_start_read_after_complete_ = true; - } - }); - } - - void do_write() - { - //auto self = this->shared_from_this(); - is_writing = true; - boost::asio::async_write(adaptor_.socket(), buffers_, - [&](const boost::system::error_code& ec, std::size_t /*bytes_transferred*/) - { - is_writing = false; - res.clear(); - res_body_copy_.clear(); - if (!ec) - { - if (close_connection_) - { - adaptor_.close(); - CROW_LOG_DEBUG << this << " from write(1)"; - check_destroy(); - } - } - else - { - CROW_LOG_DEBUG << this << " from write(2)"; - check_destroy(); - } - }); - } - - void check_destroy() - { - CROW_LOG_DEBUG << this << " is_reading " << is_reading << " is_writing " << is_writing; - if (!is_reading && !is_writing) - { - CROW_LOG_DEBUG << this << " delete (idle) "; - delete this; - } - } - - void cancel_deadline_timer() - { - CROW_LOG_DEBUG << this << " timer cancelled: " << timer_cancel_key_.first << ' ' << timer_cancel_key_.second; - timer_queue.cancel(timer_cancel_key_); - } - - void start_deadline(/*int timeout = 5*/) - { - cancel_deadline_timer(); - - timer_cancel_key_ = timer_queue.add([this] - { - if (!adaptor_.is_open()) - { - return; - } - adaptor_.close(); - }); - CROW_LOG_DEBUG << this << " timer added: " << timer_cancel_key_.first << ' ' << timer_cancel_key_.second; - } - - private: - Adaptor adaptor_; - Handler* handler_; - - boost::array<char, 4096> buffer_; - - HTTPParser<Connection> parser_; - request req_; - response res; - - bool close_connection_ = false; - - const std::string& server_name_; - std::vector<boost::asio::const_buffer> buffers_; - - std::string content_length_; - std::string date_str_; - std::string res_body_copy_; - - //boost::asio::deadline_timer deadline_; - detail::dumb_timer_queue::key timer_cancel_key_; - - bool is_reading{}; - bool is_writing{}; - bool need_to_call_after_handlers_{}; - bool need_to_start_read_after_complete_{}; - bool add_keep_alive_{}; - - std::tuple<Middlewares...>* middlewares_; - detail::context<Middlewares...> ctx_; - - std::function<std::string()>& get_cached_date_str; - detail::dumb_timer_queue& timer_queue; - }; - -} |