30 = std::function<std::string(
const std::string&,
const std::unordered_map<std::string, std::string>&)>;
31 using ErrorHandler = std::function<void(
const std::exception&)>;
33 HttpServer(
const typename boost::asio::basic_socket_acceptor<Protocol>::endpoint_type& endpoint,
35 : _acceptor(_ioContext, endpoint)
36 , _handler(std::move(handler)) {
44 void start(
const ErrorHandler& errorHandler = { }, std::initializer_list<int> signals = { SIGINT, SIGTERM }) {
45 _errorHandler = errorHandler;
48 if (signals.size() != 0) {
49 _signals.emplace(_ioContext);
50 for (
const int signal : signals) {
51 _signals->add(signal);
53 _signals->async_wait([
this](
const boost::system::error_code& error,
int) {
74 boost::asio::post(_ioContext, [
this] {
75 boost::system::error_code error;
77 _signals->cancel(error);
79 _acceptor.close(error);
84 class Connection :
public std::enable_shared_from_this<Connection> {
87 : _socket(std::move(socket))
88 , _handler(std::move(handler))
89 , _errorHandler(std::move(errorHandler)) {
93 auto self = this->shared_from_this();
94 boost::beast::http::async_read(_socket, _buffer, _request,
95 [self](
const boost::system::error_code& error, std::size_t) {
96 self->handleRequest(error);
101 void handleRequest(
const boost::system::error_code& error) {
102 namespace http = boost::beast::http;
104 if (error == http::error::end_of_stream) {
109 reportError(_errorHandler,
"error reading HTTP request: " + error.message());
114 std::unordered_map<std::string, std::string> headers;
115 headers.emplace(
"_method", std::string(_request.method_string()));
116 headers.emplace(
"_path", std::string(_request.target()));
117 for (
const auto& field : _request) {
118 headers.emplace(std::string(field.name_string()), std::string(field.value()));
121 _response = { http::status::ok, _request.version() };
122 _response.set(http::field::content_type,
"text/plain");
123 _response.keep_alive(
false);
125 _response.body() = _handler(_request.body(), headers);
126 }
catch (
const std::exception& exception) {
127 reportError(_errorHandler,
"error handling HTTP request: " + std::string(exception.what()));
128 _response.result(http::status::internal_server_error);
129 _response.body() =
"Internal server error";
131 _response.prepare_payload();
133 auto self = this->shared_from_this();
134 http::async_write(_socket, _response, [self](
const boost::system::error_code& writeError, std::size_t) {
136 reportError(self->_errorHandler,
"error writing HTTP response: " + writeError.message());
143 boost::system::error_code error;
144 _socket.shutdown(boost::asio::socket_base::shutdown_both, error);
145 _socket.close(error);
148 boost::asio::basic_stream_socket<Protocol> _socket;
151 boost::beast::flat_buffer _buffer;
152 boost::beast::http::request<boost::beast::http::string_body> _request;
153 boost::beast::http::response<boost::beast::http::string_body> _response;
157 _acceptor.async_accept(
158 [
this](
const boost::system::error_code& error, boost::asio::basic_stream_socket<Protocol> socket) {
159 if (error == boost::asio::error::operation_aborted) {
163 reportError(_errorHandler,
"error accepting HTTP connection: " + error.message());
164 if (_acceptor.is_open()) {
169 std::make_shared<Connection>(std::move(socket), _handler, _errorHandler)->start();
174 static void reportError(
const ErrorHandler& errorHandler,
const std::string& message) {
175 const std::runtime_error error(message);
181 boost::asio::io_context _ioContext;
182 std::optional<boost::asio::signal_set> _signals;
183 boost::asio::basic_socket_acceptor<Protocol> _acceptor;