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_SOCKET_HPP
11  
#ifndef BOOST_COROSIO_TCP_SOCKET_HPP
12  
#define BOOST_COROSIO_TCP_SOCKET_HPP
12  
#define BOOST_COROSIO_TCP_SOCKET_HPP
13  

13  

14  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/config.hpp>
15  
#include <boost/corosio/detail/platform.hpp>
15  
#include <boost/corosio/detail/platform.hpp>
16  
#include <boost/corosio/detail/except.hpp>
16  
#include <boost/corosio/detail/except.hpp>
17  
#include <boost/corosio/detail/native_handle.hpp>
17  
#include <boost/corosio/detail/native_handle.hpp>
18  
#include <boost/corosio/detail/op_base.hpp>
18  
#include <boost/corosio/detail/op_base.hpp>
19  
#include <boost/corosio/io/io_stream.hpp>
19  
#include <boost/corosio/io/io_stream.hpp>
20  
#include <boost/capy/io_result.hpp>
20  
#include <boost/capy/io_result.hpp>
21  
#include <boost/corosio/detail/buffer_param.hpp>
21  
#include <boost/corosio/detail/buffer_param.hpp>
22  
#include <boost/corosio/endpoint.hpp>
22  
#include <boost/corosio/endpoint.hpp>
23  
#include <boost/corosio/shutdown_type.hpp>
23  
#include <boost/corosio/shutdown_type.hpp>
24  
#include <boost/corosio/tcp.hpp>
24  
#include <boost/corosio/tcp.hpp>
25  
#include <boost/capy/ex/executor_ref.hpp>
25  
#include <boost/capy/ex/executor_ref.hpp>
26  
#include <boost/capy/ex/execution_context.hpp>
26  
#include <boost/capy/ex/execution_context.hpp>
27  
#include <boost/capy/ex/io_env.hpp>
27  
#include <boost/capy/ex/io_env.hpp>
28  
#include <boost/capy/concept/executor.hpp>
28  
#include <boost/capy/concept/executor.hpp>
29  

29  

30  
#include <system_error>
30  
#include <system_error>
31  

31  

32  
#include <concepts>
32  
#include <concepts>
33  
#include <coroutine>
33  
#include <coroutine>
34  
#include <cstddef>
34  
#include <cstddef>
35  
#include <stop_token>
35  
#include <stop_token>
36  
#include <type_traits>
36  
#include <type_traits>
37  

37  

