1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_TCP_SOCKET_HPP
10  
#ifndef BOOST_COROSIO_TCP_SOCKET_HPP
11  
#define BOOST_COROSIO_TCP_SOCKET_HPP
11  
#define BOOST_COROSIO_TCP_SOCKET_HPP
12  

12  

13  
#include <boost/corosio/detail/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/platform.hpp>
14  
#include <boost/corosio/detail/platform.hpp>
15  
#include <boost/corosio/detail/except.hpp>
15  
#include <boost/corosio/detail/except.hpp>
16  
#include <boost/corosio/io_stream.hpp>
16  
#include <boost/corosio/io_stream.hpp>
17  
#include <boost/capy/io_result.hpp>
17  
#include <boost/capy/io_result.hpp>
18  
#include <boost/corosio/io_buffer_param.hpp>
18  
#include <boost/corosio/io_buffer_param.hpp>
19  
#include <boost/corosio/endpoint.hpp>
19  
#include <boost/corosio/endpoint.hpp>
20  
#include <boost/capy/ex/executor_ref.hpp>
20  
#include <boost/capy/ex/executor_ref.hpp>
21  
#include <boost/capy/ex/execution_context.hpp>
21  
#include <boost/capy/ex/execution_context.hpp>
22  
#include <boost/capy/ex/io_env.hpp>
22  
#include <boost/capy/ex/io_env.hpp>
23  
#include <boost/capy/concept/executor.hpp>
23  
#include <boost/capy/concept/executor.hpp>
24  

24  

25  
#include <system_error>
25  
#include <system_error>
26  

26  

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

33  

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

35  

36  
#if BOOST_COROSIO_HAS_IOCP
36  
#if BOOST_COROSIO_HAS_IOCP
37  
using native_handle_type = std::uintptr_t;  // SOCKET
37  
using native_handle_type = std::uintptr_t;  // SOCKET
38  
#else
38  
#else
39  
using native_handle_type = int;
39  
using native_handle_type = int;
40  
#endif
40  
#endif
41  

41  

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

43  

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

47  

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

51  

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

57  

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

62  

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

68  

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

74  

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

90  

91  
    /** Options for SO_LINGER socket option. */
91  
    /** Options for SO_LINGER socket option. */
92  
    struct linger_options
92  
    struct linger_options
93  
    {
93  
    {
94  
        bool enabled = false;
94  
        bool enabled = false;
95  
        int timeout = 0;  // seconds
95  
        int timeout = 0;  // seconds
96  
    };
96  
    };
97  

97  

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

106  

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

108  

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

110  

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

112  

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

117  

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

121  

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

124  

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

127  

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

130  

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

133  

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

136  

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

140  

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

147  

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

153  

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

158  

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

165  

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

174  

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

177  

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

181  

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

183  

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

187  

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

189  

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

191  

192  
        @param ex The executor whose context will own the socket.
192  
        @param ex The executor whose context will own the socket.
193  
    */
193  
    */
194  
    template<class Ex>
194  
    template<class Ex>
