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 public:
103  Door(
104  Handler& handler,
105  boost::asio::io_context& io_context,
106  Port const& port,
107  beast::Journal j);
108 
109  // Work-around because we can't call shared_from_this in ctor
110  void
111  run();
112 
119  void
120  close() override;
121 
123  get_endpoint() const
124  {
125  return acceptor_.local_endpoint();
126  }
127 
128 private:
129  template <class ConstBufferSequence>
130  void
131  create(
132  bool ssl,
133  ConstBufferSequence const& buffers,
134  stream_type&& stream,
135  endpoint_type remote_address);
136 
137  void
138  do_accept(yield_context yield);
139 };
140 
141 template <class Handler>
143  Port const& port,
144  Handler& handler,
145  boost::asio::io_context& ioc,
146  stream_type&& stream,
147  endpoint_type remote_address,
148  beast::Journal j)
149  : port_(port)
150  , handler_(handler)
151  , ioc_(ioc)
152  , stream_(std::move(stream))
153  , socket_(stream_.socket())
154  , remote_address_(remote_address)
155  , strand_(ioc_)
156  , j_(j)
157 {
158 }
159 
160 template <class Handler>
161 void
163 {
164  boost::asio::spawn(
165  strand_,
166  std::bind(
168  this->shared_from_this(),
169  std::placeholders::_1));
170 }
171 
172 template <class Handler>
173 void
175 {
176  stream_.close();
177 }
178 
179 template <class Handler>
180 void
181 Door<Handler>::Detector::do_detect(boost::asio::yield_context do_yield)
182 {
183  boost::beast::multi_buffer buf(16);
184  stream_.expires_after(std::chrono::seconds(15));
185  boost::system::error_code ec;
186  bool const ssl = async_detect_ssl(stream_, buf, do_yield[ec]);
187  stream_.expires_never();
188  if (!ec)
189  {
190  if (ssl)
191  {
192  if (auto sp = ios().template emplace<SSLHTTPPeer<Handler>>(
193  port_,
194  handler_,
195  ioc_,
196  j_,
197  remote_address_,
198  buf.data(),
199  std::move(stream_)))
200  sp->run();
201  return;
202  }
203  if (auto sp = ios().template emplace<PlainHTTPPeer<Handler>>(
204  port_,
205  handler_,
206  ioc_,
207  j_,
208  remote_address_,
209  buf.data(),
210  std::move(stream_)))
211  sp->run();
212  return;
213  }
214  if (ec != boost::asio::error::operation_aborted)
215  {
216  JLOG(j_.trace()) << "Error detecting ssl: " << ec.message() << " from "
217  << remote_address_;
218  }
219 }
220 
221 //------------------------------------------------------------------------------
222 
223 template <class Handler>
225  Handler& handler,
226  boost::asio::io_context& io_context,
227  Port const& port,
228  beast::Journal j)
229  : j_(j)
230  , port_(port)
231  , handler_(handler)
232  , ioc_(io_context)
233  , acceptor_(io_context)
234  , strand_(io_context)
235  , ssl_(
236  port_.protocol.count("https") > 0 ||
237  port_.protocol.count("wss") > 0 || port_.protocol.count("wss2") > 0 ||
238  port_.protocol.count("peer") > 0)
239  , plain_(
240  port_.protocol.count("http") > 0 || port_.protocol.count("ws") > 0 ||
241  port_.protocol.count("ws2"))
242 {
243  error_code ec;
244  endpoint_type const local_address = endpoint_type(port.ip, port.port);
245 
246  acceptor_.open(local_address.protocol(), ec);
247  if (ec)
248  {
249  JLOG(j_.error()) << "Open port '" << port.name
250  << "' failed:" << ec.message();
251  Throw<std::exception>();
252  }
253 
254  acceptor_.set_option(
255  boost::asio::ip::tcp::acceptor::reuse_address(true), ec);
256  if (ec)
257  {
258  JLOG(j_.error()) << "Option for port '" << port.name
259  << "' failed:" << ec.message();
260  Throw<std::exception>();
261  }
262 
263  acceptor_.bind(local_address, ec);
264  if (ec)
265  {
266  JLOG(j_.error()) << "Bind port '" << port.name
267  << "' failed:" << ec.message();
268  Throw<std::exception>();
269  }
270 
271  acceptor_.listen(boost::asio::socket_base::max_connections, ec);
272  if (ec)
273  {
274  JLOG(j_.error()) << "Listen on port '" << port.name
275  << "' failed:" << ec.message();
276  Throw<std::exception>();
277  }
278 
279  JLOG(j_.info()) << "Opened " << port;
280 }
281 
282 template <class Handler>
283 void
285 {
286  boost::asio::spawn(
287  strand_,
288  std::bind(
290  this->shared_from_this(),
291  std::placeholders::_1));
292 }
293 
294 template <class Handler>
295 void
297 {
298  if (!strand_.running_in_this_thread())
299  return strand_.post(
300  std::bind(&Door<Handler>::close, this->shared_from_this()));
301  error_code ec;
302  acceptor_.close(ec);
303 }
304 
305 //------------------------------------------------------------------------------
306 
307 template <class Handler>
308 template <class ConstBufferSequence>
309 void
311  bool ssl,
312  ConstBufferSequence const& buffers,
313  stream_type&& stream,
314  endpoint_type remote_address)
315 {
316  if (ssl)
317  {
318  if (auto sp = ios().template emplace<SSLHTTPPeer<Handler>>(
319  port_,
320  handler_,
321  ioc_,
322  j_,
323  remote_address,
324  buffers,
325  std::move(stream)))
326  sp->run();
327  return;
328  }
329  if (auto sp = ios().template emplace<PlainHTTPPeer<Handler>>(
330  port_,
331  handler_,
332  ioc_,
333  j_,
334  remote_address,
335  buffers,
336  std::move(stream)))
337  sp->run();
338 }
339 
340 template <class Handler>
341 void
342 Door<Handler>::do_accept(boost::asio::yield_context do_yield)
343 {
344  while (acceptor_.is_open())
345  {
346  error_code ec;
347  endpoint_type remote_address;
348  stream_type stream(ioc_);
349  socket_type& socket = stream.socket();
350  acceptor_.async_accept(socket, remote_address, do_yield[ec]);
351  if (ec && ec != boost::asio::error::operation_aborted)
352  {
353  JLOG(j_.error()) << "accept: " << ec.message();
354  }
355  if (ec == boost::asio::error::operation_aborted)
356  break;
357  if (ec)
358  continue;
359 
360  if (ssl_ && plain_)
361  {
362  if (auto sp = ios().template emplace<Detector>(
363  port_,
364  handler_,
365  ioc_,
366  std::move(stream),
367  remote_address,
368  j_))
369  sp->run();
370  }
371  else if (ssl_ || plain_)
372  {
373  create(
374  ssl_,
375  boost::asio::null_buffers{},
376  std::move(stream),
377  remote_address);
378  }
379  }
380 }
381 
382 } // namespace ripple
383 
384 #endif
ripple::SSLHTTPPeer
Definition: SSLHTTPPeer.h:35
ripple::Door::do_accept
void do_accept(yield_context yield)
Definition: Door.h:342
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::Detector::do_detect
void do_detect(yield_context yield)
Definition: Door.h:181
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:49
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
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:224
ripple::Door::j_
const beast::Journal j_
Definition: Door.h:93
ripple::Port::name
std::string name
Definition: Port.h:48
ripple::Port::port
std::uint16_t port
Definition: Port.h:50
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:44
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:142
ripple::Door::Detector::handler_
Handler & handler_
Definition: Door.h:67
ripple::stream_type
boost::beast::ssl_stream< socket_type > stream_type
Definition: Handshake.h:40
memory
ripple::Door::create
void create(bool ssl, ConstBufferSequence const &buffers, stream_type &&stream, endpoint_type remote_address)
Definition: Door.h:310
ripple::Door::run
void run()
Definition: Door.h:284
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
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:296
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:174
ripple::PlainHTTPPeer
Definition: PlainHTTPPeer.h:32
mutex
ripple::Door::ioc_
boost::asio::io_context & ioc_
Definition: Door.h:96
ripple::Door::Detector::run
void run()
Definition: Door.h:162
ripple::Door::acceptor_
acceptor_type acceptor_
Definition: Door.h:97
ripple::Door::get_endpoint
endpoint_type get_endpoint() const
Definition: Door.h:123