resolver_service.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. //
  2. // resolver_service.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #ifndef BOOST_ASIO_DETAIL_RESOLVER_SERVICE_HPP
  11. #define BOOST_ASIO_DETAIL_RESOLVER_SERVICE_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <boost/asio/detail/push_options.hpp>
  16. #include <boost/asio/detail/push_options.hpp>
  17. #include <cstring>
  18. #include <boost/scoped_ptr.hpp>
  19. #include <boost/shared_ptr.hpp>
  20. #include <boost/weak_ptr.hpp>
  21. #include <boost/asio/detail/pop_options.hpp>
  22. #include <boost/asio/error.hpp>
  23. #include <boost/asio/io_service.hpp>
  24. #include <boost/asio/detail/bind_handler.hpp>
  25. #include <boost/asio/detail/mutex.hpp>
  26. #include <boost/asio/detail/noncopyable.hpp>
  27. #include <boost/asio/detail/service_base.hpp>
  28. #include <boost/asio/detail/socket_ops.hpp>
  29. #include <boost/asio/detail/socket_types.hpp>
  30. #include <boost/asio/detail/thread.hpp>
  31. namespace boost {
  32. namespace asio {
  33. namespace detail {
  34. template <typename Protocol>
  35. class resolver_service
  36. : public boost::asio::detail::service_base<resolver_service<Protocol> >
  37. {
  38. private:
  39. // Helper class to perform exception-safe cleanup of addrinfo objects.
  40. class auto_addrinfo
  41. : private boost::asio::detail::noncopyable
  42. {
  43. public:
  44. explicit auto_addrinfo(boost::asio::detail::addrinfo_type* ai)
  45. : ai_(ai)
  46. {
  47. }
  48. ~auto_addrinfo()
  49. {
  50. if (ai_)
  51. socket_ops::freeaddrinfo(ai_);
  52. }
  53. operator boost::asio::detail::addrinfo_type*()
  54. {
  55. return ai_;
  56. }
  57. private:
  58. boost::asio::detail::addrinfo_type* ai_;
  59. };
  60. public:
  61. // The implementation type of the resolver. The shared pointer is used as a
  62. // cancellation token to indicate to the background thread that the operation
  63. // has been cancelled.
  64. typedef boost::shared_ptr<void> implementation_type;
  65. struct noop_deleter { void operator()(void*) {} };
  66. // The endpoint type.
  67. typedef typename Protocol::endpoint endpoint_type;
  68. // The query type.
  69. typedef typename Protocol::resolver_query query_type;
  70. // The iterator type.
  71. typedef typename Protocol::resolver_iterator iterator_type;
  72. // Constructor.
  73. resolver_service(boost::asio::io_service& io_service)
  74. : boost::asio::detail::service_base<
  75. resolver_service<Protocol> >(io_service),
  76. mutex_(),
  77. work_io_service_(new boost::asio::io_service),
  78. work_(new boost::asio::io_service::work(*work_io_service_)),
  79. work_thread_(0)
  80. {
  81. }
  82. // Destructor.
  83. ~resolver_service()
  84. {
  85. shutdown_service();
  86. }
  87. // Destroy all user-defined handler objects owned by the service.
  88. void shutdown_service()
  89. {
  90. work_.reset();
  91. if (work_io_service_)
  92. {
  93. work_io_service_->stop();
  94. if (work_thread_)
  95. {
  96. work_thread_->join();
  97. work_thread_.reset();
  98. }
  99. work_io_service_.reset();
  100. }
  101. }
  102. // Construct a new resolver implementation.
  103. void construct(implementation_type& impl)
  104. {
  105. impl.reset(static_cast<void*>(0), noop_deleter());
  106. }
  107. // Destroy a resolver implementation.
  108. void destroy(implementation_type&)
  109. {
  110. }
  111. // Cancel pending asynchronous operations.
  112. void cancel(implementation_type& impl)
  113. {
  114. impl.reset(static_cast<void*>(0), noop_deleter());
  115. }
  116. // Resolve a query to a list of entries.
  117. iterator_type resolve(implementation_type&, const query_type& query,
  118. boost::system::error_code& ec)
  119. {
  120. boost::asio::detail::addrinfo_type* address_info = 0;
  121. std::string host_name = query.host_name();
  122. std::string service_name = query.service_name();
  123. boost::asio::detail::addrinfo_type hints = query.hints();
  124. socket_ops::getaddrinfo(host_name.length() ? host_name.c_str() : 0,
  125. service_name.c_str(), &hints, &address_info, ec);
  126. auto_addrinfo auto_address_info(address_info);
  127. if (ec)
  128. return iterator_type();
  129. return iterator_type::create(address_info, host_name, service_name);
  130. }
  131. template <typename Handler>
  132. class resolve_query_handler
  133. {
  134. public:
  135. resolve_query_handler(implementation_type impl, const query_type& query,
  136. boost::asio::io_service& io_service, Handler handler)
  137. : impl_(impl),
  138. query_(query),
  139. io_service_(io_service),
  140. work_(io_service),
  141. handler_(handler)
  142. {
  143. }
  144. void operator()()
  145. {
  146. // Check if the operation has been cancelled.
  147. if (impl_.expired())
  148. {
  149. iterator_type iterator;
  150. io_service_.post(boost::asio::detail::bind_handler(handler_,
  151. boost::asio::error::operation_aborted, iterator));
  152. return;
  153. }
  154. // Perform the blocking host resolution operation.
  155. boost::asio::detail::addrinfo_type* address_info = 0;
  156. std::string host_name = query_.host_name();
  157. std::string service_name = query_.service_name();
  158. boost::asio::detail::addrinfo_type hints = query_.hints();
  159. boost::system::error_code ec;
  160. socket_ops::getaddrinfo(host_name.length() ? host_name.c_str() : 0,
  161. service_name.c_str(), &hints, &address_info, ec);
  162. auto_addrinfo auto_address_info(address_info);
  163. // Invoke the handler and pass the result.
  164. iterator_type iterator;
  165. if (!ec)
  166. iterator = iterator_type::create(address_info, host_name, service_name);
  167. io_service_.post(boost::asio::detail::bind_handler(
  168. handler_, ec, iterator));
  169. }
  170. private:
  171. boost::weak_ptr<void> impl_;
  172. query_type query_;
  173. boost::asio::io_service& io_service_;
  174. boost::asio::io_service::work work_;
  175. Handler handler_;
  176. };
  177. // Asynchronously resolve a query to a list of entries.
  178. template <typename Handler>
  179. void async_resolve(implementation_type& impl, const query_type& query,
  180. Handler handler)
  181. {
  182. if (work_io_service_)
  183. {
  184. start_work_thread();
  185. work_io_service_->post(
  186. resolve_query_handler<Handler>(
  187. impl, query, this->get_io_service(), handler));
  188. }
  189. }
  190. // Resolve an endpoint to a list of entries.
  191. iterator_type resolve(implementation_type&,
  192. const endpoint_type& endpoint, boost::system::error_code& ec)
  193. {
  194. // First try resolving with the service name. If that fails try resolving
  195. // but allow the service to be returned as a number.
  196. char host_name[NI_MAXHOST];
  197. char service_name[NI_MAXSERV];
  198. int flags = endpoint.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0;
  199. socket_ops::getnameinfo(endpoint.data(), endpoint.size(),
  200. host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec);
  201. if (ec)
  202. {
  203. flags |= NI_NUMERICSERV;
  204. socket_ops::getnameinfo(endpoint.data(), endpoint.size(),
  205. host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec);
  206. }
  207. if (ec)
  208. return iterator_type();
  209. return iterator_type::create(endpoint, host_name, service_name);
  210. }
  211. template <typename Handler>
  212. class resolve_endpoint_handler
  213. {
  214. public:
  215. resolve_endpoint_handler(implementation_type impl,
  216. const endpoint_type& endpoint, boost::asio::io_service& io_service,
  217. Handler handler)
  218. : impl_(impl),
  219. endpoint_(endpoint),
  220. io_service_(io_service),
  221. work_(io_service),
  222. handler_(handler)
  223. {
  224. }
  225. void operator()()
  226. {
  227. // Check if the operation has been cancelled.
  228. if (impl_.expired())
  229. {
  230. iterator_type iterator;
  231. io_service_.post(boost::asio::detail::bind_handler(handler_,
  232. boost::asio::error::operation_aborted, iterator));
  233. return;
  234. }
  235. // First try resolving with the service name. If that fails try resolving
  236. // but allow the service to be returned as a number.
  237. char host_name[NI_MAXHOST];
  238. char service_name[NI_MAXSERV];
  239. int flags = endpoint_.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0;
  240. boost::system::error_code ec;
  241. socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(),
  242. host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec);
  243. if (ec)
  244. {
  245. flags |= NI_NUMERICSERV;
  246. socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(),
  247. host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec);
  248. }
  249. // Invoke the handler and pass the result.
  250. iterator_type iterator;
  251. if (!ec)
  252. iterator = iterator_type::create(endpoint_, host_name, service_name);
  253. io_service_.post(boost::asio::detail::bind_handler(
  254. handler_, ec, iterator));
  255. }
  256. private:
  257. boost::weak_ptr<void> impl_;
  258. endpoint_type endpoint_;
  259. boost::asio::io_service& io_service_;
  260. boost::asio::io_service::work work_;
  261. Handler handler_;
  262. };
  263. // Asynchronously resolve an endpoint to a list of entries.
  264. template <typename Handler>
  265. void async_resolve(implementation_type& impl, const endpoint_type& endpoint,
  266. Handler handler)
  267. {
  268. if (work_io_service_)
  269. {
  270. start_work_thread();
  271. work_io_service_->post(
  272. resolve_endpoint_handler<Handler>(
  273. impl, endpoint, this->get_io_service(), handler));
  274. }
  275. }
  276. private:
  277. // Helper class to run the work io_service in a thread.
  278. class work_io_service_runner
  279. {
  280. public:
  281. work_io_service_runner(boost::asio::io_service& io_service)
  282. : io_service_(io_service) {}
  283. void operator()() { io_service_.run(); }
  284. private:
  285. boost::asio::io_service& io_service_;
  286. };
  287. // Start the work thread if it's not already running.
  288. void start_work_thread()
  289. {
  290. boost::asio::detail::mutex::scoped_lock lock(mutex_);
  291. if (!work_thread_)
  292. {
  293. work_thread_.reset(new boost::asio::detail::thread(
  294. work_io_service_runner(*work_io_service_)));
  295. }
  296. }
  297. // Mutex to protect access to internal data.
  298. boost::asio::detail::mutex mutex_;
  299. // Private io_service used for performing asynchronous host resolution.
  300. boost::scoped_ptr<boost::asio::io_service> work_io_service_;
  301. // Work for the private io_service to perform.
  302. boost::scoped_ptr<boost::asio::io_service::work> work_;
  303. // Thread used for running the work io_service's run loop.
  304. boost::scoped_ptr<boost::asio::detail::thread> work_thread_;
  305. };
  306. } // namespace detail
  307. } // namespace asio
  308. } // namespace boost
  309. #include <boost/asio/detail/pop_options.hpp>
  310. #endif // BOOST_ASIO_DETAIL_RESOLVER_SERVICE_HPP