mirror of
https://github.com/CrowCpp/Crow.git
synced 2024-06-07 21:10:44 +00:00
Merge branch 'master' into #421-Websockets-should-support-a-maximum-payload
This commit is contained in:
commit
3e6c097733
@ -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 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
|
## 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>
|
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>
|
||||||
|
|
||||||
@ -46,6 +92,56 @@ Alternatively, you can define the response in the body and return it (`#!cpp ([]
|
|||||||
|
|
||||||
For more information on `crow::response` go [here](../../reference/structcrow_1_1response.html).<br><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
|
### 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>
|
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>
|
||||||
The main return type is `std::string`, although you could also return a `crow::json::wvalue` or `crow::multipart::message` directly.<br><br>
|
The main return type is `std::string`, although you could also return a `crow::json::wvalue` or `crow::multipart::message` directly.<br><br>
|
||||||
|
@ -19,8 +19,8 @@ sock.onopen = ()=>{
|
|||||||
sock.onerror = (e)=>{
|
sock.onerror = (e)=>{
|
||||||
console.log('error',e)
|
console.log('error',e)
|
||||||
}
|
}
|
||||||
sock.onclose = ()=>{
|
sock.onclose = (e)=>{
|
||||||
console.log('close')
|
console.log('close', e)
|
||||||
}
|
}
|
||||||
sock.onmessage = (e)=>{
|
sock.onmessage = (e)=>{
|
||||||
$("#log").val(
|
$("#log").val(
|
||||||
|
@ -59,6 +59,9 @@ namespace crow
|
|||||||
Crow()
|
Crow()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
std::atomic<int> websocket_count{0};
|
||||||
|
|
||||||
/// Process an Upgrade request
|
/// Process an Upgrade request
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -128,6 +131,11 @@ namespace crow
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<int> signals()
|
||||||
|
{
|
||||||
|
return signals_;
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the port that Crow will handle requests on
|
/// Set the port that Crow will handle requests on
|
||||||
self_t& port(std::uint16_t port)
|
self_t& port(std::uint16_t port)
|
||||||
{
|
{
|
||||||
@ -313,7 +321,6 @@ namespace crow
|
|||||||
{
|
{
|
||||||
server_ = std::move(std::unique_ptr<server_t>(new server_t(this, bindaddr_, port_, server_name_, &middlewares_, concurrency_, timeout_, nullptr)));
|
server_ = std::move(std::unique_ptr<server_t>(new server_t(this, bindaddr_, port_, server_name_, &middlewares_, concurrency_, timeout_, nullptr)));
|
||||||
server_->set_tick_function(tick_interval_, tick_function_);
|
server_->set_tick_function(tick_interval_, tick_function_);
|
||||||
server_->signal_clear();
|
|
||||||
for (auto snum : signals_)
|
for (auto snum : signals_)
|
||||||
{
|
{
|
||||||
server_->signal_add(snum);
|
server_->signal_add(snum);
|
||||||
|
@ -317,11 +317,15 @@ namespace crow
|
|||||||
buffers_.reserve(4 * (res.headers.size() + 5) + 3);
|
buffers_.reserve(4 * (res.headers.size() + 5) + 3);
|
||||||
|
|
||||||
if (!statusCodes.count(res.code))
|
if (!statusCodes.count(res.code))
|
||||||
res.code = 500;
|
|
||||||
{
|
{
|
||||||
|
CROW_LOG_WARNING << this << " status code "
|
||||||
|
<< "(" << res.code << ")"
|
||||||
|
<< " not defined, returning 500 instead";
|
||||||
|
res.code = 500;
|
||||||
|
}
|
||||||
|
|
||||||
auto& status = statusCodes.find(res.code)->second;
|
auto& status = statusCodes.find(res.code)->second;
|
||||||
buffers_.emplace_back(status.data(), status.size());
|
buffers_.emplace_back(status.data(), status.size());
|
||||||
}
|
|
||||||
|
|
||||||
if (res.code >= 400 && res.body.empty())
|
if (res.code >= 400 && res.body.empty())
|
||||||
res.body = statusCodes[res.code].substr(9);
|
res.body = statusCodes[res.code].substr(9);
|
||||||
|
@ -161,9 +161,21 @@ namespace crow
|
|||||||
|
|
||||||
void stop()
|
void stop()
|
||||||
{
|
{
|
||||||
io_service_.stop();
|
shutting_down_ = true; //Prevent the acceptor from taking new connections
|
||||||
|
while (handler_->websocket_count.load(std::memory_order_release) != 0) //Wait for the websockets to close properly
|
||||||
|
{
|
||||||
|
}
|
||||||
for (auto& io_service : io_service_pool_)
|
for (auto& io_service : io_service_pool_)
|
||||||
io_service->stop();
|
{
|
||||||
|
if (io_service != nullptr)
|
||||||
|
{
|
||||||
|
CROW_LOG_INFO << "Closing IO service " << &io_service;
|
||||||
|
io_service->stop(); //Close all io_services (and HTTP connections)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CROW_LOG_INFO << "Closing main IO service (" << &io_service_ << ')';
|
||||||
|
io_service_.stop(); //Close main io_service
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_clear()
|
void signal_clear()
|
||||||
@ -194,6 +206,8 @@ namespace crow
|
|||||||
}
|
}
|
||||||
|
|
||||||
void do_accept()
|
void do_accept()
|
||||||
|
{
|
||||||
|
if (!shutting_down_)
|
||||||
{
|
{
|
||||||
uint16_t service_idx = pick_io_service_idx();
|
uint16_t service_idx = pick_io_service_idx();
|
||||||
asio::io_service& is = *io_service_pool_[service_idx];
|
asio::io_service& is = *io_service_pool_[service_idx];
|
||||||
@ -223,6 +237,7 @@ namespace crow
|
|||||||
do_accept();
|
do_accept();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
asio::io_service io_service_;
|
asio::io_service io_service_;
|
||||||
@ -230,6 +245,7 @@ namespace crow
|
|||||||
std::vector<detail::task_timer*> task_timer_pool_;
|
std::vector<detail::task_timer*> task_timer_pool_;
|
||||||
std::vector<std::function<std::string()>> get_cached_date_str_pool_;
|
std::vector<std::function<std::string()>> get_cached_date_str_pool_;
|
||||||
tcp::acceptor acceptor_;
|
tcp::acceptor acceptor_;
|
||||||
|
bool shutting_down_ = false;
|
||||||
boost::asio::signal_set signals_;
|
boost::asio::signal_set signals_;
|
||||||
boost::asio::deadline_timer tick_timer_;
|
boost::asio::deadline_timer tick_timer_;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/array.hpp>
|
#include <boost/array.hpp>
|
||||||
|
#include "crow/logging.h"
|
||||||
#include "crow/socket_adaptors.h"
|
#include "crow/socket_adaptors.h"
|
||||||
#include "crow/http_request.h"
|
#include "crow/http_request.h"
|
||||||
#include "crow/TinySHA1.hpp"
|
#include "crow/TinySHA1.hpp"
|
||||||
@ -60,6 +61,7 @@ namespace crow
|
|||||||
//
|
//
|
||||||
|
|
||||||
/// A websocket connection.
|
/// A websocket connection.
|
||||||
|
|
||||||
template<typename Adaptor, typename Handler>
|
template<typename Adaptor, typename Handler>
|
||||||
class Connection : public connection
|
class Connection : public connection
|
||||||
{
|
{
|
||||||
@ -78,11 +80,13 @@ namespace crow
|
|||||||
adaptor_(std::move(adaptor)),
|
adaptor_(std::move(adaptor)),
|
||||||
handler_(handler),
|
handler_(handler),
|
||||||
max_payload_bytes_(max_payload),
|
max_payload_bytes_(max_payload),
|
||||||
|
websocket_count_(handler_->websocket_count),
|
||||||
open_handler_(std::move(open_handler)),
|
open_handler_(std::move(open_handler)),
|
||||||
message_handler_(std::move(message_handler)),
|
message_handler_(std::move(message_handler)),
|
||||||
close_handler_(std::move(close_handler)),
|
close_handler_(std::move(close_handler)),
|
||||||
error_handler_(std::move(error_handler)),
|
error_handler_(std::move(error_handler)),
|
||||||
accept_handler_(std::move(accept_handler))
|
accept_handler_(std::move(accept_handler)),
|
||||||
|
signals_(adaptor_.get_io_service())
|
||||||
{
|
{
|
||||||
if (!boost::iequals(req.get_header_value("upgrade"), "websocket"))
|
if (!boost::iequals(req.get_header_value("upgrade"), "websocket"))
|
||||||
{
|
{
|
||||||
@ -101,6 +105,11 @@ namespace crow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
signals_.clear();
|
||||||
|
for (auto snum : handler_->signals())
|
||||||
|
signals_.add(snum);
|
||||||
|
|
||||||
// Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
// Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
||||||
// Sec-WebSocket-Version: 13
|
// Sec-WebSocket-Version: 13
|
||||||
std::string magic = req.get_header_value("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
std::string magic = req.get_header_value("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||||
@ -108,6 +117,15 @@ namespace crow
|
|||||||
s.processBytes(magic.data(), magic.size());
|
s.processBytes(magic.data(), magic.size());
|
||||||
uint8_t digest[20];
|
uint8_t digest[20];
|
||||||
s.getDigestBytes(digest);
|
s.getDigestBytes(digest);
|
||||||
|
|
||||||
|
signals_.async_wait(
|
||||||
|
[&](const boost::system::error_code& e, int /*signal_number*/) {
|
||||||
|
if (!e)
|
||||||
|
{
|
||||||
|
CROW_LOG_INFO << "Quitting Websocket: " << this;
|
||||||
|
close("Server Application Terminated");
|
||||||
|
}
|
||||||
|
});
|
||||||
start(crow::utility::base64encode((unsigned char*)digest, 20));
|
start(crow::utility::base64encode((unsigned char*)digest, 20));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,6 +314,7 @@ namespace crow
|
|||||||
has_mask_ = false;
|
has_mask_ = false;
|
||||||
#else
|
#else
|
||||||
close_connection_ = true;
|
close_connection_ = true;
|
||||||
|
adaptor_.shutdown_readwrite();
|
||||||
adaptor_.close();
|
adaptor_.close();
|
||||||
if (error_handler_)
|
if (error_handler_)
|
||||||
error_handler_(*this);
|
error_handler_(*this);
|
||||||
@ -321,6 +340,7 @@ namespace crow
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
close_connection_ = true;
|
close_connection_ = true;
|
||||||
|
adaptor_.shutdown_readwrite();
|
||||||
adaptor_.close();
|
adaptor_.close();
|
||||||
if (error_handler_)
|
if (error_handler_)
|
||||||
error_handler_(*this);
|
error_handler_(*this);
|
||||||
@ -358,6 +378,7 @@ namespace crow
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
close_connection_ = true;
|
close_connection_ = true;
|
||||||
|
adaptor_.shutdown_readwrite();
|
||||||
adaptor_.close();
|
adaptor_.close();
|
||||||
if (error_handler_)
|
if (error_handler_)
|
||||||
error_handler_(*this);
|
error_handler_(*this);
|
||||||
@ -392,6 +413,7 @@ namespace crow
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
close_connection_ = true;
|
close_connection_ = true;
|
||||||
|
adaptor_.shutdown_readwrite();
|
||||||
adaptor_.close();
|
adaptor_.close();
|
||||||
if (error_handler_)
|
if (error_handler_)
|
||||||
error_handler_(*this);
|
error_handler_(*this);
|
||||||
@ -436,6 +458,7 @@ namespace crow
|
|||||||
close_connection_ = true;
|
close_connection_ = true;
|
||||||
if (error_handler_)
|
if (error_handler_)
|
||||||
error_handler_(*this);
|
error_handler_(*this);
|
||||||
|
adaptor_.shutdown_readwrite();
|
||||||
adaptor_.close();
|
adaptor_.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -474,6 +497,7 @@ namespace crow
|
|||||||
close_connection_ = true;
|
close_connection_ = true;
|
||||||
if (error_handler_)
|
if (error_handler_)
|
||||||
error_handler_(*this);
|
error_handler_(*this);
|
||||||
|
adaptor_.shutdown_readwrite();
|
||||||
adaptor_.close();
|
adaptor_.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -553,6 +577,7 @@ namespace crow
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
adaptor_.shutdown_readwrite();
|
||||||
adaptor_.close();
|
adaptor_.close();
|
||||||
close_connection_ = true;
|
close_connection_ = true;
|
||||||
if (!is_close_handler_called_)
|
if (!is_close_handler_called_)
|
||||||
@ -622,6 +647,7 @@ namespace crow
|
|||||||
if (!is_close_handler_called_)
|
if (!is_close_handler_called_)
|
||||||
if (close_handler_)
|
if (close_handler_)
|
||||||
close_handler_(*this, "uncleanly");
|
close_handler_(*this, "uncleanly");
|
||||||
|
websocket_count_--;
|
||||||
if (sending_buffers_.empty() && !is_reading)
|
if (sending_buffers_.empty() && !is_reading)
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
@ -651,12 +677,14 @@ namespace crow
|
|||||||
bool error_occured_{false};
|
bool error_occured_{false};
|
||||||
bool pong_received_{false};
|
bool pong_received_{false};
|
||||||
bool is_close_handler_called_{false};
|
bool is_close_handler_called_{false};
|
||||||
|
std::atomic<int>& websocket_count_;
|
||||||
|
|
||||||
std::function<void(crow::websocket::connection&)> open_handler_;
|
std::function<void(crow::websocket::connection&)> open_handler_;
|
||||||
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
|
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
|
||||||
std::function<void(crow::websocket::connection&, const std::string&)> close_handler_;
|
std::function<void(crow::websocket::connection&, const std::string&)> close_handler_;
|
||||||
std::function<void(crow::websocket::connection&)> error_handler_;
|
std::function<void(crow::websocket::connection&)> error_handler_;
|
||||||
std::function<bool(const crow::request&)> accept_handler_;
|
std::function<bool(const crow::request&)> accept_handler_;
|
||||||
|
boost::asio::signal_set signals_;
|
||||||
};
|
};
|
||||||
} // namespace websocket
|
} // namespace websocket
|
||||||
} // namespace crow
|
} // namespace crow
|
||||||
|
@ -571,6 +571,61 @@ TEST_CASE("multi_server")
|
|||||||
app2.stop();
|
app2.stop();
|
||||||
} // multi_server
|
} // 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")
|
TEST_CASE("json_read")
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@ -2025,7 +2080,13 @@ TEST_CASE("send_file")
|
|||||||
app.handle(req, res);
|
app.handle(req, res);
|
||||||
|
|
||||||
CHECK(200 == res.code);
|
CHECK(200 == res.code);
|
||||||
|
|
||||||
|
CHECK(res.headers.count("Content-Type"));
|
||||||
|
if (res.headers.count("Content-Type"))
|
||||||
CHECK("image/jpeg" == res.headers.find("Content-Type")->second);
|
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);
|
CHECK(to_string(statbuf_cat.st_size) == res.headers.find("Content-Length")->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2039,7 +2100,12 @@ TEST_CASE("send_file")
|
|||||||
|
|
||||||
CHECK_NOTHROW(app.handle(req, res));
|
CHECK_NOTHROW(app.handle(req, res));
|
||||||
CHECK(200 == res.code);
|
CHECK(200 == res.code);
|
||||||
|
CHECK(res.headers.count("Content-Type"));
|
||||||
|
if (res.headers.count("Content-Type"))
|
||||||
CHECK("text/plain" == res.headers.find("Content-Type")->second);
|
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);
|
CHECK(to_string(statbuf_badext.st_size) == res.headers.find("Content-Length")->second);
|
||||||
}
|
}
|
||||||
} // send_file
|
} // send_file
|
||||||
|
Loading…
Reference in New Issue
Block a user