37 #define AI_ADDRCONFIG 0
44 class ResolverFuture ;
49 const char * ipvx(
int family )
51 return family == AF_UNSPEC ?
"ip" : ( family == AF_INET ?
"ipv4" :
"ipv6" ) ;
76 typedef std::pair<Address,std::string> Pair ;
77 typedef std::vector<Address> List ;
79 ResolverFuture(
const std::string & host ,
const std::string & service ,
80 int family ,
bool dgram ,
bool for_async_hint =
false ) ;
99 std::string reason()
const ;
106 std::string failure()
const ;
107 bool fetch( List & )
const ;
108 bool fetch( Pair & )
const ;
109 bool failed()
const ;
110 std::string none()
const ;
111 std::string ipvx_()
const ;
114 bool m_numeric_service ;
117 const char * m_host_p ;
118 std::string m_service ;
119 const char * m_service_p ;
121 struct addrinfo m_ai_hint ;
124 struct addrinfo * m_ai ;
125 std::string m_reason ;
128 GNet::ResolverFuture::ResolverFuture(
const std::string & host ,
const std::string & service ,
int family ,
129 bool dgram ,
bool for_async_hint ) :
130 m_numeric_service(false) ,
131 m_socktype(dgram?SOCK_DGRAM:SOCK_STREAM) ,
133 m_host_p(m_host.c_str()) ,
135 m_service_p(m_service.c_str()) ,
137 m_test_mode(for_async_hint&&G::Test::enabled(
"getaddrinfo-slow")) ,
142 std::memset( &m_ai_hint , 0 ,
sizeof(m_ai_hint) ) ;
143 m_ai_hint.ai_flags = AI_CANONNAME |
144 ( family == AF_UNSPEC ? AI_ADDRCONFIG : 0 ) |
145 ( m_numeric_service ? AI_NUMERICSERV : 0 ) ;
146 m_ai_hint.ai_family = family ;
147 m_ai_hint.ai_socktype = m_socktype ;
150 GNet::ResolverFuture::~ResolverFuture()
153 ::freeaddrinfo( m_ai ) ;
156 void GNet::ResolverFuture::run()
159 if( m_test_mode ) sleep( 10 ) ;
160 m_rc = ::getaddrinfo( m_host_p , m_service_p , &m_ai_hint , &m_ai ) ;
163 std::string GNet::ResolverFuture::failure()
const
165 std::stringstream ss ;
166 if( m_numeric_service )
167 ss <<
"no such " << ipvx_() <<
"host: \"" << m_host <<
"\"" ;
169 ss <<
"no such " << ipvx_() <<
"host or service: \"" << m_host <<
":" << m_service <<
"\"" ;
174 std::string GNet::ResolverFuture::ipvx_()
const
176 return m_family == AF_UNSPEC ? std::string() : (ipvx(m_family)+std::string(1U,
' ')) ;
179 bool GNet::ResolverFuture::failed()
const
181 return m_rc != 0 || m_ai ==
nullptr || m_ai->ai_addr ==
nullptr || m_ai->ai_addrlen == 0 ;
184 std::string GNet::ResolverFuture::none()
const
186 return std::string(
"no usable addresses returned for \"") + m_host +
"\"" ;
189 bool GNet::ResolverFuture::fetch( Pair & pair )
const
192 for(
const struct addrinfo * p = m_ai ; p ; p = p->ai_next )
196 Address address( p->ai_addr , p->ai_addrlen ) ;
197 std::string name( p->ai_canonname ? p->ai_canonname :
"" ) ;
198 pair = std::make_pair( address , name ) ;
205 bool GNet::ResolverFuture::fetch( List & list )
const
208 for(
const struct addrinfo * p = m_ai ; p ; p = p->ai_next )
211 list.push_back( Address( p->ai_addr , p->ai_addrlen ) ) ;
213 return !list.empty() ;
216 void GNet::ResolverFuture::get( List & list )
219 m_reason = failure() ;
220 else if( !fetch(list) )
224 GNet::ResolverFuture::Pair GNet::ResolverFuture::get()
228 m_reason = failure() ;
229 else if( !fetch(result) )
234 bool GNet::ResolverFuture::error()
const
236 return !m_reason.empty() ;
239 std::string GNet::ResolverFuture::reason()
const
260 void disarm(
bool do_delete_this ) ;
264 static void start(
ResolverImp * , FutureEvent::handle_type ) ;
270 static size_t count() ;
274 virtual void onFutureEvent(
unsigned int )
override ;
275 virtual void onException( std::exception & )
override ;
278 typedef ResolverFuture::Pair Pair ;
280 bool m_do_delete_this ;
282 unique_ptr<FutureEvent> m_future_event ;
285 G::threading::thread_type m_thread ;
286 static size_t m_instance_count ;
289 size_t GNet::ResolverImp::m_instance_count = 0U ;
292 m_resolver(&resolver) ,
293 m_do_delete_this(false) ,
294 m_event_exception_handler(event_exception_handler) ,
296 m_location(location) ,
297 m_future(location.host(),location.service(),location.family(),location.dgram(),true) ,
298 m_thread(
ResolverImp::start,this,m_future_event->handle())
303 GNet::ResolverImp::~ResolverImp()
305 if( m_thread.joinable() )
307 G_WARNING(
"ResolverImp::dtor: waiting for getaddrinfo thread to complete" ) ;
313 size_t GNet::ResolverImp::count()
315 return m_instance_count ;
318 void GNet::ResolverImp::start( ResolverImp * This , FutureEvent::handle_type handle )
323 This->m_future.run() ;
332 void GNet::ResolverImp::onFutureEvent(
unsigned int e )
334 G_DEBUG(
"GNet::ResolverImp::onFutureEvent: future event: e=" << e <<
" ptr=" << m_resolver <<
" dt=" << m_do_delete_this ) ;
338 throw Resolver::Error(
"exception in worker thread" ) ;
340 Pair result = m_future.get() ;
341 if( !m_future.error() )
342 m_location.update( result.first , result.second ) ;
343 G_DEBUG(
"GNet::ResolverImp::onFutureEvent: [" << m_future.reason() <<
"][" << m_location.displayString() <<
"]" ) ;
345 if( m_resolver !=
nullptr )
346 m_resolver->done( m_future.reason() , m_location ) ;
348 if( m_do_delete_this )
352 void GNet::ResolverImp::disarm(
bool do_delete_this )
354 m_resolver = nullptr ;
355 m_do_delete_this = do_delete_this ;
358 void GNet::ResolverImp::onException( std::exception & e )
360 m_event_exception_handler.onException( e ) ;
366 m_callback(callback) ,
374 const size_t sanity_limit = 50U ;
375 if( m_busy && ResolverImp::count() < sanity_limit )
378 G_ASSERT( m_imp.get() != nullptr ) ;
379 G_DEBUG(
"GNet::Resolver::dtor: releasing still-busy thread: " << ResolverImp::count() ) ;
380 m_imp->disarm(
true ) ;
388 typedef ResolverFuture::Pair Pair ;
389 G_DEBUG(
"GNet::Resolver::resolve: resolve request [" << location.
displayString() <<
"] (" << location.
family() <<
")" ) ;
392 Pair result = future.get() ;
395 G_DEBUG(
"GNet::Resolver::resolve: resolve error [" << future.reason() <<
"]" ) ;
396 return future.reason() ;
400 G_DEBUG(
"GNet::Resolver::resolve: resolve result [" << result.first.displayString() <<
"][" << result.second <<
"]" ) ;
401 location.
update( result.first , result.second ) ;
402 return std::string() ;
406 GNet::Resolver::AddressList
GNet::Resolver::resolve(
const std::string & host ,
const std::string & service ,
int family ,
bool dgram )
409 G_DEBUG(
"GNet::Resolver::resolve: resolve-request [" << host <<
"/" << service <<
"/" << ipvx(family) <<
"]" ) ;
414 G_DEBUG(
"GNet::Resolver::resolve: resolve result: list of " << list.size() ) ;
422 if( busy() )
throw BusyError() ;
423 G_DEBUG(
"GNet::Resolver::start: resolve start [" << location.
displayString() <<
"]" ) ;
424 m_imp.reset(
new ResolverImp(*
this,m_callback,location) ) ;
428 void GNet::Resolver::done(
const std::string & error ,
const Location & location )
431 G_DEBUG(
"GNet::Resolver::done: resolve done [" << error <<
"] [" << location.
displayString() <<
"]" ) ;
433 m_callback.onResolved( error , location ) ;
443 static bool threading_works = G::threading::works() ;
444 if( threading_works )
451 G_DEBUG(
"GNet::Resolver::async: multi-threading not built-in: using synchronous domain name lookup");
458 GNet::Resolver::Callback::~Callback()
void start(const Location &)
Starts asynchronous name-to-address resolution.
A callback interface for GNet::FutureEvent.
An abstract interface for handling exceptions thrown out of event-loop callbacks (socket events and t...
static bool validData(const sockaddr *, socklen_t len)
Returns true if the sockaddr data is valid.
An object that hooks into the event loop and calls back to the client code with a small unsigned inte...
A class for synchronous or asynchronous network name to address resolution.
static bool isNumeric(const std::string &s, bool allow_minus_sign=false)
Returns true if every character is a decimal digit.
std::string displayString() const
Returns a string representation for logging and debug.
An interface used for GNet::Resolver callbacks.
A class that holds a host/service name pair and the preferred address family (if any), and also the results of a name-to-address lookup, ie.
bool dgram() const
Returns true if the name resolution should be specifically for datagram sockets.
static Address defaultAddress()
Returns a default address, being the IPv4 wildcard address with a zero port number.
virtual bool running() const =0
Returns true if called from within run().
Resolver(Callback &)
Constructor taking a callback interface reference.
void update(const Address &address, const std::string &canonical_name)
Updates the address and canonical name, typically after doing a name lookup on host() and service()...
std::string service() const
Returns the remote service name, as passed in to the constructor.
static bool async()
Returns true if the resolver supports asynchronous operation.
bool busy() const
Returns true if there is a pending resolve request.
static std::string resolve(Location &)
Does syncronous name resolution.
int family() const
Returns the preferred name resolution address family, or AF_UNSPEC.
Address a = f.get().first ;.
A private "pimple" implementation class used by GNet::Resolver to do asynchronous name resolution...
std::string host() const
Returns the remote host name, as passed in to the constructor.
static bool send(handle_type handle, unsigned int payload) g__noexcept
Pokes the event payload into the main event loop so that the callback is called once the stack is unw...
static EventLoop & instance()
Returns a reference to an instance of the class, if any.