diff --git a/include/crow/http_connection.h b/include/crow/http_connection.h index 3d214f3d2..bc775ed82 100644 --- a/include/crow/http_connection.h +++ b/include/crow/http_connection.h @@ -19,356 +19,356 @@ namespace crow { - using namespace boost; - using tcp = asio::ip::tcp; +using namespace boost; +using tcp = asio::ip::tcp; - namespace detail - { - template - struct check_before_handle_arity_3_const - { - template - struct get - { }; - }; - - template - struct check_before_handle_arity_3 - { - template - struct get - { }; - }; - - template - struct check_after_handle_arity_3_const - { - template - struct get - { }; - }; - - template - struct check_after_handle_arity_3 - { - template - struct get - { }; - }; - - template - struct is_before_handle_arity_3_impl - { - template - static std::true_type f(typename check_before_handle_arity_3_const::template get*); +namespace detail +{ +template +struct check_before_handle_arity_3_const +{ + template + struct get + { }; +}; + +template +struct check_before_handle_arity_3 +{ + template + struct get + { }; +}; + +template +struct check_after_handle_arity_3_const +{ + template + struct get + { }; +}; + +template +struct check_after_handle_arity_3 +{ + template + struct get + { }; +}; + +template +struct is_before_handle_arity_3_impl +{ + template + static std::true_type f(typename check_before_handle_arity_3_const::template get*); - template - static std::true_type f(typename check_before_handle_arity_3::template get*); + template + static std::true_type f(typename check_before_handle_arity_3::template get*); - template - static std::false_type f(...); + template + static std::false_type f(...); - public: - static const bool value = decltype(f(nullptr))::value; - }; +public: + static const bool value = decltype(f(nullptr))::value; +}; - template - struct is_after_handle_arity_3_impl - { - template - static std::true_type f(typename check_after_handle_arity_3_const::template get*); +template +struct is_after_handle_arity_3_impl +{ + template + static std::true_type f(typename check_after_handle_arity_3_const::template get*); - template - static std::true_type f(typename check_after_handle_arity_3::template get*); + template + static std::true_type f(typename check_after_handle_arity_3::template get*); - template - static std::false_type f(...); + template + static std::false_type f(...); - public: - static const bool value = decltype(f(nullptr))::value; - }; +public: + static const bool value = decltype(f(nullptr))::value; +}; - template - typename std::enable_if::value>::type - before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) - { - mw.before_handle(req, res, ctx.template get(), ctx); - } +template +typename std::enable_if < !is_before_handle_arity_3_impl::value >::type +before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) +{ + mw.before_handle(req, res, ctx.template get(), ctx); +} - template - typename std::enable_if::value>::type - before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) - { - mw.before_handle(req, res, ctx.template get()); - } +template +typename std::enable_if::value>::type +before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) +{ + mw.before_handle(req, res, ctx.template get()); +} - template - typename std::enable_if::value>::type - after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) - { - mw.after_handle(req, res, ctx.template get(), ctx); - } +template +typename std::enable_if < !is_after_handle_arity_3_impl::value >::type +after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) +{ + mw.after_handle(req, res, ctx.template get(), ctx); +} - template - typename std::enable_if::value>::type - after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) - { - mw.after_handle(req, res, ctx.template get()); - } +template +typename std::enable_if::value>::type +after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/) +{ + mw.after_handle(req, res, ctx.template get()); +} - template - bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx) - { - using parent_context_t = typename Context::template partial; - before_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); +template +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(std::get(middlewares), req, res, ctx, static_cast(ctx)); - if (res.is_completed()) - { - after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); - return true; - } + if (res.is_completed()) + { + after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); + return true; + } - if (middleware_call_helper(middlewares, req, res, ctx)) - { - after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); - return true; - } + if (middleware_call_helper < N + 1, Context, Container, Middlewares... > (middlewares, req, res, ctx)) + { + after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); + return true; + } - return false; - } + return false; +} - template - bool middleware_call_helper(Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/) - { - return false; - } +template +bool middleware_call_helper(Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/) +{ + return false; +} - template - typename std::enable_if<(N<0)>::type - after_handlers_call_helper(Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/) - { - } +template +typename std::enable_if < (N < 0) >::type +after_handlers_call_helper(Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/) +{ +} - template - 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; - using CurrentMW = typename std::tuple_element::type>::type; - after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); - } +template +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::type>::type; + after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); +} - template - 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; - using CurrentMW = typename std::tuple_element::type>::type; - after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); - after_handlers_call_helper(middlewares, ctx, req, res); - } - } +template +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::type>::type; + after_handler_call(std::get(middlewares), req, res, ctx, static_cast(ctx)); + after_handlers_call_helper < N - 1, Context, Container > (middlewares, ctx, req, res); +} +} #ifdef CROW_ENABLE_DEBUG - static std::atomic connectionCount; +static std::atomic connectionCount; #endif - template - class Connection +template +class Connection +{ +public: + Connection( + boost::asio::io_service& io_service, + Handler* handler, + const std::string& server_name, + std::tuple* middlewares, + std::function& 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) { - public: - Connection( - boost::asio::io_service& io_service, - Handler* handler, - const std::string& server_name, - std::tuple* middlewares, - std::function& 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; + connectionCount ++; + CROW_LOG_DEBUG << "Connection open, total " << connectionCount << ", " << this; #endif - } - - ~Connection() - { - res.complete_request_handler_ = nullptr; - cancel_deadline_timer(); + } + + ~Connection() + { + res.complete_request_handler_ = nullptr; + cancel_deadline_timer(); #ifdef CROW_ENABLE_DEBUG - connectionCount --; - CROW_LOG_DEBUG << "Connection closed, total " << connectionCount << ", " << this; + connectionCount --; + CROW_LOG_DEBUG << "Connection closed, total " << connectionCount << ", " << this; #endif - } + } + + decltype(std::declval().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(); + } + }); + } - decltype(std::declval().raw_socket())& socket() + 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") { - return adaptor_.raw_socket(); + 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 start() - { - adaptor_.start([this](const boost::system::error_code& ec) { - if (!ec) - { - start_deadline(); + void handle() + { + cancel_deadline_timer(); + bool is_invalid_request = false; + add_keep_alive_ = false; - do_read(); - } - else - { - check_destroy(); - } - }); - } + req_ = std::move(parser_.to_request()); + request& req = req_; - void handle_header() + if (parser_.check_version(1, 0)) { - // HTTP 1.1 Expect: 100-continue - if (parser_.check_version(1, 1) && parser_.headers.count("expect") && get_header_value(parser_.headers, "expect") == "100-continue") + // HTTP/1.0 + if (req.headers.count("connection")) { - 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(); + if (boost::iequals(req.get_header_value("connection"), "Keep-Alive")) + add_keep_alive_ = true; } + else + close_connection_ = true; } - - void handle() + else if (parser_.check_version(1, 1)) { - 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.1 + if (req.headers.count("connection")) { - // HTTP/1.0 - if (req.headers.count("connection")) - { - if (boost::iequals(req.get_header_value("connection"),"Keep-Alive")) - add_keep_alive_ = true; - } - else + 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); } - else if (parser_.check_version(1, 1)) + if (parser_.is_upgrade()) { - // HTTP/1.1 - if (req.headers.count("connection")) + if (req.get_header_value("upgrade") == "h2c") { - 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; + // TODO HTTP/2 + // currently, ignore upgrade header } - if (!req.headers.count("host")) + else { - is_invalid_request = true; - res = response(400); + close_connection_ = true; + handler_->handle_upgrade(req, res, std::move(adaptor_)); + return; } - 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(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << parser_.http_major << "." << parser_.http_minor << ' ' - << method_name(req.method) << " " << req.url; + CROW_LOG_INFO << "Request: " << boost::lexical_cast(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(); }; + 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(); - 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_); + ctx_ = detail::context(); + 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(); - } + 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_; - void complete_request() + if (need_to_call_after_handlers_) { - CROW_LOG_INFO << "Response: " << this << ' ' << req_.raw_url << ' ' << res.code << ' ' << close_connection_; + need_to_call_after_handlers_ = false; - 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); - } + // 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; - } + //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 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"}, + static std::unordered_map 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"}, + {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"}, @@ -378,225 +378,220 @@ namespace crow {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"}, - {502, "HTTP/1.1 502 Bad Gateway\r\n"}, - {503, "HTTP/1.1 503 Service Unavailable\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"; + static std::string seperator = ": "; + static std::string crlf = "\r\n"; - buffers_.clear(); - buffers_.reserve(4*(res.headers.size()+5)+3); + 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 (!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); - 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()); - 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()); + } - 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")) + 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) { - 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()); + bool ret = parser_.feed(buffer_.data(), bytes_transferred); + if (ret && adaptor_.is_open()) + { + error_while_reading = false; + } } - if (!res.headers.count("date")) + + if (error_while_reading) { - 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()); + cancel_deadline_timer(); + parser_.done(); + adaptor_.close(); + is_reading = false; + CROW_LOG_DEBUG << this << " from read(1)"; + check_destroy(); } - if (add_keep_alive_) + else if (close_connection_) { - 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()); + cancel_deadline_timer(); + parser_.done(); + is_reading = false; + check_destroy(); + // adaptor will close after write } - - 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_) + else if (!need_to_call_after_handlers_) { - 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; - } - }); - } + else + { + // res will be completed later by user + need_to_start_read_after_complete_ = true; + } + }); + } - void do_write() + 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*/) { - //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_) { - 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) + adaptor_.close(); + CROW_LOG_DEBUG << this << " from write(1)"; + check_destroy(); + } + } + else { - CROW_LOG_DEBUG << this << " delete (idle) "; - delete this; + CROW_LOG_DEBUG << this << " from write(2)"; + check_destroy(); } - } + }); + } - void cancel_deadline_timer() + void check_destroy() + { + CROW_LOG_DEBUG << this << " is_reading " << is_reading << " is_writing " << is_writing; + if (!is_reading && !is_writing) { - CROW_LOG_DEBUG << this << " timer cancelled: " << timer_cancel_key_.first << ' ' << timer_cancel_key_.second; - timer_queue.cancel(timer_cancel_key_); + CROW_LOG_DEBUG << this << " delete (idle) "; + delete this; } + } - void start_deadline(/*int timeout = 5*/) + 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] { - cancel_deadline_timer(); - - timer_cancel_key_ = timer_queue.add([this] + if (!adaptor_.is_open()) { - if (!adaptor_.is_open()) - { - return; - } - adaptor_.close(); - }); - CROW_LOG_DEBUG << this << " timer added: " << timer_cancel_key_.first << ' ' << timer_cancel_key_.second; - } + return; + } + adaptor_.close(); + }); + CROW_LOG_DEBUG << this << " timer added: " << timer_cancel_key_.first << ' ' << timer_cancel_key_.second; + } - private: - Adaptor adaptor_; - Handler* handler_; +private: + Adaptor adaptor_; + Handler* handler_; - boost::array buffer_; + boost::array buffer_; - HTTPParser parser_; - request req_; - response res; + HTTPParser parser_; + request req_; + response res; - bool close_connection_ = false; + bool close_connection_ = false; - const std::string& server_name_; - std::vector buffers_; + const std::string& server_name_; + std::vector buffers_; - std::string content_length_; - std::string date_str_; - std::string res_body_copy_; + 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_; + //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_{}; + 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_; - detail::context ctx_; + std::tuple* middlewares_; + detail::context ctx_; - std::function& get_cached_date_str; - detail::dumb_timer_queue& timer_queue; - }; + std::function& get_cached_date_str; + detail::dumb_timer_queue& timer_queue; +}; } diff --git a/include/crow/http_request.h b/include/crow/http_request.h index a70c29901..bd90f8a69 100644 --- a/include/crow/http_request.h +++ b/include/crow/http_request.h @@ -8,62 +8,62 @@ namespace crow { - template - inline const std::string& get_header_value(const T& headers, const std::string& key) +template +inline const std::string& get_header_value(const T& headers, const std::string& key) +{ + if (headers.count(key)) { - if (headers.count(key)) - { - return headers.find(key)->second; - } - static std::string empty; - return empty; + return headers.find(key)->second; } + static std::string empty; + return empty; +} - struct DetachHelper; +struct DetachHelper; - struct request - { - HTTPMethod method; - std::string raw_url; - std::string url; - query_string url_params; - ci_map headers; - std::string body; +struct request +{ + HTTPMethod method; + std::string raw_url; + std::string url; + query_string url_params; + ci_map headers; + std::string body; - void* middleware_context{}; - boost::asio::io_service* io_service{}; + void* middleware_context{}; + boost::asio::io_service* io_service{}; - request() - : method(HTTPMethod::Get) - { - } + request() + : method(HTTPMethod::Get) + { + } - request(HTTPMethod method, std::string raw_url, std::string url, query_string url_params, ci_map headers, std::string body) - : method(method), raw_url(std::move(raw_url)), url(std::move(url)), url_params(std::move(url_params)), headers(std::move(headers)), body(std::move(body)) - { - } + request(HTTPMethod method, std::string raw_url, std::string url, query_string url_params, ci_map headers, std::string body) + : method(method), raw_url(std::move(raw_url)), url(std::move(url)), url_params(std::move(url_params)), headers(std::move(headers)), body(std::move(body)) + { + } - void add_header(std::string key, std::string value) - { - headers.emplace(std::move(key), std::move(value)); - } + void add_header(std::string key, std::string value) + { + headers.emplace(std::move(key), std::move(value)); + } - const std::string& get_header_value(const std::string& key) const - { - return crow::get_header_value(headers, key); - } + const std::string& get_header_value(const std::string& key) const + { + return crow::get_header_value(headers, key); + } - template - void post(CompletionHandler handler) - { - io_service->post(handler); - } + template + void post(CompletionHandler handler) + { + io_service->post(handler); + } - template - void dispatch(CompletionHandler handler) - { - io_service->dispatch(handler); - } + template + void dispatch(CompletionHandler handler) + { + io_service->dispatch(handler); + } - }; +}; } diff --git a/include/crow/http_response.h b/include/crow/http_response.h index 794017479..8de3fded0 100644 --- a/include/crow/http_response.h +++ b/include/crow/http_response.h @@ -8,128 +8,105 @@ namespace crow { - template - class Connection; - struct response +template +class Connection; +struct response +{ + template + friend class crow::Connection; + + int code{200}; + std::string body; + + // `headers' stores HTTP headers. + ci_map headers; + + void set_header(std::string key, std::string value) + { + headers.erase(key); + headers.emplace(std::move(key), std::move(value)); + } + void add_header(std::string key, std::string value) + { + headers.emplace(std::move(key), std::move(value)); + } + + const std::string& get_header_value(const std::string& key) { + return crow::get_header_value(headers, key); + } + + response() {} + explicit response(int code) : code(code) {} + response(std::string body) : body(std::move(body)) {} + response(int code, std::string body): + code(code), body(std::move(body)) {} + response(response&& r) + { + *this = std::move(r); + } + + response& operator = (const response& r) = delete; + + response& operator = (response&& r) noexcept + { + body = std::move(r.body); + code = r.code; + headers = std::move(r.headers); + completed_ = r.completed_; + return *this; + } + + bool is_completed() const noexcept + { + return completed_; + } + + void clear() + { + body.clear(); + code = 200; + headers.clear(); + completed_ = false; + } + + void redirect(const std::string& location) + { + code = 301; + set_header("Location", location); + } + + void write(const std::string& body_part) + { + body += body_part; + } + + void end() + { + if (!completed_) { - template - friend class crow::Connection; - - int code{200}; - std::string body; - json::wvalue json_value; - - // `headers' stores HTTP headers. - ci_map headers; - - void set_header(std::string key, std::string value) - { - headers.erase(key); - headers.emplace(std::move(key), std::move(value)); - } - void add_header(std::string key, std::string value) - { - headers.emplace(std::move(key), std::move(value)); - } - - const std::string& get_header_value(const std::string& key) - { - return crow::get_header_value(headers, key); - } - - - response() {} - explicit response(int code) : code(code) {} - response(std::string body) : body(std::move(body)) {} - response(json::wvalue&& json_value) : json_value(std::move(json_value)) - { - json_mode(); - } - response(int code, std::string body) : code(code), body(std::move(body)) {} - response(const json::wvalue& json_value) : body(json::dump(json_value)) - { - json_mode(); - } - response(int code, const json::wvalue& json_value) : code(code), body(json::dump(json_value)) - { - json_mode(); - } - - response(response&& r) - { - *this = std::move(r); - } - - response& operator = (const response& r) = delete; - - response& operator = (response&& r) noexcept - { - body = std::move(r.body); - json_value = std::move(r.json_value); - code = r.code; - headers = std::move(r.headers); - completed_ = r.completed_; - return *this; - } - - bool is_completed() const noexcept - { - return completed_; - } - - void clear() - { - body.clear(); - json_value.clear(); - code = 200; - headers.clear(); - completed_ = false; - } - - void redirect(const std::string& location) - { - code = 301; - set_header("Location", location); - } - - void write(const std::string& body_part) - { - body += body_part; - } - - void end() - { - if (!completed_) - { - completed_ = true; - - if (complete_request_handler_) - { - complete_request_handler_(); - } - } - } - - void end(const std::string& body_part) - { - body += body_part; - end(); - } - - bool is_alive() - { - return is_alive_helper_ && is_alive_helper_(); - } - - private: - bool completed_{}; - std::function complete_request_handler_; - std::function is_alive_helper_; - - //In case of a JSON object, set the Content-Type header - void json_mode() - { - set_header("Content-Type", "application/json"); - } - }; + completed_ = true; + + if (complete_request_handler_) + { + complete_request_handler_(); + } + } + } + + void end(const std::string& body_part) + { + body += body_part; + end(); + } + + bool is_alive() + { + return is_alive_helper_ && is_alive_helper_(); + } + +private: + bool completed_{}; + std::function complete_request_handler_; + std::function is_alive_helper_; +}; } diff --git a/include/crow/query_string.h b/include/crow/query_string.h index ee61e30af..e74a5a46c 100644 --- a/include/crow/query_string.h +++ b/include/crow/query_string.h @@ -399,6 +399,10 @@ namespace crow return ret; } + bool empty() const { + return key_value_pairs_.empty(); + } + private: std::string url_; std::vector key_value_pairs_;