1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
// Copyright (c) 2026 Steve Gerbino
3  
// Copyright (c) 2026 Steve Gerbino
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_TCP_ACCEPTOR_HPP
11  
#ifndef BOOST_COROSIO_TCP_ACCEPTOR_HPP
12  
#define BOOST_COROSIO_TCP_ACCEPTOR_HPP
12  
#define BOOST_COROSIO_TCP_ACCEPTOR_HPP
13  

13  

14  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/config.hpp>
15  
#include <boost/corosio/detail/except.hpp>
15  
#include <boost/corosio/detail/except.hpp>
16  
#include <boost/corosio/io/io_object.hpp>
16  
#include <boost/corosio/io/io_object.hpp>
17  
#include <boost/capy/io_result.hpp>
17  
#include <boost/capy/io_result.hpp>
18  
#include <boost/corosio/endpoint.hpp>
18  
#include <boost/corosio/endpoint.hpp>
 
19 +
#include <boost/corosio/tcp.hpp>
19  
#include <boost/corosio/tcp_socket.hpp>
20  
#include <boost/corosio/tcp_socket.hpp>
20  
#include <boost/capy/ex/executor_ref.hpp>
21  
#include <boost/capy/ex/executor_ref.hpp>
21  
#include <boost/capy/ex/execution_context.hpp>
22  
#include <boost/capy/ex/execution_context.hpp>
22  
#include <boost/capy/ex/io_env.hpp>
23  
#include <boost/capy/ex/io_env.hpp>
23  
#include <boost/capy/concept/executor.hpp>
24  
#include <boost/capy/concept/executor.hpp>
24  

25  

25  
#include <system_error>
26  
#include <system_error>
26  

27  

27  
#include <concepts>
28  
#include <concepts>
28  
#include <coroutine>
29  
#include <coroutine>
29  
#include <cstddef>
30  
#include <cstddef>
30  
#include <memory>
31  
#include <memory>
31  
#include <stop_token>
32  
#include <stop_token>
32  
#include <type_traits>
33  
#include <type_traits>
33  

34  

