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/server/impl/io_list.h>
24 #include <ripple/basics/contract.h>
25 #include <ripple/basics/Log.h>
26 #include <ripple/server/impl/PlainHTTPPeer.h>
27 #include <ripple/server/impl/SSLHTTPPeer.h>
28 #include <boost/beast/core/detect_ssl.hpp>
29 #include <boost/beast/core/multi_buffer.hpp>
30 #include <boost/beast/core/tcp_stream.hpp>
31 #include <boost/asio/basic_waitable_timer.hpp>
32 #include <boost/asio/buffer.hpp>
33 #include <boost/asio/io_context.hpp>
34 #include <boost/asio/ip/tcp.hpp>
35 #include <boost/asio/spawn.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
48  : public io_list::work
49  , public std::enable_shared_from_this<Door<Handler>>
50 {
51 private:
53  using timer_type = boost::asio::basic_waitable_timer<clock_type>;
54  using error_code = boost::system::error_code;
55  using yield_context = boost::asio::yield_context;
56  using protocol_type = boost::asio::ip::tcp;
57  using acceptor_type = protocol_type::acceptor;
58  using endpoint_type = protocol_type::endpoint;
59  using socket_type = boost::asio::ip::tcp::socket;
60  using stream_type = boost::beast::tcp_stream;
61 
62  // Detects SSL on a socket
63  class Detector
64  : 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:
78  Detector(
79  Port const& port,
80  Handler& handler,
81  boost::asio::io_context& ioc,
82  stream_type&& stream,
83  endpoint_type remote_address,
84  beast::Journal j);
85  void run();
86  void close() override;
87 
88  private:
89  void do_detect (yield_context yield);
90  };
91 
93  Port const& port_;
94  Handler& handler_;
95  boost::asio::io_context& ioc_;
97  boost::asio::io_context::strand strand_;
98  bool ssl_;
99  bool plain_;
100 
101 public:
102  Door(Handler& handler, boost::asio::io_context& io_context,
103  Port const& port, beast::Journal j);
104 
105  // Work-around because we can't call shared_from_this in ctor
106  void run();
107 
114  void close() override;
115 
117  {
118  return acceptor_.local_endpoint();
119  }
120 
121 private:
122  template <class ConstBufferSequence>
123  void create (bool ssl, ConstBufferSequence const& buffers,
124  stream_type&& stream, endpoint_type remote_address);
125 
126  void do_accept (yield_context yield);
127 };
128 
129 template <class Handler>
131  Port const& port,
132  Handler& handler,
133  boost::asio::io_context& ioc,
134  stream_type&& stream,
135  endpoint_type remote_address,
136  beast::Journal j)
137  : port_(port)
138  , handler_(handler)
139  , ioc_(ioc)
140  , stream_(std::move(stream))
141  , socket_(stream_.socket())
142  , remote_address_(remote_address)
143  , strand_(ioc_)
144  , j_(j)
145 {
146 }
147 
148 template<class Handler>
149 void
152 {
153  boost::asio::spawn(strand_, std::bind (&Detector::do_detect,
154  this->shared_from_this(), std::placeholders::_1));
155 }
156 
157 template<class Handler>
158 void
161 {
162  stream_.close();
163 }
164 
165 template<class Handler>
166 void
168 do_detect(boost::asio::yield_context do_yield)
169 {
170  boost::beast::multi_buffer buf(16);
171  stream_.expires_after(std::chrono::seconds(15));
172  boost::system::error_code ec;
173  bool const ssl = async_detect_ssl(stream_, buf, do_yield[ec]);
174  stream_.expires_never();
175  if (! ec)
176  {
177  if (ssl)
178  {
179  if (auto sp = ios().template emplace<SSLHTTPPeer<Handler>>(
180  port_, handler_, ioc_, j_, remote_address_,
181  buf.data(), std::move(stream_)))
182  sp->run();
183  return;
184  }
185  if (auto sp = ios().template emplace<PlainHTTPPeer<Handler>>(
186  port_, handler_, ioc_, j_, remote_address_,
187  buf.data(), std::move(stream_)))
188  sp->run();
189  return;
190  }
191  if (ec != boost::asio::error::operation_aborted)
192  {
193  JLOG(j_.trace()) <<
194  "Error detecting ssl: " << ec.message() <<
195  " from " << remote_address_;
196  }
197 }
198 
199 //------------------------------------------------------------------------------
200 
201 template<class Handler>
203 Door(Handler& handler, boost::asio::io_context& io_context,
204  Port const& port, beast::Journal j)
205  : j_(j)
206  , port_(port)
207  , handler_(handler)
208  , ioc_(io_context)
209  , acceptor_(io_context)
210  , strand_(io_context)
211  , ssl_(
212  port_.protocol.count("https") > 0 ||
213  port_.protocol.count("wss") > 0 ||
214  port_.protocol.count("wss2") > 0 ||
215  port_.protocol.count("peer") > 0)
216  , plain_(
217  port_.protocol.count("http") > 0 ||
218  port_.protocol.count("ws") > 0 ||
219  port_.protocol.count("ws2"))
220 {
221  error_code ec;
222  endpoint_type const local_address =
223  endpoint_type(port.ip, port.port);
224 
225  acceptor_.open(local_address.protocol(), ec);
226  if (ec)
227  {
228  JLOG(j_.error()) <<
229  "Open port '" << port.name << "' failed:" << ec.message();
230  Throw<std::exception> ();
231  }
232 
233  acceptor_.set_option(
234  boost::asio::ip::tcp::acceptor::reuse_address(true), ec);
235  if (ec)
236  {
237  JLOG(j_.error()) <<
238  "Option for port '" << port.name << "' failed:" << ec.message();
239  Throw<std::exception> ();
240  }
241 
242  acceptor_.bind(local_address, ec);
243  if (ec)
244  {
245  JLOG(j_.error()) <<
246  "Bind port '" << port.name << "' failed:" << ec.message();
247  Throw<std::exception> ();
248  }
249 
250  acceptor_.listen(boost::asio::socket_base::max_connections, ec);
251  if (ec)
252  {
253  JLOG(j_.error()) <<
254  "Listen on port '" << port.name << "' failed:" << ec.message();
255  Throw<std::exception> ();
256  }
257 
258  JLOG(j_.info()) <<
259  "Opened " << port;
260 }
261 
262 template<class Handler>
263 void
266 {
267  boost::asio::spawn(strand_, std::bind(&Door<Handler>::do_accept,
268  this->shared_from_this(), std::placeholders::_1));
269 }
270 
271 template<class Handler>
272 void
275 {
276  if (! strand_.running_in_this_thread())
277  return strand_.post(std::bind(
278  &Door<Handler>::close, this->shared_from_this()));
279  error_code ec;
280  acceptor_.close(ec);
281 }
282 
283 //------------------------------------------------------------------------------
284 
285 template<class Handler>
286 template<class ConstBufferSequence>
287 void
289 create(bool ssl, ConstBufferSequence const& buffers,
290  stream_type&& stream, endpoint_type remote_address)
291 {
292  if (ssl)
293  {
294  if (auto sp = ios().template emplace<SSLHTTPPeer<Handler>>(
295  port_, handler_, ioc_, j_, remote_address,
296  buffers, std::move(stream)))
297  sp->run();
298  return;
299  }
300  if (auto sp = ios().template emplace<PlainHTTPPeer<Handler>>(
301  port_, handler_, ioc_, j_, remote_address,
302  buffers, std::move(stream)))
303  sp->run();
304 }
305 
306 template<class Handler>
307 void
309 do_accept(boost::asio::yield_context do_yield)
310 {
311  while (acceptor_.is_open())
312  {
313  error_code ec;
314  endpoint_type remote_address;
315  stream_type stream (ioc_);
316  socket_type &socket = stream.socket();
317  acceptor_.async_accept (socket, remote_address, do_yield[ec]);
318  if (ec && ec != boost::asio::error::operation_aborted)
319  {
320  JLOG(j_.error()) <<
321  "accept: " << ec.message();
322  }
323  if (ec == boost::asio::error::operation_aborted)
324  break;
325  if (ec)
326  continue;
327 
328  if (ssl_ && plain_)
329  {
330  if (auto sp = ios().template emplace<Detector>(
331  port_, handler_, ioc_, std::move(stream),
332  remote_address, j_))
333  sp->run();
334  }
335  else if (ssl_ || plain_)
336  {
337  create(ssl_, boost::asio::null_buffers{},
338  std::move(stream), remote_address);
339  }
340  }
341 }
342 
343 } // ripple
344 
345 #endif
ripple::SSLHTTPPeer
Definition: SSLHTTPPeer.h:35
ripple::Door::do_accept
void do_accept(yield_context yield)
Definition: Door.h:309
std::chrono::steady_clock
ripple::Door::stream_type
boost::beast::tcp_stream stream_type
Definition: Door.h:60
std::bind
T bind(T... args)
ripple::Door::Detector::port_
Port const & port_
Definition: Door.h:68
ripple::Door::ssl_
bool ssl_
Definition: Door.h:98
ripple::Door::Detector::do_detect
void do_detect(yield_context yield)
Definition: Door.h:168
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:287
functional
ripple::Port::ip
boost::asio::ip::address ip
Definition: Port.h:43
ripple::Door::Detector::strand_
boost::asio::io_context::strand strand_
Definition: Door.h:74
ripple::Door::socket_type
boost::asio::ip::tcp::socket socket_type
Definition: Door.h:59
std::chrono::seconds
ripple::stream_type
boost::beast::ssl_stream< socket_type > stream_type
Definition: Handshake.h:40
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:53
ripple::io_list::work::ios
io_list & ios()
Return the io_list associated with the work.
Definition: io_list.h:59
ripple::Door::Detector::ioc_
boost::asio::io_context & ioc_
Definition: Door.h:70
ripple::Door::Door
Door(Handler &handler, boost::asio::io_context &io_context, Port const &port, beast::Journal j)
Definition: Door.h:203
ripple::Door::j_
const beast::Journal j_
Definition: Door.h:92
ripple::Port::name
std::string name
Definition: Port.h:42
ripple::Port::port
std::uint16_t port
Definition: Port.h:44
ripple::Door::Detector::j_
const beast::Journal j_
Definition: Door.h:75
ripple::Door::error_code
boost::system::error_code error_code
Definition: Door.h:54
ripple::Door::protocol_type
boost::asio::ip::tcp protocol_type
Definition: Door.h:56
ripple::Door::Detector::socket_
socket_type & socket_
Definition: Door.h:72
ripple::Door::Detector
Definition: Door.h:63
ripple::Door::Detector::stream_
stream_type stream_
Definition: Door.h:71
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:57
chrono
ripple::Door::plain_
bool plain_
Definition: Door.h:99
std::enable_shared_from_this
beast::Journal::error
Stream error() const
Definition: Journal.h:307
beast::Journal::info
Stream info() const
Definition: Journal.h:297
ripple::Door::endpoint_type
protocol_type::endpoint endpoint_type
Definition: Door.h:58
ripple::Door::port_
Port const & port_
Definition: Door.h:93
ripple::Door::handler_
Handler & handler_
Definition: Door.h:94
ripple::io_list::work
Definition: io_list.h:38
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:60
ripple::Door::Detector::remote_address_
endpoint_type remote_address_
Definition: Door.h:73
ripple::Port
Configuration information for a Server listening port.
Definition: Port.h:38
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:130
ripple::Door::Detector::handler_
Handler & handler_
Definition: Door.h:69
memory
ripple::Door::create
void create(bool ssl, ConstBufferSequence const &buffers, stream_type &&stream, endpoint_type remote_address)
Definition: Door.h:289
ripple::Door::run
void run()
Definition: Door.h:265
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:55
std
STL namespace.
ripple::Door::close
void close() override
Close the Door listening socket and connections.
Definition: Door.h:274
condition_variable
ripple::Door::strand_
boost::asio::io_context::strand strand_
Definition: Door.h:97
ripple::Door::Detector::close
void close() override
Definition: Door.h:160
ripple::PlainHTTPPeer
Definition: PlainHTTPPeer.h:32
mutex
ripple::Door::ioc_
boost::asio::io_context & ioc_
Definition: Door.h:95
ripple::Door::Detector::run
void run()
Definition: Door.h:151
ripple::Door::acceptor_
acceptor_type acceptor_
Definition: Door.h:96
ripple::Door::get_endpoint
endpoint_type get_endpoint() const
Definition: Door.h:116