rippled
BaseHTTPPeer.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_BASEHTTPPEER_H_INCLUDED
21 #define RIPPLE_SERVER_BASEHTTPPEER_H_INCLUDED
22 
23 #include <ripple/basics/Log.h>
24 #include <ripple/server/Session.h>
25 #include <ripple/server/impl/io_list.h>
26 #include <ripple/beast/net/IPAddressConversion.h>
27 #include <boost/beast/core/stream_traits.hpp>
28 #include <boost/beast/http/read.hpp>
29 #include <boost/beast/http/message.hpp>
30 #include <boost/beast/http/parser.hpp>
31 #include <boost/beast/http/dynamic_body.hpp>
32 #include <boost/asio/ip/tcp.hpp>
33 #include <boost/asio/ssl/stream.hpp>
34 #include <boost/asio/streambuf.hpp>
35 #include <boost/asio/spawn.hpp>
36 #include <atomic>
37 #include <cassert>
38 #include <chrono>
39 #include <functional>
40 #include <memory>
41 #include <mutex>
42 #include <type_traits>
43 #include <vector>
44 
45 namespace ripple {
46 
48 template<class Handler, class Impl>
50  : public io_list::work
51  , public Session
52 {
53 protected:
55  using error_code = boost::system::error_code;
56  using endpoint_type = boost::asio::ip::tcp::endpoint;
57  using yield_context = boost::asio::yield_context;
58 
59  enum
60  {
61  // Size of our read/write buffer
62  bufferSize = 4 * 1024,
63 
64  // Max seconds without completing a message
66  timeoutSecondsLocal = 3 //used for localhost clients
67  };
68 
69  struct buffer
70  {
71  buffer(void const* ptr, std::size_t len)
72  : data(new char[len])
73  , bytes(len)
74  , used(0)
75  {
76  memcpy(data.get(), ptr, len);
77  }
78 
82  };
83 
84  Port const& port_;
85  Handler& handler_;
86  boost::asio::executor_work_guard<boost::asio::executor> work_;
87  boost::asio::strand<boost::asio::executor> strand_;
90 
93 
94  boost::asio::streambuf read_buf_;
99  bool graceful_ = false;
100  bool complete_ = false;
101  boost::system::error_code ec_;
102 
103  int request_count_ = 0;
106 
107  //--------------------------------------------------------------------------
108 
109 public:
110  template <class ConstBufferSequence>
111  BaseHTTPPeer(
112  Port const& port,
113  Handler& handler,
114  boost::asio::executor const& executor,
116  endpoint_type remote_address,
117  ConstBufferSequence const& buffers);
118 
119  virtual
120  ~BaseHTTPPeer();
121 
122  Session&
124  {
125  return *this;
126  }
127 
128  void close() override;
129 
130 protected:
131  Impl&
133  {
134  return *static_cast<Impl*>(this);
135  }
136 
137  void
138  fail(error_code ec, char const* what);
139 
140  void
141  start_timer();
142 
143  void
144  cancel_timer();
145 
146  void
147  on_timer();
148 
149  void
150  do_read(yield_context do_yield);
151 
152  void
153  on_write(error_code const& ec,
154  std::size_t bytes_transferred);
155 
156  void
157  do_writer(std::shared_ptr <Writer> const& writer,
158  bool keep_alive, yield_context do_yield);
159 
160  virtual
161  void
162  do_request() = 0;
163 
164  virtual
165  void
166  do_close() = 0;
167 
168  // Session
169 
171  journal() override
172  {
173  return journal_;
174  }
175 
176  Port const&
177  port() override
178  {
179  return port_;
180  }
181 
183  remoteAddress() override
184  {
186  }
187 
189  request() override
190  {
191  return message_;
192  }
193 
194  void
195  write(void const* buffer, std::size_t bytes) override;
196 
197  void
198  write(std::shared_ptr <Writer> const& writer,
199  bool keep_alive) override;
200 
202  detach() override;
203 
204  void
205  complete() override;
206 
207  void
208  close(bool graceful) override;
209 };
210 
211 //------------------------------------------------------------------------------
212 
213 template <class Handler, class Impl>
214 template <class ConstBufferSequence>
216  Port const& port,
217  Handler& handler,
218  boost::asio::executor const& executor,
219  beast::Journal journal,
220  endpoint_type remote_address,
221  ConstBufferSequence const& buffers)
222  : port_(port)
223  , handler_(handler)
224  , work_(executor)
225  , strand_(executor)
226  , remote_address_(remote_address)
227  , journal_(journal)
228 {
229  read_buf_.commit(boost::asio::buffer_copy(read_buf_.prepare(
230  boost::asio::buffer_size(buffers)), buffers));
231  static std::atomic <int> sid;
232  nid_ = ++sid;
233  id_ = std::string("#") + std::to_string(nid_) + " ";
234  JLOG(journal_.trace()) << id_ <<
235  "accept: " << remote_address_.address();
236 }
237 
238 template<class Handler, class Impl>
241 {
242  handler_.onClose(session(), ec_);
243  JLOG(journal_.trace()) << id_ <<
244  "destroyed: " << request_count_ <<
245  ((request_count_ == 1) ? " request" : " requests");
246 }
247 
248 template<class Handler, class Impl>
249 void
252 {
253  if(! strand_.running_in_this_thread())
254  return post(
255  strand_,
256  std::bind(
257  (void (BaseHTTPPeer::*)(void)) & BaseHTTPPeer::close,
258  impl().shared_from_this()));
259  boost::beast::get_lowest_layer(impl().stream_).close();
260 }
261 
262 //------------------------------------------------------------------------------
263 
264 template<class Handler, class Impl>
265 void
267 fail(error_code ec, char const* what)
268 {
269  if(! ec_ && ec != boost::asio::error::operation_aborted)
270  {
271  ec_ = ec;
272  JLOG(journal_.trace()) << id_ <<
273  std::string(what) << ": " << ec.message();
274  boost::beast::get_lowest_layer(impl().stream_).close();
275  }
276 }
277 
278 template<class Handler, class Impl>
279 void
282 {
283  boost::beast::get_lowest_layer(impl().stream_).expires_after(
285  remote_address_.address().is_loopback() ?
286  timeoutSecondsLocal :
287  timeoutSeconds)
288  );
289 }
290 
291 // Convenience for discarding the error code
292 template<class Handler, class Impl>
293 void
296 {
297  boost::beast::get_lowest_layer(impl().stream_).expires_never();
298 }
299 
300 // Called when session times out
301 template<class Handler, class Impl>
302 void
305 {
306  auto ec = boost::system::errc::make_error_code(
307  boost::system::errc::timed_out);
308  fail(ec, "timer");
309 }
310 
311 //------------------------------------------------------------------------------
312 
313 template<class Handler, class Impl>
314 void
317 {
318  complete_ = false;
319  error_code ec;
320  start_timer();
321  boost::beast::http::async_read(impl().stream_,
322  read_buf_, message_, do_yield[ec]);
323  cancel_timer();
324  if(ec == boost::beast::http::error::end_of_stream)
325  return do_close();
326  if(ec == boost::beast::error::timeout)
327  return on_timer();
328  if(ec)
329  return fail(ec, "http::read");
330  do_request();
331 }
332 
333 // Send everything in the write queue.
334 // The write queue must not be empty upon entry.
335 template<class Handler, class Impl>
336 void
339  std::size_t bytes_transferred)
340 {
341  cancel_timer();
342  if(ec == boost::beast::error::timeout)
343  return on_timer();
344  if(ec)
345  return fail(ec, "write");
346  bytes_out_ += bytes_transferred;
347  {
348  std::lock_guard lock(mutex_);
349  wq2_.clear();
350  wq2_.reserve(wq_.size());
351  std::swap(wq2_, wq_);
352  }
353  if(! wq2_.empty())
354  {
356  v.reserve(wq2_.size());
357  for(auto const& b : wq2_)
358  v.emplace_back(b.data.get(), b.bytes);
359  start_timer();
360  return boost::asio::async_write(
361  impl().stream_,
362  v,
363  bind_executor(
364  strand_,
365  std::bind(
366  &BaseHTTPPeer::on_write,
367  impl().shared_from_this(),
368  std::placeholders::_1,
369  std::placeholders::_2)));
370  }
371  if(! complete_)
372  return;
373  if(graceful_)
374  return do_close();
375  boost::asio::spawn(strand_,
377  impl().shared_from_this(), std::placeholders::_1));
378 }
379 
380 template<class Handler, class Impl>
381 void
384  bool keep_alive, yield_context do_yield)
385 {
386  std::function <void(void)> resume;
387  {
388  auto const p = impl().shared_from_this();
390  [this, p, writer, keep_alive]()
391  {
392  boost::asio::spawn(strand_, std::bind(
393  &BaseHTTPPeer<Handler, Impl>::do_writer, p, writer, keep_alive,
394  std::placeholders::_1));
395  });
396  }
397 
398  for(;;)
399  {
400  if(! writer->prepare(bufferSize, resume))
401  return;
402  error_code ec;
403  auto const bytes_transferred = boost::asio::async_write(
404  impl().stream_, writer->data(), boost::asio::transfer_at_least(1),
405  do_yield[ec]);
406  if(ec)
407  return fail(ec, "writer");
408  writer->consume(bytes_transferred);
409  if(writer->complete())
410  break;
411  }
412 
413  if(! keep_alive)
414  return do_close();
415 
416  boost::asio::spawn(strand_, std::bind(&BaseHTTPPeer<Handler, Impl>::do_read,
417  impl().shared_from_this(), std::placeholders::_1));
418 }
419 
420 //------------------------------------------------------------------------------
421 
422 // Send a copy of the data.
423 template<class Handler, class Impl>
424 void
427  void const* buf, std::size_t bytes)
428 {
429  if(bytes == 0)
430  return;
431  if([&]
432  {
433  std::lock_guard lock(mutex_);
434  wq_.emplace_back(buf, bytes);
435  return wq_.size() == 1 && wq2_.size() == 0;
436  }())
437  {
438  if(! strand_.running_in_this_thread())
439  return post(
440  strand_,
441  std::bind(
442  &BaseHTTPPeer::on_write,
443  impl().shared_from_this(),
444  error_code{},
445  0));
446  else
447  return on_write(error_code{}, 0);
448  }
449 }
450 
451 template<class Handler, class Impl>
452 void
455  bool keep_alive)
456 {
457  boost::asio::spawn(bind_executor(
458  strand_,
459  std::bind(
461  impl().shared_from_this(),
462  writer,
463  keep_alive,
464  std::placeholders::_1)));
465 }
466 
467 // DEPRECATED
468 // Make the Session asynchronous
469 template<class Handler, class Impl>
473 {
474  return impl().shared_from_this();
475 }
476 
477 // DEPRECATED
478 // Called to indicate the response has been written(but not sent)
479 template<class Handler, class Impl>
480 void
483 {
484  if(! strand_.running_in_this_thread())
485  return post(
486  strand_,
487  std::bind(
489  impl().shared_from_this()));
490 
491  message_ = {};
492  complete_ = true;
493 
494  {
495  std::lock_guard lock(mutex_);
496  if(! wq_.empty() && ! wq2_.empty())
497  return;
498  }
499 
500  // keep-alive
501  boost::asio::spawn(bind_executor(
502  strand_,
503  std::bind(
505  impl().shared_from_this(),
506  std::placeholders::_1)));
507 }
508 
509 // DEPRECATED
510 // Called from the Handler to close the session.
511 template<class Handler, class Impl>
512 void
514 close(bool graceful)
515 {
516  if(! strand_.running_in_this_thread())
517  return post(
518  strand_,
519  std::bind(
520  (void (BaseHTTPPeer::*)(bool)) &
522  impl().shared_from_this(),
523  graceful));
524 
525  complete_ = true;
526  if(graceful)
527  {
528  graceful_ = true;
529  {
530  std::lock_guard lock(mutex_);
531  if(! wq_.empty() || ! wq2_.empty())
532  return;
533  }
534  return do_close();
535  }
536 
537  boost::beast::get_lowest_layer(impl().stream_).close();
538 }
539 
540 } // ripple
541 
542 #endif
std::chrono::system_clock
ripple::BaseHTTPPeer::nid_
std::size_t nid_
Definition: BaseHTTPPeer.h:92
std::bind
T bind(T... args)
std::string
STL class.
std::shared_ptr
STL class.
ripple::BaseHTTPPeer::request
http_request_type & request() override
Returns the current HTTP request.
Definition: BaseHTTPPeer.h:189
ripple::BaseHTTPPeer::start_timer
void start_timer()
Definition: BaseHTTPPeer.h:281
ripple::BaseHTTPPeer
Represents an active connection.
Definition: BaseHTTPPeer.h:49
functional
ripple::http_request_type
boost::beast::http::request< boost::beast::http::dynamic_body > http_request_type
Definition: Handoff.h:31
ripple::BaseHTTPPeer::~BaseHTTPPeer
virtual ~BaseHTTPPeer()
Definition: BaseHTTPPeer.h:240
std::vector::reserve
T reserve(T... args)
ripple::BaseHTTPPeer::handler_
Handler & handler_
Definition: BaseHTTPPeer.h:85
vector
ripple::BaseHTTPPeer::complete_
bool complete_
Definition: BaseHTTPPeer.h:100
std::chrono::seconds
ripple::BaseHTTPPeer::impl
Impl & impl()
Definition: BaseHTTPPeer.h:132
std::unique_ptr::get
T get(T... args)
std::lock_guard
STL class.
ripple::BaseHTTPPeer::close
void close() override
Definition: BaseHTTPPeer.h:251
ripple::BaseHTTPPeer::buffer::bytes
std::size_t bytes
Definition: BaseHTTPPeer.h:80
ripple::BaseHTTPPeer::remote_address_
endpoint_type remote_address_
Definition: BaseHTTPPeer.h:88
std::function
ripple::BaseHTTPPeer::fail
void fail(error_code ec, char const *what)
Definition: BaseHTTPPeer.h:267
ripple::Session
Persistent state information for a connection session.
Definition: Session.h:40
beast::IPAddressConversion::from_asio
static IP::Endpoint from_asio(boost::asio::ip::address const &address)
Definition: IPAddressConversion.h:58
ripple::BaseHTTPPeer::message_
http_request_type message_
Definition: BaseHTTPPeer.h:95
ripple::BaseHTTPPeer::cancel_timer
void cancel_timer()
Definition: BaseHTTPPeer.h:295
ripple::BaseHTTPPeer::ec_
boost::system::error_code ec_
Definition: BaseHTTPPeer.h:101
ripple::BaseHTTPPeer::detach
std::shared_ptr< Session > detach() override
Detach the session.
Definition: BaseHTTPPeer.h:472
ripple::BaseHTTPPeer::request_count_
int request_count_
Definition: BaseHTTPPeer.h:103
ripple::BaseHTTPPeer::BaseHTTPPeer
BaseHTTPPeer(Port const &port, Handler &handler, boost::asio::executor const &executor, beast::Journal journal, endpoint_type remote_address, ConstBufferSequence const &buffers)
Definition: BaseHTTPPeer.h:215
ripple::BaseHTTPPeer::do_request
virtual void do_request()=0
ripple::BaseHTTPPeer::buffer::data
std::unique_ptr< char[]> data
Definition: BaseHTTPPeer.h:79
chrono
ripple::BaseHTTPPeer::mutex_
std::mutex mutex_
Definition: BaseHTTPPeer.h:98
ripple::BaseHTTPPeer::bufferSize
@ bufferSize
Definition: BaseHTTPPeer.h:62
ripple::BaseHTTPPeer::buffer::buffer
buffer(void const *ptr, std::size_t len)
Definition: BaseHTTPPeer.h:71
std::to_string
T to_string(T... args)
ripple::BaseHTTPPeer::session
Session & session()
Definition: BaseHTTPPeer.h:123
ripple::BaseHTTPPeer::remoteAddress
beast::IP::Endpoint remoteAddress() override
Returns the remote address of the connection.
Definition: BaseHTTPPeer.h:183
ripple::BaseHTTPPeer< Handler, PlainHTTPPeer< Handler > >::error_code
boost::system::error_code error_code
Definition: BaseHTTPPeer.h:55
ripple::BaseHTTPPeer::on_write
void on_write(error_code const &ec, std::size_t bytes_transferred)
Definition: BaseHTTPPeer.h:338
ripple::io_list::work
Definition: io_list.h:38
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:60
ripple::BaseHTTPPeer::graceful_
bool graceful_
Definition: BaseHTTPPeer.h:99
atomic
ripple::Port
Configuration information for a Server listening port.
Definition: Port.h:38
ripple::BaseHTTPPeer::do_close
virtual void do_close()=0
ripple::BaseHTTPPeer::bytes_out_
std::size_t bytes_out_
Definition: BaseHTTPPeer.h:105
ripple::BaseHTTPPeer::journal
beast::Journal journal() override
Returns the Journal to use for logging.
Definition: BaseHTTPPeer.h:171
memory
ripple::BaseHTTPPeer::port
Port const & port() override
Returns the Port settings for this connection.
Definition: BaseHTTPPeer.h:177
std::swap
T swap(T... args)
ripple::BaseHTTPPeer::id_
std::string id_
Definition: BaseHTTPPeer.h:91
ripple::BaseHTTPPeer::do_read
void do_read(yield_context do_yield)
Definition: BaseHTTPPeer.h:316
std::vector::emplace_back
T emplace_back(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::BaseHTTPPeer::port_
Port const & port_
Definition: BaseHTTPPeer.h:84
ripple::BaseHTTPPeer::bytes_in_
std::size_t bytes_in_
Definition: BaseHTTPPeer.h:104
ripple::BaseHTTPPeer::do_writer
void do_writer(std::shared_ptr< Writer > const &writer, bool keep_alive, yield_context do_yield)
Definition: BaseHTTPPeer.h:383
ripple::BaseHTTPPeer::wq2_
std::vector< buffer > wq2_
Definition: BaseHTTPPeer.h:97
ripple::BaseHTTPPeer::wq_
std::vector< buffer > wq_
Definition: BaseHTTPPeer.h:96
ripple::BaseHTTPPeer< Handler, PlainHTTPPeer< Handler > >::endpoint_type
boost::asio::ip::tcp::endpoint endpoint_type
Definition: BaseHTTPPeer.h:56
cassert
ripple::BaseHTTPPeer::read_buf_
boost::asio::streambuf read_buf_
Definition: BaseHTTPPeer.h:94
mutex
std::size_t
beast::IP::Endpoint
A version-independent IP address and port combination.
Definition: IPEndpoint.h:39
ripple::BaseHTTPPeer::write
void write(void const *buffer, std::size_t bytes) override
Definition: BaseHTTPPeer.h:426
ripple::BaseHTTPPeer::timeoutSeconds
@ timeoutSeconds
Definition: BaseHTTPPeer.h:65
ripple::BaseHTTPPeer< Handler, PlainHTTPPeer< Handler > >::yield_context
boost::asio::yield_context yield_context
Definition: BaseHTTPPeer.h:57
ripple::BaseHTTPPeer::timeoutSecondsLocal
@ timeoutSecondsLocal
Definition: BaseHTTPPeer.h:66
ripple::BaseHTTPPeer::buffer
Definition: BaseHTTPPeer.h:69
ripple::BaseHTTPPeer::journal_
const beast::Journal journal_
Definition: BaseHTTPPeer.h:89
ripple::BaseHTTPPeer::on_timer
void on_timer()
Definition: BaseHTTPPeer.h:304
std::unique_ptr< char[]>
ripple::BaseHTTPPeer::complete
void complete() override
Indicate that the response is complete.
Definition: BaseHTTPPeer.h:482
ripple::BaseHTTPPeer::buffer::used
std::size_t used
Definition: BaseHTTPPeer.h:81
type_traits
ripple::BaseHTTPPeer::work_
boost::asio::executor_work_guard< boost::asio::executor > work_
Definition: BaseHTTPPeer.h:86
ripple::BaseHTTPPeer::strand_
boost::asio::strand< boost::asio::executor > strand_
Definition: BaseHTTPPeer.h:87