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.hpp>
20  
#include <boost/corosio/tcp_socket.hpp>
20  
#include <boost/corosio/tcp_socket.hpp>
21  
#include <boost/capy/ex/executor_ref.hpp>
21  
#include <boost/capy/ex/executor_ref.hpp>
22  
#include <boost/capy/ex/execution_context.hpp>
22  
#include <boost/capy/ex/execution_context.hpp>
23  
#include <boost/capy/ex/io_env.hpp>
23  
#include <boost/capy/ex/io_env.hpp>
24  
#include <boost/capy/concept/executor.hpp>
24  
#include <boost/capy/concept/executor.hpp>
25  

25  

26  
#include <system_error>
26  
#include <system_error>
27  

27  

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

33  

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

35  

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

37  

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

41  

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

44  

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

49  

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

53  

54  
    @par Example
54  
    @par Example
55  
    @code
55  
    @code
56  
    // Convenience constructor: open + SO_REUSEADDR + bind + listen
56  
    // Convenience constructor: open + SO_REUSEADDR + bind + listen
57  
    io_context ioc;
57  
    io_context ioc;
58  
    tcp_acceptor acc( ioc, endpoint( 8080 ) );
58  
    tcp_acceptor acc( ioc, endpoint( 8080 ) );
59  

59  

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

67  

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

90  

91  
        accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
91  
        accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
92  
            : acc_(acc)
92  
            : acc_(acc)
93  
            , peer_(peer)
93  
            , peer_(peer)
94  
        {
94  
        {
95  
        }
95  
        }
96  

96  

97  
        bool await_ready() const noexcept
97  
        bool await_ready() const noexcept
98  
        {
98  
        {
99  
            return token_.stop_requested();
99  
            return token_.stop_requested();
100  
        }
100  
        }
101  

101  

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

106  

107  
            if (!ec_ && peer_impl_)
107  
            if (!ec_ && peer_impl_)
108  
                peer_.h_.reset(peer_impl_);
108  
                peer_.h_.reset(peer_impl_);
109  
            return {ec_};
109  
            return {ec_};
110  
        }
110  
        }
111  

111  

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

120  

121  
public:
121  
public:
122  
    /** Destructor.
122  
    /** Destructor.
123  

123  

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

127  

128  
    /** Construct an acceptor from an execution context.
128  
    /** Construct an acceptor from an execution context.
129  

129  

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

133  

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

135  

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

138  

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

142  

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

146  

147  
    /** Construct an acceptor from an executor.
147  
    /** Construct an acceptor from an executor.
148  

148  

149  
        The acceptor is associated with the executor's context.
149  
        The acceptor is associated with the executor's context.
150  

150  

151  
        @param ex The executor whose context will own the acceptor.
151  
        @param ex The executor whose context will own the acceptor.
152  
    */
152  
    */
153  
    template<class Ex>
153  
    template<class Ex>
154  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) &&
154  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) &&
155  
        capy::Executor<Ex>
155  
        capy::Executor<Ex>
156  
    explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
156  
    explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
157  
    {
157  
    {
158  
    }
158  
    }
159  

159  

160  
    /** Convenience constructor from an executor.
160  
    /** Convenience constructor from an executor.
161  

161  

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

165  

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

174  

175  
    /** Move constructor.
175  
    /** Move constructor.
176  

176  

177  
        Transfers ownership of the acceptor resources.
177  
        Transfers ownership of the acceptor resources.
178  

178  

179  
        @param other The acceptor to move from.
179  
        @param other The acceptor to move from.
180  

180  

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

186  

187  
    /** Move assignment operator.
187  
    /** Move assignment operator.
188  

188  

189  
        Closes any existing acceptor and transfers ownership.
189  
        Closes any existing acceptor and transfers ownership.
190  

190  

191  
        @param other The acceptor to move from.
191  
        @param other The acceptor to move from.
192  

192  

193  
        @pre No awaitables returned by either `*this` or @p other's
193  
        @pre No awaitables returned by either `*this` or @p other's
194  
            methods exist.
194  
            methods exist.
195  
        @pre The execution context associated with @p other must
195  
        @pre The execution context associated with @p other must
196  
            outlive this acceptor.
196  
            outlive this acceptor.
197  

197  

198  
        @return Reference to this acceptor.
198  
        @return Reference to this acceptor.
199  
    */
