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

26  

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

28  

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

35  

36  
namespace boost::corosio {
36  
namespace boost::corosio {
37  

37  

38 -
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
38 +
#if BOOST_COROSIO_HAS_IOCP
39  
using native_handle_type = std::uintptr_t; // SOCKET
39  
using native_handle_type = std::uintptr_t; // SOCKET
40  
#else
40  
#else
41  
using native_handle_type = int;
41  
using native_handle_type = int;
42  
#endif
42  
#endif
43  

43  

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

45  

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

49  

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

53  

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

59  

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

64  

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

70  

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

76  

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

92  

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

101  

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

103  

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

105  

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

107  

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

112  

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

114  

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

124  

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

126  

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

137  

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

140  

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

144  

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

151  

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

157  

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

162  

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

169  

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

177  

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

180  

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

184  

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

186  

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

190  

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

192  

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

194  

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

203  

204  
    /** Move constructor.
204  
    /** Move constructor.
205  

205  

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

207  

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

209  

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

217  

218  
    /** Move assignment operator.
218  
    /** Move assignment operator.
219  

219  

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

221  

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

223  

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

230  

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

242  

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

245  

246  
    /** Open the socket.
246  
    /** Open the socket.
247  

247  

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

253  

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

256  

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

260  

261  
    /** Close the socket.
261  
    /** Close the socket.
262  

262  

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

267  

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

269  

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

280  

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

282  

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

286  

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

291  

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

293  

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

302  

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

305  

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

308  

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

322  

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

324  

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

329  

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

331  

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

335  

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

337  

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

342  

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

344  

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

348  

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

354  

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

360  

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

363  

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

368  

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

376  

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

379  

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

383  

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

385  

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

388  

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

394  

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

396  

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

410  

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

412  

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

414  

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

421  

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

423  

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

441  

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

443  

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

447  

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

450  

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

458  

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

460  

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

463  

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

466  

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

474  

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

477  

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

479  

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

482  

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

485  

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

491  

492  
} // namespace boost::corosio
492  
} // namespace boost::corosio
493  

493  

494  
#endif
494  
#endif