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/io/io_stream.hpp>
17  
#include <boost/corosio/io/io_stream.hpp>
18  
#include <boost/capy/io_result.hpp>
18  
#include <boost/capy/io_result.hpp>
19  
#include <boost/corosio/detail/buffer_param.hpp>
19  
#include <boost/corosio/detail/buffer_param.hpp>
20  
#include <boost/corosio/endpoint.hpp>
20  
#include <boost/corosio/endpoint.hpp>
21  
#include <boost/corosio/tcp.hpp>
21  
#include <boost/corosio/tcp.hpp>
22  
#include <boost/capy/ex/executor_ref.hpp>
22  
#include <boost/capy/ex/executor_ref.hpp>
23  
#include <boost/capy/ex/execution_context.hpp>
23  
#include <boost/capy/ex/execution_context.hpp>
24  
#include <boost/capy/ex/io_env.hpp>
24  
#include <boost/capy/ex/io_env.hpp>
25  
#include <boost/capy/concept/executor.hpp>
25  
#include <boost/capy/concept/executor.hpp>
26  

26  

27  
#include <system_error>
27  
#include <system_error>
28  

28  

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

34  

35  
namespace boost::corosio {
35  
namespace boost::corosio {
36  

36  

37  
/// Represent a platform-specific socket descriptor (`int` on POSIX, `SOCKET` on Windows).
37  
/// Represent a platform-specific socket descriptor (`int` on POSIX, `SOCKET` on Windows).
38  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
38  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
39  
using native_handle_type = std::uintptr_t;
39  
using native_handle_type = std::uintptr_t;
40  
#else
40  
#else
41  
using native_handle_type = int;
41  
using native_handle_type = int;
42  
#endif
42  
#endif
43  

43  

44  
/** An asynchronous TCP socket for coroutine I/O.
44  
/** An asynchronous TCP socket for coroutine I/O.
45  

45  

46  
    This class provides asynchronous TCP socket operations that return
46  
    This class provides asynchronous TCP socket operations that return
47  
    awaitable types. Each operation participates in the affine awaitable
47  
    awaitable types. Each operation participates in the affine awaitable
48  
    protocol, ensuring coroutines resume on the correct executor.
48  
    protocol, ensuring coroutines resume on the correct executor.
49  

49  

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

53  

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

59  

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

64  

65  
    @par Example
65  
    @par Example
66  
    @code
66  
    @code
67  
    io_context ioc;
67  
    io_context ioc;
68  
    tcp_socket s(ioc);
68  
    tcp_socket s(ioc);
69  
    s.open();
69  
    s.open();
70  

70  

71  
    // Using structured bindings
71  
    // Using structured bindings
72  
    auto [ec] = co_await s.connect(
72  
    auto [ec] = co_await s.connect(
73  
        endpoint(ipv4_address::loopback(), 8080));
73  
        endpoint(ipv4_address::loopback(), 8080));
74  
    if (ec)
74  
    if (ec)
75  
        co_return;
75  
        co_return;
76  

76  

77  
    char buf[1024];
77  
    char buf[1024];
78  
    auto [read_ec, n] = co_await s.read_some(
78  
    auto [read_ec, n] = co_await s.read_some(
79  
        capy::mutable_buffer(buf, sizeof(buf)));
79  
        capy::mutable_buffer(buf, sizeof(buf)));
80  
    @endcode
80  
    @endcode
81  
*/
81  
*/
82  
class BOOST_COROSIO_DECL tcp_socket : public io_stream
82  
class BOOST_COROSIO_DECL tcp_socket : public io_stream
83  
{
83  
{
84  
public:
84  
public:
85  
    /** Different ways a socket may be shutdown. */
85  
    /** Different ways a socket may be shutdown. */
86  
    enum shutdown_type
86  
    enum shutdown_type
87  
    {
87  
    {
88  
        shutdown_receive,
88  
        shutdown_receive,
89  
        shutdown_send,
89  
        shutdown_send,
90  
        shutdown_both
90  
        shutdown_both
91  
    };
91  
    };
92  

92  

93  
    /** Define backend hooks for TCP socket operations.
93  
    /** Define backend hooks for TCP socket operations.
94  

94  

95  
        Platform backends (epoll, IOCP, kqueue, select) derive from
95  
        Platform backends (epoll, IOCP, kqueue, select) derive from
96  
        this to implement socket I/O, connection, and option management.
96  
        this to implement socket I/O, connection, and option management.
97  
    */
97  
    */
98  
    struct implementation : io_stream::implementation
98  
    struct implementation : io_stream::implementation
99  
    {
99  
    {
100  
        /** Initiate an asynchronous connect to the given endpoint.
100  
        /** Initiate an asynchronous connect to the given endpoint.
101  

101  

102  
            @param h Coroutine handle to resume on completion.
102  
            @param h Coroutine handle to resume on completion.
103  
            @param ex Executor for dispatching the completion.
103  
            @param ex Executor for dispatching the completion.
104  
            @param ep The remote endpoint to connect to.
104  
            @param ep The remote endpoint to connect to.
105  
            @param token Stop token for cancellation.
105  
            @param token Stop token for cancellation.
106  
            @param ec Output error code.
106  
            @param ec Output error code.
107  

107  

108  
            @return Coroutine handle to resume immediately.
108  
            @return Coroutine handle to resume immediately.
109  
        */
109  
        */
110  
        virtual std::coroutine_handle<> connect(
110  
        virtual std::coroutine_handle<> connect(
111  
            std::coroutine_handle<> h,
111  
            std::coroutine_handle<> h,
112  
            capy::executor_ref ex,
112  
            capy::executor_ref ex,
113  
            endpoint ep,
113  
            endpoint ep,
114  
            std::stop_token token,
114  
            std::stop_token token,
115  
            std::error_code* ec) = 0;
115  
            std::error_code* ec) = 0;
116  

116  

117  
        /** Shut down the socket for the given direction(s).
117  
        /** Shut down the socket for the given direction(s).
118  

118  

119  
            @param what The shutdown direction.
119  
            @param what The shutdown direction.
120  

120  

121  
            @return Error code on failure, empty on success.
121  
            @return Error code on failure, empty on success.
122  
        */
122  
        */
123  
        virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
123  
        virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
124  

124  

125  
        /// Return the platform socket descriptor.
125  
        /// Return the platform socket descriptor.
126  
        virtual native_handle_type native_handle() const noexcept = 0;
126  
        virtual native_handle_type native_handle() const noexcept = 0;
127  

127  

128  
        /** Request cancellation of pending asynchronous operations.
128  
        /** Request cancellation of pending asynchronous operations.
129  

129  

130  
            All outstanding operations complete with operation_canceled error.
130  
            All outstanding operations complete with operation_canceled error.
131  
            Check `ec == cond::canceled` for portable comparison.
131  
            Check `ec == cond::canceled` for portable comparison.
132  
        */
132  
        */
133  
        virtual void cancel() noexcept = 0;
133  
        virtual void cancel() noexcept = 0;
134  

134  

135  
        /** Set a socket option.
135  
        /** Set a socket option.
136  

136  

137  
            @param level The protocol level (e.g. `SOL_SOCKET`).
137  
            @param level The protocol level (e.g. `SOL_SOCKET`).
138  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
138  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
139  
            @param data Pointer to the option value.
139  
            @param data Pointer to the option value.
140  
            @param size Size of the option value in bytes.
140  
            @param size Size of the option value in bytes.
141  
            @return Error code on failure, empty on success.
141  
            @return Error code on failure, empty on success.
142  
        */
142  
        */
143  
        virtual std::error_code set_option(
143  
        virtual std::error_code set_option(
144  
            int level,
144  
            int level,
145  
            int optname,
145  
            int optname,
146  
            void const* data,
146  
            void const* data,
147  
            std::size_t size) noexcept = 0;
147  
            std::size_t size) noexcept = 0;
148  

148  

149  
        /** Get a socket option.
149  
        /** Get a socket option.
150  

150  

151  
            @param level The protocol level (e.g. `SOL_SOCKET`).
151  
            @param level The protocol level (e.g. `SOL_SOCKET`).
152  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
152  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
153  
            @param data Pointer to receive the option value.
153  
            @param data Pointer to receive the option value.
154  
            @param size On entry, the size of the buffer. On exit,
154  
            @param size On entry, the size of the buffer. On exit,
155  
                the size of the option value.
155  
                the size of the option value.
156  
            @return Error code on failure, empty on success.
156  
            @return Error code on failure, empty on success.
157  
        */
157  
        */
158  
        virtual std::error_code
158  
        virtual std::error_code
159  
        get_option(int level, int optname, void* data, std::size_t* size)
159  
        get_option(int level, int optname, void* data, std::size_t* size)
160  
            const noexcept = 0;
160  
            const noexcept = 0;
161  

161  

162  
        /// Return the cached local endpoint.
162  
        /// Return the cached local endpoint.
163  
        virtual endpoint local_endpoint() const noexcept = 0;
163  
        virtual endpoint local_endpoint() const noexcept = 0;
164  

164  

165  
        /// Return the cached remote endpoint.
165  
        /// Return the cached remote endpoint.
166  
        virtual endpoint remote_endpoint() const noexcept = 0;
166  
        virtual endpoint remote_endpoint() const noexcept = 0;
167  
    };
167  
    };
168  

168  

169  
    /// Represent the awaitable returned by @ref connect.
169  
    /// Represent the awaitable returned by @ref connect.
170  
    struct connect_awaitable
170  
    struct connect_awaitable
171  
    {
171  
    {
172  
        tcp_socket& s_;
172  
        tcp_socket& s_;
173  
        endpoint endpoint_;
173  
        endpoint endpoint_;
174  
        std::stop_token token_;
174  
        std::stop_token token_;
175  
        mutable std::error_code ec_;
175  
        mutable std::error_code ec_;
176  

176  

177  
        connect_awaitable(tcp_socket& s, endpoint ep) noexcept
177  
        connect_awaitable(tcp_socket& s, endpoint ep) noexcept
178  
            : s_(s)
178  
            : s_(s)
179  
            , endpoint_(ep)
179  
            , endpoint_(ep)
180  
        {
180  
        {
181  
        }
181  
        }
182  

182  

183  
        bool await_ready() const noexcept
183  
        bool await_ready() const noexcept
184  
        {
184  
        {
185  
            return token_.stop_requested();
185  
            return token_.stop_requested();
186  
        }
186  
        }
187  

187  

188  
        capy::io_result<> await_resume() const noexcept
188  
        capy::io_result<> await_resume() const noexcept
189  
        {
189  
        {
190  
            if (token_.stop_requested())
190  
            if (token_.stop_requested())
191  
                return {make_error_code(std::errc::operation_canceled)};
191  
                return {make_error_code(std::errc::operation_canceled)};
192  
            return {ec_};
192  
            return {ec_};
193  
        }
193  
        }
194  

194  

195  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
195  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
196  
            -> std::coroutine_handle<>
196  
            -> std::coroutine_handle<>
197  
        {
197  
        {
198  
            token_ = env->stop_token;
198  
            token_ = env->stop_token;
199  
            return s_.get().connect(h, env->executor, endpoint_, token_, &ec_);
199  
            return s_.get().connect(h, env->executor, endpoint_, token_, &ec_);
200  
        }
200  
        }
201  
    };
201  
    };
202  

202  

203  
public:
203  
public:
204  
    /** Destructor.
204  
    /** Destructor.
205  

205  

206  
        Closes the socket if open, cancelling any pending operations.
206  
        Closes the socket if open, cancelling any pending operations.
207  
    */
207  
    */
208  
    ~tcp_socket() override;
208  
    ~tcp_socket() override;
209  

209  

210  
    /** Construct a socket from an execution context.
210  
    /** Construct a socket from an execution context.
211  

211  

212  
        @param ctx The execution context that will own this socket.
212  
        @param ctx The execution context that will own this socket.
213  
    */
213  
    */
214  
    explicit tcp_socket(capy::execution_context& ctx);
214  
    explicit tcp_socket(capy::execution_context& ctx);
215  

215  

216  
    /** Construct a socket from an executor.
216  
    /** Construct a socket from an executor.
217  

217  

218  
        The socket is associated with the executor's context.
218  
        The socket is associated with the executor's context.
219  

219  

220  
        @param ex The executor whose context will own the socket.
220  
        @param ex The executor whose context will own the socket.
221  
    */
221  
    */
222  
    template<class Ex>
222  
    template<class Ex>
223  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
223  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
224  
        capy::Executor<Ex>
224  
        capy::Executor<Ex>
225  
    explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context())
225  
    explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context())
226  
    {
226  
    {
227  
    }
227  
    }
228  

228  

229  
    /** Move constructor.
229  
    /** Move constructor.
230  

230  

231  
        Transfers ownership of the socket resources.
231  
        Transfers ownership of the socket resources.
232  

232  

233  
        @param other The socket to move from.
233  
        @param other The socket to move from.
234  

234  

235  
        @pre No awaitables returned by @p other's methods exist.
235  
        @pre No awaitables returned by @p other's methods exist.
236  
        @pre @p other is not referenced as a peer in any outstanding
236  
        @pre @p other is not referenced as a peer in any outstanding
237  
            accept awaitable.
237  
            accept awaitable.
238  
        @pre The execution context associated with @p other must
238  
        @pre The execution context associated with @p other must
239  
            outlive this socket.
239  
            outlive this socket.
240  
    */
240  
    */
241  
    tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {}
241  
    tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {}
242  

242  

243  
    /** Move assignment operator.
243  
    /** Move assignment operator.
244  

244  

245  
        Closes any existing socket and transfers ownership.
245  
        Closes any existing socket and transfers ownership.
246  

246  

247  
        @param other The socket to move from.
247  
        @param other The socket to move from.
248  

248  

249  
        @pre No awaitables returned by either `*this` or @p other's
249  
        @pre No awaitables returned by either `*this` or @p other's
250  
            methods exist.
250  
            methods exist.
251  
        @pre Neither `*this` nor @p other is referenced as a peer in
251  
        @pre Neither `*this` nor @p other is referenced as a peer in
252  
            any outstanding accept awaitable.
252  
            any outstanding accept awaitable.
253  
        @pre The execution context associated with @p other must
253  
        @pre The execution context associated with @p other must
254  
            outlive this socket.
254  
            outlive this socket.
255  

255  

256  
        @return Reference to this socket.
256  
        @return Reference to this socket.
257  
    */
257  
    */
258  
    tcp_socket& operator=(tcp_socket&& other) noexcept
258  
    tcp_socket& operator=(tcp_socket&& other) noexcept
259  
    {
259  
    {
260  
        if (this != &other)
260  
        if (this != &other)
261  
        {
261  
        {
262  
            close();
262  
            close();
263  
            h_ = std::move(other.h_);
263  
            h_ = std::move(other.h_);
264  
        }
264  
        }
265  
        return *this;
265  
        return *this;
266  
    }
266  
    }
267  

267  

268  
    tcp_socket(tcp_socket const&)            = delete;
268  
    tcp_socket(tcp_socket const&)            = delete;
269  
    tcp_socket& operator=(tcp_socket const&) = delete;
269  
    tcp_socket& operator=(tcp_socket const&) = delete;
270  

270  

271  
    /** Open the socket.
271  
    /** Open the socket.
272  

272  

273  
        Creates a TCP socket and associates it with the platform
273  
        Creates a TCP socket and associates it with the platform
274  
        reactor (IOCP on Windows). Calling @ref connect on a closed
274  
        reactor (IOCP on Windows). Calling @ref connect on a closed
275  
        socket opens it automatically with the endpoint's address family,
275  
        socket opens it automatically with the endpoint's address family,
276  
        so explicit `open()` is only needed when socket options must be
276  
        so explicit `open()` is only needed when socket options must be
277  
        set before connecting.
277  
        set before connecting.
278  

278  

279  
        @param proto The protocol (IPv4 or IPv6). Defaults to
279  
        @param proto The protocol (IPv4 or IPv6). Defaults to
280  
            `tcp::v4()`.
280  
            `tcp::v4()`.
281  

281  

282  
        @throws std::system_error on failure.
282  
        @throws std::system_error on failure.
283  
    */
283  
    */
284  
    void open(tcp proto = tcp::v4());
284  
    void open(tcp proto = tcp::v4());
285  

285  

286  
    /** Close the socket.
286  
    /** Close the socket.
287  

287  

288  
        Releases socket resources. Any pending operations complete
288  
        Releases socket resources. Any pending operations complete
289  
        with `errc::operation_canceled`.
289  
        with `errc::operation_canceled`.
290  
    */
290  
    */
291  
    void close();
291  
    void close();
292  

292  

293  
    /** Check if the socket is open.
293  
    /** Check if the socket is open.
294  

294  

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

305  

306  
    /** Initiate an asynchronous connect operation.
306  
    /** Initiate an asynchronous connect operation.
307  

307  

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

311  

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

316  

317  
        @param ep The remote endpoint to connect to.
317  
        @param ep The remote endpoint to connect to.
318  

318  

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

327  

328  
        @throws std::system_error if the socket needs to be opened
328  
        @throws std::system_error if the socket needs to be opened
329  
            and the open fails.
329  
            and the open fails.
330  

330  

331  
        @par Preconditions
331  
        @par Preconditions
332  
        This socket must outlive the returned awaitable.
332  
        This socket must outlive the returned awaitable.
333  

333  

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

347  

348  
    /** Cancel any pending asynchronous operations.
348  
    /** Cancel any pending asynchronous operations.
349  

349  

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

354  

355  
    /** Get the native socket handle.
355  
    /** Get the native socket handle.
356  

356  

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

360  

361  
        @return The native socket handle, or -1/INVALID_SOCKET if not open.
361  
        @return The native socket handle, or -1/INVALID_SOCKET if not open.
362  

362  

363  
        @par Preconditions
363  
        @par Preconditions
364  
        None. May be called on closed sockets.
364  
        None. May be called on closed sockets.
365  
    */
365  
    */
366  
    native_handle_type native_handle() const noexcept;
366  
    native_handle_type native_handle() const noexcept;
367  

367  

368  
    /** Disable sends or receives on the socket.
368  
    /** Disable sends or receives on the socket.
369  

369  

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

373  

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

379  

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

385  

386  
        @li @ref shutdown_both combines both effects: sends a FIN and
386  
        @li @ref shutdown_both combines both effects: sends a FIN and
387  
            disables reading.
387  
            disables reading.
388  

388  

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

393  

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

401  

402  
        Any error from the underlying system call is silently discarded
402  
        Any error from the underlying system call is silently discarded
403  
        because it is unlikely to be helpful.
403  
        because it is unlikely to be helpful.
404  

404  

405  
        @param what Determines what operations will no longer be allowed.
405  
        @param what Determines what operations will no longer be allowed.
406  
    */
406  
    */
407  
    void shutdown(shutdown_type what);
407  
    void shutdown(shutdown_type what);
408  

408  

409  
    /** Set a socket option.
409  
    /** Set a socket option.
410  

410  

411  
        Applies a type-safe socket option to the underlying socket.
411  
        Applies a type-safe socket option to the underlying socket.
412  
        The option type encodes the protocol level and option name.
412  
        The option type encodes the protocol level and option name.
413  

413  

414  
        @par Example
414  
        @par Example
415  
        @code
415  
        @code
416  
        sock.set_option( socket_option::no_delay( true ) );
416  
        sock.set_option( socket_option::no_delay( true ) );
417  
        sock.set_option( socket_option::receive_buffer_size( 65536 ) );
417  
        sock.set_option( socket_option::receive_buffer_size( 65536 ) );
418  
        @endcode
418  
        @endcode
419  

419  

420  
        @param opt The option to set.
420  
        @param opt The option to set.
421  

421  

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

435  

436  
    /** Get a socket option.
436  
    /** Get a socket option.
437  

437  

438  
        Retrieves the current value of a type-safe socket option.
438  
        Retrieves the current value of a type-safe socket option.
439  

439  

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

446  

447  
        @return The current option value.
447  
        @return The current option value.
448  

448  

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

466  

467  
    /** Get the local endpoint of the socket.
467  
    /** Get the local endpoint of the socket.
468  

468  

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

472  

473  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
473  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
474  
            the socket is not connected.
474  
            the socket is not connected.
475  

475  

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

483  

484  
    /** Get the remote endpoint of the socket.
484  
    /** Get the remote endpoint of the socket.
485  

485  

486  
        Returns the remote address and port to which the socket is connected.
486  
        Returns the remote address and port to which the socket is connected.
487  
        The endpoint is cached when the connection is established.
487  
        The endpoint is cached when the connection is established.
488  

488  

489  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
489  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
490  
            the socket is not connected.
490  
            the socket is not connected.
491  

491  

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

499  

500  
protected:
500  
protected:
501  
    tcp_socket() noexcept = default;
501  
    tcp_socket() noexcept = default;
502  

502  

503  
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
503  
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
504  

504  

505  
private:
505  
private:
506  
    friend class tcp_acceptor;
506  
    friend class tcp_acceptor;
507  

507  

508  
    /// Open the socket for the given protocol triple.
508  
    /// Open the socket for the given protocol triple.
509  
    void open_for_family(int family, int type, int protocol);
509  
    void open_for_family(int family, int type, int protocol);
510  

510  

511  
    inline implementation& get() const noexcept
511  
    inline implementation& get() const noexcept
512  
    {
512  
    {
513  
        return *static_cast<implementation*>(h_.get());
513  
        return *static_cast<implementation*>(h_.get());
514  
    }
514  
    }
515  
};
515  
};
516  

516  

517  
} // namespace boost::corosio
517  
} // namespace boost::corosio
518  

518  

519  
#endif
519  
#endif