diff options
Diffstat (limited to 'http.cpp')
-rw-r--r-- | http.cpp | 278 |
1 files changed, 0 insertions, 278 deletions
@@ -30,281 +30,3 @@ namespace net = boost::asio; // from <boost/asio.hpp> using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> namespace websocket = beast::websocket; -namespace { - -//------------------------------------------------------------------------------ - -// Report a failure -void fail(beast::error_code ec, char const* what) -{ - std::cerr << what << ": " << ec.message() << "\n"; -} - -// Handles an HTTP server connection -class session : public std::enable_shared_from_this<session> -{ - boost::asio::io_context& ioc_; - beast::tcp_stream stream_; - beast::flat_buffer buffer_; - Server& m_server; - std::optional<http::request_parser<http::string_body>> parser_; - request_type req_; - std::shared_ptr<response_type> res_; // std::shared_ptr<void> ? - - void handle_request(::Server& server, request_type&& req) - { - stream_.expires_after(std::chrono::seconds(300)); // timeout on write by server much longer than read timeout from client - auto sp = std::make_shared<response_type>(response::generate_response(req, server)); - - res_ = sp; - - // Write the response - http::async_write( - stream_, - *sp, - beast::bind_front_handler( - &session::on_write, - shared_from_this(), - sp->need_eof())); - } - - void handle_websocket() - { - beast::get_lowest_layer(stream_).expires_never(); - make_websocket_session(ioc_, std::move(stream_), response::get_websocket_address(req_, m_server), parser_->release()); - } - -public: - // Take ownership of the stream - session( - boost::asio::io_context& ioc, - tcp::socket&& socket, - Server& server): - ioc_(ioc), - stream_(std::move(socket)), - m_server(server) - { - } - - // Start the asynchronous operation - void run() - { - // We need to be executing within a strand to perform async operations - // on the I/O objects in this session. - net::dispatch(stream_.get_executor(), - beast::bind_front_handler( - &session::do_read, - shared_from_this())); - } - - void do_read() - { - // Make the request empty before reading, - // otherwise the operation behavior is undefined. - req_ = {}; - - // this is the way to reset the parser. it's necessary. - // https://github.com/boostorg/beast/issues/927 - parser_.emplace(); - parser_->body_limit(1000000000); // 1GB limit - - // Set the timeout. - stream_.expires_after(std::chrono::seconds(30)); - - // Read a request - http::async_read(stream_, buffer_, *parser_, - beast::bind_front_handler( - &session::on_read, - shared_from_this())); - } - - void - on_read( - beast::error_code ec, - std::size_t bytes_transferred - ) - { - boost::ignore_unused(bytes_transferred); - - // This means they closed the connection - if (ec == http::error::end_of_stream) - return do_close(); - - if (ec == http::error::partial_message) - return; // ignore - - if (ec) - return fail(ec, "read"); - - req_ = parser_->get(); - - if (websocket::is_upgrade(req_)) - { - handle_websocket(); - return; - } - - // Send the response - handle_request(m_server, std::move(req_)); - } - - void - on_write( - bool close, - beast::error_code ec, - std::size_t bytes_transferred - ) - { - boost::ignore_unused(bytes_transferred); - - if(ec) - return fail(ec, "write"); - - if(close) - { - // This means we should close the connection, usually because - // the response indicated the "Connection: close" semantic. - return do_close(); - } - - // We're done with the response so delete it - res_ = nullptr; - - // Read another request - do_read(); - } - - void - do_close() - { - // Send a TCP shutdown - beast::error_code ec; - stream_.socket().shutdown(tcp::socket::shutdown_send, ec); - // At this point the connection is closed gracefully - } -}; - -//------------------------------------------------------------------------------ - -// Accepts incoming connections and launches the sessions -class listener : public std::enable_shared_from_this<listener> -{ - net::io_context& ioc_; - tcp::acceptor acceptor_; - Server& m_server; - -public: - listener( - net::io_context& ioc, - tcp::endpoint endpoint, - Server& server) - : ioc_(ioc) - , acceptor_(net::make_strand(ioc)) - , m_server(server) - { - beast::error_code ec; - - // Open the acceptor - acceptor_.open(endpoint.protocol(), ec); - if(ec) - { - fail(ec, "open"); - return; - } - - // Allow address reuse - acceptor_.set_option(net::socket_base::reuse_address(true), ec); - if(ec) - { - fail(ec, "set_option"); - return; - } - - // Bind to the server address - acceptor_.bind(endpoint, ec); - if(ec) - { - fail(ec, "bind"); - return; - } - - // Start listening for connections - acceptor_.listen( - net::socket_base::max_listen_connections, ec); - if(ec) - { - fail(ec, "listen"); - return; - } - } - - // Start accepting incoming connections - void - run() - { - do_accept(); - } - -private: - void - do_accept() - { - // The new connection gets its own strand - acceptor_.async_accept( - net::make_strand(ioc_), - beast::bind_front_handler( - &listener::on_accept, - shared_from_this())); - } - - void - on_accept(beast::error_code ec, tcp::socket socket) - { - if(ec) - { - fail(ec, "accept"); - } - else - { - // Create the session and run it - std::make_shared<session>( - ioc_, - std::move(socket), - m_server)->run(); - } - - // Accept another connection - do_accept(); - } -}; - -} // anonymous namespace - -//------------------------------------------------------------------------------ - -namespace HTTP { - - Server::Server(Config& config, boost::asio::io_context& ioc, const Socket& socket, plugins_container_type& plugins, Statistics& statistics) - : ::Server(config, ioc, socket, plugins, statistics) - { - } - - Server::~Server() - { - } - - int Server::start() - { - auto const address = net::ip::make_address(m_socket.address); - auto const port = static_cast<unsigned short>(std::atoi(m_socket.port.data())); - - // Create and launch a listening port - std::make_shared<listener>( - m_ioc, - tcp::endpoint{address, port}, - *this)->run(); - - return EXIT_SUCCESS; - } - -} // namespace HTTP |