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  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
37  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
38  
using native_handle_type = std::uintptr_t; // SOCKET
38  
using native_handle_type = std::uintptr_t; // SOCKET
39  
#else
39  
#else
40  
using native_handle_type = int;
40  
using native_handle_type = int;
41  
#endif
41  
#endif
42  

42  

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

44  

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

48  

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

52  

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

58  

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

63  

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

69  

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

75  

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

91  

92  
    struct implementation : io_stream::implementation
92  
    struct implementation : io_stream::implementation
93  
    {
93  
    {
94  
        virtual std::coroutine_handle<> connect(
94  
        virtual std::coroutine_handle<> connect(
95  
            std::coroutine_handle<>,
95  
            std::coroutine_handle<>,
96  
            capy::executor_ref,
96  
            capy::executor_ref,
97  
            endpoint,
97  
            endpoint,
98  
            std::stop_token,
98  
            std::stop_token,
99  
            std::error_code*) = 0;
99  
            std::error_code*) = 0;
100  

100  

101  
        virtual std::error_code shutdown(shutdown_type) noexcept = 0;
101  
        virtual std::error_code shutdown(shutdown_type) noexcept = 0;
102  

102  

103  
        virtual native_handle_type native_handle() const noexcept = 0;
103  
        virtual native_handle_type native_handle() const noexcept = 0;
104  

104  

105  
        /** Request cancellation of pending asynchronous operations.
105  
        /** Request cancellation of pending asynchronous operations.
106  

106  

107  
            All outstanding operations complete with operation_canceled error.
107  
            All outstanding operations complete with operation_canceled error.
108  
            Check `ec == cond::canceled` for portable comparison.
108  
            Check `ec == cond::canceled` for portable comparison.
109  
        */
109  
        */
110  
        virtual void cancel() noexcept = 0;
110  
        virtual void cancel() noexcept = 0;
111  

111  

112  
        /** Set a socket option.
112  
        /** Set a socket option.
113  

113  

114  
            @param level The protocol level (e.g. `SOL_SOCKET`).
114  
            @param level The protocol level (e.g. `SOL_SOCKET`).
115  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
115  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
116  
            @param data Pointer to the option value.
116  
            @param data Pointer to the option value.
117  
            @param size Size of the option value in bytes.
117  
            @param size Size of the option value in bytes.
118  
            @return Error code on failure, empty on success.
118  
            @return Error code on failure, empty on success.
119  
        */
119  
        */
120  
        virtual std::error_code set_option(
120  
        virtual std::error_code set_option(
121  
            int level,
121  
            int level,
122  
            int optname,
122  
            int optname,
123  
            void const* data,
123  
            void const* data,
124  
            std::size_t size) noexcept = 0;
124  
            std::size_t size) noexcept = 0;
125  

125  

126  
        /** Get a socket option.
126  
        /** Get a socket option.
127  

127  

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

138  

139  
        /// Returns the cached local endpoint.
139  
        /// Returns the cached local endpoint.
140  
        virtual endpoint local_endpoint() const noexcept = 0;
140  
        virtual endpoint local_endpoint() const noexcept = 0;
141  

141  

142  
        /// Returns the cached remote endpoint.
142  
        /// Returns the cached remote endpoint.
143  
        virtual endpoint remote_endpoint() const noexcept = 0;
143  
        virtual endpoint remote_endpoint() const noexcept = 0;
144  
    };
144  
    };
145  

145  

146  
    struct connect_awaitable
146  
    struct connect_awaitable
147  
    {
147  
    {
148  
        tcp_socket& s_;
148  
        tcp_socket& s_;
149  
        endpoint endpoint_;
149  
        endpoint endpoint_;
150  
        std::stop_token token_;
150  
        std::stop_token token_;
151  
        mutable std::error_code ec_;
151  
        mutable std::error_code ec_;
152  

152  

153  
        connect_awaitable(tcp_socket& s, endpoint ep) noexcept
153  
        connect_awaitable(tcp_socket& s, endpoint ep) noexcept
154  
            : s_(s)
154  
            : s_(s)
155  
            , endpoint_(ep)
155  
            , endpoint_(ep)
156  
        {
156  
        {
157  
        }
157  
        }
158  

158  

159  
        bool await_ready() const noexcept
159  
        bool await_ready() const noexcept
160  
        {
160  
        {
161  
            return token_.stop_requested();
161  
            return token_.stop_requested();
162  
        }
162  
        }
163  

163  

164  
        capy::io_result<> await_resume() const noexcept
164  
        capy::io_result<> await_resume() const noexcept
165  
        {
165  
        {
166  
            if (token_.stop_requested())
166  
            if (token_.stop_requested())
167  
                return {make_error_code(std::errc::operation_canceled)};
167  
                return {make_error_code(std::errc::operation_canceled)};
168  
            return {ec_};
168  
            return {ec_};
169  
        }
169  
        }
170  

170  

171  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
171  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
172  
            -> std::coroutine_handle<>
172  
            -> std::coroutine_handle<>
173  
        {
173  
        {
174  
            token_ = env->stop_token;
174  
            token_ = env->stop_token;
175  
            return s_.get().connect(h, env->executor, endpoint_, token_, &ec_);
175  
            return s_.get().connect(h, env->executor, endpoint_, token_, &ec_);
176  
        }
176  
        }
177  
    };
177  
    };
178  

178  

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

181  

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

185  

186  
    /** Construct a socket from an execution context.
186  
    /** Construct a socket from an execution context.
187  

187  

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

191  

192  
    /** Construct a socket from an executor.
192  
    /** Construct a socket from an executor.
193  

193  

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

195  

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

204  

205  
    /** Move constructor.
205  
    /** Move constructor.
206  

206  

207  
        Transfers ownership of the socket resources.
207  
        Transfers ownership of the socket resources.
208  

208  

209  
        @param other The socket to move from.
209  
        @param other The socket to move from.
210  

210  

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

218  

219  
    /** Move assignment operator.
219  
    /** Move assignment operator.
220  

220  

221  
        Closes any existing socket and transfers ownership.
221  
        Closes any existing socket and transfers ownership.
222  

222  

223  
        @param other The socket to move from.
223  
        @param other The socket to move from.
224  

224  

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

231  

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

243  

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

246  

247  
    /** Open the socket.
247  
    /** Open the socket.
248  

248  

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

254  

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

257  

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

261  

262  
    /** Close the socket.
262  
    /** Close the socket.
263  

263  

264  
        Releases socket resources. Any pending operations complete
264  
        Releases socket resources. Any pending operations complete
265  
        with `errc::operation_canceled`.
265  
        with `errc::operation_canceled`.
266  
    */
266  
    */
267  
    void close();
267  
    void close();
268  

268  

269  
    /** Check if the socket is open.
269  
    /** Check if the socket is open.
270  

270  

271  
        @return `true` if the socket is open and ready for operations.
271  
        @return `true` if the socket is open and ready for operations.
272  
    */
272  
    */
273  
    bool is_open() const noexcept
273  
    bool is_open() const noexcept
274  
    {
274  
    {
275  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
275  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
276  
        return h_ && get().native_handle() != ~native_handle_type(0);
276  
        return h_ && get().native_handle() != ~native_handle_type(0);
277  
#else
277  
#else
278  
        return h_ && get().native_handle() >= 0;
278  
        return h_ && get().native_handle() >= 0;
279  
#endif
279  
#endif
280  
    }
280  
    }
281  

281  

282  
    /** Initiate an asynchronous connect operation.
282  
    /** Initiate an asynchronous connect operation.
283  

283  

284  
        If the socket is not already open, it is opened automatically
284  
        If the socket is not already open, it is opened automatically
285  
        using the address family of @p ep (IPv4 or IPv6). If the socket
285  
        using the address family of @p ep (IPv4 or IPv6). If the socket
286  
        is already open, the existing file descriptor is used as-is.
286  
        is already open, the existing file descriptor is used as-is.
287  

287  

288  
        The operation supports cancellation via `std::stop_token` through
288  
        The operation supports cancellation via `std::stop_token` through
289  
        the affine awaitable protocol. If the associated stop token is
289  
        the affine awaitable protocol. If the associated stop token is
290  
        triggered, the operation completes immediately with
290  
        triggered, the operation completes immediately with
291  
        `errc::operation_canceled`.
291  
        `errc::operation_canceled`.
292  

292  

293  
        @param ep The remote endpoint to connect to.
293  
        @param ep The remote endpoint to connect to.
294  

294  

295  
        @return An awaitable that completes with `io_result<>`.
295  
        @return An awaitable that completes with `io_result<>`.
296  
            Returns success (default error_code) on successful connection,
296  
            Returns success (default error_code) on successful connection,
297  
            or an error code on failure including:
297  
            or an error code on failure including:
298  
            - connection_refused: No server listening at endpoint
298  
            - connection_refused: No server listening at endpoint
299  
            - timed_out: Connection attempt timed out
299  
            - timed_out: Connection attempt timed out
300  
            - network_unreachable: No route to host
300  
            - network_unreachable: No route to host
301  
            - operation_canceled: Cancelled via stop_token or cancel().
301  
            - operation_canceled: Cancelled via stop_token or cancel().
302  
                Check `ec == cond::canceled` for portable comparison.
302  
                Check `ec == cond::canceled` for portable comparison.
303  

303  

304  
        @throws std::system_error if the socket needs to be opened
304  
        @throws std::system_error if the socket needs to be opened
305  
            and the open fails.
305  
            and the open fails.
306  

306  

307  
        @par Preconditions
307  
        @par Preconditions
308  
        This socket must outlive the returned awaitable.
308  
        This socket must outlive the returned awaitable.
309  

309  

310  
        @par Example
310  
        @par Example
311  
        @code
311  
        @code
312  
        // Socket opened automatically with correct address family:
312  
        // Socket opened automatically with correct address family:
313  
        auto [ec] = co_await s.connect(endpoint);
313  
        auto [ec] = co_await s.connect(endpoint);
314  
        if (ec) { ... }
314  
        if (ec) { ... }
315  
        @endcode
315  
        @endcode
316  
    */
316  
    */
317  
    auto connect(endpoint ep)
317  
    auto connect(endpoint ep)
318  
    {
318  
    {
319  
        if (!is_open())
319  
        if (!is_open())
320  
            open(ep.is_v6() ? tcp::v6() : tcp::v4());
320  
            open(ep.is_v6() ? tcp::v6() : tcp::v4());
321  
        return connect_awaitable(*this, ep);
321  
        return connect_awaitable(*this, ep);
322  
    }
322  
    }
323  

323  

324  
    /** Cancel any pending asynchronous operations.
324  
    /** Cancel any pending asynchronous operations.
325  

325  

326  
        All outstanding operations complete with `errc::operation_canceled`.
326  
        All outstanding operations complete with `errc::operation_canceled`.
327  
        Check `ec == cond::canceled` for portable comparison.
327  
        Check `ec == cond::canceled` for portable comparison.
328  
    */
328  
    */
329  
    void cancel();
329  
    void cancel();
330  

330  

331  
    /** Get the native socket handle.
331  
    /** Get the native socket handle.
332  

332  

333  
        Returns the underlying platform-specific socket descriptor.
333  
        Returns the underlying platform-specific socket descriptor.
334  
        On POSIX systems this is an `int` file descriptor.
334  
        On POSIX systems this is an `int` file descriptor.
335  
        On Windows this is a `SOCKET` handle.
335  
        On Windows this is a `SOCKET` handle.
336  

336  

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

338  

339  
        @par Preconditions
339  
        @par Preconditions
340  
        None. May be called on closed sockets.
340  
        None. May be called on closed sockets.
341  
    */
341  
    */
342  
    native_handle_type native_handle() const noexcept;
342  
    native_handle_type native_handle() const noexcept;
343  

343  

344  
    /** Disable sends or receives on the socket.
344  
    /** Disable sends or receives on the socket.
345  

345  

346  
        TCP connections are full-duplex: each direction (send and receive)
346  
        TCP connections are full-duplex: each direction (send and receive)
347  
        operates independently. This function allows you to close one or
347  
        operates independently. This function allows you to close one or
348  
        both directions without destroying the socket.
348  
        both directions without destroying the socket.
349  

349  

350  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
350  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
351  
            signaling that you have no more data to send. You can still
351  
            signaling that you have no more data to send. You can still
352  
            receive data until the peer also closes their send direction.
352  
            receive data until the peer also closes their send direction.
353  
            This is the most common use case, typically called before
353  
            This is the most common use case, typically called before
354  
            close() to ensure graceful connection termination.
354  
            close() to ensure graceful connection termination.
355  

355  

356  
        @li @ref shutdown_receive disables reading on the socket. This
356  
        @li @ref shutdown_receive disables reading on the socket. This
357  
            does NOT send anything to the peer - they are not informed
357  
            does NOT send anything to the peer - they are not informed
358  
            and may continue sending data. Subsequent reads will fail
358  
            and may continue sending data. Subsequent reads will fail
359  
            or return end-of-file. Incoming data may be discarded or
359  
            or return end-of-file. Incoming data may be discarded or
360  
            buffered depending on the operating system.
360  
            buffered depending on the operating system.
361  

361  

362  
        @li @ref shutdown_both combines both effects: sends a FIN and
362  
        @li @ref shutdown_both combines both effects: sends a FIN and
363  
            disables reading.
363  
            disables reading.
364  

364  

365  
        When the peer shuts down their send direction (sends a FIN),
365  
        When the peer shuts down their send direction (sends a FIN),
366  
        subsequent read operations will complete with `capy::cond::eof`.
366  
        subsequent read operations will complete with `capy::cond::eof`.
367  
        Use the portable condition test rather than comparing error
367  
        Use the portable condition test rather than comparing error
368  
        codes directly:
368  
        codes directly:
369  

369  

370  
        @code
370  
        @code
371  
        auto [ec, n] = co_await sock.read_some(buffer);
371  
        auto [ec, n] = co_await sock.read_some(buffer);
372  
        if (ec == capy::cond::eof)
372  
        if (ec == capy::cond::eof)
373  
        {
373  
        {
374  
            // Peer closed their send direction
374  
            // Peer closed their send direction
375  
        }
375  
        }
376  
        @endcode
376  
        @endcode
377  

377  

378  
        Any error from the underlying system call is silently discarded
378  
        Any error from the underlying system call is silently discarded
379  
        because it is unlikely to be helpful.
379  
        because it is unlikely to be helpful.
380  

380  

381  
        @param what Determines what operations will no longer be allowed.
381  
        @param what Determines what operations will no longer be allowed.
382  
    */
382  
    */
383  
    void shutdown(shutdown_type what);
383  
    void shutdown(shutdown_type what);
384  

384  

385  
    /** Set a socket option.
385  
    /** Set a socket option.
386  

386  

387  
        Applies a type-safe socket option to the underlying socket.
387  
        Applies a type-safe socket option to the underlying socket.
388  
        The option type encodes the protocol level and option name.
388  
        The option type encodes the protocol level and option name.
389  

389  

390  
        @par Example
390  
        @par Example
391  
        @code
391  
        @code
392  
        sock.set_option( socket_option::no_delay( true ) );
392  
        sock.set_option( socket_option::no_delay( true ) );
393  
        sock.set_option( socket_option::receive_buffer_size( 65536 ) );
393  
        sock.set_option( socket_option::receive_buffer_size( 65536 ) );
394  
        @endcode
394  
        @endcode
395  

395  

396  
        @param opt The option to set.
396  
        @param opt The option to set.
397  

397  

398  
        @throws std::logic_error if the socket is not open.
398  
        @throws std::logic_error if the socket is not open.
399  
        @throws std::system_error on failure.
399  
        @throws std::system_error on failure.
400  
    */
400  
    */
401  
    template<class Option>
401  
    template<class Option>
402  
    void set_option(Option const& opt)
402  
    void set_option(Option const& opt)
403  
    {
403  
    {
404  
        if (!is_open())
404  
        if (!is_open())
405  
            detail::throw_logic_error("set_option: socket not open");
405  
            detail::throw_logic_error("set_option: socket not open");
406  
        std::error_code ec = get().set_option(
406  
        std::error_code ec = get().set_option(
407  
            Option::level(), Option::name(), opt.data(), opt.size());
407  
            Option::level(), Option::name(), opt.data(), opt.size());
408  
        if (ec)
408  
        if (ec)
409  
            detail::throw_system_error(ec, "tcp_socket::set_option");
409  
            detail::throw_system_error(ec, "tcp_socket::set_option");
410  
    }
410  
    }
411  

411  

412  
    /** Get a socket option.
412  
    /** Get a socket option.
413  

413  

414  
        Retrieves the current value of a type-safe socket option.
414  
        Retrieves the current value of a type-safe socket option.
415  

415  

416  
        @par Example
416  
        @par Example
417  
        @code
417  
        @code
418  
        auto nd = sock.get_option<socket_option::no_delay>();
418  
        auto nd = sock.get_option<socket_option::no_delay>();
419  
        if ( nd.value() )
419  
        if ( nd.value() )
420  
            // Nagle's algorithm is disabled
420  
            // Nagle's algorithm is disabled
421  
        @endcode
421  
        @endcode
422  

422  

423  
        @return The current option value.
423  
        @return The current option value.
424  

424  

425  
        @throws std::logic_error if the socket is not open.
425  
        @throws std::logic_error if the socket is not open.
426  
        @throws std::system_error on failure.
426  
        @throws std::system_error on failure.
427  
    */
427  
    */
428  
    template<class Option>
428  
    template<class Option>
429  
    Option get_option() const
429  
    Option get_option() const
430  
    {
430  
    {
431  
        if (!is_open())
431  
        if (!is_open())
432  
            detail::throw_logic_error("get_option: socket not open");
432  
            detail::throw_logic_error("get_option: socket not open");
433  
        Option opt{};
433  
        Option opt{};
434  
        std::size_t sz = opt.size();
434  
        std::size_t sz = opt.size();
435  
        std::error_code ec =
435  
        std::error_code ec =
436  
            get().get_option(Option::level(), Option::name(), opt.data(), &sz);
436  
            get().get_option(Option::level(), Option::name(), opt.data(), &sz);
437  
        if (ec)
437  
        if (ec)
438  
            detail::throw_system_error(ec, "tcp_socket::get_option");
438  
            detail::throw_system_error(ec, "tcp_socket::get_option");
439  
        opt.resize(sz);
439  
        opt.resize(sz);
440  
        return opt;
440  
        return opt;
441  
    }
441  
    }
442  

442  

443  
    /** Get the local endpoint of the socket.
443  
    /** Get the local endpoint of the socket.
444  

444  

445  
        Returns the local address and port to which the socket is bound.
445  
        Returns the local address and port to which the socket is bound.
446  
        For a connected socket, this is the local side of the connection.
446  
        For a connected socket, this is the local side of the connection.
447  
        The endpoint is cached when the connection is established.
447  
        The endpoint is cached when the connection is established.
448  

448  

449  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
449  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
450  
            the socket is not connected.
450  
            the socket is not connected.
451  

451  

452  
        @par Thread Safety
452  
        @par Thread Safety
453  
        The cached endpoint value is set during connect/accept completion
453  
        The cached endpoint value is set during connect/accept completion
454  
        and cleared during close(). This function may be called concurrently
454  
        and cleared during close(). This function may be called concurrently
455  
        with I/O operations, but must not be called concurrently with
455  
        with I/O operations, but must not be called concurrently with
456  
        connect(), accept(), or close().
456  
        connect(), accept(), or close().
457  
    */
457  
    */
458  
    endpoint local_endpoint() const noexcept;
458  
    endpoint local_endpoint() const noexcept;
459  

459  

460  
    /** Get the remote endpoint of the socket.
460  
    /** Get the remote endpoint of the socket.
461  

461  

462  
        Returns the remote address and port to which the socket is connected.
462  
        Returns the remote address and port to which the socket is connected.
463  
        The endpoint is cached when the connection is established.
463  
        The endpoint is cached when the connection is established.
464  

464  

465  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
465  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
466  
            the socket is not connected.
466  
            the socket is not connected.
467  

467  

468  
        @par Thread Safety
468  
        @par Thread Safety
469  
        The cached endpoint value is set during connect/accept completion
469  
        The cached endpoint value is set during connect/accept completion
470  
        and cleared during close(). This function may be called concurrently
470  
        and cleared during close(). This function may be called concurrently
471  
        with I/O operations, but must not be called concurrently with
471  
        with I/O operations, but must not be called concurrently with
472  
        connect(), accept(), or close().
472  
        connect(), accept(), or close().
473  
    */
473  
    */
474  
    endpoint remote_endpoint() const noexcept;
474  
    endpoint remote_endpoint() const noexcept;
475  

475  

476  
protected:
476  
protected:
477  
    tcp_socket() noexcept = default;
477  
    tcp_socket() noexcept = default;
478  

478  

479  
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
479  
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
480  

480  

481  
private:
481  
private:
482  
    friend class tcp_acceptor;
482  
    friend class tcp_acceptor;
483  

483  

484  
    /// Open the socket for the given protocol triple.
484  
    /// Open the socket for the given protocol triple.
485  
    void open_for_family(int family, int type, int protocol);
485  
    void open_for_family(int family, int type, int protocol);
486  

486  

487  
    inline implementation& get() const noexcept
487  
    inline implementation& get() const noexcept
488  
    {
488  
    {
489  
        return *static_cast<implementation*>(h_.get());
489  
        return *static_cast<implementation*>(h_.get());
490  
    }
490  
    }
491  
};
491  
};
492  

492  

493  
} // namespace boost::corosio
493  
} // namespace boost::corosio
494  

494  

495  
#endif
495  
#endif