Skip to content

Add extra log when status code is not defined. #434

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

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
104 changes: 100 additions & 4 deletions docs/guides/routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Using `/hello` means the client will need to access `http://example.com/hello` i
A path can have parameters, for example `/hello/<int>` will allow a client to input an int into the url which will be in the handler (something like `http://example.com/hello/42`).<br>
Parameters can be `<int>`, `<uint>`, `<double>`, `<string>`, or `<path>`.<br>
It's worth noting that the parameters also need to be defined in the handler, an example of using parameters would be to add 2 numbers based on input:
```cpp
```cpp
CROW_ROUTE(app, "/add/<int>/<int>")
([](int a, int b)
{
Expand All @@ -27,6 +27,52 @@ You can change the HTTP methods the route uses from just the default `GET` by us

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

Crow defines the following methods:
```
DELETE
GET
HEAD
POST
PUT

CONNECT
OPTIONS
TRACE

PATCH
PURGE

COPY
LOCK
MKCOL
MOVE
PROPFIND
PROPPATCH
SEARCH
UNLOCK
BIND
REBIND
UNBIND
ACL

REPORT
MKACTIVITY
CHECKOUT
MERGE

SEARCH
NOTIFY
SUBSCRIBE
UNSUBSCRIBE

MKCALENDAR

LINK
UNLINK

SOURCE
```

## 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 All @@ -45,6 +91,56 @@ Please note that in order to return a response defined as a parameter you'll nee
Alternatively, you can define the response in the body and return it (`#!cpp ([](){return crow::response()})`).<br>

For more information on `crow::response` go [here](../../reference/structcrow_1_1response.html).<br><br>

Crow defines the following status codes:
```
100 Continue
101 Switching Protocols

200 OK
201 Created
202 Accepted
203 Non-Authoritative Information
204 No Content
205 Reset Content
206 Partial Content

300 Multiple Choices
301 Moved Permanently
302 Found
303 See Other
304 Not Modified
307 Temporary Redirect
308 Permanent Redirect

400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
405 Method Not Allowed
407 Proxy Authentication Required
409 Conflict
410 Gone
413 Payload Too Large
415 Unsupported Media Type
416 Range Not Satisfiable
417 Expectation Failed
428 Precondition Required
429 Too Many Requests
451 Unavailable For Legal Reasons

500 Internal Server Error
501 Not Implemented
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout
506 Variant Also Negotiates
```

!!! note

If your status code is not defined in the list above (e.g. `crow::response(123)`) Crow will return `500 Internal Server Error` instead.


### Return statement
A `crow::response` is very strictly tied to a route. If you can have something in a response constructor, you can return it in a handler.<br><br>
Expand All @@ -60,14 +156,14 @@ to use the returnable class, you only need your class to publicly extend `crow::

Your class should look like the following:
```cpp
class a : public crow::returnable
class a : public crow::returnable
{
a() : returnable("text/plain"){};

...
...
...

std::string dump() override
{
return this.as_string();
Expand Down
10 changes: 7 additions & 3 deletions include/crow/http_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,16 @@ namespace crow
buffers_.reserve(4 * (res.headers.size() + 5) + 3);

if (!statusCodes.count(res.code))
res.code = 500;
{
auto& status = statusCodes.find(res.code)->second;
buffers_.emplace_back(status.data(), status.size());
CROW_LOG_WARNING << this << " status code "
<< "(" << res.code << ")"
<< " not defined, returning 500 instead";
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);

Expand Down
74 changes: 70 additions & 4 deletions tests/unittest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,61 @@ TEST_CASE("multi_server")
app2.stop();
} // multi_server


TEST_CASE("undefined_status_code")
{
SimpleApp app;
CROW_ROUTE(app, "/get123")
([] {
//this status does not exists statusCodes map defined in include/crow/http_connection.h
const int undefinedStatusCode = 123;
return response(undefinedStatusCode, "this should return 500");
});

CROW_ROUTE(app, "/get200")
([] {
return response(200, "ok");
});

auto _ = app.bindaddr(LOCALHOST_ADDRESS).port(45471).run_async();
app.wait_for_server_start();

asio::io_service is;
auto sendRequestAndGetStatusCode = [&](const std::string& route) -> unsigned {
asio::ip::tcp::socket socket(is);
socket.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), app.port()));

boost::asio::streambuf request;
std::ostream request_stream(&request);
request_stream << "GET " << route << " HTTP/1.0\r\n";
request_stream << "Host: " << LOCALHOST_ADDRESS << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";

// Send the request.
boost::asio::write(socket, request);

boost::asio::streambuf response;
boost::asio::read_until(socket, response, "\r\n");

std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
unsigned status_code = 0;
response_stream >> status_code;

return status_code;
};

unsigned statusCode = sendRequestAndGetStatusCode("/get200");
CHECK(statusCode == 200);

statusCode = sendRequestAndGetStatusCode("/get123");
CHECK(statusCode == 500);

app.stop();
} // undefined_status_code

TEST_CASE("json_read")
{
{
Expand Down Expand Up @@ -2025,8 +2080,14 @@ TEST_CASE("send_file")
app.handle(req, res);

CHECK(200 == res.code);
CHECK("image/jpeg" == res.headers.find("Content-Type")->second);
CHECK(to_string(statbuf_cat.st_size) == res.headers.find("Content-Length")->second);

CHECK(res.headers.count("Content-Type"));
if (res.headers.count("Content-Type"))
CHECK("image/jpeg" == res.headers.find("Content-Type")->second);

CHECK(res.headers.count("Content-Length"));
if (res.headers.count("Content-Length"))
CHECK(to_string(statbuf_cat.st_size) == res.headers.find("Content-Length")->second);
}

//Unknown extension check
Expand All @@ -2039,8 +2100,13 @@ TEST_CASE("send_file")

CHECK_NOTHROW(app.handle(req, res));
CHECK(200 == res.code);
CHECK("text/plain" == res.headers.find("Content-Type")->second);
CHECK(to_string(statbuf_badext.st_size) == res.headers.find("Content-Length")->second);
CHECK(res.headers.count("Content-Type"));
if (res.headers.count("Content-Type"))
CHECK("text/plain" == res.headers.find("Content-Type")->second);

CHECK(res.headers.count("Content-Length"));
if (res.headers.count("Content-Length"))
CHECK(to_string(statbuf_badext.st_size) == res.headers.find("Content-Length")->second);
}
} // send_file

Expand Down