mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 21:34:46 +02:00
Various improvements to http_server_fast.cpp:
fix #578 - Receive request in a single read - Use fields_alloc for response - Fix command line usage information - Add command line option to spin the io_service
This commit is contained in:
committed by
Vinnie Falco
parent
a281ca5384
commit
7e61a48607
@@ -8,6 +8,7 @@ HTTP:
|
||||
* Refactor file_body for best practices
|
||||
* Add http-server-threaded example
|
||||
* Documentation tidying
|
||||
* Various improvements to http_server_fast.cpp
|
||||
|
||||
WebSocket:
|
||||
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
@@ -34,11 +35,7 @@ public:
|
||||
|
||||
http_worker(tcp::acceptor& acceptor, const std::string& doc_root) :
|
||||
acceptor_(acceptor),
|
||||
doc_root_(doc_root),
|
||||
socket_(acceptor.get_io_service()),
|
||||
alloc_(8192),
|
||||
request_deadline_(acceptor.get_io_service(),
|
||||
std::chrono::steady_clock::time_point::max())
|
||||
doc_root_(doc_root)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -49,6 +46,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
using alloc_t = fields_alloc<char>;
|
||||
using request_body_t = http::basic_dynamic_body<beast::static_buffer_n<1024 * 1024>>;
|
||||
|
||||
// The acceptor used to listen for incoming connections.
|
||||
@@ -58,24 +56,26 @@ private:
|
||||
std::string doc_root_;
|
||||
|
||||
// The socket for the currently connected client.
|
||||
tcp::socket socket_;
|
||||
tcp::socket socket_{acceptor_.get_io_service()};
|
||||
|
||||
// The buffer for performing reads
|
||||
beast::static_buffer_n<8192> buffer_;
|
||||
|
||||
// The allocator used for the fields in the request and reply.
|
||||
alloc_t alloc_{8192};
|
||||
|
||||
// The parser for reading the requests
|
||||
using alloc_type = fields_alloc<char>;
|
||||
alloc_type alloc_;
|
||||
boost::optional<http::request_parser<request_body_t, alloc_type>> parser_;
|
||||
boost::optional<http::request_parser<request_body_t, alloc_t>> parser_;
|
||||
|
||||
// The timer putting a time limit on requests.
|
||||
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)()};
|
||||
|
||||
// The response message.
|
||||
http::response<http::string_body> response_;
|
||||
boost::optional<http::response<http::string_body, http::basic_fields<alloc_t>>> response_;
|
||||
|
||||
// The response serializer.
|
||||
boost::optional<http::response_serializer<http::string_body>> serializer_;
|
||||
boost::optional<http::response_serializer<http::string_body, http::basic_fields<alloc_t>>> serializer_;
|
||||
|
||||
void accept()
|
||||
{
|
||||
@@ -98,12 +98,12 @@ private:
|
||||
request_deadline_.expires_from_now(
|
||||
std::chrono::seconds(60));
|
||||
|
||||
read_header();
|
||||
read_request();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void read_header()
|
||||
void read_request()
|
||||
{
|
||||
// On each read the parser needs to be destroyed and
|
||||
// recreated. We store it in a boost::optional to
|
||||
@@ -121,21 +121,6 @@ private:
|
||||
std::make_tuple(),
|
||||
std::make_tuple(alloc_));
|
||||
|
||||
http::async_read_header(
|
||||
socket_,
|
||||
buffer_,
|
||||
*parser_,
|
||||
[this](beast::error_code ec)
|
||||
{
|
||||
if (ec)
|
||||
accept();
|
||||
else
|
||||
read_body();
|
||||
});
|
||||
}
|
||||
|
||||
void read_body()
|
||||
{
|
||||
http::async_read(
|
||||
socket_,
|
||||
buffer_,
|
||||
@@ -149,29 +134,32 @@ private:
|
||||
});
|
||||
}
|
||||
|
||||
void process_request(http::request<request_body_t, http::basic_fields<alloc_type>> const& req)
|
||||
void process_request(http::request<request_body_t, http::basic_fields<alloc_t>> const& req)
|
||||
{
|
||||
response_.version = 11;
|
||||
response_.set(http::field::connection, "close");
|
||||
response_.emplace(
|
||||
std::piecewise_construct,
|
||||
std::make_tuple(),
|
||||
std::make_tuple(alloc_));
|
||||
|
||||
response_->set(http::field::connection, "close");
|
||||
|
||||
switch (req.method())
|
||||
{
|
||||
case http::verb::get:
|
||||
response_.result(http::status::ok);
|
||||
response_.set(http::field::server, "Beast");
|
||||
response_->result(http::status::ok);
|
||||
response_->set(http::field::server, "Beast");
|
||||
load_file(req.target());
|
||||
break;
|
||||
|
||||
default:
|
||||
// We return responses indicating an error if
|
||||
// we do not recognize the request method.
|
||||
response_.result(http::status::bad_request);
|
||||
response_.set(http::field::content_type, "text/plain");
|
||||
response_.body = "Invalid request-method '" + req.method_string().to_string() + "'";
|
||||
response_->result(http::status::bad_request);
|
||||
response_->set(http::field::content_type, "text/plain");
|
||||
response_->body = "Invalid request-method '" + req.method_string().to_string() + "'";
|
||||
break;
|
||||
}
|
||||
|
||||
response_.prepare_payload();
|
||||
write_response();
|
||||
}
|
||||
|
||||
@@ -180,9 +168,9 @@ private:
|
||||
// Request path must be absolute and not contain "..".
|
||||
if (target.empty() || target[0] != '/' || target.find("..") != std::string::npos)
|
||||
{
|
||||
response_.result(http::status::not_found);
|
||||
response_.set(http::field::content_type, "text/plain");
|
||||
response_.body = "File not found\r\n";
|
||||
response_->result(http::status::not_found);
|
||||
response_->set(http::field::content_type, "text/plain");
|
||||
response_->body = "File not found\r\n";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -193,22 +181,23 @@ private:
|
||||
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
|
||||
if (!is)
|
||||
{
|
||||
response_.result(http::status::not_found);
|
||||
response_.set(http::field::content_type, "text/plain");
|
||||
response_.body = "File not found\r\n";
|
||||
response_->result(http::status::not_found);
|
||||
response_->set(http::field::content_type, "text/plain");
|
||||
response_->body = "File not found\r\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill out the reply to be sent to the client.
|
||||
response_.set(http::field::content_type, mime_type(target.to_string()));
|
||||
response_.body.clear();
|
||||
response_->set(http::field::content_type, mime_type(target.to_string()));
|
||||
response_->body.clear();
|
||||
for (char buf[2048]; is.read(buf, sizeof(buf)).gcount() > 0;)
|
||||
response_.body.append(buf, static_cast<std::size_t>(is.gcount()));
|
||||
response_->body.append(buf, static_cast<std::size_t>(is.gcount()));
|
||||
}
|
||||
|
||||
void write_response()
|
||||
{
|
||||
serializer_.emplace(response_);
|
||||
response_->prepare_payload();
|
||||
serializer_.emplace(*response_);
|
||||
|
||||
http::async_write(
|
||||
socket_,
|
||||
@@ -216,6 +205,8 @@ private:
|
||||
[this](beast::error_code ec)
|
||||
{
|
||||
socket_.shutdown(tcp::socket::shutdown_send, ec);
|
||||
serializer_.reset();
|
||||
response_.reset();
|
||||
accept();
|
||||
});
|
||||
}
|
||||
@@ -247,13 +238,13 @@ int main(int argc, char* argv[])
|
||||
try
|
||||
{
|
||||
// Check command line arguments.
|
||||
if (argc != 5)
|
||||
if (argc != 6)
|
||||
{
|
||||
std::cerr << "Usage: http_server <address> <port> <doc_root> <num_workers>\n";
|
||||
std::cerr << "Usage: http_server_fast <address> <port> <doc_root> <num_workers> {spin|block}\n";
|
||||
std::cerr << " For IPv4, try:\n";
|
||||
std::cerr << " receiver 0.0.0.0 80 . 100\n";
|
||||
std::cerr << " http_server_fast 0.0.0.0 80 . 100 block\n";
|
||||
std::cerr << " For IPv6, try:\n";
|
||||
std::cerr << " receiver 0::0 80 . 100\n";
|
||||
std::cerr << " http_server_fast 0::0 80 . 100 block\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -261,6 +252,7 @@ int main(int argc, char* argv[])
|
||||
unsigned short port = static_cast<unsigned short>(std::atoi(argv[2]));
|
||||
std::string doc_root = argv[3];
|
||||
int num_workers = std::atoi(argv[4]);
|
||||
bool spin = (std::strcmp(argv[5], "spin") == 0);
|
||||
|
||||
boost::asio::io_service ios{1};
|
||||
tcp::acceptor acceptor{ios, {address, port}};
|
||||
@@ -272,7 +264,10 @@ int main(int argc, char* argv[])
|
||||
workers.back().start();
|
||||
}
|
||||
|
||||
ios.run();
|
||||
if (spin)
|
||||
for (;;) ios.poll();
|
||||
else
|
||||
ios.run();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
Reference in New Issue
Block a user