forked from boostorg/beast
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
|
* Refactor file_body for best practices
|
||||||
* Add http-server-threaded example
|
* Add http-server-threaded example
|
||||||
* Documentation tidying
|
* Documentation tidying
|
||||||
|
* Various improvements to http_server_fast.cpp
|
||||||
|
|
||||||
WebSocket:
|
WebSocket:
|
||||||
|
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <list>
|
#include <list>
|
||||||
@@ -34,11 +35,7 @@ public:
|
|||||||
|
|
||||||
http_worker(tcp::acceptor& acceptor, const std::string& doc_root) :
|
http_worker(tcp::acceptor& acceptor, const std::string& doc_root) :
|
||||||
acceptor_(acceptor),
|
acceptor_(acceptor),
|
||||||
doc_root_(doc_root),
|
doc_root_(doc_root)
|
||||||
socket_(acceptor.get_io_service()),
|
|
||||||
alloc_(8192),
|
|
||||||
request_deadline_(acceptor.get_io_service(),
|
|
||||||
std::chrono::steady_clock::time_point::max())
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +46,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using alloc_t = fields_alloc<char>;
|
||||||
using request_body_t = http::basic_dynamic_body<beast::static_buffer_n<1024 * 1024>>;
|
using request_body_t = http::basic_dynamic_body<beast::static_buffer_n<1024 * 1024>>;
|
||||||
|
|
||||||
// The acceptor used to listen for incoming connections.
|
// The acceptor used to listen for incoming connections.
|
||||||
@@ -58,24 +56,26 @@ private:
|
|||||||
std::string doc_root_;
|
std::string doc_root_;
|
||||||
|
|
||||||
// The socket for the currently connected client.
|
// The socket for the currently connected client.
|
||||||
tcp::socket socket_;
|
tcp::socket socket_{acceptor_.get_io_service()};
|
||||||
|
|
||||||
// The buffer for performing reads
|
// The buffer for performing reads
|
||||||
beast::static_buffer_n<8192> buffer_;
|
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
|
// The parser for reading the requests
|
||||||
using alloc_type = fields_alloc<char>;
|
boost::optional<http::request_parser<request_body_t, alloc_t>> parser_;
|
||||||
alloc_type alloc_;
|
|
||||||
boost::optional<http::request_parser<request_body_t, alloc_type>> parser_;
|
|
||||||
|
|
||||||
// The timer putting a time limit on requests.
|
// 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.
|
// 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.
|
// 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()
|
void accept()
|
||||||
{
|
{
|
||||||
@@ -98,12 +98,12 @@ private:
|
|||||||
request_deadline_.expires_from_now(
|
request_deadline_.expires_from_now(
|
||||||
std::chrono::seconds(60));
|
std::chrono::seconds(60));
|
||||||
|
|
||||||
read_header();
|
read_request();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_header()
|
void read_request()
|
||||||
{
|
{
|
||||||
// On each read the parser needs to be destroyed and
|
// On each read the parser needs to be destroyed and
|
||||||
// recreated. We store it in a boost::optional to
|
// recreated. We store it in a boost::optional to
|
||||||
@@ -121,21 +121,6 @@ private:
|
|||||||
std::make_tuple(),
|
std::make_tuple(),
|
||||||
std::make_tuple(alloc_));
|
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(
|
http::async_read(
|
||||||
socket_,
|
socket_,
|
||||||
buffer_,
|
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_.emplace(
|
||||||
response_.set(http::field::connection, "close");
|
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);
|
response_->result(http::status::ok);
|
||||||
response_.set(http::field::server, "Beast");
|
response_->set(http::field::server, "Beast");
|
||||||
load_file(req.target());
|
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);
|
response_->result(http::status::bad_request);
|
||||||
response_.set(http::field::content_type, "text/plain");
|
response_->set(http::field::content_type, "text/plain");
|
||||||
response_.body = "Invalid request-method '" + req.method_string().to_string() + "'";
|
response_->body = "Invalid request-method '" + req.method_string().to_string() + "'";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
response_.prepare_payload();
|
|
||||||
write_response();
|
write_response();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,9 +168,9 @@ private:
|
|||||||
// 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);
|
response_->result(http::status::not_found);
|
||||||
response_.set(http::field::content_type, "text/plain");
|
response_->set(http::field::content_type, "text/plain");
|
||||||
response_.body = "File not found\r\n";
|
response_->body = "File not found\r\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,22 +181,23 @@ private:
|
|||||||
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
|
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
|
||||||
if (!is)
|
if (!is)
|
||||||
{
|
{
|
||||||
response_.result(http::status::not_found);
|
response_->result(http::status::not_found);
|
||||||
response_.set(http::field::content_type, "text/plain");
|
response_->set(http::field::content_type, "text/plain");
|
||||||
response_.body = "File not found\r\n";
|
response_->body = "File not found\r\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill out the reply to be sent to the client.
|
// Fill out the reply to be sent to the client.
|
||||||
response_.set(http::field::content_type, mime_type(target.to_string()));
|
response_->set(http::field::content_type, mime_type(target.to_string()));
|
||||||
response_.body.clear();
|
response_->body.clear();
|
||||||
for (char buf[2048]; is.read(buf, sizeof(buf)).gcount() > 0;)
|
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()
|
void write_response()
|
||||||
{
|
{
|
||||||
serializer_.emplace(response_);
|
response_->prepare_payload();
|
||||||
|
serializer_.emplace(*response_);
|
||||||
|
|
||||||
http::async_write(
|
http::async_write(
|
||||||
socket_,
|
socket_,
|
||||||
@@ -216,6 +205,8 @@ private:
|
|||||||
[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();
|
||||||
|
response_.reset();
|
||||||
accept();
|
accept();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -247,13 +238,13 @@ int main(int argc, char* argv[])
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Check command line arguments.
|
// 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 << " 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 << " 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;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,6 +252,7 @@ int main(int argc, char* argv[])
|
|||||||
unsigned short port = static_cast<unsigned short>(std::atoi(argv[2]));
|
unsigned short port = static_cast<unsigned short>(std::atoi(argv[2]));
|
||||||
std::string doc_root = argv[3];
|
std::string doc_root = argv[3];
|
||||||
int num_workers = std::atoi(argv[4]);
|
int num_workers = std::atoi(argv[4]);
|
||||||
|
bool spin = (std::strcmp(argv[5], "spin") == 0);
|
||||||
|
|
||||||
boost::asio::io_service ios{1};
|
boost::asio::io_service ios{1};
|
||||||
tcp::acceptor acceptor{ios, {address, port}};
|
tcp::acceptor acceptor{ios, {address, port}};
|
||||||
@@ -272,7 +264,10 @@ int main(int argc, char* argv[])
|
|||||||
workers.back().start();
|
workers.back().start();
|
||||||
}
|
}
|
||||||
|
|
||||||
ios.run();
|
if (spin)
|
||||||
|
for (;;) ios.poll();
|
||||||
|
else
|
||||||
|
ios.run();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user