95.45% Lines (21/22) 100.00% Functions (7/7)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // Copyright (c) 2026 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/corosio 8   // Official repository: https://github.com/cppalliance/corosio
9   // 9   //
10   10  
11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_RESOLVER_HPP 11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_RESOLVER_HPP
12   #define BOOST_COROSIO_NATIVE_NATIVE_RESOLVER_HPP 12   #define BOOST_COROSIO_NATIVE_NATIVE_RESOLVER_HPP
13   13  
14   #include <boost/corosio/resolver.hpp> 14   #include <boost/corosio/resolver.hpp>
15   #include <boost/corosio/backend.hpp> 15   #include <boost/corosio/backend.hpp>
16   16  
17   #ifndef BOOST_COROSIO_MRDOCS 17   #ifndef BOOST_COROSIO_MRDOCS
18   #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_SELECT || \ 18   #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_SELECT || \
19   BOOST_COROSIO_HAS_KQUEUE 19   BOOST_COROSIO_HAS_KQUEUE
20   #include <boost/corosio/native/detail/posix/posix_resolver_service.hpp> 20   #include <boost/corosio/native/detail/posix/posix_resolver_service.hpp>
21   #endif 21   #endif
22   22  
23   #if BOOST_COROSIO_HAS_IOCP 23   #if BOOST_COROSIO_HAS_IOCP
24   #include <boost/corosio/native/detail/iocp/win_resolver_service.hpp> 24   #include <boost/corosio/native/detail/iocp/win_resolver_service.hpp>
25   #endif 25   #endif
26   #endif // !BOOST_COROSIO_MRDOCS 26   #endif // !BOOST_COROSIO_MRDOCS
27   27  
28   namespace boost::corosio { 28   namespace boost::corosio {
29   29  
30   /** An asynchronous DNS resolver with devirtualized operations. 30   /** An asynchronous DNS resolver with devirtualized operations.
31   31  
32   This class template inherits from @ref resolver and shadows 32   This class template inherits from @ref resolver and shadows
33   the `resolve` operations with versions that call the backend 33   the `resolve` operations with versions that call the backend
34   implementation directly, allowing the compiler to inline 34   implementation directly, allowing the compiler to inline
35   through the entire call chain. 35   through the entire call chain.
36   36  
37   Non-async operations (`cancel`) remain unchanged and dispatch 37   Non-async operations (`cancel`) remain unchanged and dispatch
38   through the compiled library. 38   through the compiled library.
39   39  
40   A `native_resolver` IS-A `resolver` and can be passed to any 40   A `native_resolver` IS-A `resolver` and can be passed to any
41   function expecting `resolver&`. 41   function expecting `resolver&`.
42   42  
43   @tparam Backend A backend tag value (e.g., `epoll`). 43   @tparam Backend A backend tag value (e.g., `epoll`).
44   44  
45   @par Thread Safety 45   @par Thread Safety
46   Same as @ref resolver. 46   Same as @ref resolver.
47   47  
48   @see resolver, epoll_t, iocp_t 48   @see resolver, epoll_t, iocp_t
49   */ 49   */
50   template<auto Backend> 50   template<auto Backend>
51   class native_resolver : public resolver 51   class native_resolver : public resolver
52   { 52   {
53   using backend_type = decltype(Backend); 53   using backend_type = decltype(Backend);
54   using impl_type = typename backend_type::resolver_type; 54   using impl_type = typename backend_type::resolver_type;
55   55  
HITCBC 56   2 impl_type& get_impl() noexcept 56   2 impl_type& get_impl() noexcept
57   { 57   {
HITCBC 58   2 return *static_cast<impl_type*>(h_.get()); 58   2 return *static_cast<impl_type*>(h_.get());
59   } 59   }
60   60  
61   struct native_resolve_awaitable 61   struct native_resolve_awaitable
62   { 62   {
63   native_resolver& self_; 63   native_resolver& self_;
64   std::string host_; 64   std::string host_;
65   std::string service_; 65   std::string service_;
66   resolve_flags flags_; 66   resolve_flags flags_;
67   std::stop_token token_; 67   std::stop_token token_;
68   mutable std::error_code ec_; 68   mutable std::error_code ec_;
69   mutable resolver_results results_; 69   mutable resolver_results results_;
70   70  
HITCBC 71   2 native_resolve_awaitable( 71   2 native_resolve_awaitable(
72   native_resolver& self, 72   native_resolver& self,
73   std::string_view host, 73   std::string_view host,
74   std::string_view service, 74   std::string_view service,
75   resolve_flags flags) noexcept 75   resolve_flags flags) noexcept
HITCBC 76   2 : self_(self) 76   2 : self_(self)
HITCBC 77   4 , host_(host) 77   4 , host_(host)
HITCBC 78   4 , service_(service) 78   4 , service_(service)
HITCBC 79   2 , flags_(flags) 79   2 , flags_(flags)
80   { 80   {
HITCBC 81   2 } 81   2 }
82   82  
HITCBC 83   2 bool await_ready() const noexcept 83   2 bool await_ready() const noexcept
84   { 84   {
HITCBC 85   2 return token_.stop_requested(); 85   2 return token_.stop_requested();
86   } 86   }
87   87  
HITCBC 88   2 capy::io_result<resolver_results> await_resume() const noexcept 88   2 capy::io_result<resolver_results> await_resume() const noexcept
89   { 89   {
HITCBC 90   2 if (token_.stop_requested()) 90   2 if (token_.stop_requested())
MISUBC 91   return {make_error_code(std::errc::operation_canceled), {}}; 91   return {make_error_code(std::errc::operation_canceled), {}};
HITCBC 92   2 return {ec_, std::move(results_)}; 92   2 return {ec_, std::move(results_)};
93   } 93   }
94   94  
HITCBC 95   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 95   2 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
96   -> std::coroutine_handle<> 96   -> std::coroutine_handle<>
97   { 97   {
HITCBC 98   2 token_ = env->stop_token; 98   2 token_ = env->stop_token;
HITCBC 99   6 return self_.get_impl().resolve( 99   6 return self_.get_impl().resolve(
HITCBC 100   2 h, env->executor, host_, service_, flags_, token_, &ec_, 100   2 h, env->executor, host_, service_, flags_, token_, &ec_,
HITCBC 101   4 &results_); 101   4 &results_);
102   } 102   }
103   }; 103   };
104   104  
105   struct native_reverse_awaitable 105   struct native_reverse_awaitable
106   { 106   {
107   native_resolver& self_; 107   native_resolver& self_;
108   endpoint ep_; 108   endpoint ep_;
109   reverse_flags flags_; 109   reverse_flags flags_;
110   std::stop_token token_; 110   std::stop_token token_;
111   mutable std::error_code ec_; 111   mutable std::error_code ec_;
112   mutable reverse_resolver_result result_; 112   mutable reverse_resolver_result result_;
113   113  
114   native_reverse_awaitable( 114   native_reverse_awaitable(
115   native_resolver& self, 115   native_resolver& self,
116   endpoint const& ep, 116   endpoint const& ep,
117   reverse_flags flags) noexcept 117   reverse_flags flags) noexcept
118   : self_(self) 118   : self_(self)
119   , ep_(ep) 119   , ep_(ep)
120   , flags_(flags) 120   , flags_(flags)
121   { 121   {
122   } 122   }
123   123  
124   bool await_ready() const noexcept 124   bool await_ready() const noexcept
125   { 125   {
126   return token_.stop_requested(); 126   return token_.stop_requested();
127   } 127   }
128   128  
129   capy::io_result<reverse_resolver_result> await_resume() const noexcept 129   capy::io_result<reverse_resolver_result> await_resume() const noexcept
130   { 130   {
131   if (token_.stop_requested()) 131   if (token_.stop_requested())
132   return {make_error_code(std::errc::operation_canceled), {}}; 132   return {make_error_code(std::errc::operation_canceled), {}};
133   return {ec_, std::move(result_)}; 133   return {ec_, std::move(result_)};
134   } 134   }
135   135  
136   auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 136   auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
137   -> std::coroutine_handle<> 137   -> std::coroutine_handle<>
138   { 138   {
139   token_ = env->stop_token; 139   token_ = env->stop_token;
140   return self_.get_impl().reverse_resolve( 140   return self_.get_impl().reverse_resolve(
141   h, env->executor, ep_, flags_, token_, &ec_, &result_); 141   h, env->executor, ep_, flags_, token_, &ec_, &result_);
142   } 142   }
143   }; 143   };
144   144  
145   public: 145   public:
146   /** Construct a native resolver from an execution context. 146   /** Construct a native resolver from an execution context.
147   147  
148   @param ctx The execution context that will own this resolver. 148   @param ctx The execution context that will own this resolver.
149   */ 149   */
HITCBC 150   6 explicit native_resolver(capy::execution_context& ctx) : resolver(ctx) {} 150   6 explicit native_resolver(capy::execution_context& ctx) : resolver(ctx) {}
151   151  
152   /** Construct a native resolver from an executor. 152   /** Construct a native resolver from an executor.
153   153  
154   @param ex The executor whose context will own the resolver. 154   @param ex The executor whose context will own the resolver.
155   */ 155   */
156   template<class Ex> 156   template<class Ex>
157   requires(!std::same_as<std::remove_cvref_t<Ex>, native_resolver>) && 157   requires(!std::same_as<std::remove_cvref_t<Ex>, native_resolver>) &&
158   capy::Executor<Ex> 158   capy::Executor<Ex>
159   explicit native_resolver(Ex const& ex) : native_resolver(ex.context()) 159   explicit native_resolver(Ex const& ex) : native_resolver(ex.context())
160   { 160   {
161   } 161   }
162   162  
163   /** Move construct. 163   /** Move construct.
164   164  
165   @pre No awaitables returned by @p other's `resolve` methods 165   @pre No awaitables returned by @p other's `resolve` methods
166   exist. 166   exist.
167   @pre The execution context associated with @p other must 167   @pre The execution context associated with @p other must
168   outlive this resolver. 168   outlive this resolver.
169   */ 169   */
170   native_resolver(native_resolver&&) noexcept = default; 170   native_resolver(native_resolver&&) noexcept = default;
171   171  
172   /** Move assign. 172   /** Move assign.
173   173  
174   @pre No awaitables returned by either `*this` or the source's 174   @pre No awaitables returned by either `*this` or the source's
175   `resolve` methods exist. 175   `resolve` methods exist.
176   @pre The execution context associated with the source must 176   @pre The execution context associated with the source must
177   outlive this resolver. 177   outlive this resolver.
178   */ 178   */
179   native_resolver& operator=(native_resolver&&) noexcept = default; 179   native_resolver& operator=(native_resolver&&) noexcept = default;
180   180  
181   native_resolver(native_resolver const&) = delete; 181   native_resolver(native_resolver const&) = delete;
182   native_resolver& operator=(native_resolver const&) = delete; 182   native_resolver& operator=(native_resolver const&) = delete;
183   183  
184   /** Asynchronously resolve a host and service to endpoints. 184   /** Asynchronously resolve a host and service to endpoints.
185   185  
186   Calls the backend implementation directly, bypassing virtual 186   Calls the backend implementation directly, bypassing virtual
187   dispatch. Otherwise identical to @ref resolver::resolve. 187   dispatch. Otherwise identical to @ref resolver::resolve.
188   188  
189   This resolver must outlive the returned awaitable. 189   This resolver must outlive the returned awaitable.
190   190  
191   @param host The host name or address string. 191   @param host The host name or address string.
192   @param service The service name or port string. 192   @param service The service name or port string.
193   193  
194   @return An awaitable yielding `io_result<resolver_results>`. 194   @return An awaitable yielding `io_result<resolver_results>`.
195   195  
196   @note `resolver_results` is an alias for `std::vector<resolver_entry>`; 196   @note `resolver_results` is an alias for `std::vector<resolver_entry>`;
197   copying it deep-copies every entry. See @ref resolver::resolve. 197   copying it deep-copies every entry. See @ref resolver::resolve.
198   */ 198   */
HITCBC 199   2 auto resolve(std::string_view host, std::string_view service) 199   2 auto resolve(std::string_view host, std::string_view service)
200   { 200   {
201   return native_resolve_awaitable( 201   return native_resolve_awaitable(
HITCBC 202   2 *this, host, service, resolve_flags::none); 202   2 *this, host, service, resolve_flags::none);
203   } 203   }
204   204  
205   /** Asynchronously resolve a host and service with flags. 205   /** Asynchronously resolve a host and service with flags.
206   206  
207   This resolver must outlive the returned awaitable. 207   This resolver must outlive the returned awaitable.
208   208  
209   @param host The host name or address string. 209   @param host The host name or address string.
210   @param service The service name or port string. 210   @param service The service name or port string.
211   @param flags Flags controlling resolution behavior. 211   @param flags Flags controlling resolution behavior.
212   212  
213   @return An awaitable yielding `io_result<resolver_results>`. 213   @return An awaitable yielding `io_result<resolver_results>`.
214   */ 214   */
215   auto resolve( 215   auto resolve(
216   std::string_view host, std::string_view service, resolve_flags flags) 216   std::string_view host, std::string_view service, resolve_flags flags)
217   { 217   {
218   return native_resolve_awaitable(*this, host, service, flags); 218   return native_resolve_awaitable(*this, host, service, flags);
219   } 219   }
220   220  
221   /** Asynchronously reverse-resolve an endpoint. 221   /** Asynchronously reverse-resolve an endpoint.
222   222  
223   Calls the backend implementation directly, bypassing virtual 223   Calls the backend implementation directly, bypassing virtual
224   dispatch. Otherwise identical to the endpoint overload of 224   dispatch. Otherwise identical to the endpoint overload of
225   @ref resolver::resolve. 225   @ref resolver::resolve.
226   226  
227   This resolver must outlive the returned awaitable. 227   This resolver must outlive the returned awaitable.
228   228  
229   @param ep The endpoint to resolve. 229   @param ep The endpoint to resolve.
230   230  
231   @return An awaitable yielding 231   @return An awaitable yielding
232   `io_result<reverse_resolver_result>`. 232   `io_result<reverse_resolver_result>`.
233   */ 233   */
234   auto resolve(endpoint const& ep) 234   auto resolve(endpoint const& ep)
235   { 235   {
236   return native_reverse_awaitable(*this, ep, reverse_flags::none); 236   return native_reverse_awaitable(*this, ep, reverse_flags::none);
237   } 237   }
238   238  
239   /** Asynchronously reverse-resolve an endpoint with flags. 239   /** Asynchronously reverse-resolve an endpoint with flags.
240   240  
241   This resolver must outlive the returned awaitable. 241   This resolver must outlive the returned awaitable.
242   242  
243   @param ep The endpoint to resolve. 243   @param ep The endpoint to resolve.
244   @param flags Flags controlling resolution behavior. 244   @param flags Flags controlling resolution behavior.
245   245  
246   @return An awaitable yielding 246   @return An awaitable yielding
247   `io_result<reverse_resolver_result>`. 247   `io_result<reverse_resolver_result>`.
248   */ 248   */
249   auto resolve(endpoint const& ep, reverse_flags flags) 249   auto resolve(endpoint const& ep, reverse_flags flags)
250   { 250   {
251   return native_reverse_awaitable(*this, ep, flags); 251   return native_reverse_awaitable(*this, ep, flags);
252   } 252   }
253   }; 253   };
254   254  
255   } // namespace boost::corosio 255   } // namespace boost::corosio
256   256  
257   #endif 257   #endif