Older/Server/HttpSession.cpp

101 lines
3.5 KiB
C++
Raw Normal View History

2023-07-21 16:17:01 +08:00
#include "HttpSession.h"
#include "WebsocketSession.h"
#include <boost/config.hpp>
#include <boost/url/parse_path.hpp>
#include <boost/url/url_view.hpp>
#include <iostream>
#include <limits>
HttpSession::HttpSession(boost::asio::ip::tcp::socket &&socket, const std::shared_ptr<SharedState> &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<http::string_body> 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<std::uint64_t>::max());
m_parser->header_limit(std::numeric_limits<std::uint32_t>::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<WebSocketSession>(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();
}