include/boost/corosio/native/native_resolver.hpp

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