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

25  

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

27  

28  
#include <concepts>
28  
#include <concepts>
29  
#include <coroutine>
29  
#include <coroutine>
30  
#include <cstddef>
30  
#include <cstddef>
31  
#include <memory>
31  
#include <memory>
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
37  
#if BOOST_COROSIO_HAS_IOCP
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  
    /** Options for SO_LINGER socket option. */
92  
    /** Options for SO_LINGER socket option. */
93  
    struct linger_options
93  
    struct linger_options
94  
    {
94  
    {
95  
        bool enabled = false;
95  
        bool enabled = false;
96  
        int timeout  = 0; // seconds
96  
        int timeout  = 0; // seconds
97  
    };
97  
    };
98  

98  

99  
    struct implementation : io_stream::implementation
99  
    struct implementation : io_stream::implementation
100  
    {
100  
    {
101  
        virtual std::coroutine_handle<> connect(
101  
        virtual std::coroutine_handle<> connect(
102  
            std::coroutine_handle<>,
102  
            std::coroutine_handle<>,
103  
            capy::executor_ref,
103  
            capy::executor_ref,
104  
            endpoint,
104  
            endpoint,
105  
            std::stop_token,
105  
            std::stop_token,
106  
            std::error_code*) = 0;
106  
            std::error_code*) = 0;
107  

107  

108  
        virtual std::error_code shutdown(shutdown_type) noexcept = 0;
108  
        virtual std::error_code shutdown(shutdown_type) noexcept = 0;
109  

109  

110  
        virtual native_handle_type native_handle() const noexcept = 0;
110  
        virtual native_handle_type native_handle() const noexcept = 0;
111  

111  

112  
        /** Request cancellation of pending asynchronous operations.
112  
        /** Request cancellation of pending asynchronous operations.
113  

113  

114  
            All outstanding operations complete with operation_canceled error.
114  
            All outstanding operations complete with operation_canceled error.
115  
            Check `ec == cond::canceled` for portable comparison.
115  
            Check `ec == cond::canceled` for portable comparison.
116  
        */
116  
        */
117  
        virtual void cancel() noexcept = 0;
117  
        virtual void cancel() noexcept = 0;
118  

118  

119  
        // Socket options
119  
        // Socket options
120  
        virtual std::error_code set_no_delay(bool value) noexcept = 0;
120  
        virtual std::error_code set_no_delay(bool value) noexcept = 0;
121  
        virtual bool no_delay(std::error_code& ec) const noexcept = 0;
121  
        virtual bool no_delay(std::error_code& ec) const noexcept = 0;
122  

122  

123  
        virtual std::error_code set_keep_alive(bool value) noexcept = 0;
123  
        virtual std::error_code set_keep_alive(bool value) noexcept = 0;
124  
        virtual bool keep_alive(std::error_code& ec) const noexcept = 0;
124  
        virtual bool keep_alive(std::error_code& ec) const noexcept = 0;
125  

125  

126  
        virtual std::error_code set_receive_buffer_size(int size) noexcept  = 0;
126  
        virtual std::error_code set_receive_buffer_size(int size) noexcept  = 0;
127  
        virtual int receive_buffer_size(std::error_code& ec) const noexcept = 0;
127  
        virtual int receive_buffer_size(std::error_code& ec) const noexcept = 0;
128  

128  

129  
        virtual std::error_code set_send_buffer_size(int size) noexcept  = 0;
129  
        virtual std::error_code set_send_buffer_size(int size) noexcept  = 0;
130  
        virtual int send_buffer_size(std::error_code& ec) const noexcept = 0;
130  
        virtual int send_buffer_size(std::error_code& ec) const noexcept = 0;
131  

131  

132  
        virtual std::error_code
132  
        virtual std::error_code
133  
        set_linger(bool enabled, int timeout) noexcept                    = 0;
133  
        set_linger(bool enabled, int timeout) noexcept                    = 0;
134  
        virtual linger_options linger(std::error_code& ec) const noexcept = 0;
134  
        virtual linger_options linger(std::error_code& ec) const noexcept = 0;
135  

135  

136  
        /// Returns the cached local endpoint.
136  
        /// Returns the cached local endpoint.
137  
        virtual endpoint local_endpoint() const noexcept = 0;
137  
        virtual endpoint local_endpoint() const noexcept = 0;
138  

138  

139  
        /// Returns the cached remote endpoint.
139  
        /// Returns the cached remote endpoint.
140  
        virtual endpoint remote_endpoint() const noexcept = 0;
140  
        virtual endpoint remote_endpoint() const noexcept = 0;
141  
    };
141  
    };
