Hostname resolution support for Peers

This commit is contained in:
Nik Bougalis
2013-12-16 14:46:44 -08:00
committed by Vinnie Falco
parent 88a8433d31
commit 3e2b5dcc3d
7 changed files with 353 additions and 2 deletions

View File

@@ -1107,6 +1107,9 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\peers\NameResolver.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\peers\PackedMessage.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -2390,6 +2393,7 @@
<ClInclude Include="..\..\src\ripple_app\paths\RippleCalc.h" />
<ClInclude Include="..\..\src\ripple_app\paths\RippleLineCache.h" />
<ClInclude Include="..\..\src\ripple_app\paths\RippleState.h" />
<ClInclude Include="..\..\src\ripple_app\peers\NameResolver.h" />
<ClInclude Include="..\..\src\ripple_app\peers\PackedMessage.h" />
<ClInclude Include="..\..\src\ripple_app\peers\PeerDoor.h" />
<ClInclude Include="..\..\src\ripple_app\peers\ClusterNodeStatus.h" />

View File

@@ -1392,6 +1392,9 @@
<ClCompile Include="..\..\src\ripple_app\main\CollectorManager.cpp">
<Filter>[2] Old Ripple\ripple_app\main</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\peers\NameResolver.cpp">
<Filter>[2] Old Ripple\ripple_app\peers</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\ripple_basics\containers\KeyCache.h">
@@ -2862,6 +2865,9 @@
<ClInclude Include="..\..\src\ripple_app\main\CollectorManager.h">
<Filter>[2] Old Ripple\ripple_app\main</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple_app\peers\NameResolver.h">
<Filter>[2] Old Ripple\ripple_app\peers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="..\..\src\ripple_data\protocol\ripple.proto">

View File

@@ -0,0 +1,261 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
namespace ripple {
class NameResolverImpl
: public NameResolver
, public AsyncObject <NameResolverImpl>
{
public:
typedef std::pair<std::string, std::string> HostAndPort;
Journal m_journal;
boost::asio::io_service& m_io_service;
boost::asio::io_service::strand m_strand;
boost::asio::ip::tcp::resolver m_resolver;
Atomic <int> m_cancel_called;
bool m_canceled;
bool m_idle;
// Notification that we need to exit
WaitableEvent m_stopped;
// Represents a unit of work for the resolver to do
struct Work
{
std::vector <std::string> names;
HandlerType handler;
template <class StringSequence>
Work (StringSequence const& names_, HandlerType const& handler_)
: handler(handler_)
{
names.reserve(names_.size());
std::reverse_copy (names_.begin(), names_.end(),
std::back_inserter(names));
}
};
std::deque <Work> m_work;
NameResolverImpl (boost::asio::io_service& io_service,
Journal journal)
: m_journal (journal)
, m_io_service (io_service)
, m_strand (io_service)
, m_resolver (io_service)
, m_canceled (false)
, m_idle (true)
, m_stopped (true)
{
addReference ();
}
~NameResolverImpl ()
{
check_precondition (m_work.empty());
check_precondition (m_canceled);
}
////-------------------------------------------------------------------------
//// AsyncObject
void asyncHandlersComplete()
{
m_stopped.signal ();
}
//-------------------------------------------------------------------------
// NameResolver
void do_cancel (CompletionCounter)
{
m_journal.debug << "Canceling";
m_canceled = true;
m_work.clear ();
m_resolver.cancel ();
removeReference ();
}
void cancel ()
{
if (meets_precondition(m_cancel_called.exchange (1) == 0))
{
m_io_service.dispatch ( m_strand.wrap ( boost::bind (
&NameResolverImpl::do_cancel,
this, CompletionCounter(this))));
m_journal.debug << "Waiting to stop";
m_stopped.wait();
m_journal.debug << "Stopped";
}
}
// Resolving the name has completed - dispatch and continue
void do_finish (
std::string name,
const boost::system::error_code& ec,
HandlerType handler,
boost::asio::ip::tcp::resolver::iterator iter,
CompletionCounter)
{
if (ec == boost::asio::error::operation_aborted)
return;
std::vector <IPAddress> addresses;
// If we get an error message back, we don't return any
// results that we may have gotten.
if (ec == 0)
{
while (iter != boost::asio::ip::tcp::resolver::iterator())
{
addresses.push_back (IPAddressConversion::from_asio(*iter));
++iter;
}
}
handler (name, addresses, ec);
m_io_service.post (m_strand.wrap (boost::bind (
&NameResolverImpl::do_work, this,
CompletionCounter(this))));
}
HostAndPort parseName(std::string const& str)
{
std::string host (str);
std::string port;
std::string::size_type colon (host.find(':'));
if(colon != std::string::npos)
{
port = host.substr(colon + 1);
host.erase(colon);
}
return std::make_pair(host, port);
}
void do_work (CompletionCounter)
{
if (m_cancel_called.get() == 1)
return;
// We don't have any work to do at this time
if (m_work.empty())
{
m_idle = true;
m_journal.debug << "Sleeping";
return;
}
if (m_work.front().names.empty())
m_work.pop_front();
std::string const name (m_work.front().names.back());
HandlerType handler (m_work.front().handler);
m_work.front().names.pop_back();
HostAndPort const hp (parseName(name));
if (hp.first.empty())
{
m_journal.error <<
"Unable to parse '" << name << "'";
m_io_service.post (m_strand.wrap (boost::bind (
&NameResolverImpl::do_work, this,
CompletionCounter(this))));
return;
}
boost::asio::ip::tcp::resolver::query query (
hp.first, hp.second);
m_resolver.async_resolve (query, boost::bind (
&NameResolverImpl::do_finish, this, name,
boost::asio::placeholders::error, handler,
boost::asio::placeholders::iterator,
CompletionCounter(this)));
}
void do_resolve (std::vector <std::string> const& names,
HandlerType const& handler, CompletionCounter)
{
check_precondition (! names.empty());
if (m_cancel_called.get() == 0)
{
// TODO NIKB use emplace_back once we move to C++11
m_work.push_back(Work(names, handler));
m_journal.debug <<
"Queued new job with " << names.size() <<
" tasks. " << m_work.size() << " jobs outstanding.";
if (m_work.size() == 1)
{
check_precondition (m_idle);
m_journal.debug << "Waking up";
m_idle = false;
m_io_service.post (m_strand.wrap (boost::bind (
&NameResolverImpl::do_work, this,
CompletionCounter(this))));
}
}
}
void resolve (
std::vector <std::string> const& names,
HandlerType const& handler)
{
check_precondition (m_cancel_called.get() == 0);
check_precondition (!names.empty());
// TODO NIKB use rvalue references to construct and move
// reducing cost.
m_io_service.dispatch (m_strand.wrap (boost::bind (
&NameResolverImpl::do_resolve, this,
names, handler, CompletionCounter(this))));
}
};
//-----------------------------------------------------------------------------
NameResolver::~NameResolver()
{
}
NameResolver* NameResolver::New (
boost::asio::io_service& io_service,
Journal journal)
{
return new NameResolverImpl (io_service, journal);
}
}

