$search
00001 // 00002 // resolver_service.hpp 00003 // ~~~~~~~~~~~~~~~~~~~~ 00004 // 00005 // Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com) 00006 // 00007 // Distributed under the Boost Software License, Version 1.0. (See accompanying 00008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 00009 // 00010 00011 #ifndef ASIO_DETAIL_RESOLVER_SERVICE_HPP 00012 #define ASIO_DETAIL_RESOLVER_SERVICE_HPP 00013 00014 #if defined(_MSC_VER) && (_MSC_VER >= 1200) 00015 # pragma once 00016 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 00017 00018 #include "asio/detail/push_options.hpp" 00019 00020 #include "asio/detail/push_options.hpp" 00021 #include <cstring> 00022 #include <boost/scoped_ptr.hpp> 00023 #include <boost/shared_ptr.hpp> 00024 #include <boost/weak_ptr.hpp> 00025 #include "asio/detail/pop_options.hpp" 00026 00027 #include "asio/error.hpp" 00028 #include "asio/io_service.hpp" 00029 #include "asio/detail/bind_handler.hpp" 00030 #include "asio/detail/mutex.hpp" 00031 #include "asio/detail/noncopyable.hpp" 00032 #include "asio/detail/service_base.hpp" 00033 #include "asio/detail/socket_ops.hpp" 00034 #include "asio/detail/socket_types.hpp" 00035 #include "asio/detail/thread.hpp" 00036 00037 namespace asio { 00038 namespace detail { 00039 00040 template <typename Protocol> 00041 class resolver_service 00042 : public asio::detail::service_base<resolver_service<Protocol> > 00043 { 00044 private: 00045 // Helper class to perform exception-safe cleanup of addrinfo objects. 00046 class auto_addrinfo 00047 : private asio::detail::noncopyable 00048 { 00049 public: 00050 explicit auto_addrinfo(asio::detail::addrinfo_type* ai) 00051 : ai_(ai) 00052 { 00053 } 00054 00055 ~auto_addrinfo() 00056 { 00057 if (ai_) 00058 socket_ops::freeaddrinfo(ai_); 00059 } 00060 00061 operator asio::detail::addrinfo_type*() 00062 { 00063 return ai_; 00064 } 00065 00066 private: 00067 asio::detail::addrinfo_type* ai_; 00068 }; 00069 00070 public: 00071 // The implementation type of the resolver. The shared pointer is used as a 00072 // cancellation token to indicate to the background thread that the operation 00073 // has been cancelled. 00074 typedef boost::shared_ptr<void> implementation_type; 00075 struct noop_deleter { void operator()(void*) {} }; 00076 00077 // The endpoint type. 00078 typedef typename Protocol::endpoint endpoint_type; 00079 00080 // The query type. 00081 typedef typename Protocol::resolver_query query_type; 00082 00083 // The iterator type. 00084 typedef typename Protocol::resolver_iterator iterator_type; 00085 00086 // Constructor. 00087 resolver_service(asio::io_service& io_service) 00088 : asio::detail::service_base< 00089 resolver_service<Protocol> >(io_service), 00090 mutex_(), 00091 work_io_service_(new asio::io_service), 00092 work_(new asio::io_service::work(*work_io_service_)), 00093 work_thread_(0) 00094 { 00095 } 00096 00097 // Destructor. 00098 ~resolver_service() 00099 { 00100 shutdown_service(); 00101 } 00102 00103 // Destroy all user-defined handler objects owned by the service. 00104 void shutdown_service() 00105 { 00106 work_.reset(); 00107 if (work_io_service_) 00108 { 00109 work_io_service_->stop(); 00110 if (work_thread_) 00111 { 00112 work_thread_->join(); 00113 work_thread_.reset(); 00114 } 00115 work_io_service_.reset(); 00116 } 00117 } 00118 00119 // Construct a new resolver implementation. 00120 void construct(implementation_type& impl) 00121 { 00122 impl.reset(static_cast<void*>(0), noop_deleter()); 00123 } 00124 00125 // Destroy a resolver implementation. 00126 void destroy(implementation_type&) 00127 { 00128 } 00129 00130 // Cancel pending asynchronous operations. 00131 void cancel(implementation_type& impl) 00132 { 00133 impl.reset(static_cast<void*>(0), noop_deleter()); 00134 } 00135 00136 // Resolve a query to a list of entries. 00137 iterator_type resolve(implementation_type&, const query_type& query, 00138 asio::error_code& ec) 00139 { 00140 asio::detail::addrinfo_type* address_info = 0; 00141 std::string host_name = query.host_name(); 00142 std::string service_name = query.service_name(); 00143 asio::detail::addrinfo_type hints = query.hints(); 00144 00145 socket_ops::getaddrinfo(host_name.length() ? host_name.c_str() : 0, 00146 service_name.c_str(), &hints, &address_info, ec); 00147 auto_addrinfo auto_address_info(address_info); 00148 00149 if (ec) 00150 return iterator_type(); 00151 00152 return iterator_type::create(address_info, host_name, service_name); 00153 } 00154 00155 template <typename Handler> 00156 class resolve_query_handler 00157 { 00158 public: 00159 resolve_query_handler(implementation_type impl, const query_type& query, 00160 asio::io_service& io_service, Handler handler) 00161 : impl_(impl), 00162 query_(query), 00163 io_service_(io_service), 00164 work_(io_service), 00165 handler_(handler) 00166 { 00167 } 00168 00169 void operator()() 00170 { 00171 // Check if the operation has been cancelled. 00172 if (impl_.expired()) 00173 { 00174 iterator_type iterator; 00175 io_service_.post(asio::detail::bind_handler(handler_, 00176 asio::error::operation_aborted, iterator)); 00177 return; 00178 } 00179 00180 // Perform the blocking host resolution operation. 00181 asio::detail::addrinfo_type* address_info = 0; 00182 std::string host_name = query_.host_name(); 00183 std::string service_name = query_.service_name(); 00184 asio::detail::addrinfo_type hints = query_.hints(); 00185 asio::error_code ec; 00186 socket_ops::getaddrinfo(host_name.length() ? host_name.c_str() : 0, 00187 service_name.c_str(), &hints, &address_info, ec); 00188 auto_addrinfo auto_address_info(address_info); 00189 00190 // Invoke the handler and pass the result. 00191 iterator_type iterator; 00192 if (!ec) 00193 iterator = iterator_type::create(address_info, host_name, service_name); 00194 io_service_.post(asio::detail::bind_handler( 00195 handler_, ec, iterator)); 00196 } 00197 00198 private: 00199 boost::weak_ptr<void> impl_; 00200 query_type query_; 00201 asio::io_service& io_service_; 00202 asio::io_service::work work_; 00203 Handler handler_; 00204 }; 00205 00206 // Asynchronously resolve a query to a list of entries. 00207 template <typename Handler> 00208 void async_resolve(implementation_type& impl, const query_type& query, 00209 Handler handler) 00210 { 00211 if (work_io_service_) 00212 { 00213 start_work_thread(); 00214 work_io_service_->post( 00215 resolve_query_handler<Handler>( 00216 impl, query, this->get_io_service(), handler)); 00217 } 00218 } 00219 00220 // Resolve an endpoint to a list of entries. 00221 iterator_type resolve(implementation_type&, 00222 const endpoint_type& endpoint, asio::error_code& ec) 00223 { 00224 // First try resolving with the service name. If that fails try resolving 00225 // but allow the service to be returned as a number. 00226 char host_name[NI_MAXHOST]; 00227 char service_name[NI_MAXSERV]; 00228 int flags = endpoint.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0; 00229 socket_ops::getnameinfo(endpoint.data(), endpoint.size(), 00230 host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); 00231 if (ec) 00232 { 00233 flags |= NI_NUMERICSERV; 00234 socket_ops::getnameinfo(endpoint.data(), endpoint.size(), 00235 host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); 00236 } 00237 00238 if (ec) 00239 return iterator_type(); 00240 00241 return iterator_type::create(endpoint, host_name, service_name); 00242 } 00243 00244 template <typename Handler> 00245 class resolve_endpoint_handler 00246 { 00247 public: 00248 resolve_endpoint_handler(implementation_type impl, 00249 const endpoint_type& endpoint, asio::io_service& io_service, 00250 Handler handler) 00251 : impl_(impl), 00252 endpoint_(endpoint), 00253 io_service_(io_service), 00254 work_(io_service), 00255 handler_(handler) 00256 { 00257 } 00258 00259 void operator()() 00260 { 00261 // Check if the operation has been cancelled. 00262 if (impl_.expired()) 00263 { 00264 iterator_type iterator; 00265 io_service_.post(asio::detail::bind_handler(handler_, 00266 asio::error::operation_aborted, iterator)); 00267 return; 00268 } 00269 00270 00271 // First try resolving with the service name. If that fails try resolving 00272 // but allow the service to be returned as a number. 00273 char host_name[NI_MAXHOST]; 00274 char service_name[NI_MAXSERV]; 00275 int flags = endpoint_.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0; 00276 asio::error_code ec; 00277 socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(), 00278 host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); 00279 if (ec) 00280 { 00281 flags |= NI_NUMERICSERV; 00282 socket_ops::getnameinfo(endpoint_.data(), endpoint_.size(), 00283 host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); 00284 } 00285 00286 // Invoke the handler and pass the result. 00287 iterator_type iterator; 00288 if (!ec) 00289 iterator = iterator_type::create(endpoint_, host_name, service_name); 00290 io_service_.post(asio::detail::bind_handler( 00291 handler_, ec, iterator)); 00292 } 00293 00294 private: 00295 boost::weak_ptr<void> impl_; 00296 endpoint_type endpoint_; 00297 asio::io_service& io_service_; 00298 asio::io_service::work work_; 00299 Handler handler_; 00300 }; 00301 00302 // Asynchronously resolve an endpoint to a list of entries. 00303 template <typename Handler> 00304 void async_resolve(implementation_type& impl, const endpoint_type& endpoint, 00305 Handler handler) 00306 { 00307 if (work_io_service_) 00308 { 00309 start_work_thread(); 00310 work_io_service_->post( 00311 resolve_endpoint_handler<Handler>( 00312 impl, endpoint, this->get_io_service(), handler)); 00313 } 00314 } 00315 00316 private: 00317 // Helper class to run the work io_service in a thread. 00318 class work_io_service_runner 00319 { 00320 public: 00321 work_io_service_runner(asio::io_service& io_service) 00322 : io_service_(io_service) {} 00323 void operator()() { io_service_.run(); } 00324 private: 00325 asio::io_service& io_service_; 00326 }; 00327 00328 // Start the work thread if it's not already running. 00329 void start_work_thread() 00330 { 00331 asio::detail::mutex::scoped_lock lock(mutex_); 00332 if (work_thread_ == 0) 00333 { 00334 work_thread_.reset(new asio::detail::thread( 00335 work_io_service_runner(*work_io_service_))); 00336 } 00337 } 00338 00339 // Mutex to protect access to internal data. 00340 asio::detail::mutex mutex_; 00341 00342 // Private io_service used for performing asynchronous host resolution. 00343 boost::scoped_ptr<asio::io_service> work_io_service_; 00344 00345 // Work for the private io_service to perform. 00346 boost::scoped_ptr<asio::io_service::work> work_; 00347 00348 // Thread used for running the work io_service's run loop. 00349 boost::scoped_ptr<asio::detail::thread> work_thread_; 00350 }; 00351 00352 } // namespace detail 00353 } // namespace asio 00354 00355 #include "asio/detail/pop_options.hpp" 00356 00357 #endif // ASIO_DETAIL_RESOLVER_SERVICE_HPP