#include "HttpSession.h" #include "WebsocketSession.h" #include #include #include #include #include HttpSession::HttpSession(boost::asio::ip::tcp::socket &&socket, const std::shared_ptr &state) : m_stream(std::move(socket)), m_state(state) { // m_buffer.reserve(1000 * 1000 * 1000); } void HttpSession::run() { doRead(); } void HttpSession::errorReply(const Request &request, boost::beast::http::status status, boost::beast::string_view message) { using namespace boost::beast; // invalid route http::response res{status, request.version()}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, "text/html"); res.keep_alive(request.keep_alive()); res.body() = message; res.prepare_payload(); reply(std::move(res)); } void HttpSession::doRead() { // Construct a new parser for each message m_parser.emplace(); // Apply a reasonable limit to the allowed size // of the body in bytes to prevent abuse. m_parser->body_limit(std::numeric_limits::max()); m_parser->header_limit(std::numeric_limits::max()); m_buffer.clear(); // Set the timeout. m_stream.expires_after(std::chrono::seconds(30)); boost::beast::http::async_read( m_stream, m_buffer, *m_parser, [self{shared_from_this()}](const boost::system::error_code &ec, std::size_t bytes_transferred) { self->onRead(ec, bytes_transferred); }); } void HttpSession::onRead(boost::beast::error_code ec, std::size_t) { // This means they closed the connection if (ec == boost::beast::http::error::end_of_stream) { m_stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); return; } if (ec) { if (ec == boost::asio::error::operation_aborted) return; LOG(info) << ec << " : " << ec.message(); return; } auto &request = m_parser->get(); // See if it is a WebSocket Upgrade if (boost::beast::websocket::is_upgrade(request)) { // Create a websocket session, transferring ownership // of both the socket and the HTTP request. auto session = std::make_shared(m_stream.release_socket(), m_state); session->run(m_parser->release()); return; } boost::urls::url_view view(request.target()); auto path = boost::urls::parse_path(view.path()); TemplateMatches matches; auto handler = m_state->find(*path, matches); if (handler) { (*handler)(*this, request, matches); } else { auto message = "The resource '" + std::string(path->buffer()) + "' was not found."; errorReply(request, boost::beast::http::status::not_found, message); LOG(error) << message; } } void HttpSession::onWrite(boost::beast::error_code ec, std::size_t, bool close) { if (ec) { if (ec == boost::asio::error::operation_aborted) return; std::cerr << "write: " << ec.message() << "\n"; } if (close) { // This means we should close the connection, usually because // the response indicated the "Connection: close" semantic. m_stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); return; } // Read another request doRead(); }