199  
    */
200  
    tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
200  
    tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
201  
    {
201  
    {
202  
        if (this != &other)
202  
        if (this != &other)
203  
        {
203  
        {
204  
            close();
204  
            close();
205  
            h_ = std::move(other.h_);
205  
            h_ = std::move(other.h_);
206  
        }
206  
        }
207  
        return *this;
207  
        return *this;
208  
    }
208  
    }
209  

209  

210  
    tcp_acceptor(tcp_acceptor const&)            = delete;
210  
    tcp_acceptor(tcp_acceptor const&)            = delete;
211  
    tcp_acceptor& operator=(tcp_acceptor const&) = delete;
211  
    tcp_acceptor& operator=(tcp_acceptor const&) = delete;
212  

212  

213  
    /** Create the acceptor socket without binding or listening.
213  
    /** Create the acceptor socket without binding or listening.
214  

214  

215  
        Creates a TCP socket with dual-stack enabled for IPv6.
215  
        Creates a TCP socket with dual-stack enabled for IPv6.
216  
        Does not set SO_REUSEADDR — call `set_option` explicitly
216  
        Does not set SO_REUSEADDR — call `set_option` explicitly
217  
        if needed.
217  
        if needed.
218  

218  

219  
        If the acceptor is already open, this function is a no-op.
219  
        If the acceptor is already open, this function is a no-op.
220  

220  

221  
        @param proto The protocol (IPv4 or IPv6). Defaults to
221  
        @param proto The protocol (IPv4 or IPv6). Defaults to
222  
            `tcp::v4()`.
222  
            `tcp::v4()`.
223  

223  

224  
        @throws std::system_error on failure.
224  
        @throws std::system_error on failure.
225  

225  

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

233  

234  
        @see bind, listen
234  
        @see bind, listen
235  
    */
235  
    */
236  
    void open(tcp proto = tcp::v4());
236  
    void open(tcp proto = tcp::v4());
237  

237  

238  
    /** Bind to a local endpoint.
238  
    /** Bind to a local endpoint.
239  

239  

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

243  

244  
        @param ep The local endpoint to bind to.
244  
        @param ep The local endpoint to bind to.
245  

245  

246  
        @return An error code indicating success or the reason for
246  
        @return An error code indicating success or the reason for
247  
            failure.
247  
            failure.
248  

248  

249  
        @par Error Conditions
249  
        @par Error Conditions
250  
        @li `errc::address_in_use`: The endpoint is already in use.
250  
        @li `errc::address_in_use`: The endpoint is already in use.
251  
        @li `errc::address_not_available`: The address is not available
251  
        @li `errc::address_not_available`: The address is not available
252  
            on any local interface.
252  
            on any local interface.
253  
        @li `errc::permission_denied`: Insufficient privileges to bind
253  
        @li `errc::permission_denied`: Insufficient privileges to bind
254  
            to the endpoint (e.g., privileged port).
254  
            to the endpoint (e.g., privileged port).
255  

255  

256  
        @throws std::logic_error if the acceptor is not open.
256  
        @throws std::logic_error if the acceptor is not open.
257  
    */
257  
    */
258  
    [[nodiscard]] std::error_code bind(endpoint ep);
258  
    [[nodiscard]] std::error_code bind(endpoint ep);
259  

259  