View File

@@ -0,0 +1,67 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEER_NAMERESOLVER_H_INCLUDED
#define RIPPLE_PEER_NAMERESOLVER_H_INCLUDED
namespace ripple {
class NameResolver
{
public:
typedef boost::function <
void (std::string, std::vector<IPAddress>, boost::system::error_code)>
HandlerType;
static NameResolver* New (boost::asio::io_service& io_service,
Journal journal);
virtual ~NameResolver () = 0;
/** Cancel all pending resolutions.
This call blocks until all pending work items are canceled. It is
guaranteed that no handlers will be called after this function
returns.
You *must* call this function before the object is destroyed.
*/
virtual void cancel () = 0;
/** resolve all hostnames on the list
@param names the names to be resolved
@param handler the handler to call
@note the handler may be called multiple times for a single name
since a name may resolve to multiple IPs.
*/
/** @{ */
template <class Handler>
void resolve (std::vector <std::string> const& names, Handler handler)
{
resolve (names, HandlerType (handler));
}
virtual void resolve (
std::vector <std::string> const& names,
HandlerType const& handler) = 0;
/** @} */
};
};
#endif

View File

@@ -22,6 +22,9 @@ SETUP_LOG (Peers)
class PeerFinderLog;
template <> char const* LogPartition::getPartitionName <PeerFinderLog> () { return "PeerFinder"; }
class NameResolverLog;
template <> char const* LogPartition::getPartitionName <NameResolverLog> () { return "NameResolver"; }
class PeersImp
: public Peers
, public Stoppable
@@ -86,6 +89,8 @@ public:
Peer::pointer peerConnect (const std::string& strIp, int iPort);
ScopedPointer <NameResolver> m_resolver;
//--------------------------------------------------------------------------
PeersImp (Stoppable& parent,
@@ -107,7 +112,11 @@ public:
, mPhase (0)
, mScanTimer (io_service)
, mPolicyTimer (io_service)
, m_resolver (NameResolver::New (
io_service,
Journal()))
{
}
//--------------------------------------------------------------------------
@@ -229,6 +238,7 @@ public:
void onStop ()
{
m_resolver->cancel();
}
void onChildrenStopped ()

View File

@@ -113,4 +113,3 @@ public:
extern void splitIpPort (const std::string& strIpPort, std::string& strIp, int& iPort);
#endif
// vim:ts=4

View File

@@ -26,6 +26,11 @@
#include "../ripple/resource/ripple_resource.h"
#include "../ripple/validators/ripple_validators.h"
#include <deque>
#include "peers/NameResolver.h"
#include "peers/NameResolver.cpp"
namespace ripple {
#include "ledger/LedgerTiming.cpp"
@@ -41,5 +46,4 @@ namespace ripple {
#include "peers/Peer.cpp"
#include "peers/PackedMessage.cpp"
#include "peers/Peers.cpp"
}