Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HEAD and OPTIONS handling #117

Merged
merged 8 commits into from
Apr 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/guides/routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ you can see the first `<int>` is defined as `a` and the second as `b`. If you we
##Methods
You can change the HTTP methods the route uses from just the default `GET` by using `method()`, your route macro should look like `CROW_ROUTE(app, "/add/<int>/<int>").methods(crow::HTTPMethod::GET, crow::HTTPMethod::PATCH)` or `CROW_ROUTE(app, "/add/<int>/<int>").methods("GET"_method, "PATCH"_method)`.

!!! note

Crow handles `HEAD` and `OPTIONS` methods automatically. So adding those to your handler has no effect.

##Handler
Basically a piece of code that gets executed whenever the client calls the associated route, usually in the form of a [lambda expression](https://en.cppreference.com/w/cpp/language/lambda). It can be as simple as `#!cpp ([](){return "Hello World"})`.<br><br>

Expand Down Expand Up @@ -66,4 +70,4 @@ class a : public crow::returnable
return this.as_string();
}
}
```
```
2 changes: 1 addition & 1 deletion include/crow/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ namespace crow
{
#ifndef CROW_DISABLE_STATIC_DIR
route<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)
([](const crow::request&, crow::response& res, std::string file_path_partial)
([](crow::response& res, std::string file_path_partial)
{
res.set_static_file_info(CROW_STATIC_DIRECTORY + file_path_partial);
res.end();
Expand Down
2 changes: 1 addition & 1 deletion include/crow/http_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ namespace crow

}

if (!res.headers.count("content-length"))
if (!res.manual_length_header && !res.headers.count("content-length"))
{
content_length_ = std::to_string(res.body.size());
static std::string content_length_tag = "Content-Length: ";
Expand Down
9 changes: 8 additions & 1 deletion include/crow/http_response.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ namespace crow
std::string body; ///< The actual payload containing the response data.
ci_map headers; ///< HTTP headers.
bool compressed = true; ///< If compression is enabled and this is false, the individual response will not be compressed.
bool is_head_response = false; ///< Whether this is a response to a HEAD request.
bool manual_length_header = false; ///< Whether Crow should automatically add a "Content-Length" header.

/// Set the value of an existing header in the response.
void set_header(std::string key, std::string value)
Expand Down Expand Up @@ -147,7 +149,12 @@ namespace crow
if (!completed_)
{
completed_ = true;

if (is_head_response)
{
set_header("Content-Length", std::to_string(body.size()));
body = "";
manual_length_header = true;
}
if (complete_request_handler_)
{
complete_request_handler_();
Expand Down
69 changes: 65 additions & 4 deletions include/crow/routing.h
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,13 @@ namespace crow
{
}

private:
///Check whether or not the trie is empty.
bool is_empty()
{
return nodes_.size() > 1;
}

private:
void optimizeNode(Node* node)
{
for(auto x : node->param_childrens)
Expand Down Expand Up @@ -675,7 +681,7 @@ namespace crow
optimizeNode(head());
}

public:
public:
void validate()
{
if (!head()->IsSimpleNode())
Expand Down Expand Up @@ -1000,6 +1006,7 @@ namespace crow
}
}

//TODO maybe add actual_method
template <typename Adaptor>
void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
{
Expand Down Expand Up @@ -1075,9 +1082,63 @@ namespace crow

void handle(const request& req, response& res)
{
HTTPMethod method_actual = req.method;
if (req.method >= HTTPMethod::InternalMethodCount)
return;
auto& per_method = per_methods_[static_cast<int>(req.method)];
else if (req.method == HTTPMethod::HEAD)
{
method_actual = HTTPMethod::GET;
res.is_head_response = true;
}
else if (req.method == HTTPMethod::OPTIONS)
{
std::string allow = "OPTIONS, HEAD, ";

if (req.url == "/*")
{
for(int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i ++)
{
if (per_methods_[i].trie.is_empty())
{
allow += method_name(static_cast<HTTPMethod>(i)) + ", ";
}
}
allow = allow.substr(0, allow.size()-2);
res = response(204);
res.set_header("Allow", allow);
res.manual_length_header = true;
res.end();
return;
}
else
{
for(int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i ++)
{
if (per_methods_[i].trie.find(req.url).first)
{
allow += method_name(static_cast<HTTPMethod>(i)) + ", ";
}
}
if (allow != "OPTIONS, HEAD, ")
{
allow = allow.substr(0, allow.size()-2);
res = response(204);
res.set_header("Allow", allow);
res.manual_length_header = true;
res.end();
return;
}
else
{
CROW_LOG_DEBUG << "Cannot match rules " << req.url;
res = response(404);
res.end();
return;
}
}
}

auto& per_method = per_methods_[static_cast<int>(method_actual)];
auto& trie = per_method.trie;
auto& rules = per_method.rules;

Expand All @@ -1091,7 +1152,7 @@ namespace crow
{
if (per_method.trie.find(req.url).first)
{
CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(req.method);
CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(method_actual);
res = response(405);
res.end();
return;
Expand Down
48 changes: 48 additions & 0 deletions tests/unittest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,54 @@ TEST_CASE("http_method")

CHECK(405 == res.code);
}

{
request req;
response res;

req.url = "/get_only";
req.method = "HEAD"_method;
app.handle(req, res);

CHECK(200 == res.code);
CHECK("" == res.body);
}

{
request req;
response res;

req.url = "/";
req.method = "OPTIONS"_method;
app.handle(req, res);

CHECK(204 == res.code);
CHECK("OPTIONS, HEAD, GET, POST" == res.get_header_value("Allow"));
}

{
request req;
response res;

req.url = "/does_not_exist";
req.method = "OPTIONS"_method;
app.handle(req, res);

CHECK(404 == res.code);
}

{
request req;
response res;

req.url = "/*";
req.method = "OPTIONS"_method;
app.handle(req, res);

CHECK(204 == res.code);
CHECK("OPTIONS, HEAD, GET, POST, PATCH, PURGE" == res.get_header_value("Allow"));

}
}

TEST_CASE("server_handling_error_request")
Expand Down