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#include <boost/asio/basic_waitable_timer.hpp>
29#include <boost/asio/buffer.hpp>
30#include <boost/asio/io_context.hpp>
31#include <boost/asio/ip/tcp.hpp>
32#include <boost/asio/spawn.hpp>
33#include <boost/beast/core/detect_ssl.hpp>
34#include <boost/beast/core/multi_buffer.hpp>
35#include <boost/beast/core/tcp_stream.hpp>
36#include <boost/container/flat_map.hpp>
37#include <chrono>
38#include <condition_variable>
39#include <functional>
40#include <memory>
41#include <mutex>
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::io_context::strand 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::io_context::strand 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_(ioc_)
159 , j_(j)
160{
161}
162
163template <class Handler>
164void
166{
167 boost::asio::spawn(
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_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_(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{
310 boost::asio::spawn(
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 strand_.post(
324 std::bind(&Door<Handler>::close, this->shared_from_this()));
325 error_code ec;
326 acceptor_.close(ec);
327}
328
329//------------------------------------------------------------------------------
330
331template <class Handler>
332template <class ConstBufferSequence>
333void
335 bool ssl,
336 ConstBufferSequence const& buffers,
337 stream_type&& stream,
338 endpoint_type remote_address)
339{
340 if (ssl)
341 {
342 if (auto sp = ios().template emplace<SSLHTTPPeer<Handler>>(
343 port_,
344 handler_,
345 ioc_,
346 j_,
347 remote_address,
348 buffers,
349 std::move(stream)))
350 sp->run();
351 return;
352 }
353 if (auto sp = ios().template emplace<PlainHTTPPeer<Handler>>(
354 port_,
355 handler_,
356 ioc_,
357 j_,
358 remote_address,
359 buffers,
360 std::move(stream)))
361 sp->run();
362}
363
364template <class Handler>
365void
366Door<Handler>::do_accept(boost::asio::yield_context do_yield)
367{
368 while (acceptor_.is_open())
369 {
370 error_code ec;
371 endpoint_type remote_address;
372 stream_type stream(ioc_);
373 socket_type& socket = stream.socket();
374 acceptor_.async_accept(socket, remote_address, do_yield[ec]);
375 if (ec)
376 {
377 if (ec == boost::asio::error::operation_aborted)
378 break;
379 JLOG(j_.error()) << "accept: " << ec.message();
380 if (ec == boost::asio::error::no_descriptors)
381 {
382 JLOG(j_.info()) << "re-opening acceptor";
383 reOpen();
384 }
385 continue;
386 }
387
388 if (ssl_ && plain_)
389 {
390 if (auto sp = ios().template emplace<Detector>(
391 port_,
392 handler_,
393 ioc_,
394 std::move(stream),
395 remote_address,
396 j_))
397 sp->run();
398 }
399 else if (ssl_ || plain_)
400 {
401 create(
402 ssl_,
403 boost::asio::null_buffers{},
404 std::move(stream),
405 remote_address);
406 }
407 }
408}
409
410} // namespace ripple
411
412#endif
T bind(T... args)
A generic endpoint for log messages.
Definition: Journal.h:59
Stream error() const
Definition: Journal.h:335
Stream info() const
Definition: Journal.h:323
Stream trace() const
Severity stream access functions.
Definition: Journal.h:311
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
boost::asio::io_context::strand strand_
Definition: Door.h:72
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:366
void create(bool ssl, ConstBufferSequence const &buffers, stream_type &&stream, endpoint_type remote_address)
Definition: Door.h:334
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::asio::io_context::strand strand_
Definition: Door.h:98
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
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
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:43
STL namespace.
T str(T... args)
Configuration information for a Server listening port.
Definition: Port.h:49
std::uint16_t port
Definition: Port.h:54
boost::asio::ip::address ip
Definition: Port.h:53
std::string name
Definition: Port.h:52