mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-29 05:11:55 +01:00
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
This commit is contained in:
@@ -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 <boost/container/static_vector.hpp>
|
||||
|
||||
namespace Battlenet
|
||||
namespace
|
||||
{
|
||||
template<template<typename> typename SocketImpl>
|
||||
LoginHttpSession<SocketImpl>::LoginHttpSession(boost::asio::ip::tcp::socket&& socket, LoginHttpSessionWrapper& owner)
|
||||
: BaseSocket(std::move(socket), SslContext::instance()), _owner(owner)
|
||||
{
|
||||
}
|
||||
|
||||
template<template<typename> typename SocketImpl>
|
||||
LoginHttpSession<SocketImpl>::~LoginHttpSession() = default;
|
||||
|
||||
template<template<typename> typename SocketImpl>
|
||||
void LoginHttpSession<SocketImpl>::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<template<typename> typename SocketImpl>
|
||||
void LoginHttpSession<SocketImpl>::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<BaseSocket, Trinity::Net::Http::SslSocket<LoginHttpSession<Trinity::Net::Http::SslSocket>>>)
|
||||
{
|
||||
this->AsyncHandshake();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->ResetHttpParser();
|
||||
this->AsyncRead();
|
||||
}
|
||||
}
|
||||
|
||||
template<template<typename> typename SocketImpl>
|
||||
Trinity::Net::Http::RequestHandlerResult LoginHttpSession<SocketImpl>::RequestHandler(Trinity::Net::Http::RequestContext& context)
|
||||
{
|
||||
return sLoginService.HandleRequest(_owner.shared_from_this(), context);
|
||||
}
|
||||
|
||||
std::shared_ptr<Trinity::Net::Http::SessionState> ObtainSessionState(Trinity::Net::Http::RequestContext& context, boost::asio::ip::address const& remoteAddress)
|
||||
{
|
||||
using namespace std::string_literals;
|
||||
|
||||
std::shared_ptr<Trinity::Net::Http::SessionState> state;
|
||||
|
||||
auto cookieItr = context.request.find(boost::beast::http::field::cookie);
|
||||
if (cookieItr != context.request.end())
|
||||
{
|
||||
std::vector<std::string_view> 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<Trinity::Net::Http::SslSocket>;
|
||||
template class LoginHttpSession<Trinity::Net::Http::Socket>;
|
||||
|
||||
LoginHttpSessionWrapper::LoginHttpSessionWrapper(boost::asio::ip::tcp::socket&& socket)
|
||||
template<typename SocketImpl>
|
||||
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<std::shared_ptr<Trinity::Net::SocketConnectionInitializer>, 4> initializers;
|
||||
|
||||
initializers.stable_emplace_back(std::make_shared<Trinity::Net::IpBanCheckConnectionInitializer<BaseSocket>>(this));
|
||||
|
||||
if constexpr (std::is_same_v<BaseSocket, Trinity::Net::Http::SslSocket>)
|
||||
initializers.stable_emplace_back(std::make_shared<Trinity::Net::SslHandshakeConnectionInitializer<BaseSocket>>(this));
|
||||
|
||||
initializers.stable_emplace_back(std::make_shared<Trinity::Net::Http::HttpConnectionInitializer<BaseSocket>>(this));
|
||||
initializers.stable_emplace_back(std::make_shared<Trinity::Net::ReadConnectionInitializer<BaseSocket>>(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<Trinity::Net::Http::SessionState> ObtainSessionState(Trinity::Net::Http::RequestContext& context) const override
|
||||
{
|
||||
return ::ObtainSessionState(context, this->GetRemoteIpAddress());
|
||||
}
|
||||
|
||||
Battlenet::LoginHttpSession& _owner;
|
||||
};
|
||||
|
||||
template<>
|
||||
LoginHttpSocketImpl<Trinity::Net::Http::SslSocket>::LoginHttpSocketImpl(Trinity::Net::IoContextTcpSocket&& socket, Battlenet::LoginHttpSession& owner)
|
||||
: BaseSocket(std::move(socket), Battlenet::SslContext::instance()), _owner(owner)
|
||||
{
|
||||
if (!SslContext::UsesDevWildcardCertificate())
|
||||
_socket = std::make_shared<LoginHttpSession<Trinity::Net::Http::SslSocket>>(std::move(socket), *this);
|
||||
else
|
||||
_socket = std::make_shared<LoginHttpSession<Trinity::Net::Http::Socket>>(std::move(socket), *this);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Battlenet
|
||||
{
|
||||
LoginHttpSession::LoginHttpSession(Trinity::Net::IoContextTcpSocket&& socket)
|
||||
: _socket(!SslContext::UsesDevWildcardCertificate()
|
||||
? std::shared_ptr<AbstractSocket>(std::make_shared<LoginHttpSocketImpl<Trinity::Net::Http::SslSocket>>(std::move(socket), *this))
|
||||
: std::shared_ptr<AbstractSocket>(std::make_shared<LoginHttpSocketImpl<Trinity::Net::Http::Socket>>(std::move(socket), *this)))
|
||||
{
|
||||
}
|
||||
|
||||
void LoginHttpSession::Start()
|
||||
{
|
||||
TC_LOG_TRACE("server.http.session", "{} Accepted connection", GetClientInfo());
|
||||
|
||||
return _socket->Start();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user