#include "expresscpp/impl/session.hpp" #include "boost/asio.hpp" #include "boost/asio/strand.hpp" #include "boost/beast/http/field.hpp" #include "expresscpp/console.hpp" #include "expresscpp/expresscpp.hpp" #include "expresscpp/impl/listener.hpp" namespace expresscpp { Session::Session(boost::asio::ip::tcp::socket socket, ExpressCpp *express_cpp) : socket_(std::move(socket)), express_cpp_(express_cpp) { assert(express_cpp_ != nullptr); } Session::~Session() { Console::Debug("destroying session"); } void Session::run() { do_read(); } void Session::do_read() { parser_ = std::make_unique(); parser_->body_limit(std::numeric_limits::max()); auto self = shared_from_this(); boost::beast::http::async_read( socket_, buffer_, *parser_, [self](boost::beast::error_code ec, std::size_t bytes_transferred) { self->on_read(ec, bytes_transferred); }); } void Session::on_read(boost::beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); // This means they closed the connection if (ec == boost::beast::http::error::end_of_stream) { return do_close(); } if (ec) { Console::Trace(fmt::format("{}:{}", "read", ec.message())); return; } auto http_request = parser_->get(); const auto path = std::string(http_request.target()); const auto method = beastVerbToHttpMethod(http_request.method()); const auto keep_alive = http_request.keep_alive(); auto req = std::make_shared(path, method); auto res = std::make_shared(this); res->KeepAlive(keep_alive); for (auto const &field : http_request) { const std::string name = boost::beast::http::to_string(field.name()).data(); const std::string value(field.value().data(), field.value().size()); req->setHeader(name, value); } req->setBody(http_request.body()); express_cpp_->HandleRequest(req, res, nullptr); // try to match with slash if not yet found if (!res->response_sent_) { auto path_with_slash = std::string(http_request.target()) + "/"; req->setPath(path_with_slash); express_cpp_->HandleRequest(req, res, nullptr); } if (!res->response_sent_) { Console::Error(fmt::format(R"(no response sent for path "{}")", req->getPath())); } } void Session::on_write(bool close, boost::beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if (ec) { Console::Trace(fmt::format("{}:{}", "write", ec.message())); return; } 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 Session::do_close() { // Send a TCP shutdown boost::beast::error_code ec; socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); // At this point the connection is closed gracefully } } // namespace expresscpp