From 4aa991e7e4450232df4ceda0b2f439bccce1d260 Mon Sep 17 00:00:00 2001 From: Shauren Date: Tue, 8 Apr 2025 19:15:16 +0200 Subject: Core/Network: Socket refactors * Devirtualize calls to Read and Update by marking concrete implementations as final * Removed derived class template argument * Specialize boost::asio::basic_stream_socket for boost::asio::io_context instead of type-erased any_io_executor * Make socket initialization easier composable (before entering Read loop) * Remove use of deprecated boost::asio::null_buffers and boost::beast::ssl_stream (cherry picked from commit e8b2be3527c7683e8bfca70ed7706fc20da566fd) --- src/server/bnetserver/REST/LoginHttpSession.cpp | 169 +++++++++++------------- 1 file changed, 80 insertions(+), 89 deletions(-) (limited to 'src/server/bnetserver/REST/LoginHttpSession.cpp') diff --git a/src/server/bnetserver/REST/LoginHttpSession.cpp b/src/server/bnetserver/REST/LoginHttpSession.cpp index aff579de7f9..23a317d3726 100644 --- a/src/server/bnetserver/REST/LoginHttpSession.cpp +++ b/src/server/bnetserver/REST/LoginHttpSession.cpp @@ -17,126 +17,117 @@ #include "LoginHttpSession.h" #include "DatabaseEnv.h" +#include "HttpSocket.h" +#include "HttpSslSocket.h" +#include "IpBanCheckConnectionInitializer.h" #include "LoginRESTService.h" #include "SslContext.h" #include "Util.h" +#include -namespace Battlenet -{ -template typename SocketImpl> -LoginHttpSession::LoginHttpSession(boost::asio::ip::tcp::socket&& socket, LoginHttpSessionWrapper& owner) - : BaseSocket(std::move(socket), SslContext::instance()), _owner(owner) -{ -} - -template typename SocketImpl> -LoginHttpSession::~LoginHttpSession() = default; - -template typename SocketImpl> -void LoginHttpSession::Start() -{ - std::string ip_address = this->GetRemoteIpAddress().to_string(); - TC_LOG_TRACE("server.http.session", "{} Accepted connection", this->GetClientInfo()); - - // Verify that this IP is not in the ip_banned table - LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); - - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO); - stmt->setString(0, ip_address); - - this->_queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt) - .WithPreparedCallback([sess = this->shared_from_this()](PreparedQueryResult result) { sess->CheckIpCallback(std::move(result)); })); -} - -template typename SocketImpl> -void LoginHttpSession::CheckIpCallback(PreparedQueryResult result) -{ - if (result) - { - bool banned = false; - do - { - Field* fields = result->Fetch(); - if (fields[0].GetUInt64() != 0) - banned = true; - - } while (result->NextRow()); - - if (banned) - { - TC_LOG_DEBUG("server.http.session", "{} tries to log in using banned IP!", this->GetClientInfo()); - this->CloseSocket(); - return; - } - } - - if constexpr (std::is_same_v>>) - { - this->AsyncHandshake(); - } - else - { - this->ResetHttpParser(); - this->AsyncRead(); - } -} - -template typename SocketImpl> -Trinity::Net::Http::RequestHandlerResult LoginHttpSession::RequestHandler(Trinity::Net::Http::RequestContext& context) +namespace { - return sLoginService.HandleRequest(_owner.shared_from_this(), context); -} - std::shared_ptr ObtainSessionState(Trinity::Net::Http::RequestContext& context, boost::asio::ip::address const& remoteAddress) { using namespace std::string_literals; - std::shared_ptr state; - auto cookieItr = context.request.find(boost::beast::http::field::cookie); if (cookieItr != context.request.end()) { std::vector cookies = Trinity::Tokenize(Trinity::Net::Http::ToStdStringView(cookieItr->value()), ';', false); - std::size_t eq = 0; - auto sessionIdItr = std::find_if(cookies.begin(), cookies.end(), [&](std::string_view cookie) + auto sessionIdItr = std::ranges::find_if(cookies, [](std::string_view const& cookie) { - std::string_view name = cookie; - eq = cookie.find('='); - if (eq != std::string_view::npos) - name = cookie.substr(0, eq); - - return name == LoginHttpSessionWrapper::SESSION_ID_COOKIE; + return cookie.length() > Battlenet::LoginHttpSession::SESSION_ID_COOKIE.length() + && cookie.starts_with(Battlenet::LoginHttpSession::SESSION_ID_COOKIE); }); if (sessionIdItr != cookies.end()) { - std::string_view value = sessionIdItr->substr(eq + 1); - state = sLoginService.FindAndRefreshSessionState(value, remoteAddress); + sessionIdItr->remove_prefix(Battlenet::LoginHttpSession::SESSION_ID_COOKIE.length()); + state = sLoginService.FindAndRefreshSessionState(*sessionIdItr, remoteAddress); } } - if (!state) { state = sLoginService.CreateNewSessionState(remoteAddress); std::string_view host = Trinity::Net::Http::ToStdStringView(context.request[boost::beast::http::field::host]); - if (std::size_t port = host.find(':'); port != std::string_view::npos) + if (size_t port = host.find(':'); port != std::string_view::npos) host.remove_suffix(host.length() - port); - context.response.insert(boost::beast::http::field::set_cookie, Trinity::StringFormat("{}={}; Path=/bnetserver; Domain={}; Secure; HttpOnly; SameSite=None", - LoginHttpSessionWrapper::SESSION_ID_COOKIE, boost::uuids::to_string(state->Id), host)); + context.response.insert(boost::beast::http::field::set_cookie, Trinity::StringFormat("{}{}; Path=/bnetserver; Domain={}; Secure; HttpOnly; SameSite=None", + Battlenet::LoginHttpSession::SESSION_ID_COOKIE, boost::uuids::to_string(state->Id), host)); } - return state; } -template class LoginHttpSession; -template class LoginHttpSession; +template +class LoginHttpSocketImpl final : public SocketImpl +{ +public: + using BaseSocket = SocketImpl; + + explicit LoginHttpSocketImpl(Trinity::Net::IoContextTcpSocket&& socket, Battlenet::LoginHttpSession& owner) + : BaseSocket(std::move(socket)), _owner(owner) + { + } + + LoginHttpSocketImpl(LoginHttpSocketImpl const&) = delete; + LoginHttpSocketImpl(LoginHttpSocketImpl&&) = delete; + LoginHttpSocketImpl& operator=(LoginHttpSocketImpl const&) = delete; + LoginHttpSocketImpl& operator=(LoginHttpSocketImpl&&) = delete; + + ~LoginHttpSocketImpl() = default; + + void Start() override + { + // build initializer chain + boost::container::static_vector, 4> initializers; + + initializers.stable_emplace_back(std::make_shared>(this)); + + if constexpr (std::is_same_v) + initializers.stable_emplace_back(std::make_shared>(this)); + + initializers.stable_emplace_back(std::make_shared>(this)); + initializers.stable_emplace_back(std::make_shared>(this)); + + Trinity::Net::SocketConnectionInitializer::SetupChain(std::span(initializers.data(), initializers.size()))->Start(); + } + + Trinity::Net::Http::RequestHandlerResult RequestHandler(Trinity::Net::Http::RequestContext& context) override + { + return sLoginService.HandleRequest(_owner.shared_from_this(), context); + } + +protected: + std::shared_ptr ObtainSessionState(Trinity::Net::Http::RequestContext& context) const override + { + return ::ObtainSessionState(context, this->GetRemoteIpAddress()); + } + + Battlenet::LoginHttpSession& _owner; +}; + +template<> +LoginHttpSocketImpl::LoginHttpSocketImpl(Trinity::Net::IoContextTcpSocket&& socket, Battlenet::LoginHttpSession& owner) + : BaseSocket(std::move(socket), Battlenet::SslContext::instance()), _owner(owner) +{ +} +} + +namespace Battlenet +{ +LoginHttpSession::LoginHttpSession(Trinity::Net::IoContextTcpSocket&& socket) + : _socket(!SslContext::UsesDevWildcardCertificate() + ? std::shared_ptr(std::make_shared>(std::move(socket), *this)) + : std::shared_ptr(std::make_shared>(std::move(socket), *this))) +{ +} -LoginHttpSessionWrapper::LoginHttpSessionWrapper(boost::asio::ip::tcp::socket&& socket) +void LoginHttpSession::Start() { - if (!SslContext::UsesDevWildcardCertificate()) - _socket = std::make_shared>(std::move(socket), *this); - else - _socket = std::make_shared>(std::move(socket), *this); + TC_LOG_TRACE("server.http.session", "{} Accepted connection", GetClientInfo()); + + return _socket->Start(); } } -- cgit v1.2.3