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