/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #ifndef TRINITYCORE_HTTP_SERVICE_H #define TRINITYCORE_HTTP_SERVICE_H #include "AsioHacksFwd.h" #include "Concepts.h" #include "Define.h" #include "EnumFlag.h" #include "HttpCommon.h" #include "HttpSessionState.h" #include "Optional.h" #include "SocketMgr.h" #include #include #include #include #include namespace Trinity::Net::Http { class AbstractSocket; enum class RequestHandlerFlag { None = 0x0, DoNotLogRequestContent = 0x1, DoNotLogResponseContent = 0x2, }; DEFINE_ENUM_FLAG(RequestHandlerFlag); struct RequestHandler { std::function session, RequestContext& context)> Func; EnumFlag Flags = RequestHandlerFlag::None; }; class TC_NETWORK_API DispatcherService { public: explicit DispatcherService(std::string_view loggerSuffix) : _logger("server.http.dispatcher.") { _logger.append(loggerSuffix); } RequestHandlerResult HandleRequest(std::shared_ptr session, RequestContext& context); static RequestHandlerResult HandleBadRequest(std::shared_ptr session, RequestContext& context); static RequestHandlerResult HandleUnauthorized(std::shared_ptr session, RequestContext& context); static RequestHandlerResult HandlePathNotFound(std::shared_ptr session, RequestContext& context); protected: void RegisterHandler(boost::beast::http::verb method, std::string_view path, std::function session, RequestContext& context)> handler, RequestHandlerFlag flags = RequestHandlerFlag::None); private: using HttpMethodHandlerMap = std::map>; HttpMethodHandlerMap _getHandlers; HttpMethodHandlerMap _postHandlers; std::string _logger; }; class TC_NETWORK_API SessionService { public: explicit SessionService(std::string_view loggerSuffix) : _logger("server.http.session.") { _logger.append(loggerSuffix); } void Start(Asio::IoContext& ioContext); void Stop(); std::shared_ptr FindAndRefreshSessionState(std::string_view id, boost::asio::ip::address const& address); void MarkSessionInactive(boost::uuids::uuid const& id); protected: void InitAndStoreSessionState(std::shared_ptr state, boost::asio::ip::address const& address); void KillInactiveSessions(); private: std::shared_mutex _sessionsMutex; std::map> _sessions; std::mutex _inactiveSessionsMutex; std::set _inactiveSessions; std::unique_ptr _inactiveSessionsKillTimer; std::string _logger; }; template concept HttpRequestHandler = invocable_r, RequestContext&>; template class HttpService : public SocketMgr, public DispatcherService, public SessionService { public: HttpService(std::string_view loggerSuffix) : DispatcherService(loggerSuffix), SessionService(loggerSuffix), _ioContext(nullptr), _logger("server.http.") { _logger.append(loggerSuffix); } bool StartNetwork(Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, int32 threadCount = 1) override { if (!SocketMgr::StartNetwork(ioContext, bindIp, port, threadCount)) return false; SessionService::Start(ioContext); return true; } void StopNetwork() override { SessionService::Stop(); SocketMgr::StopNetwork(); } // http handling using DispatcherService::RegisterHandler; template Callable> void RegisterHandler(boost::beast::http::verb method, std::string_view path, Callable handler, RequestHandlerFlag flags = RequestHandlerFlag::None) { this->DispatcherService::RegisterHandler(method, path, [handler = std::move(handler)](std::shared_ptr session, RequestContext& context) -> RequestHandlerResult { return handler(std::static_pointer_cast(std::move(session)), context); }, flags); } // session tracking virtual std::shared_ptr CreateNewSessionState(boost::asio::ip::address const& address) { std::shared_ptr state = std::make_shared(); InitAndStoreSessionState(state, address); return state; } protected: class Thread : public NetworkThread { protected: void SocketRemoved(std::shared_ptr const& session) override { if (Optional id = session->GetSessionId()) _service->MarkSessionInactive(*id); } private: friend HttpService; SessionService* _service; }; NetworkThread* CreateThreads() const override { Thread* threads = new Thread[this->GetNetworkThreadCount()]; for (int32 i = 0; i < this->GetNetworkThreadCount(); ++i) threads[i]._service = const_cast(this); return threads; } Asio::IoContext* _ioContext; std::string _logger; }; } #endif // TRINITYCORE_HTTP_SERVICE_H