TrinityCore
Loading...
Searching...
No Matches
HttpService.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "HttpService.h"
19#include "BaseHttpSocket.h"
20#include "CryptoRandom.h"
21#include "Timezone.h"
22#include <boost/beast/version.hpp>
23#include <boost/uuid/string_generator.hpp>
24#include <boost/uuid/uuid_io.hpp>
25#include <fmt/chrono.h>
26
27namespace Trinity::Net::Http
28{
30{
32}
33
38
39RequestHandlerResult DispatcherService::HandleRequest(std::shared_ptr<AbstractSocket> session, RequestContext& context)
40{
41 TC_LOG_DEBUG(_logger, "{} Starting request {} {}", session->GetClientInfo(),
42 ToStdStringView(context.request.method_string()), ToStdStringView(context.request.target()));
43
44 std::string_view path = [&]
45 {
46 std::string_view path = ToStdStringView(context.request.target());
47 size_t queryIndex = path.find('?');
48 if (queryIndex != std::string_view::npos)
49 path = path.substr(0, queryIndex);
50 return path;
51 }();
52
53 context.handler = [&]() -> HttpMethodHandlerMap::mapped_type const*
54 {
55 switch (context.request.method())
56 {
57 case boost::beast::http::verb::get:
58 case boost::beast::http::verb::head:
59 {
60 auto itr = _getHandlers.find(path);
61 return itr != _getHandlers.end() ? &itr->second : nullptr;
62 }
63 case boost::beast::http::verb::post:
64 {
65 auto itr = _postHandlers.find(path);
66 return itr != _postHandlers.end() ? &itr->second : nullptr;
67 }
68 default:
69 break;
70 }
71 return nullptr;
72 }();
73
74 SystemTimePoint responseDate = SystemTimePoint::clock::now();
75 context.response.set(boost::beast::http::field::date, StringFormat("{:%a, %d %b %Y %T GMT}", responseDate - Timezone::GetSystemZoneOffsetAt(responseDate)));
76 context.response.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING);
77 context.response.keep_alive(context.request.keep_alive());
78
79 if (!context.handler)
80 return HandlePathNotFound(std::move(session), context);
81
82 return context.handler->Func(std::move(session), context);
83}
84
85RequestHandlerResult DispatcherService::HandleBadRequest(std::shared_ptr<AbstractSocket> /*session*/, RequestContext& context)
86{
87 context.response.result(boost::beast::http::status::bad_request);
89}
90
91RequestHandlerResult DispatcherService::HandleUnauthorized(std::shared_ptr<AbstractSocket> /*session*/, RequestContext& context)
92{
93 context.response.result(boost::beast::http::status::unauthorized);
95}
96
97RequestHandlerResult DispatcherService::HandlePathNotFound(std::shared_ptr<AbstractSocket> /*session*/, RequestContext& context)
98{
99 context.response.result(boost::beast::http::status::not_found);
101}
102
103void DispatcherService::RegisterHandler(boost::beast::http::verb method, std::string_view path,
104 std::function<RequestHandlerResult(std::shared_ptr<AbstractSocket> session, RequestContext& context)> handler,
106{
107 HttpMethodHandlerMap& handlerMap = [&]() -> HttpMethodHandlerMap&
108 {
109 switch (method)
110 {
111 case boost::beast::http::verb::get:
112 return _getHandlers;
113 case boost::beast::http::verb::post:
114 return _postHandlers;
115 default:
116 {
117 std::string_view methodString = ToStdStringView(boost::beast::http::to_string(method));
118 ABORT_MSG("Tried to register a handler for unsupported HTTP method " STRING_VIEW_FMT, STRING_VIEW_FMT_ARG(methodString));
119 }
120 }
121 }();
122
123 handlerMap[std::string(path)] = { .Func = std::move(handler), .Flags = flags };
124 TC_LOG_INFO(_logger, "Registered new handler for {} {}", ToStdStringView(boost::beast::http::to_string(method)), path);
125}
126
127void SessionService::InitAndStoreSessionState(std::shared_ptr<SessionState> state, boost::asio::ip::address const& address)
128{
129 state->RemoteAddress = address;
130
131 // Generate session id
132 {
133 std::scoped_lock lock{ _sessionsMutex };
134
135 while (state->Id.is_nil() || _sessions.contains(state->Id))
136 std::copy_n(Trinity::Crypto::GetRandomBytes<16>().begin(), 16, state->Id.begin());
137
138 TC_LOG_DEBUG(_logger, "Client at {} created new session {}", address, boost::uuids::to_string(state->Id));
139 _sessions[state->Id] = std::move(state);
140 }
141}
142
144{
145 _inactiveSessionsKillTimer = std::make_unique<Asio::DeadlineTimer>(ioContext);
146 _inactiveSessionsKillTimer->expires_after(1min);
147 _inactiveSessionsKillTimer->async_wait([this](boost::system::error_code const& err)
148 {
149 if (err)
150 return;
151
153 });
154}
155
157{
159 {
160 std::scoped_lock lock{ _sessionsMutex };
161 _sessions.clear();
162 }
163 {
164 std::scoped_lock lock{ _inactiveSessionsMutex };
165 _inactiveSessions.clear();
166 }
167}
168
169std::shared_ptr<SessionState> SessionService::FindAndRefreshSessionState(std::string_view id, boost::asio::ip::address const& address)
171 std::shared_ptr<SessionState> state;
172
173 {
174 std::shared_lock lock{ _sessionsMutex };
175 auto itr = _sessions.find(boost::uuids::string_generator()(id.begin(), id.end()));
176 if (itr == _sessions.end())
177 {
178 TC_LOG_DEBUG(_logger, "Client at {} attempted to use a session {} that was expired", address, id);
179 return nullptr; // no session
180 }
181
182 state = itr->second;
183 }
184
185 if (state->RemoteAddress != address)
186 {
187 TC_LOG_ERROR(_logger, "Client at {} attempted to use a session {} that was last accessed from {}, denied access",
188 address, id, state->RemoteAddress);
189 return nullptr;
190 }
191
192 {
193 std::scoped_lock inactiveSessionsLock{ _inactiveSessionsMutex };
194 _inactiveSessions.erase(state->Id);
195 }
196
197 return state;
198}
199
200void SessionService::MarkSessionInactive(boost::uuids::uuid const& id)
201{
202 bool wasActive = true;
203 {
204 std::scoped_lock inactiveSessionsLock{ _inactiveSessionsMutex };
205 wasActive = _inactiveSessions.insert(id).second;
206 }
207
208 if (wasActive)
209 {
210 std::shared_lock lock{ _sessionsMutex };
211 auto itr = _sessions.find(id);
212 if (itr != _sessions.end())
213 {
214 itr->second->InactiveTimestamp = TimePoint::clock::now() + Minutes(5);
215 TC_LOG_TRACE(_logger, "Session {} marked as inactive", boost::uuids::to_string(id));
216 }
217 }
218}
219
221{
222 std::set<boost::uuids::uuid> inactiveSessions;
223
224 {
225 std::scoped_lock lock{ _inactiveSessionsMutex };
226 std::swap(_inactiveSessions, inactiveSessions);
227 }
228
229 {
230 TimePoint now = TimePoint::clock::now();
231 std::size_t inactiveSessionsCount = inactiveSessions.size();
232
233 std::scoped_lock lock{ _sessionsMutex };
234 for (auto itr = inactiveSessions.begin(); itr != inactiveSessions.end(); )
235 {
236 auto sessionItr = _sessions.find(*itr);
237 if (sessionItr == _sessions.end() || sessionItr->second->InactiveTimestamp < now)
238 {
239 _sessions.erase(sessionItr);
240 itr = inactiveSessions.erase(itr);
241 }
242 else
243 ++itr;
244 }
245
246 TC_LOG_DEBUG(_logger, "Killed {} inactive sessions", inactiveSessionsCount - inactiveSessions.size());
247 }
248
249 {
250 // restore sessions not killed to inactive queue
251 std::scoped_lock lock{ _inactiveSessionsMutex };
252 for (auto itr = inactiveSessions.begin(); itr != inactiveSessions.end(); )
253 {
254 auto node = inactiveSessions.extract(itr++);
255 _inactiveSessions.insert(std::move(node));
256 }
257 }
258
259 _inactiveSessionsKillTimer->expires_after(1min);
260 _inactiveSessionsKillTimer->async_wait([this](boost::system::error_code const& err)
261 {
262 if (err)
263 return;
264
266 });
267}
268}
#define STRING_VIEW_FMT_ARG(str)
Definition Define.h:147
#define STRING_VIEW_FMT
Definition Define.h:146
uint16 flags
std::chrono::system_clock::time_point SystemTimePoint
Definition Duration.h:41
std::chrono::steady_clock::time_point TimePoint
time_point shorthand typedefs
Definition Duration.h:40
std::chrono::minutes Minutes
Minutes shorthand typedef.
Definition Duration.h:32
#define ABORT_MSG
Definition Errors.h:88
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
#define TC_LOG_TRACE(filterType__, message__,...)
Definition Log.h:178
constexpr bool HasFlag(T flag) const
Definition EnumFlag.h:106
RequestHandlerResult HandleRequest(std::shared_ptr< AbstractSocket > session, RequestContext &context)
static RequestHandlerResult HandlePathNotFound(std::shared_ptr< AbstractSocket > session, RequestContext &context)
static RequestHandlerResult HandleUnauthorized(std::shared_ptr< AbstractSocket > session, RequestContext &context)
static RequestHandlerResult HandleBadRequest(std::shared_ptr< AbstractSocket > session, RequestContext &context)
void RegisterHandler(boost::beast::http::verb method, std::string_view path, std::function< RequestHandlerResult(std::shared_ptr< AbstractSocket > session, RequestContext &context)> handler, RequestHandlerFlag flags=RequestHandlerFlag::None)
std::map< std::string, RequestHandler, std::less<> > HttpMethodHandlerMap
Definition HttpService.h:74
std::map< boost::uuids::uuid, std::shared_ptr< SessionState > > _sessions
std::unique_ptr< Asio::DeadlineTimer > _inactiveSessionsKillTimer
std::set< boost::uuids::uuid > _inactiveSessions
std::shared_ptr< SessionState > FindAndRefreshSessionState(std::string_view id, boost::asio::ip::address const &address)
void Start(Asio::IoContext &ioContext)
void InitAndStoreSessionState(std::shared_ptr< SessionState > state, boost::asio::ip::address const &address)
void MarkSessionInactive(boost::uuids::uuid const &id)
std::string_view ToStdStringView(boost::beast::string_view bsw)
Definition HttpCommon.h:43
TC_NETWORK_API bool CanLogResponseContent(RequestContext const &context)
TC_NETWORK_API bool CanLogRequestContent(RequestContext const &context)
Minutes GetSystemZoneOffsetAt(SystemTimePoint date)
Definition Timezone.cpp:138
std::string StringFormat(FormatString< Args... > fmt, Args &&... args) noexcept
Default TC string format function.
struct RequestHandler const * handler
Definition HttpCommon.h:37
std::function< RequestHandlerResult(std::shared_ptr< AbstractSocket > session, RequestContext &context)> Func
Definition HttpService.h:50
EnumFlag< RequestHandlerFlag > Flags
Definition HttpService.h:51