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 implementation : io_stream::implementation
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(std::move(other))
210 -
        impl_ = other.impl_;
 
211 -
        other.impl_ = nullptr;
 
212  
    {
210  
    {
213  
    }
211  
    }
214  

212  

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

214  

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

217  

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

219  

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

221  

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

236  

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

239  

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

241  

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

245  

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

249  

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

251  

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

256  

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

258  

262  
        @return `true` if the socket is open and ready for operations.
259  
        @return `true` if the socket is open and ready for operations.
263  
    */
260  
    */
264  
    bool is_open() const noexcept
261  
    bool is_open() const noexcept
265  
    {
262  
    {
266 -
        return impl_ != nullptr;
263 +
#if BOOST_COROSIO_HAS_IOCP
 
264 +
        return h_ && get().native_handle() != ~native_handle_type(0);
 
265 +
#else
 
266 +
        return h_ && get().native_handle() >= 0;
 
267 +
#endif
267  
    }
268  
    }
268  

269  

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

271  

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

274  

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

279  

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

281  

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

290  

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

292  

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

295  

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

308  

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

310  

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

315  

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

317  

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

321  

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

323  

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

328  

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

330  

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

334  

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

340  

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

346  

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

349  

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

354  

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

362  

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

365  

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

369  

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

375  

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

377  

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

381  

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

383  

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

388  

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

390  

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

392  

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

397  

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

399  

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

402  

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

404  

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

409  

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

411  

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

413  

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

418  

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

420  

420  
        @param size The desired receive buffer size in bytes.
421  
        @param size The desired receive buffer size in bytes.
421  

422  

422  
        @throws std::logic_error if the socket is not open.
423  
        @throws std::logic_error if the socket is not open.
423  
        @throws std::system_error on failure.
424  
        @throws std::system_error on failure.
424  

425  

425  
        @note The operating system may adjust the actual buffer size.
426  
        @note The operating system may adjust the actual buffer size.
426  
    */
427  
    */
427  
    void set_receive_buffer_size(int size);
428  
    void set_receive_buffer_size(int size);
428  

429  

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

431  

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

433  

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

438  

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

440  

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

442  

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

445  

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

449  

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

451  

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

453  

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

458  

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

460  

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

462  

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

466  

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

471  

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

473  

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

475  

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

480  

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

482  

482  
        Returns the local address and port to which the socket is bound.
483  
        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.
484  
        For a connected socket, this is the local side of the connection.
484  
        The endpoint is cached when the connection is established.
485  
        The endpoint is cached when the connection is established.
485  

486  

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

489  

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

497  

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

499  

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

502  

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

505  

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

513  

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

516  

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

522  

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

524  

524  
#endif
525  
#endif