resolver_service.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. //
  2. // resolver_service.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2010 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 ASIO_DETAIL_RESOLVER_SERVICE_HPP
  11. #define 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 "asio/detail/push_options.hpp"
  16. #include "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 "asio/detail/pop_options.hpp"
  22. #include "asio/error.hpp"
  23. #include "asio/io_service.hpp"
  24. #include "asio/ip/basic_resolver_iterator.hpp"
  25. #include "asio/ip/basic_resolver_query.hpp"
  26. #include "asio/detail/bind_handler.hpp"
  27. #include "asio/detail/fenced_block.hpp"
  28. #include "asio/detail/mutex.hpp"
  29. #include "asio/detail/noncopyable.hpp"
  30. #include "asio/detail/operation.hpp"
  31. #include "asio/detail/service_base.hpp"
  32. #include "asio/detail/socket_ops.hpp"
  33. #include "asio/detail/socket_types.hpp"
  34. #include "asio/detail/thread.hpp"
  35. namespace asio {
  36. namespace detail {
  37. template <typename Protocol>
  38. class resolver_service
  39. : public asio::detail::service_base<resolver_service<Protocol> >
  40. {
  41. private:
  42. // Helper class to perform exception-safe cleanup of addrinfo objects.
  43. class auto_addrinfo
  44. : private asio::detail::noncopyable
  45. {
  46. public:
  47. explicit auto_addrinfo(asio::detail::addrinfo_type* ai)
  48. : ai_(ai)
  49. {
  50. }
  51. ~auto_addrinfo()
  52. {
  53. if (ai_)
  54. socket_ops::freeaddrinfo(ai_);
  55. }
  56. operator asio::detail::addrinfo_type*()
  57. {
  58. return ai_;
  59. }
  60. private:
  61. asio::detail::addrinfo_type* ai_;
  62. };
  63. public:
  64. // The implementation type of the resolver. The shared pointer is used as a
  65. // cancellation token to indicate to the background thread that the operation
  66. // has been cancelled.
  67. typedef boost::shared_ptr<void> implementation_type;
  68. struct noop_deleter { void operator()(void*) {} };
  69. // The endpoint type.
  70. typedef typename Protocol::endpoint endpoint_type;
  71. // The query type.
  72. typedef asio::ip::basic_resolver_query<Protocol> query_type;
  73. // The iterator type.
  74. typedef asio::ip::basic_resolver_iterator<Protocol> iterator_type;
  75. // Constructor.
  76. resolver_service(asio::io_service& io_service)
  77. : asio::detail::service_base<
  78. resolver_service<Protocol> >(io_service),
  79. mutex_(),
  80. io_service_impl_(asio::use_service<io_service_impl>(io_service)),
  81. work_io_service_(new asio::io_service),
  82. work_io_service_impl_(asio::use_service<
  83. io_service_impl>(*work_io_service_)),
  84. work_(new asio::io_service::work(*work_io_service_)),
  85. work_thread_(0)
  86. {
  87. }
  88. // Destructor.
  89. ~resolver_service()
  90. {
  91. shutdown_service();
  92. }
  93. // Destroy all user-defined handler objects owned by the service.
  94. void shutdown_service()
  95. {
  96. work_.reset();
  97. if (work_io_service_)
  98. {
  99. work_io_service_->stop();
  100. if (work_thread_)
  101. {
  102. work_thread_->join();
  103. work_thread_.reset();
  104. }
  105. work_io_service_.reset();
  106. }
  107. }
  108. // Construct a new resolver implementation.
  109. void construct(implementation_type& impl)
  110. {
  111. impl.reset(static_cast<void*>(0), noop_deleter());
  112. }
  113. // Destroy a resolver implementation.
  114. void destroy(implementation_type&)
  115. {
  116. }
  117. // Cancel pending asynchronous operations.
  118. void cancel(implementation_type& impl)
  119. {
  120. impl.reset(static_cast<void*>(0), noop_deleter());
  121. }
  122. // Resolve a query to a list of entries.
  123. iterator_type resolve(implementation_type&, const query_type& query,
  124. asio::error_code& ec)
  125. {
  126. asio::detail::addrinfo_type* address_info = 0;
  127. std::string host_name = query.host_name();
  128. std::string service_name = query.service_name();
  129. asio::detail::addrinfo_type hints = query.hints();
  130. socket_ops::getaddrinfo(!host_name.empty() ? host_name.c_str() : 0,
  131. service_name.c_str(), &hints, &address_info, ec);
  132. auto_addrinfo auto_address_info(address_info);
  133. if (ec)
  134. return iterator_type();
  135. return iterator_type::create(address_info, host_name, service_name);
  136. }
  137. template <typename Handler>
  138. class resolve_op
  139. : public operation
  140. {
  141. public:
  142. resolve_op(implementation_type impl, const query_type& query,
  143. io_service_impl& io_service_impl, Handler handler)
  144. : operation(&resolve_op::do_complete),
  145. impl_(impl),
  146. query_(query),
  147. io_service_impl_(io_service_impl),
  148. handler_(handler)
  149. {
  150. }
  151. static void do_complete(io_service_impl* owner, operation* base,
  152. asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
  153. {
  154. // Take ownership of the operation object.
  155. resolve_op* o(static_cast<resolve_op*>(base));
  156. typedef handler_alloc_traits<Handler, resolve_op> alloc_traits;
  157. handler_ptr<alloc_traits> ptr(o->handler_, o);
  158. if (owner)
  159. {
  160. if (owner != &o->io_service_impl_)
  161. {
  162. // The operation is being run on the worker io_service. Time to
  163. // perform the resolver operation.
  164. if (o->impl_.expired())
  165. {
  166. // THe operation has been cancelled.
  167. o->ec_ = asio::error::operation_aborted;
  168. }
  169. else
  170. {
  171. // Perform the blocking host resolution operation.
  172. asio::detail::addrinfo_type* address_info = 0;
  173. std::string host_name = o->query_.host_name();
  174. std::string service_name = o->query_.service_name();
  175. asio::detail::addrinfo_type hints = o->query_.hints();
  176. socket_ops::getaddrinfo(!host_name.empty() ? host_name.c_str() : 0,
  177. service_name.c_str(), &hints, &address_info, o->ec_);
  178. auto_addrinfo auto_address_info(address_info);
  179. o->iter_ = iterator_type::create(
  180. address_info, host_name, service_name);
  181. }
  182. o->io_service_impl_.post_deferred_completion(o);
  183. ptr.release();
  184. }
  185. else
  186. {
  187. // The operation has been returned to the main io_serice. The
  188. // completion handler is ready to be delivered.
  189. // Make a copy of the handler so that the memory can be deallocated
  190. // before the upcall is made. Even if we're not about to make an
  191. // upcall, a sub-object of the handler may be the true owner of the
  192. // memory associated with the handler. Consequently, a local copy of
  193. // the handler is required to ensure that any owning sub-object
  194. // remains valid until after we have deallocated the memory here.
  195. detail::binder2<Handler, asio::error_code, iterator_type>
  196. handler(o->handler_, o->ec_, o->iter_);
  197. ptr.reset();
  198. asio::detail::fenced_block b;
  199. asio_handler_invoke_helpers::invoke(handler, handler);
  200. }
  201. }
  202. }
  203. private:
  204. boost::weak_ptr<void> impl_;
  205. query_type query_;
  206. io_service_impl& io_service_impl_;
  207. Handler handler_;
  208. asio::error_code ec_;
  209. iterator_type iter_;
  210. };
  211. // Asynchronously resolve a query to a list of entries.
  212. template <typename Handler>
  213. void async_resolve(implementation_type& impl, const query_type& query,
  214. Handler handler)
  215. {
  216. // Allocate and construct an operation to wrap the handler.
  217. typedef resolve_op<Handler> value_type;
  218. typedef handler_alloc_traits<Handler, value_type> alloc_traits;
  219. raw_handler_ptr<alloc_traits> raw_ptr(handler);
  220. handler_ptr<alloc_traits> ptr(raw_ptr,
  221. impl, query, io_service_impl_, handler);
  222. if (work_io_service_)
  223. {
  224. start_work_thread();
  225. io_service_impl_.work_started();
  226. work_io_service_impl_.post_immediate_completion(ptr.get());
  227. ptr.release();
  228. }
  229. }
  230. // Resolve an endpoint to a list of entries.
  231. iterator_type resolve(implementation_type&,
  232. const endpoint_type& endpoint, asio::error_code& ec)
  233. {
  234. // First try resolving with the service name. If that fails try resolving
  235. // but allow the service to be returned as a number.
  236. char host_name[NI_MAXHOST];
  237. char service_name[NI_MAXSERV];
  238. int flags = endpoint.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0;
  239. socket_ops::getnameinfo(endpoint.data(), endpoint.size(),
  240. host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec);
  241. if (ec)
  242. {
  243. flags |= NI_NUMERICSERV;
  244. socket_ops::getnameinfo(endpoint.data(), endpoint.size(),
  245. host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec);
  246. }
  247. if (ec)
  248. return iterator_type();
  249. return iterator_type::create(endpoint, host_name, service_name);
  250. }
  251. template <typename Handler>
  252. class resolve_endpoint_op
  253. : public operation
  254. {
  255. public:
  256. resolve_endpoint_op(implementation_type impl, const endpoint_type& ep,
  257. io_service_impl& io_service_impl, Handler handler)
  258. : operation(&resolve_endpoint_op::do_complete),
  259. impl_(impl),
  260. ep_(ep),
  261. io_service_impl_(io_service_impl),
  262. handler_(handler)
  263. {
  264. }
  265. static void do_complete(io_service_impl* owner, operation* base,
  266. asio::error_code /*ec*/, std::size_t /*bytes_transferred*/)
  267. {
  268. // Take ownership of the operation object.
  269. resolve_endpoint_op* o(static_cast<resolve_endpoint_op*>(base));
  270. typedef handler_alloc_traits<Handler, resolve_endpoint_op> alloc_traits;
  271. handler_ptr<alloc_traits> ptr(o->handler_, o);
  272. if (owner)
  273. {
  274. if (owner != &o->io_service_impl_)
  275. {
  276. // The operation is being run on the worker io_service. Time to
  277. // perform the resolver operation.
  278. if (o->impl_.expired())
  279. {
  280. // THe operation has been cancelled.
  281. o->ec_ = asio::error::operation_aborted;
  282. }
  283. else
  284. {
  285. // Perform the blocking endoint resolution operation.
  286. char host_name[NI_MAXHOST];
  287. char service_name[NI_MAXSERV];
  288. int flags = o->ep_.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0;
  289. socket_ops::getnameinfo(o->ep_.data(), o->ep_.size(),
  290. host_name, NI_MAXHOST, service_name,
  291. NI_MAXSERV, flags, o->ec_);
  292. if (o->ec_)
  293. {
  294. flags |= NI_NUMERICSERV;
  295. socket_ops::getnameinfo(o->ep_.data(), o->ep_.size(),
  296. host_name, NI_MAXHOST, service_name,
  297. NI_MAXSERV, flags, o->ec_);
  298. }
  299. o->iter_ = iterator_type::create(o->ep_, host_name, service_name);
  300. }
  301. o->io_service_impl_.post_deferred_completion(o);
  302. ptr.release();
  303. }
  304. else
  305. {
  306. // The operation has been returned to the main io_serice. The
  307. // completion handler is ready to be delivered.
  308. // Make a copy of the handler so that the memory can be deallocated
  309. // before the upcall is made. Even if we're not about to make an
  310. // upcall, a sub-object of the handler may be the true owner of the
  311. // memory associated with the handler. Consequently, a local copy of
  312. // the handler is required to ensure that any owning sub-object
  313. // remains valid until after we have deallocated the memory here.
  314. detail::binder2<Handler, asio::error_code, iterator_type>
  315. handler(o->handler_, o->ec_, o->iter_);
  316. ptr.reset();
  317. asio::detail::fenced_block b;
  318. asio_handler_invoke_helpers::invoke(handler, handler);
  319. }
  320. }
  321. }
  322. private:
  323. boost::weak_ptr<void> impl_;
  324. endpoint_type ep_;
  325. io_service_impl& io_service_impl_;
  326. Handler handler_;
  327. asio::error_code ec_;
  328. iterator_type iter_;
  329. };
  330. // Asynchronously resolve an endpoint to a list of entries.
  331. template <typename Handler>
  332. void async_resolve(implementation_type& impl, const endpoint_type& endpoint,
  333. Handler handler)
  334. {
  335. // Allocate and construct an operation to wrap the handler.
  336. typedef resolve_endpoint_op<Handler> value_type;
  337. typedef handler_alloc_traits<Handler, value_type> alloc_traits;
  338. raw_handler_ptr<alloc_traits> raw_ptr(handler);
  339. handler_ptr<alloc_traits> ptr(raw_ptr,
  340. impl, endpoint, io_service_impl_, handler);
  341. if (work_io_service_)
  342. {
  343. start_work_thread();
  344. io_service_impl_.work_started();
  345. work_io_service_impl_.post_immediate_completion(ptr.get());
  346. ptr.release();
  347. }
  348. }
  349. private:
  350. // Helper class to run the work io_service in a thread.
  351. class work_io_service_runner
  352. {
  353. public:
  354. work_io_service_runner(asio::io_service& io_service)
  355. : io_service_(io_service) {}
  356. void operator()() { io_service_.run(); }
  357. private:
  358. asio::io_service& io_service_;
  359. };
  360. // Start the work thread if it's not already running.
  361. void start_work_thread()
  362. {
  363. asio::detail::mutex::scoped_lock lock(mutex_);
  364. if (!work_thread_)
  365. {
  366. work_thread_.reset(new asio::detail::thread(
  367. work_io_service_runner(*work_io_service_)));
  368. }
  369. }
  370. // Mutex to protect access to internal data.
  371. asio::detail::mutex mutex_;
  372. // The io_service implementation used to post completions.
  373. io_service_impl& io_service_impl_;
  374. // Private io_service used for performing asynchronous host resolution.
  375. boost::scoped_ptr<asio::io_service> work_io_service_;
  376. // The work io_service implementation used to post completions.
  377. io_service_impl& work_io_service_impl_;
  378. // Work for the private io_service to perform.
  379. boost::scoped_ptr<asio::io_service::work> work_;
  380. // Thread used for running the work io_service's run loop.
  381. boost::scoped_ptr<asio::detail::thread> work_thread_;
  382. };
  383. } // namespace detail
  384. } // namespace asio
  385. #include "asio/detail/pop_options.hpp"
  386. #endif // ASIO_DETAIL_RESOLVER_SERVICE_HPP