31 = std::function<std::string(
const std::string&,
const std::unordered_map<std::string, std::string>&)>;
32 using ErrorHandler = std::function<void(
const std::exception&)>;
34 HttpServer(
const typename boost::asio::basic_socket_acceptor<Protocol>::endpoint_type& endpoint,
36 : _acceptor(_ioContext, endpoint)
37 , _handler(std::move(handler)) {
45 void start(
const ErrorHandler& errorHandler = { }, std::initializer_list<int> signals = { SIGINT, SIGTERM }) {
46 _errorHandler = errorHandler;
49 if (signals.size() != 0) {
50 _signals.emplace(_ioContext);
51 for (
const int signal : signals) {
52 _signals->add(signal);
54 _signals->async_wait([
this](
const boost::system::error_code& error,
int) {
75 boost::asio::post(_ioContext, [
this] {
76 boost::system::error_code error;
78 _signals->cancel(error);
80 _acceptor.close(error);
85 class Connection :
public std::enable_shared_from_this<Connection> {
88 : _socket(std::move(socket))
89 , _handler(std::move(handler))
90 , _errorHandler(std::move(errorHandler)) {
94 auto self = this->shared_from_this();
95 boost::beast::http::async_read(_socket, _buffer, _request,
96 [self](
const boost::system::error_code& error, std::size_t) {
97 self->handleRequest(error);
102 void handleRequest(
const boost::system::error_code& error) {
103 namespace http = boost::beast::http;
105 if (error == http::error::end_of_stream) {
110 reportError(_errorHandler,
"error reading HTTP request: " + error.message());
115 std::unordered_map<std::string, std::string> headers;
116 headers.emplace(
"_method", std::string(_request.method_string()));
117 headers.emplace(
"_path", std::string(_request.target()));
118 for (
const auto& field : _request) {
119 headers.emplace(std::string(field.name_string()), std::string(field.value()));
122 _response = { http::status::ok, _request.version() };
123 _response.set(http::field::content_type,
"text/plain");
124 _response.keep_alive(
false);
126 _response.body() = _handler(_request.body(), headers);
127 }
catch (
const std::exception& exception) {
128 reportError(_errorHandler,
"error handling HTTP request: " + std::string(exception.what()));
129 _response.result(http::status::internal_server_error);
130 _response.body() =
"Internal server error";
132 _response.prepare_payload();
134 auto self = this->shared_from_this();
135 http::async_write(_socket, _response, [self](
const boost::system::error_code& writeError, std::size_t) {
137 reportError(self->_errorHandler,
"error writing HTTP response: " + writeError.message());
144 boost::system::error_code error;
145 _socket.shutdown(boost::asio::socket_base::shutdown_both, error);
146 _socket.close(error);
149 boost::asio::basic_stream_socket<Protocol> _socket;
152 boost::beast::flat_buffer _buffer;
153 boost::beast::http::request<boost::beast::http::string_body> _request;
154 boost::beast::http::response<boost::beast::http::string_body> _response;
158 _acceptor.async_accept(
159 [
this](
const boost::system::error_code& error, boost::asio::basic_stream_socket<Protocol> socket) {
160 if (error == boost::asio::error::operation_aborted) {
164 reportError(_errorHandler,
"error accepting HTTP connection: " + error.message());
165 if (_acceptor.is_open()) {
170 std::make_shared<Connection>(std::move(socket), _handler, _errorHandler)->start();
175 static void reportError(
const ErrorHandler& errorHandler, std::string_view message) {
176 const std::runtime_error error { std::string(message) };
182 boost::asio::io_context _ioContext;
183 std::optional<boost::asio::signal_set> _signals;
184 boost::asio::basic_socket_acceptor<Protocol> _acceptor;