rippled
ResolverAsio.cpp
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 #include <ripple/basics/ResolverAsio.h>
21 #include <ripple/basics/Log.h>
22 #include <ripple/beast/net/IPAddressConversion.h>
23 #include <ripple/beast/net/IPEndpoint.h>
24 #include <boost/asio.hpp>
25 #include <atomic>
26 #include <cassert>
27 #include <condition_variable>
28 #include <deque>
29 #include <locale>
30 #include <memory>
31 #include <mutex>
32 
33 namespace ripple {
34 
39 template <class Derived>
41 {
42 protected:
44  : m_pending (0)
45  { }
46 
47 public:
49  {
50  // Destroying the object with I/O pending? Not a clean exit!
51  assert (m_pending.load () == 0);
52  }
53 
59  {
60  public:
61  explicit CompletionCounter (Derived* owner)
62  : m_owner (owner)
63  {
64  ++m_owner->m_pending;
65  }
66 
68  : m_owner (other.m_owner)
69  {
70  ++m_owner->m_pending;
71  }
72 
74  {
75  if (--m_owner->m_pending == 0)
76  m_owner->asyncHandlersComplete ();
77  }
78 
80 
81  private:
82  Derived* m_owner;
83  };
84 
85  void addReference ()
86  {
87  ++m_pending;
88  }
89 
91  {
92  if (--m_pending == 0)
93  (static_cast<Derived*>(this))->asyncHandlersComplete();
94  }
95 
96 private:
97  // The number of handlers pending.
99 };
100 
102  : public ResolverAsio
103  , public AsyncObject <ResolverAsioImpl>
104 {
105 public:
107 
109 
110  boost::asio::io_service& m_io_service;
111  boost::asio::io_service::strand m_strand;
112  boost::asio::ip::tcp::resolver m_resolver;
113 
117 
120 
121  // Represents a unit of work for the resolver to do
122  struct Work
123  {
126 
127  template <class StringSequence>
128  Work (StringSequence const& names_, HandlerType const& handler_)
129  : handler (handler_)
130  {
131  names.reserve(names_.size ());
132 
133  std::reverse_copy (names_.begin (), names_.end (),
135  }
136  };
137 
139 
140  ResolverAsioImpl (boost::asio::io_service& io_service,
141  beast::Journal journal)
142  : m_journal (journal)
143  , m_io_service (io_service)
144  , m_strand (io_service)
145  , m_resolver (io_service)
146  , m_asyncHandlersCompleted (true)
147  , m_stop_called (false)
148  , m_stopped (true)
149  {
150 
151  }
152 
153  ~ResolverAsioImpl () override
154  {
155  assert (m_work.empty ());
156  assert (m_stopped);
157  }
158 
159  //-------------------------------------------------------------------------
160  // AsyncObject
162  {
165  m_cv.notify_all();
166  }
167 
168  //--------------------------------------------------------------------------
169  //
170  // Resolver
171  //
172  //--------------------------------------------------------------------------
173 
174  void start () override
175  {
176  assert (m_stopped == true);
177  assert (m_stop_called == false);
178 
179  if (m_stopped.exchange (false) == true)
180  {
181  {
183  m_asyncHandlersCompleted = false;
184  }
185  addReference ();
186  }
187  }
188 
189  void stop_async () override
190  {
191  if (m_stop_called.exchange (true) == false)
192  {
193  m_io_service.dispatch (m_strand.wrap (std::bind (
195  this, CompletionCounter (this))));
196 
197  JLOG(m_journal.debug()) << "Queued a stop request";
198  }
199  }
200 
201  void stop () override
202  {
203  stop_async ();
204 
205  JLOG(m_journal.debug()) << "Waiting to stop";
207  m_cv.wait(lk, [this]{return m_asyncHandlersCompleted;});
208  lk.unlock();
209  JLOG(m_journal.debug()) << "Stopped";
210  }
211 
212  void resolve (
213  std::vector <std::string> const& names,
214  HandlerType const& handler) override
215  {
216  assert (m_stop_called == false);
217  assert (m_stopped == true);
218  assert (!names.empty());
219 
220  // TODO NIKB use rvalue references to construct and move
221  // reducing cost.
222  m_io_service.dispatch (m_strand.wrap (std::bind (
224  names, handler, CompletionCounter (this))));
225  }
226 
227  //-------------------------------------------------------------------------
228  // Resolver
229  void do_stop (CompletionCounter)
230  {
231  assert (m_stop_called == true);
232 
233  if (m_stopped.exchange (true) == false)
234  {
235  m_work.clear ();
236  m_resolver.cancel ();
237 
238  removeReference ();
239  }
240  }
241 
242  void do_finish (
243  std::string name,
244  boost::system::error_code const& ec,
245  HandlerType handler,
246  boost::asio::ip::tcp::resolver::iterator iter,
247  CompletionCounter)
248  {
249  if (ec == boost::asio::error::operation_aborted)
250  return;
251 
253 
254  // If we get an error message back, we don't return any
255  // results that we may have gotten.
256  if (!ec)
257  {
258  while (iter != boost::asio::ip::tcp::resolver::iterator())
259  {
261  ++iter;
262  }
263  }
264 
265  handler (name, addresses);
266 
267  m_io_service.post (m_strand.wrap (std::bind (
269  CompletionCounter (this))));
270  }
271 
273  {
274  // first attempt to parse as an endpoint (IP addr + port).
275  // If that doesn't succeed, fall back to generic name + port parsing
276 
277  if (auto const result = beast::IP::Endpoint::from_string_checked(str))
278  {
279  return make_pair (
280  result->address().to_string(),
281  std::to_string(result->port()));
282  }
283 
284  // generic name/port parsing, which doesn't work for
285  // IPv6 addresses in particular because it considers a colon
286  // a port separator
287 
288  // Attempt to find the first and last non-whitespace
289  auto const find_whitespace = std::bind (
290  &std::isspace <std::string::value_type>,
291  std::placeholders::_1,
292  std::locale ());
293 
294  auto host_first = std::find_if_not (
295  str.begin (), str.end (), find_whitespace);
296 
297  auto port_last = std::find_if_not (
298  str.rbegin (), str.rend(), find_whitespace).base();
299 
300  // This should only happen for all-whitespace strings
301  if (host_first >= port_last)
302  return std::make_pair(std::string (), std::string ());
303 
304  // Attempt to find the first and last valid port separators
305  auto const find_port_separator = [](char const c) -> bool
306  {
307  if (std::isspace (static_cast<unsigned char>(c)))
308  return true;
309 
310  if (c == ':')
311  return true;
312 
313  return false;
314  };
315 
316  auto host_last = std::find_if (
317  host_first, port_last, find_port_separator);
318 
319  auto port_first = std::find_if_not (
320  host_last, port_last, find_port_separator);
321 
322  return make_pair (
323  std::string (host_first, host_last),
324  std::string (port_first, port_last));
325  }
326 
327  void do_work (CompletionCounter)
328  {
329  if (m_stop_called == true)
330  return;
331 
332  // We don't have any work to do at this time
333  if (m_work.empty ())
334  return;
335 
336  std::string const name (m_work.front ().names.back());
337  HandlerType handler (m_work.front ().handler);
338 
339  m_work.front ().names.pop_back ();
340 
341  if (m_work.front ().names.empty ())
342  m_work.pop_front();
343 
344  auto const [host, port] = parseName (name);
345 
346  if (host.empty ())
347  {
348  JLOG(m_journal.error()) <<
349  "Unable to parse '" << name << "'";
350 
351  m_io_service.post (m_strand.wrap (std::bind (
353  CompletionCounter (this))));
354 
355  return;
356  }
357 
358  boost::asio::ip::tcp::resolver::query query (
359  host, port);
360 
361  m_resolver.async_resolve (query, std::bind (
362  &ResolverAsioImpl::do_finish, this, name,
363  std::placeholders::_1, handler,
364  std::placeholders::_2,
365  CompletionCounter (this)));
366  }
367 
369  HandlerType const& handler, CompletionCounter)
370  {
371  assert (! names.empty());
372 
373  if (m_stop_called == false)
374  {
375  m_work.emplace_back (names, handler);
376 
377  JLOG(m_journal.debug()) <<
378  "Queued new job with " << names.size() <<
379  " tasks. " << m_work.size() << " jobs outstanding.";
380 
381  if (m_work.size() > 0)
382  {
383  m_io_service.post (m_strand.wrap (std::bind (
385  CompletionCounter (this))));
386  }
387  }
388  }
389 };
390 
391 //-----------------------------------------------------------------------------
392 
394  boost::asio::io_service& io_service,
395  beast::Journal journal)
396 {
397  return std::make_unique<ResolverAsioImpl> (io_service, journal);
398 }
399 
400 //-----------------------------------------------------------------------------
401 Resolver::~Resolver() = default;
402 }
ripple::AsyncObject::m_pending
std::atomic< int > m_pending
Definition: ResolverAsio.cpp:98
ripple::ResolverAsioImpl::m_work
std::deque< Work > m_work
Definition: ResolverAsio.cpp:138
std::reverse_copy
T reverse_copy(T... args)
locale
std::bind
T bind(T... args)
std::string
STL class.
ripple::ResolverAsioImpl::stop
void stop() override
Issue a synchronous stop request.
Definition: ResolverAsio.cpp:201
ripple::ResolverAsioImpl::Work
Definition: ResolverAsio.cpp:122
std::pair
std::vector::reserve
T reserve(T... args)
ripple::ResolverAsioImpl::m_io_service
boost::asio::io_service & m_io_service
Definition: ResolverAsio.cpp:110
ripple::ResolverAsioImpl::Work::names
std::vector< std::string > names
Definition: ResolverAsio.cpp:124
std::vector< std::string >
std::find_if_not
T find_if_not(T... args)
std::vector::size
T size(T... args)
ripple::Resolver::~Resolver
virtual ~Resolver()=0
std::back_inserter
T back_inserter(T... args)
ripple::ResolverAsioImpl::m_mut
std::mutex m_mut
Definition: ResolverAsio.cpp:115
ripple::ResolverAsioImpl::m_asyncHandlersCompleted
bool m_asyncHandlersCompleted
Definition: ResolverAsio.cpp:116
ripple::ResolverAsioImpl
Definition: ResolverAsio.cpp:101
std::lock_guard
STL class.
std::function
ripple::AsyncObject::CompletionCounter::CompletionCounter
CompletionCounter(Derived *owner)
Definition: ResolverAsio.cpp:61
ripple::ResolverAsio::New
static std::unique_ptr< ResolverAsio > New(boost::asio::io_service &, beast::Journal)
Definition: ResolverAsio.cpp:393
ripple::AsyncObject::CompletionCounter::operator=
CompletionCounter & operator=(CompletionCounter const &)=delete
ripple::AsyncObject::CompletionCounter::CompletionCounter
CompletionCounter(CompletionCounter const &other)
Definition: ResolverAsio.cpp:67
ripple::ResolverAsioImpl::parseName
HostAndPort parseName(std::string const &str)
Definition: ResolverAsio.cpp:272
ripple::ResolverAsioImpl::Work::handler
HandlerType handler
Definition: ResolverAsio.cpp:125
std::vector::push_back
T push_back(T... args)
beast::IPAddressConversion::from_asio
static IP::Endpoint from_asio(boost::asio::ip::address const &address)
Definition: IPAddressConversion.h:58
std::atomic::load
T load(T... args)
ripple::ResolverAsioImpl::Work::Work
Work(StringSequence const &names_, HandlerType const &handler_)
Definition: ResolverAsio.cpp:128
ripple::ResolverAsioImpl::m_journal
beast::Journal m_journal
Definition: ResolverAsio.cpp:108
ripple::ResolverAsioImpl::m_cv
std::condition_variable m_cv
Definition: ResolverAsio.cpp:114
std::unique_lock
STL class.
std::to_string
T to_string(T... args)
ripple::AsyncObject::CompletionCounter
RAII container that maintains the count of pending I/O.
Definition: ResolverAsio.cpp:58
beast::Journal::error
Stream error() const
Definition: Journal.h:307
ripple::AsyncObject::removeReference
void removeReference()
Definition: ResolverAsio.cpp:90
deque
ripple::ResolverAsioImpl::do_finish
void do_finish(std::string name, boost::system::error_code const &ec, HandlerType handler, boost::asio::ip::tcp::resolver::iterator iter, CompletionCounter)
Definition: ResolverAsio.cpp:242
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:60
std::condition_variable::wait
T wait(T... args)
atomic
ripple::AsyncObject::addReference
void addReference()
Definition: ResolverAsio.cpp:85
ripple::ResolverAsioImpl::resolve
void resolve(std::vector< std::string > const &names, HandlerType const &handler) override
Definition: ResolverAsio.cpp:212
ripple::ResolverAsioImpl::m_stopped
std::atomic< bool > m_stopped
Definition: ResolverAsio.cpp:119
ripple::ResolverAsioImpl::~ResolverAsioImpl
~ResolverAsioImpl() override
Definition: ResolverAsio.cpp:153
memory
std::string::rend
T rend(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
std::atomic::exchange
T exchange(T... args)
ripple::ResolverAsioImpl::stop_async
void stop_async() override
Issue an asynchronous stop request.
Definition: ResolverAsio.cpp:189
ripple::ResolverAsioImpl::start
void start() override
Issue a synchronous start request.
Definition: ResolverAsio.cpp:174
ripple::ResolverAsioImpl::asyncHandlersComplete
void asyncHandlersComplete()
Definition: ResolverAsio.cpp:161
ripple::ResolverAsioImpl::m_stop_called
std::atomic< bool > m_stop_called
Definition: ResolverAsio.cpp:118
std::string::begin
T begin(T... args)
cassert
ripple::AsyncObject::AsyncObject
AsyncObject()
Definition: ResolverAsio.cpp:43
ripple::ResolverAsioImpl::m_strand
boost::asio::io_service::strand m_strand
Definition: ResolverAsio.cpp:111
condition_variable
ripple::ResolverAsioImpl::ResolverAsioImpl
ResolverAsioImpl(boost::asio::io_service &io_service, beast::Journal journal)
Definition: ResolverAsio.cpp:140
ripple::ResolverAsio
Definition: ResolverAsio.h:29
std::vector::empty
T empty(T... args)
mutex
ripple::AsyncObject::CompletionCounter::m_owner
Derived * m_owner
Definition: ResolverAsio.cpp:82
beast::Journal::debug
Stream debug() const
Definition: Journal.h:292
ripple::AsyncObject::CompletionCounter::~CompletionCounter
~CompletionCounter()
Definition: ResolverAsio.cpp:73
std::make_pair
T make_pair(T... args)
std::string::end
T end(T... args)
beast::IP::Endpoint::from_string_checked
static boost::optional< Endpoint > from_string_checked(std::string const &s)
Create an Endpoint from a string.
Definition: IPEndpoint.cpp:37
ripple::AsyncObject
Mix-in to track when all pending I/O is complete.
Definition: ResolverAsio.cpp:40
ripple::ResolverAsioImpl::do_resolve
void do_resolve(std::vector< std::string > const &names, HandlerType const &handler, CompletionCounter)
Definition: ResolverAsio.cpp:368
std::unique_ptr
STL class.
ripple::ResolverAsioImpl::do_work
void do_work(CompletionCounter)
Definition: ResolverAsio.cpp:327
ripple::ResolverAsioImpl::m_resolver
boost::asio::ip::tcp::resolver m_resolver
Definition: ResolverAsio.cpp:112
std::condition_variable::notify_all
T notify_all(T... args)
std::string::rbegin
T rbegin(T... args)
ripple::ResolverAsioImpl::do_stop
void do_stop(CompletionCounter)
Definition: ResolverAsio.cpp:229
ripple::AsyncObject::~AsyncObject
~AsyncObject()
Definition: ResolverAsio.cpp:48