195  
        requires (!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
195  
        requires (!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
196  
                 capy::Executor<Ex>
196  
                 capy::Executor<Ex>
197  
    explicit tcp_socket(Ex const& ex)
197  
    explicit tcp_socket(Ex const& ex)
198  
        : tcp_socket(ex.context())
198  
        : 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  
    */
207  
    */
208  
    tcp_socket(tcp_socket&& other) noexcept
208  
    tcp_socket(tcp_socket&& other) noexcept
209  
        : io_stream(other.context())
209  
        : io_stream(other.context())
210  
    {
210  
    {
211  
        impl_ = other.impl_;
211  
        impl_ = other.impl_;
212  
        other.impl_ = nullptr;
212  
        other.impl_ = nullptr;
213  
    }
213  
    }
214  

214  

215  
    /** Move assignment operator.
215  
    /** Move assignment operator.
216  

216  

217  
        Closes any existing socket and transfers ownership.
217  
        Closes any existing socket and transfers ownership.
218  
        The source and destination must share the same execution context.
218  
        The source and destination must share the same execution context.
219  

219  

220  
        @param other The socket to move from.
220  
        @param other The socket to move from.
221  

221  

222  
        @return Reference to this socket.
222  
        @return Reference to this socket.
223  

223  

224  
        @throws std::logic_error if the sockets have different execution contexts.
224  
        @throws std::logic_error if the sockets have different execution contexts.
225  
    */
225  
    */
226  
    tcp_socket& operator=(tcp_socket&& other)
226  
    tcp_socket& operator=(tcp_socket&& other)
227  
    {
227  
    {
228  
        if (this != &other)
228  
        if (this != &other)
229  
        {
229  
        {
230  
            if (ctx_ != other.ctx_)
230  
            if (ctx_ != other.ctx_)
231  
                detail::throw_logic_error(
231  
                detail::throw_logic_error(
232  
                    "cannot move socket across execution contexts");
232  
                    "cannot move socket across execution contexts");
233  
            close();
233  
            close();
234  
            impl_ = other.impl_;
234  
            impl_ = other.impl_;
235  
            other.impl_ = nullptr;
235  
            other.impl_ = nullptr;
236  
        }
236  
        }
237  
        return *this;
237  
        return *this;
238  
    }
238  
    }
239  

239  

240  
    tcp_socket(tcp_socket const&) = delete;
240  
    tcp_socket(tcp_socket const&) = delete;
241  
    tcp_socket& operator=(tcp_socket const&) = delete;
241  
    tcp_socket& operator=(tcp_socket const&) = delete;
242  

242  

243  
    /** Open the socket.
243  
    /** Open the socket.
244  

244  

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

248  

249  
        @throws std::system_error on failure.
249  
        @throws std::system_error on failure.
250  
    */
250  
    */
251  
    void open();
251  
    void open();
252  

252  

253  
    /** Close the socket.
253  
    /** Close the socket.
254  

254  

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

259  

260  
    /** Check if the socket is open.
260  
    /** Check if the socket is open.
261  

261  

262  
        @return `true` if the socket is open and ready for operations.
262  
        @return `true` if the socket is open and ready for operations.
263  
    */
263  
    */
264  
    bool is_open() const noexcept
264  
    bool is_open() const noexcept
265  
    {
265  
    {
266  
        return impl_ != nullptr;
266  
        return impl_ != nullptr;
267  
    }
267  
    }
268  

268  

269  
    /** Initiate an asynchronous connect operation.
269  
    /** Initiate an asynchronous connect operation.
270  

270  

271  
        Connects the socket to the specified remote endpoint. The socket
271  
        Connects the socket to the specified remote endpoint. The socket
272  
        must be open before calling this function.
272  
        must be open before calling this function.
273  

273  

274  
        The operation supports cancellation via `std::stop_token` through
274  
        The operation supports cancellation via `std::stop_token` through
275  
        the affine awaitable protocol. If the associated stop token is
275  
        the affine awaitable protocol. If the associated stop token is
276  
        triggered, the operation completes immediately with
276  
        triggered, the operation completes immediately with
277  
        `errc::operation_canceled`.
277  
        `errc::operation_canceled`.
278  

278  

279  
        @param ep The remote endpoint to connect to.
279  
        @param ep The remote endpoint to connect to.
280  

280  

281  
        @return An awaitable that completes with `io_result<>`.
281  
        @return An awaitable that completes with `io_result<>`.
282  
            Returns success (default error_code) on successful connection,
282  
            Returns success (default error_code) on successful connection,
283  
            or an error code on failure including:
283  
            or an error code on failure including:
284  
            - connection_refused: No server listening at endpoint
284  
            - connection_refused: No server listening at endpoint
285  
            - timed_out: Connection attempt timed out
285  
            - timed_out: Connection attempt timed out
286  
            - network_unreachable: No route to host
286  
            - network_unreachable: No route to host
287  
            - operation_canceled: Cancelled via stop_token or cancel().
287  
            - operation_canceled: Cancelled via stop_token or cancel().
288  
                Check `ec == cond::canceled` for portable comparison.
288  
                Check `ec == cond::canceled` for portable comparison.
289  

289  

290  
        @throws std::logic_error if the socket is not open.
290  
        @throws std::logic_error if the socket is not open.
291  

291  

292  
        @par Preconditions
292  
        @par Preconditions
293  
        The socket must be open (`is_open() == true`).
293  
        The socket must be open (`is_open() == true`).
294  

294  

295  
        @par Example
295  
        @par Example
296  
        @code
296  
        @code
297  
        auto [ec] = co_await s.connect(endpoint);
297  
        auto [ec] = co_await s.connect(endpoint);
298  
        if (ec) { ... }
298  
        if (ec) { ... }
299  
        @endcode
299  
        @endcode
300  
    */
300  
    */
301  
    auto connect(endpoint ep)
301  
    auto connect(endpoint ep)
302  
    {
302  
    {
303  
        if (!impl_)
303  
        if (!impl_)
304  
            detail::throw_logic_error("connect: socket not open");
304  
            detail::throw_logic_error("connect: socket not open");
305  
        return connect_awaitable(*this, ep);
305  
        return connect_awaitable(*this, ep);
306  
    }
306  
    }
307  

307  

308  
    /** Cancel any pending asynchronous operations.
308  
    /** Cancel any pending asynchronous operations.
309  

309  

310  
        All outstanding operations complete with `errc::operation_canceled`.
310  
        All outstanding operations complete with `errc::operation_canceled`.
311  
        Check `ec == cond::canceled` for portable comparison.
311  
        Check `ec == cond::canceled` for portable comparison.
312  
    */
312  
    */
313  
    void cancel();
313  
    void cancel();
314  

314  

315  
    /** Get the native socket handle.
315  
    /** Get the native socket handle.
316  

316  

317  
        Returns the underlying platform-specific socket descriptor.
317  
        Returns the underlying platform-specific socket descriptor.
318  
        On POSIX systems this is an `int` file descriptor.
318  
        On POSIX systems this is an `int` file descriptor.
319  
        On Windows this is a `SOCKET` handle.
319  
        On Windows this is a `SOCKET` handle.
320  

320  

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

322  

323  
        @par Preconditions
323  
        @par Preconditions
324  
        None. May be called on closed sockets.
324  
        None. May be called on closed sockets.
325  
    */
325  
    */
326  
    native_handle_type native_handle() const noexcept;
326  
    native_handle_type native_handle() const noexcept;
327  

327  

328  
    /** Disable sends or receives on the socket.
328  
    /** Disable sends or receives on the socket.
329  

329  

330  
        TCP connections are full-duplex: each direction (send and receive)
330  
        TCP connections are full-duplex: each direction (send and receive)
331  
        operates independently. This function allows you to close one or
331  
        operates independently. This function allows you to close one or
332  
        both directions without destroying the socket.
332  
        both directions without destroying the socket.
333  

333  

334  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
334  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
335  
            signaling that you have no more data to send. You can still
335  
            signaling that you have no more data to send. You can still
336  
            receive data until the peer also closes their send direction.
336  
            receive data until the peer also closes their send direction.
337  
            This is the most common use case, typically called before
337  
            This is the most common use case, typically called before
338  
            close() to ensure graceful connection termination.
338  
            close() to ensure graceful connection termination.
339  

339  

340  
        @li @ref shutdown_receive disables reading on the socket. This
340  
        @li @ref shutdown_receive disables reading on the socket. This
341  
            does NOT send anything to the peer - they are not informed
341  
            does NOT send anything to the peer - they are not informed
342  
            and may continue sending data. Subsequent reads will fail
342  
            and may continue sending data. Subsequent reads will fail
343  
            or return end-of-file. Incoming data may be discarded or
343  
            or return end-of-file. Incoming data may be discarded or
344  
            buffered depending on the operating system.
344  
            buffered depending on the operating system.
345  

345  

346  
        @li @ref shutdown_both combines both effects: sends a FIN and
346  
        @li @ref shutdown_both combines both effects: sends a FIN and
347  
            disables reading.
347  
            disables reading.
348  

348  

349  
        When the peer shuts down their send direction (sends a FIN),
349  
        When the peer shuts down their send direction (sends a FIN),
350  
        subsequent read operations will complete with `capy::cond::eof`.
350  
        subsequent read operations will complete with `capy::cond::eof`.
351  
        Use the portable condition test rather than comparing error
351  
        Use the portable condition test rather than comparing error
352  
        codes directly:
352  
        codes directly:
353  

353  

354  
        @code
354  
        @code
355  
        auto [ec, n] = co_await sock.read_some(buffer);
355  
        auto [ec, n] = co_await sock.read_some(buffer);
356  
        if (ec == capy::cond::eof)
356  
        if (ec == capy::cond::eof)
357  
        {
357  
        {
358  
            // Peer closed their send direction
358  
            // Peer closed their send direction
359  
        }
359  
        }
360  
        @endcode
360  
        @endcode
361  

361  

362  
        Any error from the underlying system call is silently discarded
362  
        Any error from the underlying system call is silently discarded
363  
        because it is unlikely to be helpful.
363  
        because it is unlikely to be helpful.
364  

364  

365  
        @param what Determines what operations will no longer be allowed.
365  
        @param what Determines what operations will no longer be allowed.
366  
    */
366  
    */
367  
    void shutdown(shutdown_type what);
367  
    void shutdown(shutdown_type what);
368  

368  

369  
    //--------------------------------------------------------------------------
369  
    //--------------------------------------------------------------------------
370  
    //
370  
    //
371  
    // Socket Options
371  
    // Socket Options
372  
    //
372  
    //
373  
    //--------------------------------------------------------------------------
373  
    //--------------------------------------------------------------------------
374  

374  

375  
    /** Enable or disable TCP_NODELAY (disable Nagle's algorithm).
375  
    /** Enable or disable TCP_NODELAY (disable Nagle's algorithm).
376  

376  

377  
        When enabled, segments are sent as soon as possible even if
377  
        When enabled, segments are sent as soon as possible even if
378  
        there is only a small amount of data. This reduces latency
378  
        there is only a small amount of data. This reduces latency
379  
        at the potential cost of increased network traffic.
379  
        at the potential cost of increased network traffic.
380  

380  

381  
        @param value `true` to disable Nagle's algorithm (enable no-delay).
381  
        @param value `true` to disable Nagle's algorithm (enable no-delay).
382  

382  

383  
        @throws std::logic_error if the socket is not open.
383  
        @throws std::logic_error if the socket is not open.
384  
        @throws std::system_error on failure.
384  
        @throws std::system_error on failure.
385  
    */
385  
    */
386  
    void set_no_delay(bool value);
386  
    void set_no_delay(bool value);
387  

387  

388  
    /** Get the current TCP_NODELAY setting.
388  
    /** Get the current TCP_NODELAY setting.
389  

389  

390  
        @return `true` if Nagle's algorithm is disabled.
390  
        @return `true` if Nagle's algorithm is disabled.
391  

391  

392  
        @throws std::logic_error if the socket is not open.
392  
        @throws std::logic_error if the socket is not open.
393  
        @throws std::system_error on failure.
393  
        @throws std::system_error on failure.
394  
    */
394  
    */
395  
    bool no_delay() const;
395  
    bool no_delay() const;
396  

396  

397  
    /** Enable or disable SO_KEEPALIVE.
397  
    /** Enable or disable SO_KEEPALIVE.
398  

398  

399  
        When enabled, the socket will periodically send keepalive probes
399  
        When enabled, the socket will periodically send keepalive probes
400  
        to detect if the peer is still reachable.
400  
        to detect if the peer is still reachable.
401  

401  

402  
        @param value `true` to enable keepalive probes.
402  
        @param value `true` to enable keepalive probes.
403  

403  

404  
        @throws std::logic_error if the socket is not open.
404  
        @throws std::logic_error if the socket is not open.
405  
        @throws std::system_error on failure.
405  
        @throws std::system_error on failure.
406  
    */
406  
    */
407  
    void set_keep_alive(bool value);
407  
    void set_keep_alive(bool value);
408  

408  

409  
    /** Get the current SO_KEEPALIVE setting.
409  
    /** Get the current SO_KEEPALIVE setting.
410  

410  

411  
        @return `true` if keepalive is enabled.
411  
        @return `true` if keepalive is enabled.
412  

412  

413  
        @throws std::logic_error if the socket is not open.
413  
        @throws std::logic_error if the socket is not open.
414  
        @throws std::system_error on failure.
414  
        @throws std::system_error on failure.
415  
    */
415  
    */
416  
    bool keep_alive() const;
416  
    bool keep_alive() const;
417  

417  

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

419  

420  
        @param size The desired receive buffer size in bytes.
420  
        @param size The desired receive buffer size in bytes.
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  
        @note The operating system may adjust the actual buffer size.
425  
        @note The operating system may adjust the actual buffer size.
426  
    */
426  
    */
427  
    void set_receive_buffer_size(int size);
427  
    void set_receive_buffer_size(int size);
428  

428  

429  
    /** Get the receive buffer size (SO_RCVBUF).
429  
    /** Get the receive buffer size (SO_RCVBUF).
430  

430  

431  
        @return The current receive buffer size in bytes.
431  
        @return The current receive buffer size in bytes.
432  

432  

433  
        @throws std::logic_error if the socket is not open.
433  
        @throws std::logic_error if the socket is not open.
434  
        @throws std::system_error on failure.
434  
        @throws std::system_error on failure.
435  
    */
435  
    */
436  
    int receive_buffer_size() const;
436  
    int receive_buffer_size() const;
437  

437  

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

439  

440  
        @param size The desired send buffer size in bytes.
440  
        @param size The desired send buffer size in bytes.
441  

441  

442  
        @throws std::logic_error if the socket is not open.
442  
        @throws std::logic_error if the socket is not open.
443  
        @throws std::system_error on failure.
443  
        @throws std::system_error on failure.
444  

444  

445  
        @note The operating system may adjust the actual buffer size.
445  
        @note The operating system may adjust the actual buffer size.
446  
    */
446  
    */
447  
    void set_send_buffer_size(int size);
447  
    void set_send_buffer_size(int size);
448  

448  

449  
    /** Get the send buffer size (SO_SNDBUF).
449  
    /** Get the send buffer size (SO_SNDBUF).
450  

450  

451  
        @return The current send buffer size in bytes.
451  
        @return The current send buffer size in bytes.
452  

452  

453  
        @throws std::logic_error if the socket is not open.
453  
        @throws std::logic_error if the socket is not open.
454  
        @throws std::system_error on failure.
454  
        @throws std::system_error on failure.
455  
    */
455  
    */
456  
    int send_buffer_size() const;
456  
    int send_buffer_size() const;
457  

457  

458  
    /** Set the SO_LINGER option.
458  
    /** Set the SO_LINGER option.
459  

459  

460  
        Controls behavior when closing a socket with unsent data.
460  
        Controls behavior when closing a socket with unsent data.
461  

461  

462  
        @param enabled If `true`, close() will block until data is sent
462  
        @param enabled If `true`, close() will block until data is sent
463  
            or the timeout expires. If `false`, close() returns immediately.
463  
            or the timeout expires. If `false`, close() returns immediately.
464  
        @param timeout The linger timeout in seconds (only used if enabled).
464  
        @param timeout The linger timeout in seconds (only used if enabled).
465  

465  

466  
        @throws std::logic_error if the socket is not open.
466  
        @throws std::logic_error if the socket is not open.
467  
        @throws std::system_error on failure.
467  
        @throws std::system_error on failure.
468  
    */
468  
    */
469  
    void set_linger(bool enabled, int timeout);
469  
    void set_linger(bool enabled, int timeout);
470  

470  

471  
    /** Get the current SO_LINGER setting.
471  
    /** Get the current SO_LINGER setting.
472  

472  

473  
        @return The current linger options.
473  
        @return The current linger options.
474  

474  

475  
        @throws std::logic_error if the socket is not open.
475  
        @throws std::logic_error if the socket is not open.
476  
        @throws std::system_error on failure.
476  
        @throws std::system_error on failure.
477  
    */
477  
    */
478  
    linger_options linger() const;
478  
    linger_options linger() const;
479  

479  

480  
    /** Get the local endpoint of the socket.
480  
    /** Get the local endpoint of the socket.
481  

481  

482  
        Returns the local address and port to which the socket is bound.
482  
        Returns the local address and port to which the socket is bound.
483  
        For a connected socket, this is the local side of the connection.
483  
        For a connected socket, this is the local side of the connection.
484  
        The endpoint is cached when the connection is established.
484  
        The endpoint is cached when the connection is established.
485  

485  

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

488  

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

496  

497  
    /** Get the remote endpoint of the socket.
497  
    /** Get the remote endpoint of the socket.
498  

498  

499  
        Returns the remote address and port to which the socket is connected.
499  
        Returns the remote address and port to which the socket is connected.
500  
        The endpoint is cached when the connection is established.
500  
        The endpoint is cached when the connection is established.
501  

501  

502  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
502  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
503  
            the socket is not connected.
503  
            the socket is not connected.
504  

504  

505  
        @par Thread Safety
505  
        @par Thread Safety
506  
        The cached endpoint value is set during connect/accept completion
506  
        The cached endpoint value is set during connect/accept completion
507  
        and cleared during close(). This function may be called concurrently
507  
        and cleared during close(). This function may be called concurrently
508  
        with I/O operations, but must not be called concurrently with
508  
        with I/O operations, but must not be called concurrently with
509  
        connect(), accept(), or close().
509  
        connect(), accept(), or close().
510  
    */
510  
    */
511  
    endpoint remote_endpoint() const noexcept;
511  
    endpoint remote_endpoint() const noexcept;
512  

512  

513  
private:
513  
private:
514  
    friend class tcp_acceptor;
514  
    friend class tcp_acceptor;
515  

515  

516  
    inline socket_impl& get() const noexcept
516  
    inline socket_impl& get() const noexcept
517  
    {
517  
    {
518  
        return *static_cast<socket_impl*>(impl_);
518  
        return *static_cast<socket_impl*>(impl_);
519  
    }
519  
    }
520  
};
520  
};
521  

521  

522  
} // namespace boost::corosio
522  
} // namespace boost::corosio
523  

523  

524  
#endif
524  
#endif