#include "HttpSession.h" #include "Application.h" #include #include #include #include #include #include HttpSession::HttpSession(boost::asio::ip::tcp::socket &&socket) : m_stream(std::move(socket)) { } 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) { using namespace boost::beast; // This means they closed the connection if (ec == 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(); auto path = boost::urls::parse_path(request.target()); if (!path) { LOG(error) << request.target() << "failed, error: " << path.error().message(); errorReply(request, http::status::bad_request, "Illegal request-target"); return; } auto application = Amass::Singleton::instance(); boost::urls::matches matches; auto handler = application->find(*path, matches); if (handler) { try { (*handler)(*this, request, matches); } catch (const std::exception &e) { boost::stacktrace::stacktrace trace = boost::stacktrace::stacktrace::from_current_exception(); LOG(error) << e.what() << ", trace:\n" << trace; } } else { std::ostringstream oss; oss << "The resource '" << request.target() << "' was not found."; auto message = oss.str(); errorReply(request, 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(); }