260  
    /** Start listening for incoming connections.
260  
    /** Start listening for incoming connections.
261  

261  

262  
        The acceptor must be open and bound. Registers the acceptor
262  
        The acceptor must be open and bound. Registers the acceptor
263  
        with the platform reactor.
263  
        with the platform reactor.
264  

264  

265  
        @param backlog The maximum length of the queue of pending
265  
        @param backlog The maximum length of the queue of pending
266  
            connections. Defaults to 128.
266  
            connections. Defaults to 128.
267  

267  

268  
        @return An error code indicating success or the reason for
268  
        @return An error code indicating success or the reason for
269  
            failure.
269  
            failure.
270  

270  

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

274  

275  
    /** Close the acceptor.
275  
    /** Close the acceptor.
276  

276  

277  
        Releases acceptor resources. Any pending operations complete
277  
        Releases acceptor resources. Any pending operations complete
278  
        with `errc::operation_canceled`.
278  
        with `errc::operation_canceled`.
279  
    */
279  
    */
280  
    void close();
280  
    void close();
281  

281  

282  
    /** Check if the acceptor is listening.
282  
    /** Check if the acceptor is listening.
283  

283  

284  
        @return `true` if the acceptor is open and listening.
284  
        @return `true` if the acceptor is open and listening.
285  
    */
285  
    */
286  
    bool is_open() const noexcept
286  
    bool is_open() const noexcept
287  
    {
287  
    {
288  
        return h_ && get().is_open();
288  
        return h_ && get().is_open();
289  
    }
289  
    }
290  

290  

291  
    /** Initiate an asynchronous accept operation.
291  
    /** Initiate an asynchronous accept operation.
292  

292  

293  
        Accepts an incoming connection and initializes the provided
293  
        Accepts an incoming connection and initializes the provided
294  
        socket with the new connection. The acceptor must be listening
294  
        socket with the new connection. The acceptor must be listening
295  
        before calling this function.
295  
        before calling this function.
296  

296  

297  
        The operation supports cancellation via `std::stop_token` through
297  
        The operation supports cancellation via `std::stop_token` through
298  
        the affine awaitable protocol. If the associated stop token is
298  
        the affine awaitable protocol. If the associated stop token is
299  
        triggered, the operation completes immediately with
299  
        triggered, the operation completes immediately with
300  
        `errc::operation_canceled`.
300  
        `errc::operation_canceled`.
301  

301  

302  
        @param peer The socket to receive the accepted connection. Any
302  
        @param peer The socket to receive the accepted connection. Any
303  
            existing connection on this socket will be closed.
303  
            existing connection on this socket will be closed.
304  

304  

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

310  

311  
        @par Preconditions
311  
        @par Preconditions
312  
        The acceptor must be listening (`is_open() == true`).
312  
        The acceptor must be listening (`is_open() == true`).
313  
        The peer socket must be associated with the same execution context.
313  
        The peer socket must be associated with the same execution context.
314  

314  

315  
        Both this acceptor and @p peer must outlive the returned
315  
        Both this acceptor and @p peer must outlive the returned
316  
        awaitable.
316  
        awaitable.
317  

317  

318  
        @par Example
318  
        @par Example
319  
        @code
319  
        @code
320  
        tcp_socket peer(ioc);
320  
        tcp_socket peer(ioc);
321  
        auto [ec] = co_await acc.accept(peer);
321  
        auto [ec] = co_await acc.accept(peer);
322  
        if (!ec) {
322  
        if (!ec) {
323  
            // Use peer socket
323  
            // Use peer socket
324  
        }
324  
        }
325  
        @endcode
325  
        @endcode
326  
    */
326  
    */
327  
    auto accept(tcp_socket& peer)
327  
    auto accept(tcp_socket& peer)
328  
    {
328  
    {
329  
        if (!is_open())
329  
        if (!is_open())
330  
            detail::throw_logic_error("accept: acceptor not listening");
330  
            detail::throw_logic_error("accept: acceptor not listening");
331  
        return accept_awaitable(*this, peer);
331  
        return accept_awaitable(*this, peer);
332  
    }
332  
    }
333  

333  

334  
    /** Cancel any pending asynchronous operations.
334  
    /** Cancel any pending asynchronous operations.
335  

335  

336  
        All outstanding operations complete with `errc::operation_canceled`.
336  
        All outstanding operations complete with `errc::operation_canceled`.
337  
        Check `ec == cond::canceled` for portable comparison.
337  
        Check `ec == cond::canceled` for portable comparison.
338  
    */