34  
namespace boost::corosio {
35  
namespace boost::corosio {
35  

36  

36  
/** An asynchronous TCP acceptor for coroutine I/O.
37  
/** An asynchronous TCP acceptor for coroutine I/O.
37  

38  

38  
    This class provides asynchronous TCP accept operations that return
39  
    This class provides asynchronous TCP accept operations that return
39  
    awaitable types. The acceptor binds to a local endpoint and listens
40  
    awaitable types. The acceptor binds to a local endpoint and listens
40  
    for incoming connections.
41  
    for incoming connections.
41  

42  

42  
    Each accept operation participates in the affine awaitable protocol,
43  
    Each accept operation participates in the affine awaitable protocol,
43  
    ensuring coroutines resume on the correct executor.
44  
    ensuring coroutines resume on the correct executor.
44  

45  

45  
    @par Thread Safety
46  
    @par Thread Safety
46  
    Distinct objects: Safe.@n
47  
    Distinct objects: Safe.@n
47  
    Shared objects: Unsafe. An acceptor must not have concurrent accept
48  
    Shared objects: Unsafe. An acceptor must not have concurrent accept
48  
    operations.
49  
    operations.
49  

50  

50  
    @par Semantics
51  
    @par Semantics
51  
    Wraps the platform TCP listener. Operations dispatch to
52  
    Wraps the platform TCP listener. Operations dispatch to
52  
    OS accept APIs via the io_context reactor.
53  
    OS accept APIs via the io_context reactor.
53  

54  

54  
    @par Example
55  
    @par Example
55  
    @code
56  
    @code
 
57 +
    // Convenience constructor: open + SO_REUSEADDR + bind + listen
56  
    io_context ioc;
58  
    io_context ioc;
57 -
    tcp_acceptor acc(ioc);
59 +
    tcp_acceptor acc( ioc, endpoint( 8080 ) );
58 -
    if (auto ec = acc.listen(endpoint(8080)))  // Bind to port 8080
 
59 -
        return ec;
 
60  

60  

61 -
    tcp_socket peer(ioc);
61 +
    tcp_socket peer( ioc );
62 -
    auto [ec] = co_await acc.accept(peer);
62 +
    auto [ec] = co_await acc.accept( peer );
63 -
    if (!ec) {
63 +
    if ( !ec ) {
64  
        // peer is now a connected socket
64  
        // peer is now a connected socket
65 -
        auto [ec2, n] = co_await peer.read_some(buf);
65 +
        auto [ec2, n] = co_await peer.read_some( buf );
66  
    }
66  
    }
67  
    @endcode
67  
    @endcode
 
68 +

 
69 +
    @par Example
 
70 +
    @code
 
71 +
    // Fine-grained setup
 
72 +
    tcp_acceptor acc( ioc );
 
73 +
    acc.open( tcp::v6() );
 
74 +
    acc.set_option( socket_option::reuse_address( true ) );
 
75 +
    acc.set_option( socket_option::v6_only( true ) );
 
76 +
    if ( auto ec = acc.bind( endpoint( ipv6_address::any(), 8080 ) ) )
 
77 +
        return ec;
 
78 +
    if ( auto ec = acc.listen() )
 
79 +
        return ec;
 
80 +
    @endcode
68  
*/
81  
*/
69  
class BOOST_COROSIO_DECL tcp_acceptor : public io_object
82  
class BOOST_COROSIO_DECL tcp_acceptor : public io_object
70  
{
83  
{
71  
    struct accept_awaitable
84  
    struct accept_awaitable
72  
    {
85  
    {
73  
        tcp_acceptor& acc_;
86  
        tcp_acceptor& acc_;
74  
        tcp_socket& peer_;
87  
        tcp_socket& peer_;
75  
        std::stop_token token_;
88  
        std::stop_token token_;
76  
        mutable std::error_code ec_;
89  
        mutable std::error_code ec_;
77  
        mutable io_object::implementation* peer_impl_ = nullptr;
90  
        mutable io_object::implementation* peer_impl_ = nullptr;
78  

91  

79  
        accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
92  
        accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
80  
            : acc_(acc)
93  
            : acc_(acc)
81  
            , peer_(peer)
94  
            , peer_(peer)
82  
        {
95  
        {
83  
        }
96  
        }
84  

97  

85  
        bool await_ready() const noexcept
98  
        bool await_ready() const noexcept
86  
        {
99  
        {
87  
            return token_.stop_requested();
100  
            return token_.stop_requested();
88  
        }
101  
        }
89  

102  

90  
        capy::io_result<> await_resume() const noexcept
103  
        capy::io_result<> await_resume() const noexcept
91  
        {
104  
        {
92  
            if (token_.stop_requested())
105  
            if (token_.stop_requested())
93  
                return {make_error_code(std::errc::operation_canceled)};
106  
                return {make_error_code(std::errc::operation_canceled)};
94  

107  

95  
            if (!ec_ && peer_impl_)
108  
            if (!ec_ && peer_impl_)
96  
                peer_.h_.reset(peer_impl_);
109  
                peer_.h_.reset(peer_impl_);
97  
            return {ec_};
110  
            return {ec_};
98  
        }
111  
        }
99  

112  

100  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
113  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
101  
            -> std::coroutine_handle<>
114  
            -> std::coroutine_handle<>
102  
        {
115  
        {
103  
            token_ = env->stop_token;
116  
            token_ = env->stop_token;
104  
            return acc_.get().accept(
117  
            return acc_.get().accept(
105  
                h, env->executor, token_, &ec_, &peer_impl_);
118  
                h, env->executor, token_, &ec_, &peer_impl_);
106  
        }
119  
        }
107  
    };
120  
    };
108  

121  

109  
public:
122  
public:
110  
    /** Destructor.
123  
    /** Destructor.
111  

124  

112  
        Closes the acceptor if open, cancelling any pending operations.
125  
        Closes the acceptor if open, cancelling any pending operations.
113  
    */
126  
    */
114  
    ~tcp_acceptor() override;
127  
    ~tcp_acceptor() override;
115  

128  

116  
    /** Construct an acceptor from an execution context.
129  
    /** Construct an acceptor from an execution context.
117  

130  

118  
        @param ctx The execution context that will own this acceptor.
131  
        @param ctx The execution context that will own this acceptor.
119  
    */
132  
    */
120  
    explicit tcp_acceptor(capy::execution_context& ctx);
133  
    explicit tcp_acceptor(capy::execution_context& ctx);
121  

134  

 
135 +
    /** Convenience constructor: open + SO_REUSEADDR + bind + listen.
 
136 +

 
137 +
        Creates a fully-bound listening acceptor in a single
 
138 +
        expression. The address family is deduced from @p ep.
 
139 +

 
140 +
        @param ctx The execution context that will own this acceptor.
 
141 +
        @param ep The local endpoint to bind to.
 
142 +
        @param backlog The maximum pending connection queue length.
 
143 +

 
144 +
        @throws std::system_error on bind or listen failure.
 
145 +
    */
 
146 +
    tcp_acceptor(
 
147 +
        capy::execution_context& ctx, endpoint ep, int backlog = 128 );
 
148 +

122  
    /** Construct an acceptor from an executor.
149  
    /** Construct an acceptor from an executor.
123  

150  

124  
        The acceptor is associated with the executor's context.
151  
        The acceptor is associated with the executor's context.
125  

152  

126  
        @param ex The executor whose context will own the acceptor.
153  
        @param ex The executor whose context will own the acceptor.
127  
    */
154  
    */
128  
    template<class Ex>
155  
    template<class Ex>
129  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) &&
156  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) &&
130  
        capy::Executor<Ex>
157  
        capy::Executor<Ex>
131  
    explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
158  
    explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
132  
    {
159  
    {
133  
    }
160  
    }
134  

161  

 
162 +
    /** Convenience constructor from an executor.
 
163 +

 
164 +
        @param ex The executor whose context will own the acceptor.
 
165 +
        @param ep The local endpoint to bind to.
 
166 +
        @param backlog The maximum pending connection queue length.
 
167 +

 
168 +
        @throws std::system_error on bind or listen failure.
 
169 +
    */
 
170 +
    template<class Ex>
 
171 +
        requires capy::Executor<Ex>
 
172 +
    tcp_acceptor(Ex const& ex, endpoint ep, int backlog = 128 )
 
173 +
        : tcp_acceptor(ex.context(), ep, backlog)
 
174 +
    {
 
175 +
    }
 
176 +

135  
    /** Move constructor.
177  
    /** Move constructor.
136  

178  

137  
        Transfers ownership of the acceptor resources.
179  
        Transfers ownership of the acceptor resources.
138  

180  

139  
        @param other The acceptor to move from.
181  
        @param other The acceptor to move from.
140  

182  

141  
        @pre No awaitables returned by @p other's methods exist.
183  
        @pre No awaitables returned by @p other's methods exist.
142  
        @pre The execution context associated with @p other must
184  
        @pre The execution context associated with @p other must
143  
            outlive this acceptor.
185  
            outlive this acceptor.
144  
    */
186  
    */
145  
    tcp_acceptor(tcp_acceptor&& other) noexcept : io_object(std::move(other)) {}
187  
    tcp_acceptor(tcp_acceptor&& other) noexcept : io_object(std::move(other)) {}
146  

188  

147  
    /** Move assignment operator.
189  
    /** Move assignment operator.
148  

190  

149  
        Closes any existing acceptor and transfers ownership.
191  
        Closes any existing acceptor and transfers ownership.
150  

192  

151  
        @param other The acceptor to move from.
193  
        @param other The acceptor to move from.
152  

194  

153  
        @pre No awaitables returned by either `*this` or @p other's
195  
        @pre No awaitables returned by either `*this` or @p other's
154  
            methods exist.
196  
            methods exist.
155  
        @pre The execution context associated with @p other must
197  
        @pre The execution context associated with @p other must
156  
            outlive this acceptor.
198  
            outlive this acceptor.
157  

199  

158  
        @return Reference to this acceptor.
200  
        @return Reference to this acceptor.
159  
    */
201  
    */
160  
    tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
202  
    tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
161  
    {
203  
    {
162  
        if (this != &other)
204  
        if (this != &other)
163  
        {
205  
        {
164  
            close();
206  
            close();
165  
            h_ = std::move(other.h_);
207  
            h_ = std::move(other.h_);
166  
        }
208  
        }
167  
        return *this;
209  
        return *this;
168  
    }
210  
    }
169  

211  

170  
    tcp_acceptor(tcp_acceptor const&)            = delete;
212  
    tcp_acceptor(tcp_acceptor const&)            = delete;
171  
    tcp_acceptor& operator=(tcp_acceptor const&) = delete;
213  
    tcp_acceptor& operator=(tcp_acceptor const&) = delete;
172  

214  

173 -
    /** Open, bind, and listen on an endpoint.
215 +
    /** Create the acceptor socket without binding or listening.
174  

216  

175 -
        Creates an IPv4 TCP socket, binds it to the specified endpoint,
217 +
        Creates a TCP socket with dual-stack enabled for IPv6.
176 -
        and begins listening for incoming connections. This must be
218 +
        Does not set SO_REUSEADDR — call `set_option` explicitly
177 -
        called before initiating accept operations.
219 +
        if needed.
178  

220  

179 -
        @param ep The local endpoint to bind to. Use `endpoint(port)` to
221 +
        If the acceptor is already open, this function is a no-op.
180 -
            bind to all interfaces on a specific port.
 
181  

222  

182 -
        @param backlog The maximum length of the queue of pending
223 +
        @param proto The protocol (IPv4 or IPv6). Defaults to
183 -
            connections. Defaults to 128.
224 +
            `tcp::v4()`.
184  

225  

185 -
        @return An error code indicating success or the reason for failure.
226 +
        @throws std::system_error on failure.
186 -
            A default-constructed error code indicates success.
227 +

 
228 +
        @par Example
 
229 +
        @code
 
230 +
        acc.open( tcp::v6() );
 
231 +
        acc.set_option( socket_option::reuse_address( true ) );
 
232 +
        acc.bind( endpoint( ipv6_address::any(), 8080 ) );
 
233 +
        acc.listen();
 
234 +
        @endcode
 
235 +

 
236 +
        @see bind, listen
 
237 +
    */
 
238 +
    void open( tcp proto = tcp::v4() );
 
239 +

 
240 +
    /** Bind to a local endpoint.
 
241 +

 
242 +
        The acceptor must be open. Binds the socket to @p ep and
 
243 +
        caches the resolved local endpoint (useful when port 0 is
 
244 +
        used to request an ephemeral port).
 
245 +

 
246 +
        @param ep The local endpoint to bind to.
 
247 +

 
248 +
        @return An error code indicating success or the reason for
 
249 +
            failure.
187  

250  

188  
        @par Error Conditions
251  
        @par Error Conditions
189  
        @li `errc::address_in_use`: The endpoint is already in use.
252  
        @li `errc::address_in_use`: The endpoint is already in use.
190  
        @li `errc::address_not_available`: The address is not available
253  
        @li `errc::address_not_available`: The address is not available
191  
            on any local interface.
254  
            on any local interface.
192  
        @li `errc::permission_denied`: Insufficient privileges to bind
255  
        @li `errc::permission_denied`: Insufficient privileges to bind
193 -
        @li `errc::operation_not_supported`: The acceptor service is
 
194 -
            unavailable in the context (POSIX only).
 
195  
            to the endpoint (e.g., privileged port).
256  
            to the endpoint (e.g., privileged port).
196  

257  

197 -
        @throws Nothing.
258 +
        @throws std::logic_error if the acceptor is not open.
198  
    */
259  
    */
199 -
    [[nodiscard]] std::error_code listen(endpoint ep, int backlog = 128);
260 +
    [[nodiscard]] std::error_code bind( endpoint ep );
 
261 +

 
262 +
    /** Start listening for incoming connections.
 
263 +

 
264 +
        The acceptor must be open and bound. Registers the acceptor
 
265 +
        with the platform reactor.
 
266 +

 
267 +
        @param backlog The maximum length of the queue of pending
 
268 +
            connections. Defaults to 128.
 
269 +

 
270 +
        @return An error code indicating success or the reason for
 
271 +
            failure.
 
272 +

 
273 +
        @throws std::logic_error if the acceptor is not open.
 
274 +
    */
 
275 +
    [[nodiscard]] std::error_code listen( int backlog = 128 );
200  

276  

201  
    /** Close the acceptor.
277  
    /** Close the acceptor.
202  

278  

203  
        Releases acceptor resources. Any pending operations complete
279  
        Releases acceptor resources. Any pending operations complete
204  
        with `errc::operation_canceled`.
280  
        with `errc::operation_canceled`.
205  
    */
281  
    */
206  
    void close();
282  
    void close();
207  

283  

208  
    /** Check if the acceptor is listening.
284  
    /** Check if the acceptor is listening.
209  

285  

210  
        @return `true` if the acceptor is open and listening.
286  
        @return `true` if the acceptor is open and listening.
211  
    */
287  
    */
212  
    bool is_open() const noexcept
288  
    bool is_open() const noexcept
213  
    {
289  
    {
214  
        return h_ && get().is_open();
290  
        return h_ && get().is_open();
215  
    }
291  
    }
216  

292  

217  
    /** Initiate an asynchronous accept operation.
293  
    /** Initiate an asynchronous accept operation.
218  

294  

219  
        Accepts an incoming connection and initializes the provided
295  
        Accepts an incoming connection and initializes the provided
220  
        socket with the new connection. The acceptor must be listening
296  
        socket with the new connection. The acceptor must be listening
221  
        before calling this function.
297  
        before calling this function.
222  

298  

223  
        The operation supports cancellation via `std::stop_token` through
299  
        The operation supports cancellation via `std::stop_token` through
224  
        the affine awaitable protocol. If the associated stop token is
300  
        the affine awaitable protocol. If the associated stop token is
225  
        triggered, the operation completes immediately with
301  
        triggered, the operation completes immediately with
226  
        `errc::operation_canceled`.
302  
        `errc::operation_canceled`.
227  

303  

228  
        @param peer The socket to receive the accepted connection. Any
304  
        @param peer The socket to receive the accepted connection. Any
229  
            existing connection on this socket will be closed.
305  
            existing connection on this socket will be closed.
230  

306  

231  
        @return An awaitable that completes with `io_result<>`.
307  
        @return An awaitable that completes with `io_result<>`.
232  
            Returns success on successful accept, or an error code on
308  
            Returns success on successful accept, or an error code on
233  
            failure including:
309  
            failure including:
234  
            - operation_canceled: Cancelled via stop_token or cancel().
310  
            - operation_canceled: Cancelled via stop_token or cancel().
235  
                Check `ec == cond::canceled` for portable comparison.
311  
                Check `ec == cond::canceled` for portable comparison.
236  

312  

237  
        @par Preconditions
313  
        @par Preconditions
238  
        The acceptor must be listening (`is_open() == true`).
314  
        The acceptor must be listening (`is_open() == true`).
239  
        The peer socket must be associated with the same execution context.
315  
        The peer socket must be associated with the same execution context.
240  

316  

241  
        Both this acceptor and @p peer must outlive the returned
317  
        Both this acceptor and @p peer must outlive the returned
242  
        awaitable.
318  
        awaitable.
243  

319  

244  
        @par Example
320  
        @par Example
245  
        @code
321  
        @code
246  
        tcp_socket peer(ioc);
322  
        tcp_socket peer(ioc);
247  
        auto [ec] = co_await acc.accept(peer);
323  
        auto [ec] = co_await acc.accept(peer);
248  
        if (!ec) {
324  
        if (!ec) {
249  
            // Use peer socket
325  
            // Use peer socket
250  
        }
326  
        }
251  
        @endcode
327  
        @endcode
252  
    */
328  
    */
253  
    auto accept(tcp_socket& peer)
329  
    auto accept(tcp_socket& peer)
254  
    {
330  
    {
255  
        if (!is_open())
331  
        if (!is_open())
256  
            detail::throw_logic_error("accept: acceptor not listening");
332  
            detail::throw_logic_error("accept: acceptor not listening");
257  
        return accept_awaitable(*this, peer);
333  
        return accept_awaitable(*this, peer);
258  
    }
334  
    }
259  

335  

260  
    /** Cancel any pending asynchronous operations.
336  
    /** Cancel any pending asynchronous operations.
261  

337  

262  
        All outstanding operations complete with `errc::operation_canceled`.
338  
        All outstanding operations complete with `errc::operation_canceled`.
263  
        Check `ec == cond::canceled` for portable comparison.
339  
        Check `ec == cond::canceled` for portable comparison.
264  
    */
340  
    */
265  
    void cancel();
341  
    void cancel();
266  

342  

267  
    /** Get the local endpoint of the acceptor.
343  
    /** Get the local endpoint of the acceptor.
268  

344  

269  
        Returns the local address and port to which the acceptor is bound.
345  
        Returns the local address and port to which the acceptor is bound.
270  
        This is useful when binding to port 0 (ephemeral port) to discover
346  
        This is useful when binding to port 0 (ephemeral port) to discover
271  
        the OS-assigned port number. The endpoint is cached when listen()
347  
        the OS-assigned port number. The endpoint is cached when listen()
272  
        is called.
348  
        is called.
273  

349  

274  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
350  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
275  
            the acceptor is not listening.
351  
            the acceptor is not listening.
276  

352  

277  
        @par Thread Safety
353  
        @par Thread Safety
278  
        The cached endpoint value is set during listen() and cleared
354  
        The cached endpoint value is set during listen() and cleared
279  
        during close(). This function may be called concurrently with
355  
        during close(). This function may be called concurrently with
280  
        accept operations, but must not be called concurrently with
356  
        accept operations, but must not be called concurrently with
281  
        listen() or close().
357  
        listen() or close().
282  
    */
358  
    */
283  
    endpoint local_endpoint() const noexcept;
359  
    endpoint local_endpoint() const noexcept;
284  

360  

 
361 +
    /** Set a socket option on the acceptor.
 
362 +

 
363 +
        Applies a type-safe socket option to the underlying listening
 
364 +
        socket. The socket must be open (via `open()` or `listen()`).
 
365 +
        This is useful for setting options between `open()` and
 
366 +
        `listen()`, such as `socket_option::reuse_port`.
 
367 +

 
368 +
        @par Example
 
369 +
        @code
 
370 +
        acc.open( tcp::v6() );
 
371 +
        acc.set_option( socket_option::reuse_port( true ) );
 
372 +
        acc.bind( endpoint( ipv6_address::any(), 8080 ) );
 
373 +
        acc.listen();
 
374 +
        @endcode
 
375 +

 
376 +
        @param opt The option to set.
 
377 +

 
378 +
        @throws std::logic_error if the acceptor is not open.
 
379 +
        @throws std::system_error on failure.
 
380 +
    */
 
381 +
    template<class Option>
 
382 +
    void set_option( Option const& opt )
 
383 +
    {
 
384 +
        if (!is_open())
 
385 +
            detail::throw_logic_error(
 
386 +
                "set_option: acceptor not open" );
 
387 +
        std::error_code ec = get().set_option(
 
388 +
            Option::level(), Option::name(), opt.data(), opt.size() );
 
389 +
        if (ec)
 
390 +
            detail::throw_system_error(
 
391 +
                ec, "tcp_acceptor::set_option" );
 
392 +
    }
 
393 +

 
394 +
    /** Get a socket option from the acceptor.
 
395 +

 
396 +
        Retrieves the current value of a type-safe socket option.
 
397 +

 
398 +
        @par Example
 
399 +
        @code
 
400 +
        auto opt = acc.get_option<socket_option::reuse_address>();
 
401 +
        @endcode
 
402 +

 
403 +
        @return The current option value.
 
404 +

 
405 +
        @throws std::logic_error if the acceptor is not open.
 
406 +
        @throws std::system_error on failure.
 
407 +
    */
 
408 +
    template<class Option>
 
409 +
    Option get_option() const
 
410 +
    {
 
411 +
        if (!is_open())
 
412 +
            detail::throw_logic_error(
 
413 +
                "get_option: acceptor not open" );
 
414 +
        Option opt{};
 
415 +
        std::size_t sz = opt.size();
 
416 +
        std::error_code ec = get().get_option(
 
417 +
            Option::level(), Option::name(), opt.data(), &sz );
 
418 +
        if (ec)
 
419 +
            detail::throw_system_error(
 
420 +
                ec, "tcp_acceptor::get_option" );
 
421 +
        opt.resize( sz );
 
422 +
        return opt;
 
423 +
    }
 
424 +

285  
    struct implementation : io_object::implementation
425  
    struct implementation : io_object::implementation
286  
    {
426  
    {
287  
        virtual std::coroutine_handle<> accept(
427  
        virtual std::coroutine_handle<> accept(
288  
            std::coroutine_handle<>,
428  
            std::coroutine_handle<>,
289  
            capy::executor_ref,
429  
            capy::executor_ref,
290  
            std::stop_token,
430  
            std::stop_token,
291  
            std::error_code*,
431  
            std::error_code*,
292  
            io_object::implementation**) = 0;
432  
            io_object::implementation**) = 0;
293  

433  

294  
        /// Returns the cached local endpoint.
434  
        /// Returns the cached local endpoint.
295  
        virtual endpoint local_endpoint() const noexcept = 0;
435  
        virtual endpoint local_endpoint() const noexcept = 0;
296  

436  

297  
        /// Return true if the acceptor has a kernel resource open.
437  
        /// Return true if the acceptor has a kernel resource open.
298  
        virtual bool is_open() const noexcept = 0;
438  
        virtual bool is_open() const noexcept = 0;
299  

439  

300  
        /** Cancel any pending asynchronous operations.
440  
        /** Cancel any pending asynchronous operations.
301  

441  

302  
            All outstanding operations complete with operation_canceled error.
442  
            All outstanding operations complete with operation_canceled error.
303  
        */
443  
        */
304  
        virtual void cancel() noexcept = 0;
444  
        virtual void cancel() noexcept = 0;
 
445 +

 
446 +
        /** Set a socket option.
 
447 +

 
448 +
            @param level The protocol level.
 
449 +
            @param optname The option name.
 
450 +
            @param data Pointer to the option value.
 
451 +
            @param size Size of the option value in bytes.
 
452 +
            @return Error code on failure, empty on success.
 
453 +
        */
 
454 +
        virtual std::error_code set_option(
 
455 +
            int level, int optname,
 
456 +
            void const* data, std::size_t size) noexcept = 0;
 
457 +

 
458 +
        /** Get a socket option.
 
459 +

 
460 +
            @param level The protocol level.
 
461 +
            @param optname The option name.
 
462 +
            @param data Pointer to receive the option value.
 
463 +
            @param size On entry, the size of the buffer. On exit,
 
464 +
                the size of the option value.
 
465 +
            @return Error code on failure, empty on success.
 
466 +
        */
 
467 +
        virtual std::error_code get_option(
 
468 +
            int level, int optname,
 
469 +
            void* data, std::size_t* size) const noexcept = 0;
305  
    };
470  
    };
306  

471  

307  
protected:
472  
protected:
308  
    explicit tcp_acceptor(handle h) noexcept : io_object(std::move(h)) {}
473  
    explicit tcp_acceptor(handle h) noexcept : io_object(std::move(h)) {}
309  

474  

310  
    /// Transfer accepted peer impl to the peer socket.
475  
    /// Transfer accepted peer impl to the peer socket.
311  
    static void
476  
    static void
312  
    reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
477  
    reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
313  
    {
478  
    {
314  
        if (impl)
479  
        if (impl)
315  
            peer.h_.reset(impl);
480  
            peer.h_.reset(impl);
316  
    }
481  
    }
317  

482  

318  
private:
483  
private:
319  
    inline implementation& get() const noexcept
484  
    inline implementation& get() const noexcept
320  
    {
485  
    {
321  
        return *static_cast<implementation*>(h_.get());
486  
        return *static_cast<implementation*>(h_.get());
322  
    }
487  
    }
323  
};
488  
};
324  

489  

325  
} // namespace boost::corosio
490  
} // namespace boost::corosio
326  

491  

327  
#endif
492  
#endif