142  

142  

143  
    struct connect_awaitable
143  
    struct connect_awaitable
144  
    {
144  
    {
145  
        tcp_socket& s_;
145  
        tcp_socket& s_;
146  
        endpoint endpoint_;
146  
        endpoint endpoint_;
147  
        std::stop_token token_;
147  
        std::stop_token token_;
148  
        mutable std::error_code ec_;
148  
        mutable std::error_code ec_;
149  

149  

150  
        connect_awaitable(tcp_socket& s, endpoint ep) noexcept
150  
        connect_awaitable(tcp_socket& s, endpoint ep) noexcept
151  
            : s_(s)
151  
            : s_(s)
152  
            , endpoint_(ep)
152  
            , endpoint_(ep)
153  
        {
153  
        {
154  
        }
154  
        }
155  

155  

156  
        bool await_ready() const noexcept
156  
        bool await_ready() const noexcept
157  
        {
157  
        {
158  
            return token_.stop_requested();
158  
            return token_.stop_requested();
159  
        }
159  
        }
160  

160  

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

167  

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

175  

176  
public:
176  
public:
177  
    /** Destructor.
177  
    /** Destructor.
178  

178  

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

182  

183  
    /** Construct a socket from an execution context.
183  
    /** Construct a socket from an execution context.
184  

184  

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

188  

189  
    /** Construct a socket from an executor.
189  
    /** Construct a socket from an executor.
190  

190  

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

192  

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

201  

202  
    /** Move constructor.
202  
    /** Move constructor.
203  

203  

204  
        Transfers ownership of the socket resources.
204  
        Transfers ownership of the socket resources.
205  

205  

206  
        @param other The socket to move from.
206  
        @param other The socket to move from.
 
207 +

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

215  

210  
    /** Move assignment operator.
216  
    /** Move assignment operator.
211  

217  

212  
        Closes any existing socket and transfers ownership.
218  
        Closes any existing socket and transfers ownership.
 
219 +

213  
        @param other The socket to move from.
220  
        @param other The socket to move from.
214  

221  

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

215  
        @return Reference to this socket.
229  
        @return Reference to this socket.
216  
    */
230  
    */
217  
    tcp_socket& operator=(tcp_socket&& other) noexcept
231  
    tcp_socket& operator=(tcp_socket&& other) noexcept
218  
    {
232  
    {
219  
        if (this != &other)
233  
        if (this != &other)
220  
        {
234  
        {
221  
            close();
235  
            close();
222  
            h_ = std::move(other.h_);
236  
            h_ = std::move(other.h_);
223  
        }
237  
        }
224  
        return *this;
238  
        return *this;
225  
    }
239  
    }
226  

240  

227  
    tcp_socket(tcp_socket const&)            = delete;
241  
    tcp_socket(tcp_socket const&)            = delete;
228  
    tcp_socket& operator=(tcp_socket const&) = delete;
242  
    tcp_socket& operator=(tcp_socket const&) = delete;
229  

243  

230  
    /** Open the socket.
244  
    /** Open the socket.
231  

245  

232  
        Creates an IPv4 TCP socket and associates it with the platform
246  
        Creates an IPv4 TCP socket and associates it with the platform
233  
        reactor (IOCP on Windows). This must be called before initiating
247  
        reactor (IOCP on Windows). This must be called before initiating
234  
        I/O operations.
248  
        I/O operations.
235  

249  

236  
        @throws std::system_error on failure.
250  
        @throws std::system_error on failure.
237  
    */
251  
    */
238  
    void open();
252  
    void open();
239  

253  

240  
    /** Close the socket.
254  
    /** Close the socket.
241  

255  

242  
        Releases socket resources. Any pending operations complete
256  
        Releases socket resources. Any pending operations complete
243  
        with `errc::operation_canceled`.
257  
        with `errc::operation_canceled`.
244  
    */
258  
    */
245  
    void close();
259  
    void close();
246  

260  

247  
    /** Check if the socket is open.
261  
    /** Check if the socket is open.
248  

262  

249  
        @return `true` if the socket is open and ready for operations.
263  
        @return `true` if the socket is open and ready for operations.
250  
    */
264  
    */
251  
    bool is_open() const noexcept
265  
    bool is_open() const noexcept
252  
    {
266  
    {
253  
#if BOOST_COROSIO_HAS_IOCP
267  
#if BOOST_COROSIO_HAS_IOCP
254  
        return h_ && get().native_handle() != ~native_handle_type(0);
268  
        return h_ && get().native_handle() != ~native_handle_type(0);
255  
#else
269  
#else
256  
        return h_ && get().native_handle() >= 0;
270  
        return h_ && get().native_handle() >= 0;
257  
#endif
271  
#endif
258  
    }
272  
    }
259  

273  

260  
    /** Initiate an asynchronous connect operation.
274  
    /** Initiate an asynchronous connect operation.
261  

275  

262  
        Connects the socket to the specified remote endpoint. The socket
276  
        Connects the socket to the specified remote endpoint. The socket
263  
        must be open before calling this function.
277  
        must be open before calling this function.
264  

278  

265  
        The operation supports cancellation via `std::stop_token` through
279  
        The operation supports cancellation via `std::stop_token` through
266  
        the affine awaitable protocol. If the associated stop token is
280  
        the affine awaitable protocol. If the associated stop token is
267  
        triggered, the operation completes immediately with
281  
        triggered, the operation completes immediately with
268  
        `errc::operation_canceled`.
282  
        `errc::operation_canceled`.
269  

283  

270  
        @param ep The remote endpoint to connect to.
284  
        @param ep The remote endpoint to connect to.
271  

285  

272  
        @return An awaitable that completes with `io_result<>`.
286  
        @return An awaitable that completes with `io_result<>`.
273  
            Returns success (default error_code) on successful connection,
287  
            Returns success (default error_code) on successful connection,
274  
            or an error code on failure including:
288  
            or an error code on failure including:
275  
            - connection_refused: No server listening at endpoint
289  
            - connection_refused: No server listening at endpoint
276  
            - timed_out: Connection attempt timed out
290  
            - timed_out: Connection attempt timed out
277  
            - network_unreachable: No route to host
291  
            - network_unreachable: No route to host
278  
            - operation_canceled: Cancelled via stop_token or cancel().
292  
            - operation_canceled: Cancelled via stop_token or cancel().
279  
                Check `ec == cond::canceled` for portable comparison.
293  
                Check `ec == cond::canceled` for portable comparison.
280  

294  

281  
        @throws std::logic_error if the socket is not open.
295  
        @throws std::logic_error if the socket is not open.
282  

296  

283  
        @par Preconditions
297  
        @par Preconditions
284  
        The socket must be open (`is_open() == true`).
298  
        The socket must be open (`is_open() == true`).
 
299 +

 
300 +
        This socket must outlive the returned awaitable.
285  

301  

286  
        @par Example
302  
        @par Example
287  
        @code
303  
        @code
288  
        auto [ec] = co_await s.connect(endpoint);
304  
        auto [ec] = co_await s.connect(endpoint);
289  
        if (ec) { ... }
305  
        if (ec) { ... }
290  
        @endcode
306  
        @endcode
291  
    */
307  
    */
292  
    auto connect(endpoint ep)
308  
    auto connect(endpoint ep)
293  
    {
309  
    {
294  
        if (!is_open())
310  
        if (!is_open())
295  
            detail::throw_logic_error("connect: socket not open");
311  
            detail::throw_logic_error("connect: socket not open");
296  
        return connect_awaitable(*this, ep);
312  
        return connect_awaitable(*this, ep);
297  
    }
313  
    }
298  

314  

299  
    /** Cancel any pending asynchronous operations.
315  
    /** Cancel any pending asynchronous operations.
300  

316  

301  
        All outstanding operations complete with `errc::operation_canceled`.
317  
        All outstanding operations complete with `errc::operation_canceled`.
302  
        Check `ec == cond::canceled` for portable comparison.
318  
        Check `ec == cond::canceled` for portable comparison.
303  
    */
319  
    */
304  
    void cancel();
320  
    void cancel();
305  

321  

306  
    /** Get the native socket handle.
322  
    /** Get the native socket handle.
307  

323  

308  
        Returns the underlying platform-specific socket descriptor.
324  
        Returns the underlying platform-specific socket descriptor.
309  
        On POSIX systems this is an `int` file descriptor.
325  
        On POSIX systems this is an `int` file descriptor.
310  
        On Windows this is a `SOCKET` handle.
326  
        On Windows this is a `SOCKET` handle.
311  

327  

312  
        @return The native socket handle, or -1/INVALID_SOCKET if not open.
328  
        @return The native socket handle, or -1/INVALID_SOCKET if not open.
313  

329  

314  
        @par Preconditions
330  
        @par Preconditions
315  
        None. May be called on closed sockets.
331  
        None. May be called on closed sockets.
316  
    */
332  
    */
317  
    native_handle_type native_handle() const noexcept;
333  
    native_handle_type native_handle() const noexcept;
318  

334  

319  
    /** Disable sends or receives on the socket.
335  
    /** Disable sends or receives on the socket.
320  

336  

321  
        TCP connections are full-duplex: each direction (send and receive)
337  
        TCP connections are full-duplex: each direction (send and receive)
322  
        operates independently. This function allows you to close one or
338  
        operates independently. This function allows you to close one or
323  
        both directions without destroying the socket.
339  
        both directions without destroying the socket.
324  

340  

325  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
341  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
326  
            signaling that you have no more data to send. You can still
342  
            signaling that you have no more data to send. You can still
327  
            receive data until the peer also closes their send direction.
343  
            receive data until the peer also closes their send direction.
328  
            This is the most common use case, typically called before
344  
            This is the most common use case, typically called before
329  
            close() to ensure graceful connection termination.
345  
            close() to ensure graceful connection termination.
330  

346  

331  
        @li @ref shutdown_receive disables reading on the socket. This
347  
        @li @ref shutdown_receive disables reading on the socket. This
332  
            does NOT send anything to the peer - they are not informed
348  
            does NOT send anything to the peer - they are not informed
333  
            and may continue sending data. Subsequent reads will fail
349  
            and may continue sending data. Subsequent reads will fail
334  
            or return end-of-file. Incoming data may be discarded or
350  
            or return end-of-file. Incoming data may be discarded or
335  
            buffered depending on the operating system.
351  
            buffered depending on the operating system.
336  

352  

337  
        @li @ref shutdown_both combines both effects: sends a FIN and
353  
        @li @ref shutdown_both combines both effects: sends a FIN and
338  
            disables reading.
354  
            disables reading.
339  

355  

340  
        When the peer shuts down their send direction (sends a FIN),
356  
        When the peer shuts down their send direction (sends a FIN),
341  
        subsequent read operations will complete with `capy::cond::eof`.
357  
        subsequent read operations will complete with `capy::cond::eof`.
342  
        Use the portable condition test rather than comparing error
358  
        Use the portable condition test rather than comparing error
343  
        codes directly:
359  
        codes directly:
344  

360  

345  
        @code
361  
        @code
346  
        auto [ec, n] = co_await sock.read_some(buffer);
362  
        auto [ec, n] = co_await sock.read_some(buffer);
347  
        if (ec == capy::cond::eof)
363  
        if (ec == capy::cond::eof)
348  
        {
364  
        {
349  
            // Peer closed their send direction
365  
            // Peer closed their send direction
350  
        }
366  
        }
351  
        @endcode
367  
        @endcode
352  

368  

353  
        Any error from the underlying system call is silently discarded
369  
        Any error from the underlying system call is silently discarded
354  
        because it is unlikely to be helpful.
370  
        because it is unlikely to be helpful.
355  

371  

356  
        @param what Determines what operations will no longer be allowed.
372  
        @param what Determines what operations will no longer be allowed.
357  
    */
373  
    */
358  
    void shutdown(shutdown_type what);
374  
    void shutdown(shutdown_type what);
359  

375  

360  
    //
376  
    //
361  
    // Socket Options
377  
    // Socket Options
362  
    //
378  
    //
363  

379  

364  
    /** Enable or disable TCP_NODELAY (disable Nagle's algorithm).
380  
    /** Enable or disable TCP_NODELAY (disable Nagle's algorithm).
365  

381  

366  
        When enabled, segments are sent as soon as possible even if
382  
        When enabled, segments are sent as soon as possible even if
367  
        there is only a small amount of data. This reduces latency
383  
        there is only a small amount of data. This reduces latency
368  
        at the potential cost of increased network traffic.
384  
        at the potential cost of increased network traffic.
369  

385  

370  
        @param value `true` to disable Nagle's algorithm (enable no-delay).
386  
        @param value `true` to disable Nagle's algorithm (enable no-delay).
371  

387  

372  
        @throws std::logic_error if the socket is not open.
388  
        @throws std::logic_error if the socket is not open.
373  
        @throws std::system_error on failure.
389  
        @throws std::system_error on failure.
374  
    */
390  
    */
375  
    void set_no_delay(bool value);
391  
    void set_no_delay(bool value);
376  

392  

377  
    /** Get the current TCP_NODELAY setting.
393  
    /** Get the current TCP_NODELAY setting.
378  

394  

379  
        @return `true` if Nagle's algorithm is disabled.
395  
        @return `true` if Nagle's algorithm is disabled.
380  

396  

381  
        @throws std::logic_error if the socket is not open.
397  
        @throws std::logic_error if the socket is not open.
382  
        @throws std::system_error on failure.
398  
        @throws std::system_error on failure.
383  
    */
399  
    */
384  
    bool no_delay() const;
400  
    bool no_delay() const;
385  

401  

386  
    /** Enable or disable SO_KEEPALIVE.
402  
    /** Enable or disable SO_KEEPALIVE.
387  

403  

388  
        When enabled, the socket will periodically send keepalive probes
404  
        When enabled, the socket will periodically send keepalive probes
389  
        to detect if the peer is still reachable.
405  
        to detect if the peer is still reachable.
390  

406  

391  
        @param value `true` to enable keepalive probes.
407  
        @param value `true` to enable keepalive probes.
392  

408  

393  
        @throws std::logic_error if the socket is not open.
409  
        @throws std::logic_error if the socket is not open.
394  
        @throws std::system_error on failure.
410  
        @throws std::system_error on failure.
395  
    */
411  
    */
396  
    void set_keep_alive(bool value);
412  
    void set_keep_alive(bool value);
397  

413  

398  
    /** Get the current SO_KEEPALIVE setting.
414  
    /** Get the current SO_KEEPALIVE setting.
399  

415  

400  
        @return `true` if keepalive is enabled.
416  
        @return `true` if keepalive is enabled.
401  

417  

402  
        @throws std::logic_error if the socket is not open.
418  
        @throws std::logic_error if the socket is not open.
403  
        @throws std::system_error on failure.
419  
        @throws std::system_error on failure.
404  
    */
420  
    */
405  
    bool keep_alive() const;
421  
    bool keep_alive() const;
406  

422  

407  
    /** Set the receive buffer size (SO_RCVBUF).
423  
    /** Set the receive buffer size (SO_RCVBUF).
408  

424  

409  
        @param size The desired receive buffer size in bytes.
425  
        @param size The desired receive buffer size in bytes.
410  

426  

411  
        @throws std::logic_error if the socket is not open.
427  
        @throws std::logic_error if the socket is not open.
412  
        @throws std::system_error on failure.
428  
        @throws std::system_error on failure.
413  

429  

414  
        @note The operating system may adjust the actual buffer size.
430  
        @note The operating system may adjust the actual buffer size.
415  
    */
431  
    */
416  
    void set_receive_buffer_size(int size);
432  
    void set_receive_buffer_size(int size);
417  

433  

418  
    /** Get the receive buffer size (SO_RCVBUF).
434  
    /** Get the receive buffer size (SO_RCVBUF).
419  

435  

420  
        @return The current receive buffer size in bytes.
436  
        @return The current receive buffer size in bytes.
421  

437  

422  
        @throws std::logic_error if the socket is not open.
438  
        @throws std::logic_error if the socket is not open.
423  
        @throws std::system_error on failure.
439  
        @throws std::system_error on failure.
424  
    */
440  
    */
425  
    int receive_buffer_size() const;
441  
    int receive_buffer_size() const;
426  

442  

427  
    /** Set the send buffer size (SO_SNDBUF).
443  
    /** Set the send buffer size (SO_SNDBUF).
428  

444  

429  
        @param size The desired send buffer size in bytes.
445  
        @param size The desired send buffer size in bytes.
430  

446  

431  
        @throws std::logic_error if the socket is not open.
447  
        @throws std::logic_error if the socket is not open.
432  
        @throws std::system_error on failure.
448  
        @throws std::system_error on failure.
433  

449  

434  
        @note The operating system may adjust the actual buffer size.
450  
        @note The operating system may adjust the actual buffer size.
435  
    */
451  
    */
436  
    void set_send_buffer_size(int size);
452  
    void set_send_buffer_size(int size);
437  

453  

438  
    /** Get the send buffer size (SO_SNDBUF).
454  
    /** Get the send buffer size (SO_SNDBUF).
439  

455  

440  
        @return The current send buffer size in bytes.
456  
        @return The current send buffer size in bytes.
441  

457  

442  
        @throws std::logic_error if the socket is not open.
458  
        @throws std::logic_error if the socket is not open.
443  
        @throws std::system_error on failure.
459  
        @throws std::system_error on failure.
444  
    */
460  
    */
445  
    int send_buffer_size() const;
461  
    int send_buffer_size() const;
446  

462  

447  
    /** Set the SO_LINGER option.
463  
    /** Set the SO_LINGER option.
448  

464  

449  
        Controls behavior when closing a socket with unsent data.
465  
        Controls behavior when closing a socket with unsent data.
450  

466  

451  
        @param enabled If `true`, close() will block until data is sent
467  
        @param enabled If `true`, close() will block until data is sent
452  
            or the timeout expires. If `false`, close() returns immediately.
468  
            or the timeout expires. If `false`, close() returns immediately.
453  
        @param timeout The linger timeout in seconds (only used if enabled).
469  
        @param timeout The linger timeout in seconds (only used if enabled).
454  

470  

455  
        @throws std::logic_error if the socket is not open.
471  
        @throws std::logic_error if the socket is not open.
456  
        @throws std::system_error on failure.
472  
        @throws std::system_error on failure.
457  
    */
473  
    */
458  
    void set_linger(bool enabled, int timeout);
474  
    void set_linger(bool enabled, int timeout);
459  

475  

460  
    /** Get the current SO_LINGER setting.
476  
    /** Get the current SO_LINGER setting.
461  

477  

462  
        @return The current linger options.
478  
        @return The current linger options.
463  

479  

464  
        @throws std::logic_error if the socket is not open.
480  
        @throws std::logic_error if the socket is not open.
465  
        @throws std::system_error on failure.
481  
        @throws std::system_error on failure.
466  
    */
482  
    */
467  
    linger_options linger() const;
483  
    linger_options linger() const;
468  

484  

469  
    /** Get the local endpoint of the socket.
485  
    /** Get the local endpoint of the socket.
470  

486  

471  
        Returns the local address and port to which the socket is bound.
487  
        Returns the local address and port to which the socket is bound.
472  
        For a connected socket, this is the local side of the connection.
488  
        For a connected socket, this is the local side of the connection.
473  
        The endpoint is cached when the connection is established.
489  
        The endpoint is cached when the connection is established.
474  

490  

475  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
491  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
476  
            the socket is not connected.
492  
            the socket is not connected.
477  

493  

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

501  

486  
    /** Get the remote endpoint of the socket.
502  
    /** Get the remote endpoint of the socket.
487  

503  

488  
        Returns the remote address and port to which the socket is connected.
504  
        Returns the remote address and port to which the socket is connected.
489  
        The endpoint is cached when the connection is established.
505  
        The endpoint is cached when the connection is established.
490  

506  

491  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
507  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
492  
            the socket is not connected.
508  
            the socket is not connected.
493  

509  

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

517  

502  
protected:
518  
protected:
503  
    tcp_socket() noexcept = default;
519  
    tcp_socket() noexcept = default;
504  

520  

505  
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
521  
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
506  

522  

507  
private:
523  
private:
508  
    friend class tcp_acceptor;
524  
    friend class tcp_acceptor;
509  

525  

510  
    inline implementation& get() const noexcept
526  
    inline implementation& get() const noexcept
511  
    {
527  
    {
512  
        return *static_cast<implementation*>(h_.get());
528  
        return *static_cast<implementation*>(h_.get());
513  
    }
529  
    }
514  
};
530  
};
515  

531  

516  
} // namespace boost::corosio
532  
} // namespace boost::corosio
517  

533  

518  
#endif
534  
#endif