rippled
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 <ripple/basics/Log.h>
24 #include <ripple/basics/contract.h>
25 #include <ripple/server/impl/PlainHTTPPeer.h>
26 #include <ripple/server/impl/SSLHTTPPeer.h>
27 #include <ripple/server/impl/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 
43 namespace ripple {
44 
46 template <class Handler>
47 class Door : public io_list::work,
48  public std::enable_shared_from_this<Door<Handler>>
49 {
50 private:
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:
76  Detector(
77  Port const& port,
78  Handler& handler,
79  boost::asio::io_context& ioc,
80  stream_type&& stream,
81  endpoint_type remote_address,
82  beast::Journal j);
83  void
84  run();
85  void
86  close() override;
87 
88  private:
89  void
90  do_detect(yield_context yield);
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 
105 public:
106  Door(
107  Handler& handler,
108  boost::asio::io_context& io_context,
109  Port const& port,
110  beast::Journal j);
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 
126  get_endpoint() const
127  {
128  return acceptor_.local_endpoint();
129  }
130 
131 private:
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
141  do_accept(yield_context yield);
142 };
143 
144 template <class Handler>
146  Port const& port,
147  Handler& handler,
148  boost::asio::io_context& ioc,
149  stream_type&& stream,
150  endpoint_type remote_address,
151  beast::Journal j)
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 
163 template <class Handler>
164 void
166 {
167  boost::asio::spawn(
168  strand_,
169  std::bind(
171  this->shared_from_this(),
172  std::placeholders::_1));
173 }
174 
175 template <class Handler>
176 void
178 {
179  stream_.close();
180 }
181 
182 template <class Handler>
183 void
184 Door<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 
226 template <class Handler>
227 void
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 
283 template <class Handler>
285  Handler& handler,
286  boost::asio::io_context& io_context,
287  Port const& port,
288  beast::Journal j)
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 
306 template <class Handler>
307 void
309 {
310  boost::asio::spawn(
311  strand_,
312  std::bind(
314  this->shared_from_this(),
315  std::placeholders::_1));
316 }
317 
318 template <class Handler>
319 void
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 
331 template <class Handler>
332 template <class ConstBufferSequence>
333 void
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 
364 template <class Handler>
365 void
366 Door<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
ripple::SSLHTTPPeer
Definition: SSLHTTPPeer.h:35
ripple::Door::do_accept
void do_accept(yield_context yield)
Definition: Door.h:366
std::chrono::steady_clock
ripple::Door::stream_type
boost::beast::tcp_stream stream_type
Definition: Door.h:59
std::bind
T bind(T... args)
ripple::Door::Detector::port_
Port const & port_
Definition: Door.h:66
ripple::Door::ssl_
bool ssl_
Definition: Door.h:99
ripple::Door::reOpen
void reOpen()
Definition: Door.h:228
ripple::Door::Detector::do_detect
void do_detect(yield_context yield)
Definition: Door.h:184
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
functional
ripple::Port::ip
boost::asio::ip::address ip
Definition: Port.h:53
ripple::Door::Detector::strand_
boost::asio::io_context::strand strand_
Definition: Door.h:72
ripple::Door::socket_type
boost::asio::ip::tcp::socket socket_type
Definition: Door.h:58
std::chrono::seconds
std::stringstream
STL class.
ripple::Door
A listening socket.
Definition: Door.h:47
ripple::Door::timer_type
boost::asio::basic_waitable_timer< clock_type > timer_type
Definition: Door.h:52
ripple::io_list::work::ios
io_list & ios()
Return the io_list associated with the work.
Definition: io_list.h:60
ripple::Door::Detector::ioc_
boost::asio::io_context & ioc_
Definition: Door.h:68
ripple::Door::Door
Door(Handler &handler, boost::asio::io_context &io_context, Port const &port, beast::Journal j)
Definition: Door.h:284
ripple::Door::j_
const beast::Journal j_
Definition: Door.h:93
ripple::Port::name
std::string name
Definition: Port.h:52
ripple::Port::port
std::uint16_t port
Definition: Port.h:54
ripple::Door::Detector::j_
const beast::Journal j_
Definition: Door.h:73
ripple::Door::error_code
boost::system::error_code error_code
Definition: Door.h:53
ripple::Door::protocol_type
boost::asio::ip::tcp protocol_type
Definition: Door.h:55
ripple::Door::Detector::socket_
socket_type & socket_
Definition: Door.h:70
ripple::Door::Detector
Definition: Door.h:62
ripple::Door::Detector::stream_
stream_type stream_
Definition: Door.h:69
std::enable_shared_from_this< Door< Handler > >::shared_from_this
T shared_from_this(T... args)
ripple::Door::acceptor_type
protocol_type::acceptor acceptor_type
Definition: Door.h:56
chrono
ripple::Door::plain_
bool plain_
Definition: Door.h:100
std::enable_shared_from_this
beast::Journal::error
Stream error() const
Definition: Journal.h:333
beast::Journal::info
Stream info() const
Definition: Journal.h:321
ripple::Door::endpoint_type
protocol_type::endpoint endpoint_type
Definition: Door.h:57
ripple::Door::port_
Port const & port_
Definition: Door.h:94
ripple::Door::handler_
Handler & handler_
Definition: Door.h:95
ripple::io_list::work
Definition: io_list.h:38
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::Door::Detector::remote_address_
endpoint_type remote_address_
Definition: Door.h:71
ripple::Port
Configuration information for a Server listening port.
Definition: Port.h:48
ripple::Door::Detector::Detector
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
ripple::Door::Detector::handler_
Handler & handler_
Definition: Door.h:67
ripple::stream_type
boost::beast::ssl_stream< socket_type > stream_type
Definition: Handshake.h:43
memory
ripple::Door::create
void create(bool ssl, ConstBufferSequence const &buffers, stream_type &&stream, endpoint_type remote_address)
Definition: Door.h:334
ripple::Door::run
void run()
Definition: Door.h:308
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
protocol
Definition: ValidatorList.h:38
ripple::Door::yield_context
boost::asio::yield_context yield_context
Definition: Door.h:54
std
STL namespace.
ripple::Door::close
void close() override
Close the Door listening socket and connections.
Definition: Door.h:320
condition_variable
ripple::Door::strand_
boost::asio::io_context::strand strand_
Definition: Door.h:98
ripple::Door::Detector::close
void close() override
Definition: Door.h:177
ripple::PlainHTTPPeer
Definition: PlainHTTPPeer.h:32
mutex
std::stringstream::str
T str(T... args)
ripple::Door::ioc_
boost::asio::io_context & ioc_
Definition: Door.h:96
ripple::Door::Detector::run
void run()
Definition: Door.h:165
ripple::Door::acceptor_
acceptor_type acceptor_
Definition: Door.h:97
ripple::Door::get_endpoint
endpoint_type get_endpoint() const
Definition: Door.h:126