diff --git a/include/crow/app.h b/include/crow/app.h index 33c2ac3e1..851baa232 100644 --- a/include/crow/app.h +++ b/include/crow/app.h @@ -76,10 +76,24 @@ namespace crow router_.handle_upgrade(req, res, adaptor); } - /// Process the request and generate a response for it - void handle(request& req, response& res) + /// Process only the method and URL of a request and provide a route (or an error response) + std::unique_ptr handle_initial(request& req, response& res) { - router_.handle(req, res); + return router_.handle_initial(req, res); + } + + /// Process the fully parsed request and generate a response for it + void handle(request& req, response& res, std::unique_ptr& found) + { + router_.handle(req, res, *found); + } + + /// Process a fully parsed request from start to finish (primarily used for debugging) + void handle_full(request& req, response& res) + { + auto found = handle_initial(req, res); + if (std::get<0>(*found)) + handle(req, res, found); } /// Create a dynamic route using a rule (**Use CROW_ROUTE instead**) @@ -327,6 +341,9 @@ namespace crow } /// Non-blocking version of \ref run() + /// + /// The output from this method needs to be saved into a variable! + /// Otherwise the call will be made on the same thread. std::future run_async() { return std::async(std::launch::async, [&] { diff --git a/include/crow/common.h b/include/crow/common.h index 9b008215b..c28b68454 100644 --- a/include/crow/common.h +++ b/include/crow/common.h @@ -275,6 +275,9 @@ namespace crow return string_params[index]; } /// @endcond + + using routing_search_result = std::tuple, routing_params>; + using routing_handle_result = std::tuple, routing_params, HTTPMethod>; } // namespace crow // clang-format off diff --git a/include/crow/http_connection.h b/include/crow/http_connection.h index 9d4a2b433..17401ef78 100644 --- a/include/crow/http_connection.h +++ b/include/crow/http_connection.h @@ -48,6 +48,7 @@ namespace crow adaptor_(io_service, adaptor_ctx_), handler_(handler), parser_(this), + req_(parser_.req), server_name_(server_name), middlewares_(middlewares), get_cached_date_str(get_cached_date_str_f), @@ -94,10 +95,18 @@ namespace crow }); } + void handle_url() + { + routing_handle_result_ = handler_->handle_initial(req_, res); + // if no route is found for the request method, return the response without parsing or processing anything further. + if (!std::get<0>(*routing_handle_result_)) + complete_request(); + } + void handle_header() { // HTTP 1.1 Expect: 100-continue - if (parser_.http_major == 1 && parser_.http_minor == 1 && get_header_value(parser_.headers, "expect") == "100-continue") // Using the parser because the request isn't made yet. + if (req_.http_ver_major == 1 && req_.http_ver_minor == 1 && get_header_value(req_.headers, "expect") == "100-continue") // Using the parser because the request isn't made yet. { buffers_.clear(); static std::string expect_100_continue = "HTTP/1.1 100 Continue\r\n\r\n"; @@ -108,29 +117,27 @@ namespace crow void handle() { + // TODO(EDev): This should be looked into, it might be a good idea to add it to handle_url() and then restart the timer once everything passes cancel_deadline_timer(); bool is_invalid_request = false; add_keep_alive_ = false; - req_ = std::move(parser_.to_request()); - request& req = req_; + req_.remote_ip_address = adaptor_.remote_endpoint().address().to_string(); - req.remote_ip_address = adaptor_.remote_endpoint().address().to_string(); + add_keep_alive_ = req_.keep_alive; + close_connection_ = req_.close_connection; - add_keep_alive_ = req.keep_alive; - close_connection_ = req.close_connection; - - if (req.check_version(1, 1)) // HTTP/1.1 + if (req_.check_version(1, 1)) // HTTP/1.1 { - if (!req.headers.count("host")) + if (!req_.headers.count("host")) { is_invalid_request = true; res = response(400); } - if (req.upgrade) + if (req_.upgrade) { // h2 or h2c headers - if (req.get_header_value("upgrade").substr(0, 2) == "h2") + if (req_.get_header_value("upgrade").substr(0, 2) == "h2") { // TODO(ipkn): HTTP/2 // currently, ignore upgrade header @@ -138,13 +145,13 @@ namespace crow else { close_connection_ = true; - handler_->handle_upgrade(req, res, std::move(adaptor_)); + handler_->handle_upgrade(req_, res, std::move(adaptor_)); return; } } } - CROW_LOG_INFO << "Request: " << utility::lexical_cast(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << (char)(req.http_ver_major + '0') << "." << (char)(req.http_ver_minor + '0') << ' ' << method_name(req.method) << " " << req.url; + CROW_LOG_INFO << "Request: " << utility::lexical_cast(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << (char)(req_.http_ver_major + '0') << "." << (char)(req_.http_ver_minor + '0') << ' ' << method_name(req_.method) << " " << req_.url; need_to_call_after_handlers_ = false; @@ -156,12 +163,12 @@ namespace crow }; ctx_ = detail::context(); - req.middleware_context = static_cast(&ctx_); - req.middleware_container = static_cast(middlewares_); - req.io_service = &adaptor_.get_io_service(); + req_.middleware_context = static_cast(&ctx_); + req_.middleware_container = static_cast(middlewares_); + req_.io_service = &adaptor_.get_io_service(); detail::middleware_call_helper({}, *middlewares_, req, res, ctx_); + 0, decltype(ctx_), decltype(*middlewares_)>({}, *middlewares_, req_, res, ctx_); if (!res.completed_) { @@ -169,7 +176,7 @@ namespace crow this->complete_request(); }; need_to_call_after_handlers_ = true; - handler_->handle(req, res); + handler_->handle(req_, res, routing_handle_result_); if (add_keep_alive_) res.set_header("connection", "Keep-Alive"); } @@ -593,7 +600,8 @@ namespace crow std::array buffer_; HTTPParser parser_; - request req_; + std::unique_ptr routing_handle_result_; + request& req_; response res; bool close_connection_ = false; diff --git a/include/crow/http_parser_merged.h b/include/crow/http_parser_merged.h index 737172636..547ed68ba 100644 --- a/include/crow/http_parser_merged.h +++ b/include/crow/http_parser_merged.h @@ -98,6 +98,7 @@ enum http_connection_flags // This is basically 7 booleans placed into 1 integer \ /* Callback-related errors */ \ CROW_XX(CB_message_begin, "the on_message_begin callback failed") \ + CROW_XX(CB_method, "the on_method callback failed") \ CROW_XX(CB_url, "the \"on_url\" callback failed") \ CROW_XX(CB_header_field, "the \"on_header_field\" callback failed") \ CROW_XX(CB_header_value, "the \"on_header_value\" callback failed") \ @@ -179,6 +180,7 @@ enum http_errno { struct http_parser_settings { http_cb on_message_begin; + http_cb on_method; http_data_cb on_url; http_data_cb on_header_field; http_data_cb on_header_value; @@ -855,6 +857,8 @@ reexecute: goto error; } + CROW_CALLBACK_NOTIFY_NOADVANCE(method); + ++parser->index; break; } diff --git a/include/crow/http_request.h b/include/crow/http_request.h index a85eee824..6be82e7ae 100644 --- a/include/crow/http_request.h +++ b/include/crow/http_request.h @@ -29,12 +29,14 @@ namespace crow HTTPMethod method; std::string raw_url; ///< The full URL containing the `?` and URL parameters. std::string url; ///< The endpoint without any parameters. - query_string url_params; ///< The parameters associated with the request. (everything after the `?`) + query_string url_params; ///< The parameters associated with the request. (everything after the `?` in the URL) ci_map headers; std::string body; std::string remote_ip_address; ///< The IP address from which the request was sent. unsigned char http_ver_major, http_ver_minor; - bool keep_alive, close_connection, upgrade; + bool keep_alive, ///< Whether or not the server should send a `connection: Keep-Alive` header to the client. + close_connection, ///< Whether or not the server should shut down the TCP connection once a response is sent. + upgrade; ///< Whether or noth the server should change the HTTP connection to a different connection. void* middleware_context{}; void* middleware_container{}; diff --git a/include/crow/parser.h b/include/crow/parser.h index 095565b32..001951f9d 100644 --- a/include/crow/parser.h +++ b/include/crow/parser.h @@ -22,10 +22,22 @@ namespace crow self->clear(); return 0; } + static int on_method(http_parser* self_) + { + HTTPParser* self = static_cast(self_); + self->req.method = static_cast(self->method); + + return 0; + } static int on_url(http_parser* self_, const char* at, size_t length) { HTTPParser* self = static_cast(self_); - self->raw_url.insert(self->raw_url.end(), at, at + length); + self->req.raw_url.insert(self->req.raw_url.end(), at, at + length); + self->req.url_params = query_string(self->req.raw_url); + self->req.url = self->req.raw_url.substr(0, self->qs_point != 0 ? self->qs_point : std::string::npos); + + self->process_url(); + return 0; } static int on_header_field(http_parser* self_, const char* at, size_t length) @@ -36,7 +48,7 @@ namespace crow case 0: if (!self->header_value.empty()) { - self->headers.emplace(std::move(self->header_field), std::move(self->header_value)); + self->req.headers.emplace(std::move(self->header_field), std::move(self->header_value)); } self->header_field.assign(at, at + length); self->header_building_state = 1; @@ -67,7 +79,7 @@ namespace crow HTTPParser* self = static_cast(self_); if (!self->header_field.empty()) { - self->headers.emplace(std::move(self->header_field), std::move(self->header_value)); + self->req.headers.emplace(std::move(self->header_field), std::move(self->header_value)); } self->set_connection_parameters(); @@ -78,17 +90,13 @@ namespace crow static int on_body(http_parser* self_, const char* at, size_t length) { HTTPParser* self = static_cast(self_); - self->body.insert(self->body.end(), at, at + length); + self->req.body.insert(self->req.body.end(), at, at + length); return 0; } static int on_message_complete(http_parser* self_) { HTTPParser* self = static_cast(self_); - // url params - self->url = self->raw_url.substr(0, self->qs_point != 0 ? self->qs_point : std::string::npos); - self->url_params = query_string(self->raw_url); - self->process_message(); return 0; } @@ -104,6 +112,7 @@ namespace crow { const static http_parser_settings settings_{ on_message_begin, + on_method, on_url, on_header_field, on_header_value, @@ -127,19 +136,16 @@ namespace crow void clear() { - url.clear(); - raw_url.clear(); + req = crow::request(); header_field.clear(); header_value.clear(); - headers.clear(); - url_params.clear(); - body.clear(); header_building_state = 0; qs_point = 0; - http_major = 0; - http_minor = 0; - keep_alive = false; - close_connection = false; + } + + inline void process_url() + { + handler_->handle_url(); } inline void process_header() @@ -154,36 +160,32 @@ namespace crow inline void set_connection_parameters() { + req.http_ver_major = http_major; + req.http_ver_minor = http_minor; + //NOTE(EDev): it seems that the problem is with crow's policy on closing the connection for HTTP_VERSION < 1.0, the behaviour for that in crow is "don't close the connection, but don't send a keep-alive either" // HTTP1.1 = always send keep_alive, HTTP1.0 = only send if header exists, HTTP?.? = never send - keep_alive = (http_major == 1 && http_minor == 0) ? - ((flags & F_CONNECTION_KEEP_ALIVE) ? true : false) : - ((http_major == 1 && http_minor == 1) ? true : false); + req.keep_alive = (http_major == 1 && http_minor == 0) ? + ((flags & F_CONNECTION_KEEP_ALIVE) ? true : false) : + ((http_major == 1 && http_minor == 1) ? true : false); // HTTP1.1 = only close if close header exists, HTTP1.0 = always close unless keep_alive header exists, HTTP?.?= never close - close_connection = (http_major == 1 && http_minor == 0) ? - ((flags & F_CONNECTION_KEEP_ALIVE) ? false : true) : - ((http_major == 1 && http_minor == 1) ? ((flags & F_CONNECTION_CLOSE) ? true : false) : false); + req.close_connection = (http_major == 1 && http_minor == 0) ? + ((flags & F_CONNECTION_KEEP_ALIVE) ? false : true) : + ((http_major == 1 && http_minor == 1) ? ((flags & F_CONNECTION_CLOSE) ? true : false) : false); + req.upgrade = static_cast(upgrade); } - /// Take the parsed HTTP request data and convert it to a \ref crow.request - request to_request() const - { - return request{static_cast(method), std::move(raw_url), std::move(url), std::move(url_params), std::move(headers), std::move(body), http_major, http_minor, keep_alive, close_connection, static_cast(upgrade)}; - } - - std::string raw_url; - std::string url; + /// The final request that this parser outputs. + /// + /// Data parsed is put directly into this object as soon as the related callback returns. (e.g. the request will have the cooorect method as soon as on_method() returns) + request req; + private: int header_building_state = 0; std::string header_field; std::string header_value; - ci_map headers; - query_string url_params; ///< What comes after the `?` in the URL. - std::string body; - bool keep_alive; ///< Whether or not the server should send a `connection: Keep-Alive` header to the client. - bool close_connection; ///< Whether or not the server should shut down the TCP connection once a response is sent. Handler* handler_; ///< This is currently an HTTP connection object (\ref crow.Connection). }; diff --git a/include/crow/routing.h b/include/crow/routing.h index 8ad887bc5..db361cb24 100644 --- a/include/crow/routing.h +++ b/include/crow/routing.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -773,27 +773,33 @@ namespace crow switch (node->param) { case ParamType::INT: - CROW_LOG_DEBUG << std::string(2 * level, ' ') << ""; + CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ " + << ""; break; case ParamType::UINT: - CROW_LOG_DEBUG << std::string(2 * level, ' ') << ""; + CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ " + << ""; break; case ParamType::DOUBLE: - CROW_LOG_DEBUG << std::string(2 * level, ' ') << ""; + CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ " + << ""; break; case ParamType::STRING: - CROW_LOG_DEBUG << std::string(2 * level, ' ') << ""; + CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ " + << ""; break; case ParamType::PATH: - CROW_LOG_DEBUG << std::string(2 * level, ' ') << ""; + CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ " + << ""; break; default: - CROW_LOG_DEBUG << std::string(2 * level, ' ') << ""; + CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ " + << ""; break; } } else - CROW_LOG_DEBUG << std::string(2 * level, ' ') << node->key; + CROW_LOG_DEBUG << std::string(3 * level, ' ') << "└➝ " << node->key; for (auto& child : node->children) { @@ -804,7 +810,7 @@ namespace crow public: void debug_print() { - CROW_LOG_DEBUG << "HEAD"; + CROW_LOG_DEBUG << "└➙ ROOT"; for (auto& child : head_.children) debug_node_print(child, 1); } @@ -817,7 +823,7 @@ namespace crow } //Rule_index, Blueprint_index, routing_params - std::tuple, routing_params> find(const std::string& req_url, const Node* node = nullptr, unsigned pos = 0, routing_params* params = nullptr, std::vector* blueprints = nullptr) const + routing_search_result find(const std::string& req_url, const Node* node = nullptr, unsigned pos = 0, routing_params* params = nullptr, std::vector* blueprints = nullptr) const { //start params as an empty struct routing_params empty; @@ -1500,7 +1506,7 @@ namespace crow } /// Is used to handle errors, you insert the error code, found route, request, and response. and it'll either call the appropriate catchall route (considering the blueprint system) and send you a status string (which is mainly used for debug messages), or just set the response code to the proper error code. - std::string get_error(unsigned short code, std::tuple, routing_params>& found, const request& req, response& res) + std::string get_error(unsigned short code, routing_search_result& found, const request& req, response& res) { res.code = code; std::vector bps_found; @@ -1530,19 +1536,36 @@ namespace crow return std::string(); } - template - void handle(request& req, response& res) + std::unique_ptr handle_initial(request& req, response& res) { HTTPMethod method_actual = req.method; - if (req.method >= HTTPMethod::InternalMethodCount) - return; + + std::unique_ptr found{new routing_handle_result}; // This is always returned to avoid a null pointer dereference. + routing_search_result search_result; + + // NOTE(EDev): This most likely will never run since the parser should handle this situation and close the connection before it gets here. + if (CROW_UNLIKELY(req.method >= HTTPMethod::InternalMethodCount)) + return found; else if (req.method == HTTPMethod::Head) { + search_result = per_methods_[static_cast(method_actual)].trie.find(req.url); // support HEAD requests using GET if not defined as method for the requested URL - if (!std::get<0>(per_methods_[static_cast(HTTPMethod::Head)].trie.find(req.url))) + if (!std::get<0>(search_result)) + { method_actual = HTTPMethod::Get; + search_result = per_methods_[static_cast(method_actual)].trie.find(req.url); + if (!std::get<0>(search_result)) // If a route is still not found, return a 404 without executing the rest of the HEAD specific code. + { + CROW_LOG_DEBUG << "Cannot match rules " << req.url; + res = response(404); //TODO(EDev): Should this redirect to catchall? + res.end(); + return found; + } + } res.skip_body = true; + *found = routing_handle_result(std::get<0>(search_result), std::get<1>(search_result), std::get<2>(search_result), method_actual); + return found; } else if (req.method == HTTPMethod::Options) { @@ -1564,7 +1587,8 @@ namespace crow res = response(204); res.set_header("Allow", allow); res.end(); - return; + *found = routing_handle_result(std::get<0>(search_result), std::get<1>(search_result), std::get<2>(search_result), method_actual); + return found; } else { @@ -1587,45 +1611,53 @@ namespace crow res = response(204); res.set_header("Allow", allow); res.end(); - return; + *found = routing_handle_result(std::get<0>(search_result), std::get<1>(search_result), std::get<2>(search_result), method_actual); + return found; } else { CROW_LOG_DEBUG << "Cannot match rules " << req.url; - res = response(404); + res = response(404); //TODO(EDev): Should this redirect to catchall? res.end(); - return; + return found; } } } - - auto& per_method = per_methods_[static_cast(method_actual)]; - auto& trie = per_method.trie; - auto& rules = per_method.rules; - - auto found = trie.find(req.url); - - unsigned rule_index = std::get<0>(found); - - if (!rule_index) + else // Every request that isn't a HEAD or OPTIONS request { - for (auto& per_method : per_methods_) + search_result = per_methods_[static_cast(method_actual)].trie.find(req.url); +// TODO(EDev): maybe ending the else here would allow the requests coming from above (after removing the return statement) to be checked on whether they actually point to a route + if (!std::get<0>(search_result)) { - if (std::get<0>(per_method.trie.find(req.url))) //Route found, but in another method + for (auto& per_method : per_methods_) { - const std::string error_message(get_error(405, found, req, res)); - CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(method_actual) << ". " << error_message; - res.end(); - return; + if (std::get<0>(per_method.trie.find(req.url))) //Route found, but in another method + { + const std::string error_message(get_error(405, search_result, req, res)); + CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(method_actual) << ". " << error_message; + res.end(); + return found; + } } - } - //Route does not exist anywhere + //Route does not exist anywhere - const std::string error_message(get_error(404, found, req, res)); - CROW_LOG_DEBUG << "Cannot match rules " << req.url << ". " << error_message; - res.end(); - return; + const std::string error_message(get_error(404, search_result, req, res)); + CROW_LOG_DEBUG << "Cannot match rules " << req.url << ". " << error_message; + res.end(); + return found; + } + + *found = routing_handle_result(std::get<0>(search_result), std::get<1>(search_result), std::get<2>(search_result), method_actual); + return found; } + } + + template + void handle(request& req, response& res, routing_handle_result found) + { + HTTPMethod method_actual = std::get<3>(found); + auto& rules = per_methods_[static_cast(method_actual)].rules; + unsigned rule_index = std::get<0>(found); if (rule_index >= rules.size()) throw std::runtime_error("Trie internal structure corrupted!"); @@ -1719,8 +1751,12 @@ namespace crow { for (int i = 0; i < static_cast(HTTPMethod::InternalMethodCount); i++) { - CROW_LOG_DEBUG << method_name(static_cast(i)); - per_methods_[i].trie.debug_print(); + Trie& trie_ = per_methods_[i].trie; + if (!trie_.is_empty()) + { + CROW_LOG_DEBUG << method_name(static_cast(i)); + trie_.debug_print(); + } } } diff --git a/tests/unittest.cpp b/tests/unittest.cpp index 77f009ee8..b12eb57d7 100644 --- a/tests/unittest.cpp +++ b/tests/unittest.cpp @@ -116,7 +116,7 @@ TEST_CASE("PathRouting") req.url = "/file"; - app.handle(req, res); + app.handle_full(req, res); CHECK(200 == res.code); } @@ -126,7 +126,7 @@ TEST_CASE("PathRouting") req.url = "/file/"; - app.handle(req, res); + app.handle_full(req, res); CHECK(404 == res.code); } { @@ -135,7 +135,7 @@ TEST_CASE("PathRouting") req.url = "/path"; - app.handle(req, res); + app.handle_full(req, res); CHECK(404 != res.code); } { @@ -144,7 +144,7 @@ TEST_CASE("PathRouting") req.url = "/path/"; - app.handle(req, res); + app.handle_full(req, res); CHECK(200 == res.code); } } // PathRouting @@ -198,7 +198,7 @@ TEST_CASE("RoutingTest") req.url = "/-1"; - app.handle(req, res); + app.handle_full(req, res); CHECK(404 == res.code); } @@ -209,7 +209,7 @@ TEST_CASE("RoutingTest") req.url = "/0/1001999"; - app.handle(req, res); + app.handle_full(req, res); CHECK(200 == res.code); @@ -222,7 +222,7 @@ TEST_CASE("RoutingTest") req.url = "/1/-100/1999"; - app.handle(req, res); + app.handle_full(req, res); CHECK(200 == res.code); @@ -236,7 +236,7 @@ TEST_CASE("RoutingTest") req.url = "/4/5000/3/-2.71828/hellhere"; req.add_header("TestHeader", "Value"); - app.handle(req, res); + app.handle_full(req, res); CHECK(200 == res.code); @@ -252,7 +252,7 @@ TEST_CASE("RoutingTest") req.url = "/5/-5/999/3.141592/hello_there/a/b/c/d"; req.add_header("TestHeader", "Value"); - app.handle(req, res); + app.handle_full(req, res); CHECK(200 == res.code); @@ -343,7 +343,7 @@ TEST_CASE("http_method") response res; req.url = "/"; - app.handle(req, res); + app.handle_full(req, res); CHECK("2" == res.body); } @@ -353,7 +353,7 @@ TEST_CASE("http_method") req.url = "/"; req.method = "POST"_method; - app.handle(req, res); + app.handle_full(req, res); CHECK("1" == res.body); } @@ -364,7 +364,7 @@ TEST_CASE("http_method") req.url = "/head_only"; req.method = "HEAD"_method; - app.handle(req, res); + app.handle_full(req, res); CHECK(202 == res.code); CHECK("" == res.body); @@ -375,7 +375,7 @@ TEST_CASE("http_method") response res; req.url = "/get_only"; - app.handle(req, res); + app.handle_full(req, res); CHECK("get" == res.body); } @@ -386,7 +386,7 @@ TEST_CASE("http_method") req.url = "/patch_only"; req.method = "PATCH"_method; - app.handle(req, res); + app.handle_full(req, res); CHECK("patch" == res.body); } @@ -397,7 +397,7 @@ TEST_CASE("http_method") req.url = "/purge_only"; req.method = "PURGE"_method; - app.handle(req, res); + app.handle_full(req, res); CHECK("purge" == res.body); } @@ -408,7 +408,7 @@ TEST_CASE("http_method") req.url = "/get_only"; req.method = "POST"_method; - app.handle(req, res); + app.handle_full(req, res); CHECK("get" != res.body); } @@ -419,7 +419,7 @@ TEST_CASE("http_method") req.url = "/get_only"; req.method = "POST"_method; - app.handle(req, res); + app.handle_full(req, res); CHECK(405 == res.code); } @@ -430,7 +430,7 @@ TEST_CASE("http_method") req.url = "/get_only"; req.method = "HEAD"_method; - app.handle(req, res); + app.handle_full(req, res); CHECK(200 == res.code); CHECK("" == res.body); @@ -442,7 +442,7 @@ TEST_CASE("http_method") req.url = "/"; req.method = "OPTIONS"_method; - app.handle(req, res); + app.handle_full(req, res); CHECK(204 == res.code); CHECK("OPTIONS, HEAD, GET, POST" == res.get_header_value("Allow")); @@ -454,7 +454,7 @@ TEST_CASE("http_method") req.url = "/does_not_exist"; req.method = "OPTIONS"_method; - app.handle(req, res); + app.handle_full(req, res); CHECK(404 == res.code); } @@ -465,7 +465,7 @@ TEST_CASE("http_method") req.url = "/*"; req.method = "OPTIONS"_method; - app.handle(req, res); + app.handle_full(req, res); CHECK(204 == res.code); CHECK("OPTIONS, HEAD, GET, POST, PATCH, PURGE" == res.get_header_value("Allow")); @@ -477,7 +477,7 @@ TEST_CASE("http_method") req.url = "/head_only"; req.method = "OPTIONS"_method; - app.handle(req, res); + app.handle_full(req, res); CHECK(204 == res.code); CHECK("OPTIONS, HEAD" == res.get_header_value("Allow")); @@ -1259,7 +1259,7 @@ TEST_CASE("TemplateRouting") req.url = "/temp"; - app.handle(req, res); + app.handle_full(req, res); CHECK("attack of killer tomatoes" == res.body); CHECK("text/html" == crow::get_header_value(res.headers, "Content-Type")); @@ -1451,14 +1451,18 @@ TEST_CASE("middleware_context") { auto& out = test_middleware_context_vector; CHECK(1 == x); - CHECK(7 == out.size()); - CHECK("1 before" == out[0]); - CHECK("2 before" == out[1]); - CHECK("3 before" == out[2]); - CHECK("handle" == out[3]); - CHECK("3 after" == out[4]); - CHECK("2 after" == out[5]); - CHECK("1 after" == out[6]); + bool cond = 7 == out.size(); + CHECK(cond); + if (cond) + { + CHECK("1 before" == out[0]); + CHECK("2 before" == out[1]); + CHECK("3 before" == out[2]); + CHECK("handle" == out[3]); + CHECK("3 after" == out[4]); + CHECK("2 after" == out[5]); + CHECK("1 after" == out[6]); + } } std::string sendmsg2 = "GET /break\r\n\r\n"; { @@ -1473,11 +1477,15 @@ TEST_CASE("middleware_context") } { auto& out = test_middleware_context_vector; - CHECK(4 == out.size()); - CHECK("1 before" == out[0]); - CHECK("2 before" == out[1]); - CHECK("2 after" == out[2]); - CHECK("1 after" == out[3]); + bool cond = 4 == out.size(); + CHECK(cond); + if (cond) + { + CHECK("1 before" == out[0]); + CHECK("2 before" == out[1]); + CHECK("2 after" == out[2]); + CHECK("1 after" == out[3]); + } } app.stop(); } // middleware_context @@ -1612,14 +1620,18 @@ TEST_CASE("middleware_blueprint") } { auto& out = test_middleware_context_vector; - CHECK(7 == out.size()); - CHECK("1 before" == out[0]); - CHECK("2 before" == out[1]); - CHECK("3 before" == out[2]); - CHECK("handle" == out[3]); - CHECK("3 after" == out[4]); - CHECK("2 after" == out[5]); - CHECK("1 after" == out[6]); + bool cond = 7 == out.size(); + CHECK(cond); + if (cond) + { + CHECK("1 before" == out[0]); + CHECK("2 before" == out[1]); + CHECK("3 before" == out[2]); + CHECK("handle" == out[3]); + CHECK("3 after" == out[4]); + CHECK("2 after" == out[5]); + CHECK("1 after" == out[6]); + } } { asio::ip::tcp::socket c(is); @@ -1633,11 +1645,15 @@ TEST_CASE("middleware_blueprint") } { auto& out = test_middleware_context_vector; - CHECK(4 == out.size()); - CHECK("1 before" == out[0]); - CHECK("2 before" == out[1]); - CHECK("2 after" == out[2]); - CHECK("1 after" == out[3]); + bool cond = 4 == out.size(); + CHECK(cond); + if (cond) + { + CHECK("1 before" == out[0]); + CHECK("2 before" == out[1]); + CHECK("2 after" == out[2]); + CHECK("1 after" == out[3]); + } } app.stop(); @@ -2200,28 +2216,28 @@ TEST_CASE("route_dynamic") request req; response res; req.url = "/"; - app.handle(req, res); + app.handle_full(req, res); CHECK(x == 2); } { request req; response res; req.url = "/set_int/42"; - app.handle(req, res); + app.handle_full(req, res); CHECK(x == 42); } { request req; response res; req.url = "/set5"; - app.handle(req, res); + app.handle_full(req, res); CHECK(x == 5); } { request req; response res; req.url = "/set4"; - app.handle(req, res); + app.handle_full(req, res); CHECK(x == 4); } } // route_dynamic @@ -2267,7 +2283,7 @@ TEST_CASE("multipart") req.add_header("Content-Type", "multipart/form-data; boundary=CROW-BOUNDARY"); req.body = test_string; - app.handle(req, res); + app.handle_full(req, res); CHECK(test_string == res.body); @@ -2286,7 +2302,7 @@ TEST_CASE("multipart") req.add_header("Content-Type", "multipart/form-data; boundary=\"CROW-BOUNDARY\""); req.body = test_string; - app.handle(req, res); + app.handle_full(req, res); CHECK(test_string == res.body); } @@ -2330,7 +2346,7 @@ TEST_CASE("send_file") req.url = "/jpg2"; - app.handle(req, res); + app.handle_full(req, res); CHECK(404 == res.code); @@ -2344,7 +2360,7 @@ TEST_CASE("send_file") req.url = "/jpg"; req.http_ver_major = 1; - app.handle(req, res); + app.handle_full(req, res); CHECK(200 == res.code); @@ -2365,7 +2381,7 @@ TEST_CASE("send_file") req.url = "/filewith.badext"; req.http_ver_major = 1; - CHECK_NOTHROW(app.handle(req, res)); + CHECK_NOTHROW(app.handle_full(req, res)); CHECK(200 == res.code); CHECK(res.headers.count("Content-Type")); if (res.headers.count("Content-Type")) @@ -2923,7 +2939,7 @@ TEST_CASE("catchall") req.url = "/place"; - app.handle(req, res); + app.handle_full(req, res); CHECK(200 == res.code); } @@ -2934,7 +2950,7 @@ TEST_CASE("catchall") req.url = "/another_place"; - app.handle(req, res); + app.handle_full(req, res); CHECK(404 == res.code); CHECK("!place" == res.body); @@ -2946,7 +2962,7 @@ TEST_CASE("catchall") req.url = "/place"; - app2.handle(req, res); + app2.handle_full(req, res); CHECK(200 == res.code); } @@ -2957,7 +2973,7 @@ TEST_CASE("catchall") req.url = "/another_place"; - app2.handle(req, res); + app2.handle_full(req, res); CHECK(404 == res.code); } @@ -3004,7 +3020,7 @@ TEST_CASE("blueprint") req.url = "/bp_prefix/bp2/hello"; - app.handle(req, res); + app.handle_full(req, res); CHECK("Hello world!" == res.body); } @@ -3015,7 +3031,7 @@ TEST_CASE("blueprint") req.url = "/bp_prefix_second/hello"; - app.handle(req, res); + app.handle_full(req, res); CHECK("Hello world!" == res.body); } @@ -3026,7 +3042,7 @@ TEST_CASE("blueprint") req.url = "/bp_prefix/bp2/bp3/hi"; - app.handle(req, res); + app.handle_full(req, res); CHECK("Hi world!" == res.body); } @@ -3037,7 +3053,7 @@ TEST_CASE("blueprint") req.url = "/bp_prefix/nonexistent"; - app.handle(req, res); + app.handle_full(req, res); CHECK(404 == res.code); } @@ -3048,7 +3064,7 @@ TEST_CASE("blueprint") req.url = "/bp_prefix_second/nonexistent"; - app.handle(req, res); + app.handle_full(req, res); CHECK(404 == res.code); } @@ -3059,7 +3075,7 @@ TEST_CASE("blueprint") req.url = "/bp_prefix/bp2/nonexistent"; - app.handle(req, res); + app.handle_full(req, res); CHECK(200 == res.code); CHECK("WRONG!!" == res.body); @@ -3071,7 +3087,7 @@ TEST_CASE("blueprint") req.url = "/bp_prefix/bp2/bp3/nonexistent"; - app.handle(req, res); + app.handle_full(req, res); CHECK(200 == res.code); CHECK("WRONG!!" == res.body);