338  
    */
339  
    void cancel();
339  
    void cancel();
340  

340  

341  
    /** Get the local endpoint of the acceptor.
341  
    /** Get the local endpoint of the acceptor.
342  

342  

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

347  

348  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
348  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
349  
            the acceptor is not listening.
349  
            the acceptor is not listening.
350  

350  

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

358  

359  
    /** Set a socket option on the acceptor.
359  
    /** Set a socket option on the acceptor.
360  

360  

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

365  

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

373  

374  
        @param opt The option to set.
374  
        @param opt The option to set.
375  

375  

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

389  

390  
    /** Get a socket option from the acceptor.
390  
    /** Get a socket option from the acceptor.
391  

391  

392  
        Retrieves the current value of a type-safe socket option.
392  
        Retrieves the current value of a type-safe socket option.
393  

393  

394  
        @par Example
394  
        @par Example
395  
        @code
395  
        @code
396  
        auto opt = acc.get_option<socket_option::reuse_address>();
396  
        auto opt = acc.get_option<socket_option::reuse_address>();
397  
        @endcode
397  
        @endcode
398  

398  

399  
        @return The current option value.
399  
        @return The current option value.
400  

400  

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

418  

419  
    struct implementation : io_object::implementation
419  
    struct implementation : io_object::implementation
420  
    {
420  
    {
421  
        virtual std::coroutine_handle<> accept(
421  
        virtual std::coroutine_handle<> accept(
422  
            std::coroutine_handle<>,
422  
            std::coroutine_handle<>,
423  
            capy::executor_ref,
423  
            capy::executor_ref,
424  
            std::stop_token,
424  
            std::stop_token,
425  
            std::error_code*,
425  
            std::error_code*,
426  
            io_object::implementation**) = 0;
426  
            io_object::implementation**) = 0;
427  

427  

428  
        /// Returns the cached local endpoint.
428  
        /// Returns the cached local endpoint.
429  
        virtual endpoint local_endpoint() const noexcept = 0;
429  
        virtual endpoint local_endpoint() const noexcept = 0;
430  

430  

431  
        /// Return true if the acceptor has a kernel resource open.
431  
        /// Return true if the acceptor has a kernel resource open.
432  
        virtual bool is_open() const noexcept = 0;
432  
        virtual bool is_open() const noexcept = 0;
433  

433  

434  
        /** Cancel any pending asynchronous operations.
434  
        /** Cancel any pending asynchronous operations.
435  

435  

436  
            All outstanding operations complete with operation_canceled error.
436  
            All outstanding operations complete with operation_canceled error.
437  
        */
437  
        */
438  
        virtual void cancel() noexcept = 0;
438  
        virtual void cancel() noexcept = 0;
439  

439  

440  
        /** Set a socket option.
440  
        /** Set a socket option.
441  

441  

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

453  

454  
        /** Get a socket option.
454  
        /** Get a socket option.
455  

455  

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

467  

468  
protected:
468  
protected:
469  
    explicit tcp_acceptor(handle h) noexcept : io_object(std::move(h)) {}
469  
    explicit tcp_acceptor(handle h) noexcept : io_object(std::move(h)) {}
470  

470  

471  
    /// Transfer accepted peer impl to the peer socket.
471  
    /// Transfer accepted peer impl to the peer socket.
472  
    static void
472  
    static void
473  
    reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
473  
    reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
474  
    {
474  
    {
475  
        if (impl)
475  
        if (impl)
476  
            peer.h_.reset(impl);
476  
            peer.h_.reset(impl);
477  
    }
477  
    }
478  

478  

479  
private:
479  
private:
480  
    inline implementation& get() const noexcept
480  
    inline implementation& get() const noexcept
481  
    {
481  
    {
482  
        return *static_cast<implementation*>(h_.get());
482  
        return *static_cast<implementation*>(h_.get());
483  
    }
483  
    }
484  
};
484  
};
485  

485  

486  
} // namespace boost::corosio
486  
} // namespace boost::corosio
487  

487  

488  
#endif
488  
#endif