Use file_body for valid requests, string_body otherwise.

This commit is contained in:
Christopher Kohlhoff
2017-07-04 21:49:41 +10:00
committed by Vinnie Falco
parent 783c52b025
commit 8179019590
2 changed files with 82 additions and 43 deletions

View File

@ -1,3 +1,9 @@
Version 75:
* Use file_body for valid requests, string_body otherwise.
--------------------------------------------------------------------------------
Version 74: Version 74:
* Add file_stdio and File concept * Add file_stdio and File concept

View File

@ -71,11 +71,17 @@ private:
boost::asio::basic_waitable_timer<std::chrono::steady_clock> request_deadline_{ boost::asio::basic_waitable_timer<std::chrono::steady_clock> request_deadline_{
acceptor_.get_io_service(), (std::chrono::steady_clock::time_point::max)()}; acceptor_.get_io_service(), (std::chrono::steady_clock::time_point::max)()};
// The response message. // The string-based response message.
boost::optional<http::response<http::string_body, http::basic_fields<alloc_t>>> response_; boost::optional<http::response<http::string_body, http::basic_fields<alloc_t>>> string_response_;
// The response serializer. // The string-based response serializer.
boost::optional<http::response_serializer<http::string_body, http::basic_fields<alloc_t>>> serializer_; boost::optional<http::response_serializer<http::string_body, http::basic_fields<alloc_t>>> string_serializer_;
// The file-based response message.
boost::optional<http::response<http::file_body, http::basic_fields<alloc_t>>> file_response_;
// The file-based response serializer.
boost::optional<http::response_serializer<http::file_body, http::basic_fields<alloc_t>>> file_serializer_;
void accept() void accept()
{ {
@ -136,77 +142,104 @@ private:
void process_request(http::request<request_body_t, http::basic_fields<alloc_t>> const& req) void process_request(http::request<request_body_t, http::basic_fields<alloc_t>> const& req)
{ {
response_.emplace(
std::piecewise_construct,
std::make_tuple(),
std::make_tuple(alloc_));
response_->set(http::field::connection, "close");
switch (req.method()) switch (req.method())
{ {
case http::verb::get: case http::verb::get:
response_->result(http::status::ok); send_file(req.target());
response_->set(http::field::server, "Beast");
load_file(req.target());
break; break;
default: default:
// We return responses indicating an error if // We return responses indicating an error if
// we do not recognize the request method. // we do not recognize the request method.
response_->result(http::status::bad_request); send_bad_response(
response_->set(http::field::content_type, "text/plain"); http::status::bad_request,
response_->body = "Invalid request-method '" + req.method_string().to_string() + "'"; "Invalid request-method '" + req.method_string().to_string() + "'\r\n");
break; break;
} }
write_response();
} }
void load_file(beast::string_view target) void send_bad_response(
http::status status,
std::string const& error)
{
string_response_.emplace(
std::piecewise_construct,
std::make_tuple(),
std::make_tuple(alloc_));
string_response_->result(status);
string_response_->set(http::field::server, "Beast");
string_response_->set(http::field::connection, "close");
string_response_->set(http::field::content_type, "text/plain");
string_response_->body = error;
string_response_->prepare_payload();
string_serializer_.emplace(*string_response_);
http::async_write(
socket_,
*string_serializer_,
[this](beast::error_code ec)
{
socket_.shutdown(tcp::socket::shutdown_send, ec);
string_serializer_.reset();
string_response_.reset();
accept();
});
}
void send_file(beast::string_view target)
{ {
// Request path must be absolute and not contain "..". // Request path must be absolute and not contain "..".
if (target.empty() || target[0] != '/' || target.find("..") != std::string::npos) if (target.empty() || target[0] != '/' || target.find("..") != std::string::npos)
{ {
response_->result(http::status::not_found); send_bad_response(
response_->set(http::field::content_type, "text/plain"); http::status::not_found,
response_->body = "File not found\r\n"; "File not found\r\n");
return; return;
} }
std::string full_path = doc_root_; std::string full_path = doc_root_;
full_path.append(target.data(), target.size()); full_path.append(
target.data(),
target.size());
// Open the file to send back. http::file_body::value_type file;
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); beast::error_code ec;
if (!is) file.open(
full_path.c_str(),
beast::file_mode::read,
ec);
if(ec)
{ {
response_->result(http::status::not_found); send_bad_response(
response_->set(http::field::content_type, "text/plain"); http::status::not_found,
response_->body = "File not found\r\n"; "File not found\r\n");
return; return;
} }
// Fill out the reply to be sent to the client. file_response_.emplace(
response_->set(http::field::content_type, mime_type(target.to_string())); std::piecewise_construct,
response_->body.clear(); std::make_tuple(),
for (char buf[2048]; is.read(buf, sizeof(buf)).gcount() > 0;) std::make_tuple(alloc_));
response_->body.append(buf, static_cast<std::size_t>(is.gcount()));
}
void write_response() file_response_->result(http::status::ok);
{ file_response_->set(http::field::server, "Beast");
response_->prepare_payload(); file_response_->set(http::field::connection, "close");
serializer_.emplace(*response_); file_response_->set(http::field::content_type, mime_type(target.to_string()));
file_response_->body = std::move(file);
file_response_->prepare_payload();
file_serializer_.emplace(*file_response_);
http::async_write( http::async_write(
socket_, socket_,
*serializer_, *file_serializer_,
[this](beast::error_code ec) [this](beast::error_code ec)
{ {
socket_.shutdown(tcp::socket::shutdown_send, ec); socket_.shutdown(tcp::socket::shutdown_send, ec);
serializer_.reset(); file_serializer_.reset();
response_.reset(); file_response_.reset();
accept(); accept();
}); });
} }