38  
namespace boost::corosio {
38  
namespace boost::corosio {
39  

39  

40  
/** An asynchronous TCP socket for coroutine I/O.
40  
/** An asynchronous TCP socket for coroutine I/O.
41  

41  

42  
    This class provides asynchronous TCP socket operations that return
42  
    This class provides asynchronous TCP socket operations that return
43  
    awaitable types. Each operation participates in the affine awaitable
43  
    awaitable types. Each operation participates in the affine awaitable
44  
    protocol, ensuring coroutines resume on the correct executor.
44  
    protocol, ensuring coroutines resume on the correct executor.
45  

45  

46  
    The socket must be opened before performing I/O operations. Operations
46  
    The socket must be opened before performing I/O operations. Operations
47  
    support cancellation through `std::stop_token` via the affine protocol,
47  
    support cancellation through `std::stop_token` via the affine protocol,
48  
    or explicitly through the `cancel()` member function.
48  
    or explicitly through the `cancel()` member function.
49  

49  

50  
    @par Thread Safety
50  
    @par Thread Safety
51  
    Distinct objects: Safe.@n
51  
    Distinct objects: Safe.@n
52  
    Shared objects: Unsafe. A socket must not have concurrent operations
52  
    Shared objects: Unsafe. A socket must not have concurrent operations
53  
    of the same type (e.g., two simultaneous reads). One read and one
53  
    of the same type (e.g., two simultaneous reads). One read and one
54  
    write may be in flight simultaneously.
54  
    write may be in flight simultaneously.
55  

55  

56  
    @par Semantics
56  
    @par Semantics
57  
    Wraps the platform TCP/IP stack. Operations dispatch to
57  
    Wraps the platform TCP/IP stack. Operations dispatch to
58  
    OS socket APIs via the io_context reactor (epoll, IOCP,
58  
    OS socket APIs via the io_context reactor (epoll, IOCP,
59  
    kqueue). Satisfies @ref capy::Stream.
59  
    kqueue). Satisfies @ref capy::Stream.
60  

60  

61  
    @par Example
61  
    @par Example
62  
    @code
62  
    @code
63  
    io_context ioc;
63  
    io_context ioc;
64  
    tcp_socket s(ioc);
64  
    tcp_socket s(ioc);
65  
    s.open();
65  
    s.open();
66  

66  

67  
    // Using structured bindings
67  
    // Using structured bindings
68  
    auto [ec] = co_await s.connect(
68  
    auto [ec] = co_await s.connect(
69  
        endpoint(ipv4_address::loopback(), 8080));
69  
        endpoint(ipv4_address::loopback(), 8080));
70  
    if (ec)
70  
    if (ec)
71  
        co_return;
71  
        co_return;
72  

72  

73  
    char buf[1024];
73  
    char buf[1024];
74  
    auto [read_ec, n] = co_await s.read_some(
74  
    auto [read_ec, n] = co_await s.read_some(
75  
        capy::mutable_buffer(buf, sizeof(buf)));
75  
        capy::mutable_buffer(buf, sizeof(buf)));
76  
    @endcode
76  
    @endcode
77  
*/
77  
*/
78  
class BOOST_COROSIO_DECL tcp_socket : public io_stream
78  
class BOOST_COROSIO_DECL tcp_socket : public io_stream
79  
{
79  
{
80  
public:
80  
public:
 
81 +
    /// The endpoint type used by this socket.
 
82 +
    using endpoint_type = corosio::endpoint;
 
83 +

81  
    using shutdown_type = corosio::shutdown_type;
84  
    using shutdown_type = corosio::shutdown_type;
82  
    using enum corosio::shutdown_type;
85  
    using enum corosio::shutdown_type;
83  

86  

84  
    /** Define backend hooks for TCP socket operations.
87  
    /** Define backend hooks for TCP socket operations.
85  

88  

86  
        Platform backends (epoll, IOCP, kqueue, select) derive from
89  
        Platform backends (epoll, IOCP, kqueue, select) derive from
87  
        this to implement socket I/O, connection, and option management.
90  
        this to implement socket I/O, connection, and option management.
88  
    */
91  
    */
89  
    struct implementation : io_stream::implementation
92  
    struct implementation : io_stream::implementation
90  
    {
93  
    {
91  
        /** Initiate an asynchronous connect to the given endpoint.
94  
        /** Initiate an asynchronous connect to the given endpoint.
92  

95  

93  
            @param h Coroutine handle to resume on completion.
96  
            @param h Coroutine handle to resume on completion.
94  
            @param ex Executor for dispatching the completion.
97  
            @param ex Executor for dispatching the completion.
95  
            @param ep The remote endpoint to connect to.
98  
            @param ep The remote endpoint to connect to.
96  
            @param token Stop token for cancellation.
99  
            @param token Stop token for cancellation.
97  
            @param ec Output error code.
100  
            @param ec Output error code.
98  

101  

99  
            @return Coroutine handle to resume immediately.
102  
            @return Coroutine handle to resume immediately.
100  
        */
103  
        */
101  
        virtual std::coroutine_handle<> connect(
104  
        virtual std::coroutine_handle<> connect(
102  
            std::coroutine_handle<> h,
105  
            std::coroutine_handle<> h,
103  
            capy::executor_ref ex,
106  
            capy::executor_ref ex,
104  
            endpoint ep,
107  
            endpoint ep,
105  
            std::stop_token token,
108  
            std::stop_token token,
106  
            std::error_code* ec) = 0;
109  
            std::error_code* ec) = 0;
107  

110  

108  
        /** Shut down the socket for the given direction(s).
111  
        /** Shut down the socket for the given direction(s).
109  

112  

110  
            @param what The shutdown direction.
113  
            @param what The shutdown direction.
111  

114  

112  
            @return Error code on failure, empty on success.
115  
            @return Error code on failure, empty on success.
113  
        */
116  
        */
114  
        virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
117  
        virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
115  

118  

116  
        /// Return the platform socket descriptor.
119  
        /// Return the platform socket descriptor.
117  
        virtual native_handle_type native_handle() const noexcept = 0;
120  
        virtual native_handle_type native_handle() const noexcept = 0;
118  

121  

119  
        /** Request cancellation of pending asynchronous operations.
122  
        /** Request cancellation of pending asynchronous operations.
120  

123  

121  
            All outstanding operations complete with operation_canceled error.
124  
            All outstanding operations complete with operation_canceled error.
122  
            Check `ec == cond::canceled` for portable comparison.
125  
            Check `ec == cond::canceled` for portable comparison.
123  
        */
126  
        */
124  
        virtual void cancel() noexcept = 0;
127  
        virtual void cancel() noexcept = 0;
125  

128  

126  
        /** Set a socket option.
129  
        /** Set a socket option.
127  

130  

128  
            @param level The protocol level (e.g. `SOL_SOCKET`).
131  
            @param level The protocol level (e.g. `SOL_SOCKET`).
129  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
132  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
130  
            @param data Pointer to the option value.
133  
            @param data Pointer to the option value.
131  
            @param size Size of the option value in bytes.
134  
            @param size Size of the option value in bytes.
132  
            @return Error code on failure, empty on success.
135  
            @return Error code on failure, empty on success.
133  
        */
136  
        */
134  
        virtual std::error_code set_option(
137  
        virtual std::error_code set_option(
135  
            int level,
138  
            int level,
136  
            int optname,
139  
            int optname,
137  
            void const* data,
140  
            void const* data,
138  
            std::size_t size) noexcept = 0;
141  
            std::size_t size) noexcept = 0;
139  

142  

140  
        /** Get a socket option.
143  
        /** Get a socket option.
141  

144  

142  
            @param level The protocol level (e.g. `SOL_SOCKET`).
145  
            @param level The protocol level (e.g. `SOL_SOCKET`).
143  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
146  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
144  
            @param data Pointer to receive the option value.
147  
            @param data Pointer to receive the option value.
145  
            @param size On entry, the size of the buffer. On exit,
148  
            @param size On entry, the size of the buffer. On exit,
146  
                the size of the option value.
149  
                the size of the option value.
147  
            @return Error code on failure, empty on success.
150  
            @return Error code on failure, empty on success.
148  
        */
151  
        */
149  
        virtual std::error_code
152  
        virtual std::error_code
150  
        get_option(int level, int optname, void* data, std::size_t* size)
153  
        get_option(int level, int optname, void* data, std::size_t* size)
151  
            const noexcept = 0;
154  
            const noexcept = 0;
152  

155  

153  
        /// Return the cached local endpoint.
156  
        /// Return the cached local endpoint.
154  
        virtual endpoint local_endpoint() const noexcept = 0;
157  
        virtual endpoint local_endpoint() const noexcept = 0;
155  

158  

156  
        /// Return the cached remote endpoint.
159  
        /// Return the cached remote endpoint.
157  
        virtual endpoint remote_endpoint() const noexcept = 0;
160  
        virtual endpoint remote_endpoint() const noexcept = 0;
158  
    };
161  
    };
159  

162  

160  
    /// Represent the awaitable returned by @ref connect.
163  
    /// Represent the awaitable returned by @ref connect.
161  
    struct connect_awaitable
164  
    struct connect_awaitable
162  
        : detail::void_op_base<connect_awaitable>
165  
        : detail::void_op_base<connect_awaitable>
163  
    {
166  
    {
164  
        tcp_socket& s_;
167  
        tcp_socket& s_;
165  
        endpoint endpoint_;
168  
        endpoint endpoint_;
166  

169  

167  
        connect_awaitable(tcp_socket& s, endpoint ep) noexcept
170  
        connect_awaitable(tcp_socket& s, endpoint ep) noexcept
168  
            : s_(s), endpoint_(ep) {}
171  
            : s_(s), endpoint_(ep) {}
169  

172  

170  
        std::coroutine_handle<> dispatch(
173  
        std::coroutine_handle<> dispatch(
171  
            std::coroutine_handle<> h, capy::executor_ref ex) const
174  
            std::coroutine_handle<> h, capy::executor_ref ex) const
172  
        {
175  
        {
173  
            return s_.get().connect(h, ex, endpoint_, token_, &ec_);
176  
            return s_.get().connect(h, ex, endpoint_, token_, &ec_);
174  
        }
177  
        }
175  
    };
178  
    };
176  

179  

177  
public:
180  
public:
178  
    /** Destructor.
181  
    /** Destructor.
179  

182  

180  
        Closes the socket if open, cancelling any pending operations.
183  
        Closes the socket if open, cancelling any pending operations.
181  
    */
184  
    */
182  
    ~tcp_socket() override;
185  
    ~tcp_socket() override;
183  

186  

184  
    /** Construct a socket from an execution context.
187  
    /** Construct a socket from an execution context.
185  

188  

186  
        @param ctx The execution context that will own this socket.
189  
        @param ctx The execution context that will own this socket.
187  
    */
190  
    */
188  
    explicit tcp_socket(capy::execution_context& ctx);
191  
    explicit tcp_socket(capy::execution_context& ctx);
189  

192  

190  
    /** Construct a socket from an executor.
193  
    /** Construct a socket from an executor.
191  

194  

192  
        The socket is associated with the executor's context.
195  
        The socket is associated with the executor's context.
193  

196  

194  
        @param ex The executor whose context will own the socket.
197  
        @param ex The executor whose context will own the socket.
195  
    */
198  
    */
196  
    template<class Ex>
199  
    template<class Ex>
197  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
200  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
198  
        capy::Executor<Ex>
201  
        capy::Executor<Ex>
199  
    explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context())
202  
    explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context())
200  
    {
203  
    {
201  
    }
204  
    }
202  

205  

203  
    /** Move constructor.
206  
    /** Move constructor.
204  

207  

205  
        Transfers ownership of the socket resources.
208  
        Transfers ownership of the socket resources.
206  

209  

207  
        @param other The socket to move from.
210  
        @param other The socket to move from.
208  

211  

209  
        @pre No awaitables returned by @p other's methods exist.
212  
        @pre No awaitables returned by @p other's methods exist.
210  
        @pre @p other is not referenced as a peer in any outstanding
213  
        @pre @p other is not referenced as a peer in any outstanding
211  
            accept awaitable.
214  
            accept awaitable.
212  
        @pre The execution context associated with @p other must
215  
        @pre The execution context associated with @p other must
213  
            outlive this socket.
216  
            outlive this socket.
214  
    */
217  
    */
215  
    tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {}
218  
    tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {}
216  

219  

217  
    /** Move assignment operator.
220  
    /** Move assignment operator.
218  

221  

219  
        Closes any existing socket and transfers ownership.
222  
        Closes any existing socket and transfers ownership.
220  

223  

221  
        @param other The socket to move from.
224  
        @param other The socket to move from.
222  

225  

223  
        @pre No awaitables returned by either `*this` or @p other's
226  
        @pre No awaitables returned by either `*this` or @p other's
224  
            methods exist.
227  
            methods exist.
225  
        @pre Neither `*this` nor @p other is referenced as a peer in
228  
        @pre Neither `*this` nor @p other is referenced as a peer in
226  
            any outstanding accept awaitable.
229  
            any outstanding accept awaitable.
227  
        @pre The execution context associated with @p other must
230  
        @pre The execution context associated with @p other must
228  
            outlive this socket.
231  
            outlive this socket.
229  

232  

230  
        @return Reference to this socket.
233  
        @return Reference to this socket.
231  
    */
234  
    */
232  
    tcp_socket& operator=(tcp_socket&& other) noexcept
235  
    tcp_socket& operator=(tcp_socket&& other) noexcept
233  
    {
236  
    {
234  
        if (this != &other)
237  
        if (this != &other)
235  
        {
238  
        {
236  
            close();
239  
            close();
237  
            h_ = std::move(other.h_);
240  
            h_ = std::move(other.h_);
238  
        }
241  
        }
239  
        return *this;
242  
        return *this;
240  
    }
243  
    }
241  

244  

242  
    tcp_socket(tcp_socket const&)            = delete;
245  
    tcp_socket(tcp_socket const&)            = delete;
243  
    tcp_socket& operator=(tcp_socket const&) = delete;
246  
    tcp_socket& operator=(tcp_socket const&) = delete;
244  

247  

245  
    /** Open the socket.
248  
    /** Open the socket.
246  

249  

247  
        Creates a TCP socket and associates it with the platform
250  
        Creates a TCP socket and associates it with the platform
248  
        reactor (IOCP on Windows). Calling @ref connect on a closed
251  
        reactor (IOCP on Windows). Calling @ref connect on a closed
249  
        socket opens it automatically with the endpoint's address family,
252  
        socket opens it automatically with the endpoint's address family,
250  
        so explicit `open()` is only needed when socket options must be
253  
        so explicit `open()` is only needed when socket options must be
251  
        set before connecting.
254  
        set before connecting.
252  

255  

253  
        @param proto The protocol (IPv4 or IPv6). Defaults to
256  
        @param proto The protocol (IPv4 or IPv6). Defaults to
254  
            `tcp::v4()`.
257  
            `tcp::v4()`.
255  

258  

256  
        @throws std::system_error on failure.
259  
        @throws std::system_error on failure.
257  
    */
260  
    */
258  
    void open(tcp proto = tcp::v4());
261  
    void open(tcp proto = tcp::v4());
259  

262  

260  
    /** Bind the socket to a local endpoint.
263  
    /** Bind the socket to a local endpoint.
261  

264  

262  
        Associates the socket with a local address and port before
265  
        Associates the socket with a local address and port before
263  
        connecting. Useful for multi-homed hosts or source-port
266  
        connecting. Useful for multi-homed hosts or source-port
264  
        pinning.
267  
        pinning.
265  

268  

266  
        @param ep The local endpoint to bind to.
269  
        @param ep The local endpoint to bind to.
267  

270  

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

273  

271  
        @par Error Conditions
274  
        @par Error Conditions
272  
        @li `errc::address_in_use`: The endpoint is already in use.
275  
        @li `errc::address_in_use`: The endpoint is already in use.
273  
        @li `errc::address_not_available`: The address is not
276  
        @li `errc::address_not_available`: The address is not
274  
            available on any local interface.
277  
            available on any local interface.
275  
        @li `errc::permission_denied`: Insufficient privileges to
278  
        @li `errc::permission_denied`: Insufficient privileges to
276  
            bind to the endpoint (e.g., privileged port).
279  
            bind to the endpoint (e.g., privileged port).
277  

280  

278  
        @throws std::logic_error if the socket is not open.
281  
        @throws std::logic_error if the socket is not open.
279  
    */
282  
    */
280  
    [[nodiscard]] std::error_code bind(endpoint ep);
283  
    [[nodiscard]] std::error_code bind(endpoint ep);
281  

284  

282  
    /** Close the socket.
285  
    /** Close the socket.
283  

286  

284  
        Releases socket resources. Any pending operations complete
287  
        Releases socket resources. Any pending operations complete
285  
        with `errc::operation_canceled`.
288  
        with `errc::operation_canceled`.
286  
    */
289  
    */
287  
    void close();
290  
    void close();
288  

291  

289  
    /** Check if the socket is open.
292  
    /** Check if the socket is open.
290  

293  

291  
        @return `true` if the socket is open and ready for operations.
294  
        @return `true` if the socket is open and ready for operations.
292  
    */
295  
    */
293  
    bool is_open() const noexcept
296  
    bool is_open() const noexcept
294  
    {
297  
    {
295  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
298  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
296  
        return h_ && get().native_handle() != ~native_handle_type(0);
299  
        return h_ && get().native_handle() != ~native_handle_type(0);
297  
#else
300  
#else
298  
        return h_ && get().native_handle() >= 0;
301  
        return h_ && get().native_handle() >= 0;
299  
#endif
302  
#endif
300  
    }
303  
    }
301  

304  

302  
    /** Initiate an asynchronous connect operation.
305  
    /** Initiate an asynchronous connect operation.
303  

306  

304  
        If the socket is not already open, it is opened automatically
307  
        If the socket is not already open, it is opened automatically
305  
        using the address family of @p ep (IPv4 or IPv6). If the socket
308  
        using the address family of @p ep (IPv4 or IPv6). If the socket
306  
        is already open, the existing file descriptor is used as-is.
309  
        is already open, the existing file descriptor is used as-is.
307  

310  

308  
        The operation supports cancellation via `std::stop_token` through
311  
        The operation supports cancellation via `std::stop_token` through
309  
        the affine awaitable protocol. If the associated stop token is
312  
        the affine awaitable protocol. If the associated stop token is
310  
        triggered, the operation completes immediately with
313  
        triggered, the operation completes immediately with
311  
        `errc::operation_canceled`.
314  
        `errc::operation_canceled`.
312  

315  

313  
        @param ep The remote endpoint to connect to.
316  
        @param ep The remote endpoint to connect to.
314  

317  

315  
        @return An awaitable that completes with `io_result<>`.
318  
        @return An awaitable that completes with `io_result<>`.
316  
            Returns success (default error_code) on successful connection,
319  
            Returns success (default error_code) on successful connection,
317  
            or an error code on failure including:
320  
            or an error code on failure including:
318  
            - connection_refused: No server listening at endpoint
321  
            - connection_refused: No server listening at endpoint
319  
            - timed_out: Connection attempt timed out
322  
            - timed_out: Connection attempt timed out
320  
            - network_unreachable: No route to host
323  
            - network_unreachable: No route to host
321  
            - operation_canceled: Cancelled via stop_token or cancel().
324  
            - operation_canceled: Cancelled via stop_token or cancel().
322  
                Check `ec == cond::canceled` for portable comparison.
325  
                Check `ec == cond::canceled` for portable comparison.
323  

326  

324  
        @throws std::system_error if the socket needs to be opened
327  
        @throws std::system_error if the socket needs to be opened
325  
            and the open fails.
328  
            and the open fails.
326  

329  

327  
        @par Preconditions
330  
        @par Preconditions
328  
        This socket must outlive the returned awaitable.
331  
        This socket must outlive the returned awaitable.
329  

332  

330  
        @par Example
333  
        @par Example
331  
        @code
334  
        @code
332  
        // Socket opened automatically with correct address family:
335  
        // Socket opened automatically with correct address family:
333  
        auto [ec] = co_await s.connect(endpoint);
336  
        auto [ec] = co_await s.connect(endpoint);
334  
        if (ec) { ... }
337  
        if (ec) { ... }
335  
        @endcode
338  
        @endcode
336  
    */
339  
    */
337  
    auto connect(endpoint ep)
340  
    auto connect(endpoint ep)
338  
    {
341  
    {
339  
        if (!is_open())
342  
        if (!is_open())
340  
            open(ep.is_v6() ? tcp::v6() : tcp::v4());
343  
            open(ep.is_v6() ? tcp::v6() : tcp::v4());
341  
        return connect_awaitable(*this, ep);
344  
        return connect_awaitable(*this, ep);
342  
    }
345  
    }
343  

346  

344  
    /** Cancel any pending asynchronous operations.
347  
    /** Cancel any pending asynchronous operations.
345  

348  

346  
        All outstanding operations complete with `errc::operation_canceled`.
349  
        All outstanding operations complete with `errc::operation_canceled`.
347  
        Check `ec == cond::canceled` for portable comparison.
350  
        Check `ec == cond::canceled` for portable comparison.
348  
    */
351  
    */
349  
    void cancel();
352  
    void cancel();
350  

353  

351  
    /** Get the native socket handle.
354  
    /** Get the native socket handle.
352  

355  

353  
        Returns the underlying platform-specific socket descriptor.
356  
        Returns the underlying platform-specific socket descriptor.
354  
        On POSIX systems this is an `int` file descriptor.
357  
        On POSIX systems this is an `int` file descriptor.
355  
        On Windows this is a `SOCKET` handle.
358  
        On Windows this is a `SOCKET` handle.
356  

359  

357  
        @return The native socket handle, or -1/INVALID_SOCKET if not open.
360  
        @return The native socket handle, or -1/INVALID_SOCKET if not open.
358  

361  

359  
        @par Preconditions
362  
        @par Preconditions
360  
        None. May be called on closed sockets.
363  
        None. May be called on closed sockets.
361  
    */
364  
    */
362  
    native_handle_type native_handle() const noexcept;
365  
    native_handle_type native_handle() const noexcept;
363  

366  

364  
    /** Disable sends or receives on the socket.
367  
    /** Disable sends or receives on the socket.
365  

368  

366  
        TCP connections are full-duplex: each direction (send and receive)
369  
        TCP connections are full-duplex: each direction (send and receive)
367  
        operates independently. This function allows you to close one or
370  
        operates independently. This function allows you to close one or
368  
        both directions without destroying the socket.
371  
        both directions without destroying the socket.
369  

372  

370  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
373  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
371  
            signaling that you have no more data to send. You can still
374  
            signaling that you have no more data to send. You can still
372  
            receive data until the peer also closes their send direction.
375  
            receive data until the peer also closes their send direction.
373  
            This is the most common use case, typically called before
376  
            This is the most common use case, typically called before
374  
            close() to ensure graceful connection termination.
377  
            close() to ensure graceful connection termination.
375  

378  

376  
        @li @ref shutdown_receive disables reading on the socket. This
379  
        @li @ref shutdown_receive disables reading on the socket. This
377  
            does NOT send anything to the peer - they are not informed
380  
            does NOT send anything to the peer - they are not informed
378  
            and may continue sending data. Subsequent reads will fail
381  
            and may continue sending data. Subsequent reads will fail
379  
            or return end-of-file. Incoming data may be discarded or
382  
            or return end-of-file. Incoming data may be discarded or
380  
            buffered depending on the operating system.
383  
            buffered depending on the operating system.
381  

384  

382  
        @li @ref shutdown_both combines both effects: sends a FIN and
385  
        @li @ref shutdown_both combines both effects: sends a FIN and
383  
            disables reading.
386  
            disables reading.
384  

387  

385  
        When the peer shuts down their send direction (sends a FIN),
388  
        When the peer shuts down their send direction (sends a FIN),
386  
        subsequent read operations will complete with `capy::cond::eof`.
389  
        subsequent read operations will complete with `capy::cond::eof`.
387  
        Use the portable condition test rather than comparing error
390  
        Use the portable condition test rather than comparing error
388  
        codes directly:
391  
        codes directly:
389  

392  

390  
        @code
393  
        @code
391  
        auto [ec, n] = co_await sock.read_some(buffer);
394  
        auto [ec, n] = co_await sock.read_some(buffer);
392  
        if (ec == capy::cond::eof)
395  
        if (ec == capy::cond::eof)
393  
        {
396  
        {
394  
            // Peer closed their send direction
397  
            // Peer closed their send direction
395  
        }
398  
        }
396  
        @endcode
399  
        @endcode
397  

400  

398  
        Any error from the underlying system call is silently discarded
401  
        Any error from the underlying system call is silently discarded
399  
        because it is unlikely to be helpful.
402  
        because it is unlikely to be helpful.
400  

403  

401  
        @param what Determines what operations will no longer be allowed.
404  
        @param what Determines what operations will no longer be allowed.
402  
    */
405  
    */
403  
    void shutdown(shutdown_type what);
406  
    void shutdown(shutdown_type what);
404  

407  

405  
    /** Set a socket option.
408  
    /** Set a socket option.
406  

409  

407  
        Applies a type-safe socket option to the underlying socket.
410  
        Applies a type-safe socket option to the underlying socket.
408  
        The option type encodes the protocol level and option name.
411  
        The option type encodes the protocol level and option name.
409  

412  

410  
        @par Example
413  
        @par Example
411  
        @code
414  
        @code
412  
        sock.set_option( socket_option::no_delay( true ) );
415  
        sock.set_option( socket_option::no_delay( true ) );
413  
        sock.set_option( socket_option::receive_buffer_size( 65536 ) );
416  
        sock.set_option( socket_option::receive_buffer_size( 65536 ) );
414  
        @endcode
417  
        @endcode
415  

418  

416  
        @param opt The option to set.
419  
        @param opt The option to set.
417  

420  

418  
        @throws std::logic_error if the socket is not open.
421  
        @throws std::logic_error if the socket is not open.
419  
        @throws std::system_error on failure.
422  
        @throws std::system_error on failure.
420  
    */
423  
    */
421  
    template<class Option>
424  
    template<class Option>
422  
    void set_option(Option const& opt)
425  
    void set_option(Option const& opt)
423  
    {
426  
    {
424  
        if (!is_open())
427  
        if (!is_open())
425  
            detail::throw_logic_error("set_option: socket not open");
428  
            detail::throw_logic_error("set_option: socket not open");
426  
        std::error_code ec = get().set_option(
429  
        std::error_code ec = get().set_option(
427  
            Option::level(), Option::name(), opt.data(), opt.size());
430  
            Option::level(), Option::name(), opt.data(), opt.size());
428  
        if (ec)
431  
        if (ec)
429  
            detail::throw_system_error(ec, "tcp_socket::set_option");
432  
            detail::throw_system_error(ec, "tcp_socket::set_option");
430  
    }
433  
    }
431  

434  

432  
    /** Get a socket option.
435  
    /** Get a socket option.
433  

436  

434  
        Retrieves the current value of a type-safe socket option.
437  
        Retrieves the current value of a type-safe socket option.
435  

438  

436  
        @par Example
439  
        @par Example
437  
        @code
440  
        @code
438  
        auto nd = sock.get_option<socket_option::no_delay>();
441  
        auto nd = sock.get_option<socket_option::no_delay>();
439  
        if ( nd.value() )
442  
        if ( nd.value() )
440  
            // Nagle's algorithm is disabled
443  
            // Nagle's algorithm is disabled
441  
        @endcode
444  
        @endcode
442  

445  

443  
        @return The current option value.
446  
        @return The current option value.
444  

447  

445  
        @throws std::logic_error if the socket is not open.
448  
        @throws std::logic_error if the socket is not open.
446  
        @throws std::system_error on failure.
449  
        @throws std::system_error on failure.
447  
    */
450  
    */
448  
    template<class Option>
451  
    template<class Option>
449  
    Option get_option() const
452  
    Option get_option() const
450  
    {
453  
    {
451  
        if (!is_open())
454  
        if (!is_open())
452  
            detail::throw_logic_error("get_option: socket not open");
455  
            detail::throw_logic_error("get_option: socket not open");
453  
        Option opt{};
456  
        Option opt{};
454  
        std::size_t sz = opt.size();
457  
        std::size_t sz = opt.size();
455  
        std::error_code ec =
458  
        std::error_code ec =
456  
            get().get_option(Option::level(), Option::name(), opt.data(), &sz);
459  
            get().get_option(Option::level(), Option::name(), opt.data(), &sz);
457  
        if (ec)
460  
        if (ec)
458  
            detail::throw_system_error(ec, "tcp_socket::get_option");
461  
            detail::throw_system_error(ec, "tcp_socket::get_option");
459  
        opt.resize(sz);
462  
        opt.resize(sz);
460  
        return opt;
463  
        return opt;
461  
    }
464  
    }
462  

465  

463  
    /** Get the local endpoint of the socket.
466  
    /** Get the local endpoint of the socket.
464  

467  

465  
        Returns the local address and port to which the socket is bound.
468  
        Returns the local address and port to which the socket is bound.
466  
        For a connected socket, this is the local side of the connection.
469  
        For a connected socket, this is the local side of the connection.
467  
        The endpoint is cached when the connection is established.
470  
        The endpoint is cached when the connection is established.
468  

471  

469  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
472  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
470  
            the socket is not connected.
473  
            the socket is not connected.
471  

474  

472  
        @par Thread Safety
475  
        @par Thread Safety
473  
        The cached endpoint value is set during connect/accept completion
476  
        The cached endpoint value is set during connect/accept completion
474  
        and cleared during close(). This function may be called concurrently
477  
        and cleared during close(). This function may be called concurrently
475  
        with I/O operations, but must not be called concurrently with
478  
        with I/O operations, but must not be called concurrently with
476  
        connect(), accept(), or close().
479  
        connect(), accept(), or close().
477  
    */
480  
    */
478  
    endpoint local_endpoint() const noexcept;
481  
    endpoint local_endpoint() const noexcept;
479  

482  

480  
    /** Get the remote endpoint of the socket.
483  
    /** Get the remote endpoint of the socket.
481  

484  

482  
        Returns the remote address and port to which the socket is connected.
485  
        Returns the remote address and port to which the socket is connected.
483  
        The endpoint is cached when the connection is established.
486  
        The endpoint is cached when the connection is established.
484  

487  

485  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
488  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
486  
            the socket is not connected.
489  
            the socket is not connected.
487  

490  

488  
        @par Thread Safety
491  
        @par Thread Safety
489  
        The cached endpoint value is set during connect/accept completion
492  
        The cached endpoint value is set during connect/accept completion
490  
        and cleared during close(). This function may be called concurrently
493  
        and cleared during close(). This function may be called concurrently
491  
        with I/O operations, but must not be called concurrently with
494  
        with I/O operations, but must not be called concurrently with
492  
        connect(), accept(), or close().
495  
        connect(), accept(), or close().
493  
    */
496  
    */
494  
    endpoint remote_endpoint() const noexcept;
497  
    endpoint remote_endpoint() const noexcept;
495  

498  

496  
protected:
499  
protected:
497  
    tcp_socket() noexcept = default;
500  
    tcp_socket() noexcept = default;
498  

501  

499  
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
502  
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
500  

503  

501  
private:
504  
private:
502  
    friend class tcp_acceptor;
505  
    friend class tcp_acceptor;
503  

506  

504  
    /// Open the socket for the given protocol triple.
507  
    /// Open the socket for the given protocol triple.
505  
    void open_for_family(int family, int type, int protocol);
508  
    void open_for_family(int family, int type, int protocol);
506  

509  

507  
    inline implementation& get() const noexcept
510  
    inline implementation& get() const noexcept
508  
    {
511  
    {
509  
        return *static_cast<implementation*>(h_.get());
512  
        return *static_cast<implementation*>(h_.get());
510  
    }
513  
    }
511  
};
514  
};
512  

515  

513  
} // namespace boost::corosio
516  
} // namespace boost::corosio
514  

517  

515  
#endif
518  
#endif