rippled
Loading...
Searching...
No Matches
Door.h
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or 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#ifndef RIPPLE_SERVER_DOOR_H_INCLUDED
21#define RIPPLE_SERVER_DOOR_H_INCLUDED
22
23#include <xrpl/basics/Log.h>
24#include <xrpl/basics/contract.h>
25#include <xrpl/server/detail/PlainHTTPPeer.h>
26#include <xrpl/server/detail/SSLHTTPPeer.h>
27#include <xrpl/server/detail/io_list.h>
28
29#include <boost/asio/basic_waitable_timer.hpp>
30#include <boost/asio/buffer.hpp>
31#include <boost/asio/io_context.hpp>
32#include <boost/asio/ip/tcp.hpp>
33#include <boost/asio/spawn.hpp>
34#include <boost/beast/core/detect_ssl.hpp>
35#include <boost/beast/core/multi_buffer.hpp>
36#include <boost/beast/core/tcp_stream.hpp>
37#include <boost/container/flat_map.hpp>
38
39#include <chrono>
40#include <functional>
41#include <memory>
42
43namespace ripple {
44
46template <class Handler>
47class Door : public io_list::work,
48 public std::enable_shared_from_this<Door<Handler>>
49{
50private:
52 using timer_type = boost::asio::basic_waitable_timer<clock_type>;
53 using error_code = boost::system::error_code;
54 using yield_context = boost::asio::yield_context;
55 using protocol_type = boost::asio::ip::tcp;
56 using acceptor_type = protocol_type::acceptor;
57 using endpoint_type = protocol_type::endpoint;
58 using socket_type = boost::asio::ip::tcp::socket;
59 using stream_type = boost::beast::tcp_stream;
60
61 // Detects SSL on a socket
62 class Detector : public io_list::work,
63 public std::enable_shared_from_this<Detector>
64 {
65 private:
66 Port const& port_;
67 Handler& handler_;
68 boost::asio::io_context& ioc_;
72 boost::asio::strand<boost::asio::io_context::executor_type> strand_;
74
75 public:
77 Port const& port,
78 Handler& handler,
79 boost::asio::io_context& ioc,
80 stream_type&& stream,
81 endpoint_type remote_address,
83 void
84 run();
85 void
86 close() override;
87
88 private:
89 void
91 };
92
94 Port const& port_;
95 Handler& handler_;
96 boost::asio::io_context& ioc_;
98 boost::asio::strand<boost::asio::io_context::executor_type> strand_;
99 bool ssl_;
100 bool plain_;
101
102 void
103 reOpen();
104
105public:
106 Door(
107 Handler& handler,
108 boost::asio::io_context& io_context,
109 Port const& port,
111
112 // Work-around because we can't call shared_from_this in ctor
113 void
114 run();
115
122 void
123 close() override;
124
127 {
128 return acceptor_.local_endpoint();
129 }
130
131private:
132 template <class ConstBufferSequence>
133 void
134 create(
135 bool ssl,
136 ConstBufferSequence const& buffers,
137 stream_type&& stream,
138 endpoint_type remote_address);
139
140 void
142};
143
144template <class Handler>
146 Port const& port,
147 Handler& handler,
148 boost::asio::io_context& ioc,
149 stream_type&& stream,
150 endpoint_type remote_address,
152 : port_(port)
153 , handler_(handler)
154 , ioc_(ioc)
155 , stream_(std::move(stream))
156 , socket_(stream_.socket())
157 , remote_address_(remote_address)
158 , strand_(boost::asio::make_strand(ioc_))
159 , j_(j)
160{
161}
162
163template <class Handler>
164void
166{
168 strand_,
169 std::bind(
171 this->shared_from_this(),
172 std::placeholders::_1));
173}
174
175template <class Handler>
176void
178{
179 stream_.close();
180}
181
182template <class Handler>
183void
184Door<Handler>::Detector::do_detect(boost::asio::yield_context do_yield)
185{
186 boost::beast::multi_buffer buf(16);
187 stream_.expires_after(std::chrono::seconds(15));
188 boost::system::error_code ec;
189 bool const ssl = async_detect_ssl(stream_, buf, do_yield[ec]);
190 stream_.expires_never();
191 if (!ec)
192 {
193 if (ssl)
194 {
195 if (auto sp = ios().template emplace<SSLHTTPPeer<Handler>>(
196 port_,
197 handler_,
198 ioc_,
199 j_,
200 remote_address_,
201 buf.data(),
202 std::move(stream_)))
203 sp->run();
204 return;
205 }
206 if (auto sp = ios().template emplace<PlainHTTPPeer<Handler>>(
207 port_,
208 handler_,
209 ioc_,
210 j_,
211 remote_address_,
212 buf.data(),
213 std::move(stream_)))
214 sp->run();
215 return;
216 }
217 if (ec != boost::asio::error::operation_aborted)
218 {
219 JLOG(j_.trace()) << "Error detecting ssl: " << ec.message() << " from "
220 << remote_address_;
221 }
222}
223
224//------------------------------------------------------------------------------
225
226template <class Handler>
227void
229{
230 error_code ec;
231
232 if (acceptor_.is_open())
233 {
234 acceptor_.close(ec);
235 if (ec)
236 {
238 ss << "Can't close acceptor: " << port_.name << ", "
239 << ec.message();
240 JLOG(j_.error()) << ss.str();
241 Throw<std::runtime_error>(ss.str());
242 }
243 }
244
245 endpoint_type const local_address = endpoint_type(port_.ip, port_.port);
246
247 acceptor_.open(local_address.protocol(), ec);
248 if (ec)
249 {
250 JLOG(j_.error()) << "Open port '" << port_.name
251 << "' failed:" << ec.message();
252 Throw<std::exception>();
253 }
254
255 acceptor_.set_option(
256 boost::asio::ip::tcp::acceptor::reuse_address(true), ec);
257 if (ec)
258 {
259 JLOG(j_.error()) << "Option for port '" << port_.name
260 << "' failed:" << ec.message();
261 Throw<std::exception>();
262 }
263
264 acceptor_.bind(local_address, ec);
265 if (ec)
266 {
267 JLOG(j_.error()) << "Bind port '" << port_.name
268 << "' failed:" << ec.message();
269 Throw<std::exception>();
270 }
271
272 acceptor_.listen(boost::asio::socket_base::max_listen_connections, ec);
273 if (ec)
274 {
275 JLOG(j_.error()) << "Listen on port '" << port_.name
276 << "' failed:" << ec.message();
277 Throw<std::exception>();
278 }
279
280 JLOG(j_.info()) << "Opened " << port_;
281}
282
283template <class Handler>
285 Handler& handler,
286 boost::asio::io_context& io_context,
287 Port const& port,
289 : j_(j)
290 , port_(port)
291 , handler_(handler)
292 , ioc_(io_context)
293 , acceptor_(io_context)
294 , strand_(boost::asio::make_strand(io_context))
295 , ssl_(
296 port_.protocol.count("https") > 0 ||
297 port_.protocol.count("wss") > 0 || port_.protocol.count("wss2") > 0 ||
298 port_.protocol.count("peer") > 0)
299 , plain_(
300 port_.protocol.count("http") > 0 || port_.protocol.count("ws") > 0 ||
301 port_.protocol.count("ws2"))
302{
303 reOpen();
304}
305
306template <class Handler>
307void
309{
311 strand_,
312 std::bind(
314 this->shared_from_this(),
315 std::placeholders::_1));
316}
317
318template <class Handler>
319void
321{
322 if (!strand_.running_in_this_thread())
323 return boost::asio::post(
324 strand_,
325 std::bind(&Door<Handler>::close, this->shared_from_this()));
326 error_code ec;
327 acceptor_.close(ec);
328}
329
330//------------------------------------------------------------------------------
331
332template <class Handler>
333template <class ConstBufferSequence>
334void
336 bool ssl,
337 ConstBufferSequence const& buffers,
338 stream_type&& stream,
339 endpoint_type remote_address)
340{
341 if (ssl)
342 {
343 if (auto sp = ios().template emplace<SSLHTTPPeer<Handler>>(
344 port_,
345 handler_,
346 ioc_,
347 j_,
348 remote_address,
349 buffers,
350 std::move(stream)))
351 sp->run();
352 return;
353 }
354 if (auto sp = ios().template emplace<PlainHTTPPeer<Handler>>(
355 port_,
356 handler_,
357 ioc_,
358 j_,
359 remote_address,
360 buffers,
361 std::move(stream)))
362 sp->run();
363}
364
365template <class Handler>
366void
367Door<Handler>::do_accept(boost::asio::yield_context do_yield)
368{
369 while (acceptor_.is_open())
370 {
371 error_code ec;
372 endpoint_type remote_address;
373 stream_type stream(ioc_);
374 socket_type& socket = stream.socket();
375 acceptor_.async_accept(socket, remote_address, do_yield[ec]);
376 if (ec)
377 {
378 if (ec == boost::asio::error::operation_aborted)
379 break;
380 JLOG(j_.error()) << "accept: " << ec.message();
381 if (ec == boost::asio::error::no_descriptors)
382 {
383 JLOG(j_.info()) << "re-opening acceptor";
384 reOpen();
385 }
386 continue;
387 }
388
389 if (ssl_ && plain_)
390 {
391 if (auto sp = ios().template emplace<Detector>(
392 port_,
393 handler_,
394 ioc_,
395 std::move(stream),
396 remote_address,
397 j_))
398 sp->run();
399 }
400 else if (ssl_ || plain_)
401 {
402 create(
403 ssl_,
404 boost::asio::null_buffers{},
405 std::move(stream),
406 remote_address);
407 }
408 }
409}
410
411} // namespace ripple
412
413#endif
T bind(T... args)
A generic endpoint for log messages.
Definition Journal.h:60
Stream error() const
Definition Journal.h:346
Stream info() const
Definition Journal.h:334
Stream trace() const
Severity stream access functions.
Definition Journal.h:322
boost::asio::strand< boost::asio::io_context::executor_type > strand_
Definition Door.h:72
Handler & handler_
Definition Door.h:67
beast::Journal const j_
Definition Door.h:73
socket_type & socket_
Definition Door.h:70
void close() override
Definition Door.h:177
endpoint_type remote_address_
Definition Door.h:71
void do_detect(yield_context yield)
Definition Door.h:184
stream_type stream_
Definition Door.h:69
Port const & port_
Definition Door.h:66
boost::asio::io_context & ioc_
Definition Door.h:68
Detector(Port const &port, Handler &handler, boost::asio::io_context &ioc, stream_type &&stream, endpoint_type remote_address, beast::Journal j)
Definition Door.h:145
A listening socket.
Definition Door.h:49
void do_accept(yield_context yield)
Definition Door.h:367
void create(bool ssl, ConstBufferSequence const &buffers, stream_type &&stream, endpoint_type remote_address)
Definition Door.h:335
protocol_type::endpoint endpoint_type
Definition Door.h:57
boost::asio::io_context & ioc_
Definition Door.h:96
Door(Handler &handler, boost::asio::io_context &io_context, Port const &port, beast::Journal j)
Definition Door.h:284
boost::beast::tcp_stream stream_type
Definition Door.h:59
boost::asio::basic_waitable_timer< clock_type > timer_type
Definition Door.h:52
beast::Journal const j_
Definition Door.h:93
protocol_type::acceptor acceptor_type
Definition Door.h:56
boost::system::error_code error_code
Definition Door.h:53
bool ssl_
Definition Door.h:99
boost::asio::ip::tcp protocol_type
Definition Door.h:55
boost::asio::strand< boost::asio::io_context::executor_type > strand_
Definition Door.h:98
void reOpen()
Definition Door.h:228
boost::asio::yield_context yield_context
Definition Door.h:54
Handler & handler_
Definition Door.h:95
void close() override
Close the Door listening socket and connections.
Definition Door.h:320
endpoint_type get_endpoint() const
Definition Door.h:126
Port const & port_
Definition Door.h:94
acceptor_type acceptor_
Definition Door.h:97
void run()
Definition Door.h:308
bool plain_
Definition Door.h:100
boost::asio::ip::tcp::socket socket_type
Definition Door.h:58
io_list & ios()
Return the io_list associated with the work.
Definition io_list.h:60
void spawn(Ctx &&ctx, F &&func)
Spawns a coroutine using boost::asio::spawn
Definition Spawn.h:87
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
boost::beast::ssl_stream< socket_type > stream_type
Definition Handshake.h:42
STL namespace.
T str(T... args)
Configuration information for a Server listening port.
Definition Port.h:50
std::uint16_t port
Definition Port.h:55
boost::asio::ip::address ip
Definition Port.h:54
std::string name
Definition Port.h:53