Clio develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
Server.hpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of clio: https://github.com/XRPLF/clio
4 Copyright (c) 2023, the clio developers.
5
6 Permission to use, copy, modify, and distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#pragma once
21
22#include "util/Taggable.hpp"
23#include "util/log/Logger.hpp"
24#include "web/AdminVerificationStrategy.hpp"
25#include "web/HttpSession.hpp"
26#include "web/SslHttpSession.hpp"
27#include "web/dosguard/DOSGuardInterface.hpp"
28#include "web/interface/Concepts.hpp"
29#include "web/ng/impl/ServerSslContext.hpp"
30
31#include <boost/asio/io_context.hpp>
32#include <boost/asio/ip/address.hpp>
33#include <boost/asio/ip/tcp.hpp>
34#include <boost/asio/socket_base.hpp>
35#include <boost/asio/ssl/context.hpp>
36#include <boost/asio/ssl/error.hpp>
37#include <boost/asio/strand.hpp>
38#include <boost/beast/core/error.hpp>
39#include <boost/beast/core/flat_buffer.hpp>
40#include <boost/beast/core/stream_traits.hpp>
41#include <boost/beast/core/tcp_stream.hpp>
42#include <fmt/core.h>
43
44#include <chrono>
45#include <cstdint>
46#include <exception>
47#include <functional>
48#include <memory>
49#include <optional>
50#include <stdexcept>
51#include <string>
52#include <utility>
53
62namespace web {
63
74template <
75 template <typename> class PlainSessionType,
76 template <typename> class SslSessionType,
77 SomeServerHandler HandlerType>
78class Detector : public std::enable_shared_from_this<Detector<PlainSessionType, SslSessionType, HandlerType>> {
79 using std::enable_shared_from_this<Detector<PlainSessionType, SslSessionType, HandlerType>>::shared_from_this;
80
81 util::Logger log_{"WebServer"};
82 boost::beast::tcp_stream stream_;
83 std::optional<std::reference_wrapper<boost::asio::ssl::context>> ctx_;
84 std::reference_wrapper<util::TagDecoratorFactory const> tagFactory_;
85 std::reference_wrapper<dosguard::DOSGuardInterface> const dosGuard_;
86 std::shared_ptr<HandlerType> const handler_;
87 boost::beast::flat_buffer buffer_;
88 std::shared_ptr<AdminVerificationStrategy> const adminVerification_;
89 std::uint32_t maxWsSendingQueueSize_;
90
91public:
104 tcp::socket&& socket,
105 std::optional<std::reference_wrapper<boost::asio::ssl::context>> ctx,
106 std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
107 std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
108 std::shared_ptr<HandlerType> handler,
109 std::shared_ptr<AdminVerificationStrategy> adminVerification,
110 std::uint32_t maxWsSendingQueueSize
111 )
112 : stream_(std::move(socket))
113 , ctx_(ctx)
114 , tagFactory_(std::cref(tagFactory))
115 , dosGuard_(dosGuard)
116 , handler_(std::move(handler))
117 , adminVerification_(std::move(adminVerification))
118 , maxWsSendingQueueSize_(maxWsSendingQueueSize)
119 {
120 }
121
128 void
129 fail(boost::system::error_code ec, char const* message)
130 {
131 if (ec == boost::asio::ssl::error::stream_truncated)
132 return;
133
134 LOG(log_.info()) << "Detector failed (" << message << "): " << ec.message();
135 }
136
138 void
140 {
141 boost::beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
142 async_detect_ssl(stream_, buffer_, boost::beast::bind_front_handler(&Detector::onDetect, shared_from_this()));
143 }
144
151 void
152 onDetect(boost::beast::error_code ec, bool result)
153 {
154 if (ec)
155 return fail(ec, "detect");
156
157 std::string ip;
158 try {
159 ip = stream_.socket().remote_endpoint().address().to_string();
160 } catch (std::exception const&) {
161 return fail(ec, "cannot get remote endpoint");
162 }
163
164 if (result) {
165 if (!ctx_)
166 return fail(ec, "SSL is not supported by this server");
167
168 std::make_shared<SslSessionType<HandlerType>>(
169 stream_.release_socket(),
170 ip,
171 adminVerification_,
172 *ctx_,
173 tagFactory_,
174 dosGuard_,
175 handler_,
176 std::move(buffer_),
177 maxWsSendingQueueSize_
178 )
179 ->run();
180 return;
181 }
182
183 std::make_shared<PlainSessionType<HandlerType>>(
184 stream_.release_socket(),
185 ip,
186 adminVerification_,
187 tagFactory_,
188 dosGuard_,
189 handler_,
190 std::move(buffer_),
191 maxWsSendingQueueSize_
192 )
193 ->run();
194 }
195};
196
206template <
207 template <typename> class PlainSessionType,
208 template <typename> class SslSessionType,
209 SomeServerHandler HandlerType>
210class Server : public std::enable_shared_from_this<Server<PlainSessionType, SslSessionType, HandlerType>> {
211 using std::enable_shared_from_this<Server<PlainSessionType, SslSessionType, HandlerType>>::shared_from_this;
212
213 util::Logger log_{"WebServer"};
214 std::reference_wrapper<boost::asio::io_context> ioc_;
215 std::optional<boost::asio::ssl::context> ctx_;
216 util::TagDecoratorFactory tagFactory_;
217 std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
218 std::shared_ptr<HandlerType> handler_;
219 tcp::acceptor acceptor_;
220 std::shared_ptr<AdminVerificationStrategy> adminVerification_;
221 std::uint32_t maxWsSendingQueueSize_;
222
223public:
237 boost::asio::io_context& ioc,
238 std::optional<boost::asio::ssl::context> ctx,
239 tcp::endpoint endpoint,
240 util::TagDecoratorFactory tagFactory,
242 std::shared_ptr<HandlerType> handler,
243 std::shared_ptr<AdminVerificationStrategy> adminVerification,
244 std::uint32_t maxWsSendingQueueSize
245 )
246 : ioc_(std::ref(ioc))
247 , ctx_(std::move(ctx))
248 , tagFactory_(tagFactory)
249 , dosGuard_(std::ref(dosGuard))
250 , handler_(std::move(handler))
251 , acceptor_(boost::asio::make_strand(ioc))
252 , adminVerification_(std::move(adminVerification))
253 , maxWsSendingQueueSize_(maxWsSendingQueueSize)
254 {
255 boost::beast::error_code ec;
256
257 acceptor_.open(endpoint.protocol(), ec);
258 if (ec)
259 return;
260
261 acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec);
262 if (ec)
263 return;
264
265 acceptor_.bind(endpoint, ec);
266 if (ec) {
267 LOG(log_.error()) << "Failed to bind to endpoint: " << endpoint << ". message: " << ec.message();
268 throw std::runtime_error(
269 fmt::format("Failed to bind to endpoint: {}:{}", endpoint.address().to_string(), endpoint.port())
270 );
271 }
272
273 acceptor_.listen(boost::asio::socket_base::max_listen_connections, ec);
274 if (ec) {
275 LOG(log_.error()) << "Failed to listen at endpoint: " << endpoint << ". message: " << ec.message();
276 throw std::runtime_error(
277 fmt::format("Failed to listen at endpoint: {}:{}", endpoint.address().to_string(), endpoint.port())
278 );
279 }
280 }
281
283 void
285 {
286 doAccept();
287 }
288
289private:
290 void
291 doAccept()
292 {
293 acceptor_.async_accept(
294 boost::asio::make_strand(ioc_.get()),
295 boost::beast::bind_front_handler(&Server::onAccept, shared_from_this())
296 );
297 }
298
299 void
300 onAccept(boost::beast::error_code ec, tcp::socket socket)
301 {
302 if (!ec) {
303 auto ctxRef =
304 ctx_ ? std::optional<std::reference_wrapper<boost::asio::ssl::context>>{ctx_.value()} : std::nullopt;
305
306 std::make_shared<Detector<PlainSessionType, SslSessionType, HandlerType>>(
307 std::move(socket),
308 ctxRef,
309 std::cref(tagFactory_),
310 dosGuard_,
311 handler_,
312 adminVerification_,
313 maxWsSendingQueueSize_
314 )
315 ->run();
316 }
317
318 doAccept();
319 }
320};
321
323template <typename HandlerType>
325
336template <typename HandlerType>
337static std::shared_ptr<HttpServer<HandlerType>>
340 boost::asio::io_context& ioc,
342 std::shared_ptr<HandlerType> const& handler
343)
344{
345 static util::Logger const log{"WebServer"}; // NOLINT(readability-identifier-naming)
346
347 auto expectedSslContext = ng::impl::makeServerSslContext(config);
348 if (not expectedSslContext) {
349 LOG(log.error()) << "Failed to create SSL context: " << expectedSslContext.error();
350 return nullptr;
351 }
352
353 auto const serverConfig = config.getObject("server");
354 auto const address = boost::asio::ip::make_address(serverConfig.get<std::string>("ip"));
355 auto const port = serverConfig.get<unsigned short>("port");
356
357 auto expectedAdminVerification = makeAdminVerificationStrategy(config);
358 if (not expectedAdminVerification.has_value()) {
359 LOG(log.error()) << expectedAdminVerification.error();
360 throw std::logic_error{expectedAdminVerification.error()};
361 }
362
363 // If the transactions number is 200 per ledger, A client which subscribes everything will send 400+ feeds for
364 // each ledger. we allow user delay 3 ledgers by default
365 auto const maxWsSendingQueueSize = serverConfig.get<uint32_t>("ws_max_sending_queue_size");
366
367 auto server = std::make_shared<HttpServer<HandlerType>>(
368 ioc,
369 std::move(expectedSslContext).value(),
370 boost::asio::ip::tcp::endpoint{address, port},
372 dosGuard,
373 handler,
374 std::move(expectedAdminVerification).value(),
375 maxWsSendingQueueSize
376 );
377
378 server->run();
379 return server;
380}
381
382} // namespace web
A simple thread-safe logger for the channel specified in the constructor.
Definition Logger.hpp:110
Pump error(SourceLocationType const &loc=CURRENT_SRC_LOCATION) const
Interface for logging at Severity::ERR severity.
Definition Logger.cpp:215
Pump info(SourceLocationType const &loc=CURRENT_SRC_LOCATION) const
Interface for logging at Severity::NFO severity.
Definition Logger.cpp:205
A factory for TagDecorator instantiation.
Definition Taggable.hpp:169
All the config data will be stored and extracted from this class.
Definition ConfigDefinition.hpp:54
ObjectView getObject(std::string_view prefix, std::optional< std::size_t > idx=std::nullopt) const
Returns the ObjectView specified with the prefix.
Definition ConfigDefinition.cpp:61
The Detector class to detect if the connection is a ssl or not.
Definition Server.hpp:78
void onDetect(boost::beast::error_code ec, bool result)
Handles detection result.
Definition Server.hpp:152
Detector(tcp::socket &&socket, std::optional< std::reference_wrapper< boost::asio::ssl::context > > ctx, std::reference_wrapper< util::TagDecoratorFactory const > tagFactory, std::reference_wrapper< dosguard::DOSGuardInterface > dosGuard, std::shared_ptr< HandlerType > handler, std::shared_ptr< AdminVerificationStrategy > adminVerification, std::uint32_t maxWsSendingQueueSize)
Create a new detector.
Definition Server.hpp:103
void run()
Initiate the detection.
Definition Server.hpp:139
void fail(boost::system::error_code ec, char const *message)
A helper function that is called when any error ocurs.
Definition Server.hpp:129
The WebServer class. It creates server socket and start listening on it.
Definition Server.hpp:210
void run()
Start accepting incoming connections.
Definition Server.hpp:284
Server(boost::asio::io_context &ioc, std::optional< boost::asio::ssl::context > ctx, tcp::endpoint endpoint, util::TagDecoratorFactory tagFactory, dosguard::DOSGuardInterface &dosGuard, std::shared_ptr< HandlerType > handler, std::shared_ptr< AdminVerificationStrategy > adminVerification, std::uint32_t maxWsSendingQueueSize)
Create a new instance of the web server.
Definition Server.hpp:236
The interface of a denial of service guard.
Definition DOSGuardInterface.hpp:44
This namespace implements the web server and related components.
Definition Types.hpp:43
static std::shared_ptr< HttpServer< HandlerType > > makeHttpServer(util::config::ClioConfigDefinition const &config, boost::asio::io_context &ioc, dosguard::DOSGuardInterface &dosGuard, std::shared_ptr< HandlerType > const &handler)
A factory function that spawns a ready to use HTTP server.
Definition Server.hpp:338
std::shared_ptr< AdminVerificationStrategy > makeAdminVerificationStrategy(std::optional< std::string > password)
Factory function for creating an admin verification strategy.
Definition AdminVerificationStrategy.cpp:75