100.00% Lines (50/50) 100.00% Functions (14/14)
TLA Baseline Branch
Line Hits Code Line Hits Code
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   // Copyright (c) 2026 Michael Vandeberg 4   // Copyright (c) 2026 Michael Vandeberg
5   // 5   //
6   // Distributed under the Boost Software License, Version 1.0. (See accompanying 6   // Distributed under the Boost Software License, Version 1.0. (See accompanying
7   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8   // 8   //
9   // Official repository: https://github.com/cppalliance/corosio 9   // Official repository: https://github.com/cppalliance/corosio
10   // 10   //
11   11  
12   #ifndef BOOST_COROSIO_TCP_SOCKET_HPP 12   #ifndef BOOST_COROSIO_TCP_SOCKET_HPP
13   #define BOOST_COROSIO_TCP_SOCKET_HPP 13   #define BOOST_COROSIO_TCP_SOCKET_HPP
14   14  
15   #include <boost/corosio/family.hpp> 15   #include <boost/corosio/family.hpp>
16   #include <boost/corosio/detail/config.hpp> 16   #include <boost/corosio/detail/config.hpp>
17   #include <boost/corosio/detail/platform.hpp> 17   #include <boost/corosio/detail/platform.hpp>
18   #include <boost/corosio/detail/except.hpp> 18   #include <boost/corosio/detail/except.hpp>
19   #include <boost/corosio/detail/native_handle.hpp> 19   #include <boost/corosio/detail/native_handle.hpp>
20   #include <boost/corosio/detail/op_base.hpp> 20   #include <boost/corosio/detail/op_base.hpp>
21   #include <boost/corosio/io/io_stream.hpp> 21   #include <boost/corosio/io/io_stream.hpp>
22   #include <boost/capy/io_result.hpp> 22   #include <boost/capy/io_result.hpp>
23   #include <boost/corosio/detail/buffer_param.hpp> 23   #include <boost/corosio/detail/buffer_param.hpp>
24   #include <boost/corosio/endpoint.hpp> 24   #include <boost/corosio/endpoint.hpp>
25   #include <boost/corosio/shutdown_type.hpp> 25   #include <boost/corosio/shutdown_type.hpp>
26   #include <boost/corosio/wait_type.hpp> 26   #include <boost/corosio/wait_type.hpp>
27   #include <boost/capy/ex/executor_ref.hpp> 27   #include <boost/capy/ex/executor_ref.hpp>
28   #include <boost/capy/ex/execution_context.hpp> 28   #include <boost/capy/ex/execution_context.hpp>
29   #include <boost/capy/ex/io_env.hpp> 29   #include <boost/capy/ex/io_env.hpp>
30   #include <boost/capy/concept/executor.hpp> 30   #include <boost/capy/concept/executor.hpp>
31   31  
32   #include <system_error> 32   #include <system_error>
33   33  
34   #include <concepts> 34   #include <concepts>
35   #include <coroutine> 35   #include <coroutine>
36   #include <cstddef> 36   #include <cstddef>
37   #include <stop_token> 37   #include <stop_token>
38   #include <type_traits> 38   #include <type_traits>
39   39  
40   namespace boost::corosio { 40   namespace boost::corosio {
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   @par !example connect_and_read 64   @par !example connect_and_read
65   */ 65   */
66   class BOOST_COROSIO_DECL tcp_socket : public io_stream 66   class BOOST_COROSIO_DECL tcp_socket : public io_stream
67   { 67   {
68   public: 68   public:
69   /// The endpoint type used by this socket. 69   /// The endpoint type used by this socket.
70   using endpoint_type = corosio::endpoint; 70   using endpoint_type = corosio::endpoint;
71   71  
72   using shutdown_type = corosio::shutdown_type; 72   using shutdown_type = corosio::shutdown_type;
73   using enum corosio::shutdown_type; 73   using enum corosio::shutdown_type;
74   74  
75   /** Define backend hooks for TCP socket operations. 75   /** Define backend hooks for TCP socket operations.
76   76  
77   Platform backends (epoll, IOCP, kqueue, select) derive from 77   Platform backends (epoll, IOCP, kqueue, select) derive from
78   this to implement socket I/O, connection, and option management. 78   this to implement socket I/O, connection, and option management.
79   */ 79   */
80   struct implementation : io_stream::implementation 80   struct implementation : io_stream::implementation
81   { 81   {
82   /** Initiate an asynchronous connect to the given endpoint. 82   /** Initiate an asynchronous connect to the given endpoint.
83   83  
84   @param h Coroutine handle to resume on completion. 84   @param h Coroutine handle to resume on completion.
85   @param ex Executor for dispatching the completion. 85   @param ex Executor for dispatching the completion.
86   @param ep The remote endpoint to connect to. 86   @param ep The remote endpoint to connect to.
87   @param token Stop token for cancellation. 87   @param token Stop token for cancellation.
88   @param ec Output error code. 88   @param ec Output error code.
89   89  
90   @return Coroutine handle to resume immediately. 90   @return Coroutine handle to resume immediately.
91   */ 91   */
92   virtual std::coroutine_handle<> connect( 92   virtual std::coroutine_handle<> connect(
93   std::coroutine_handle<> h, 93   std::coroutine_handle<> h,
94   capy::executor_ref ex, 94   capy::executor_ref ex,
95   endpoint ep, 95   endpoint ep,
96   std::stop_token token, 96   std::stop_token token,
97   std::error_code* ec) = 0; 97   std::error_code* ec) = 0;
98   98  
99   /** Initiate an asynchronous wait for socket readiness. 99   /** Initiate an asynchronous wait for socket readiness.
100   100  
101   Completes when the socket becomes ready for the 101   Completes when the socket becomes ready for the
102   specified direction, or an error condition is 102   specified direction, or an error condition is
103   reported. No bytes are transferred. 103   reported. No bytes are transferred.
104   104  
105   @param h Coroutine handle to resume on completion. 105   @param h Coroutine handle to resume on completion.
106   @param ex Executor for dispatching the completion. 106   @param ex Executor for dispatching the completion.
107   @param w The direction to wait on. 107   @param w The direction to wait on.
108   @param token Stop token for cancellation. 108   @param token Stop token for cancellation.
109   @param ec Output error code. 109   @param ec Output error code.
110   110  
111   @return Coroutine handle to resume immediately. 111   @return Coroutine handle to resume immediately.
112   */ 112   */
113   virtual std::coroutine_handle<> wait( 113   virtual std::coroutine_handle<> wait(
114   std::coroutine_handle<> h, 114   std::coroutine_handle<> h,
115   capy::executor_ref ex, 115   capy::executor_ref ex,
116   wait_type w, 116   wait_type w,
117   std::stop_token token, 117   std::stop_token token,
118   std::error_code* ec) = 0; 118   std::error_code* ec) = 0;
119   119  
120   /** Shut down the socket for the given direction(s). 120   /** Shut down the socket for the given direction(s).
121   121  
122   @param what The shutdown direction. 122   @param what The shutdown direction.
123   123  
124   @return Error code on failure, empty on success. 124   @return Error code on failure, empty on success.
125   */ 125   */
126   virtual std::error_code shutdown(shutdown_type what) noexcept = 0; 126   virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
127   127  
128   /// Return the platform socket descriptor. 128   /// Return the platform socket descriptor.
129   virtual native_handle_type native_handle() const noexcept = 0; 129   virtual native_handle_type native_handle() const noexcept = 0;
130   130  
131   /** Return the socket's address family. 131   /** Return the socket's address family.
132   132  
133   Socket options render for this family. 133   Socket options render for this family.
134   134  
135   @return The socket's address family. 135   @return The socket's address family.
136   */ 136   */
137   virtual corosio::family family() const noexcept = 0; 137   virtual corosio::family family() const noexcept = 0;
138   138  
139   /** Release ownership of the native socket handle. 139   /** Release ownership of the native socket handle.
140   140  
141   Deregisters the socket from the backend and cancels 141   Deregisters the socket from the backend and cancels
142   pending operations without closing the descriptor. The 142   pending operations without closing the descriptor. The
143   caller takes ownership. 143   caller takes ownership.
144   144  
145   @return The native handle. 145   @return The native handle.
146   */ 146   */
147   virtual native_handle_type release_socket() noexcept = 0; 147   virtual native_handle_type release_socket() noexcept = 0;
148   148  
149   /** Request cancellation of pending asynchronous operations. 149   /** Request cancellation of pending asynchronous operations.
150   150  
151   Operations still in flight complete with `operation_canceled`; an 151   Operations still in flight complete with `operation_canceled`; an
152   operation whose result is already decided reports that result. 152   operation whose result is already decided reports that result.
153   Check `ec == cond::canceled` for portable comparison. 153   Check `ec == cond::canceled` for portable comparison.
154   */ 154   */
155   virtual void cancel() noexcept = 0; 155   virtual void cancel() noexcept = 0;
156   156  
157   /** Set a socket option. 157   /** Set a socket option.
158   158  
159   @param level The protocol level (e.g. `SOL_SOCKET`). 159   @param level The protocol level (e.g. `SOL_SOCKET`).
160   @param optname The option name (e.g. `SO_KEEPALIVE`). 160   @param optname The option name (e.g. `SO_KEEPALIVE`).
161   @param data Pointer to the option value. 161   @param data Pointer to the option value.
162   @param size Size of the option value in bytes. 162   @param size Size of the option value in bytes.
163   @return Error code on failure, empty on success. 163   @return Error code on failure, empty on success.
164   */ 164   */
165   virtual std::error_code set_option( 165   virtual std::error_code set_option(
166   int level, 166   int level,
167   int optname, 167   int optname,
168   void const* data, 168   void const* data,
169   std::size_t size) noexcept = 0; 169   std::size_t size) noexcept = 0;
170   170  
171   /** Get a socket option. 171   /** Get a socket option.
172   172  
173   @param level The protocol level (e.g. `SOL_SOCKET`). 173   @param level The protocol level (e.g. `SOL_SOCKET`).
174   @param optname The option name (e.g. `SO_KEEPALIVE`). 174   @param optname The option name (e.g. `SO_KEEPALIVE`).
175   @param data Pointer to receive the option value. 175   @param data Pointer to receive the option value.
176   @param size On entry, the size of the buffer. On exit, 176   @param size On entry, the size of the buffer. On exit,
177   the size of the option value. 177   the size of the option value.
178   @return Error code on failure, empty on success. 178   @return Error code on failure, empty on success.
179   */ 179   */
180   virtual std::error_code 180   virtual std::error_code
181   get_option(int level, int optname, void* data, std::size_t* size) 181   get_option(int level, int optname, void* data, std::size_t* size)
182   const noexcept = 0; 182   const noexcept = 0;
183   183  
184   /// Return the cached local endpoint. 184   /// Return the cached local endpoint.
185   virtual endpoint local_endpoint() const noexcept = 0; 185   virtual endpoint local_endpoint() const noexcept = 0;
186   186  
187   /// Return the cached remote endpoint. 187   /// Return the cached remote endpoint.
188   virtual endpoint remote_endpoint() const noexcept = 0; 188   virtual endpoint remote_endpoint() const noexcept = 0;
189   }; 189   };
190   190  
191   /// Represent the awaitable returned by @ref connect. 191   /// Represent the awaitable returned by @ref connect.
192   struct connect_awaitable : detail::void_op_base<connect_awaitable> 192   struct connect_awaitable : detail::void_op_base<connect_awaitable>
193   { 193   {
194   tcp_socket& s_; 194   tcp_socket& s_;
195   endpoint endpoint_; 195   endpoint endpoint_;
196   196  
HITCBC 197   4471 connect_awaitable(tcp_socket& s, endpoint ep) noexcept 197   4485 connect_awaitable(tcp_socket& s, endpoint ep) noexcept
HITCBC 198   8942 : s_(s) 198   8970 : s_(s)
HITCBC 199   4471 , endpoint_(ep) 199   4485 , endpoint_(ep)
200   { 200   {
HITCBC 201   4471 } 201   4485 }
202   202  
203   std::coroutine_handle<> 203   std::coroutine_handle<>
HITCBC 204   4468 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 204   4482 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
205   { 205   {
HITCBC 206   4468 return s_.get().connect(h, ex, endpoint_, token_, &ec_); 206   4482 return s_.get().connect(h, ex, endpoint_, token_, &ec_);
207   } 207   }
208   }; 208   };
209   209  
210   /// Represent the awaitable returned by @ref wait. 210   /// Represent the awaitable returned by @ref wait.
211   struct wait_awaitable : detail::void_op_base<wait_awaitable> 211   struct wait_awaitable : detail::void_op_base<wait_awaitable>
212   { 212   {
213   tcp_socket& s_; 213   tcp_socket& s_;
214   wait_type w_; 214   wait_type w_;
215   215  
HITCBC 216   68 wait_awaitable(tcp_socket& s, wait_type w) noexcept : s_(s), w_(w) {} 216   68 wait_awaitable(tcp_socket& s, wait_type w) noexcept : s_(s), w_(w) {}
217   217  
218   std::coroutine_handle<> 218   std::coroutine_handle<>
HITCBC 219   64 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const 219   64 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
220   { 220   {
HITCBC 221   64 return s_.get().wait(h, ex, w_, token_, &ec_); 221   64 return s_.get().wait(h, ex, w_, token_, &ec_);
222   } 222   }
223   }; 223   };
224   224  
225   public: 225   public:
226   /** Destructor. 226   /** Destructor.
227   227  
228   Closes the socket if open, cancelling any pending operations. 228   Closes the socket if open, cancelling any pending operations.
229   */ 229   */
230   ~tcp_socket() override; 230   ~tcp_socket() override;
231   231  
232   /** Construct a socket from an execution context. 232   /** Construct a socket from an execution context.
233   233  
234   @param ctx The execution context that will own this socket. 234   @param ctx The execution context that will own this socket.
235   */ 235   */
236   explicit tcp_socket(capy::execution_context& ctx); 236   explicit tcp_socket(capy::execution_context& ctx);
237   237  
238   /** Construct a socket from an executor. 238   /** Construct a socket from an executor.
239   239  
240   The socket is associated with the executor's context. 240   The socket is associated with the executor's context.
241   241  
242   @param ex The executor whose context will own the socket. 242   @param ex The executor whose context will own the socket.
243   */ 243   */
244   template<class Ex> 244   template<class Ex>
245   requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) && 245   requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
246   capy::Executor<Ex> 246   capy::Executor<Ex>
HITCBC 247   1 explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context()) 247   1 explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context())
248   { 248   {
HITCBC 249   1 } 249   1 }
250   250  
251   /** Move constructor. 251   /** Move constructor.
252   252  
253   Transfers ownership of the socket resources. 253   Transfers ownership of the socket resources.
254   254  
255   @param other The socket to move from. 255   @param other The socket to move from.
256   256  
257   @pre No awaitables returned by @p other's methods exist. 257   @pre No awaitables returned by @p other's methods exist.
258   @pre @p other is not referenced as a peer in any outstanding 258   @pre @p other is not referenced as a peer in any outstanding
259   accept awaitable. 259   accept awaitable.
260   @pre The execution context associated with @p other must 260   @pre The execution context associated with @p other must
261   outlive this socket. 261   outlive this socket.
262   */ 262   */
HITCBC 263   701 tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {} 263   701 tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {}
264   264  
265   /** Move assignment operator. 265   /** Move assignment operator.
266   266  
267   Closes any existing socket and transfers ownership. 267   Closes any existing socket and transfers ownership.
268   268  
269   @param other The socket to move from. 269   @param other The socket to move from.
270   270  
271   @pre No awaitables returned by either `*this` or @p other's 271   @pre No awaitables returned by either `*this` or @p other's
272   methods exist. 272   methods exist.
273   @pre Neither `*this` nor @p other is referenced as a peer in 273   @pre Neither `*this` nor @p other is referenced as a peer in
274   any outstanding accept awaitable. 274   any outstanding accept awaitable.
275   @pre The execution context associated with @p other must 275   @pre The execution context associated with @p other must
276   outlive this socket. 276   outlive this socket.
277   277  
278   @return Reference to this socket. 278   @return Reference to this socket.
279   */ 279   */
HITCBC 280   25 tcp_socket& operator=(tcp_socket&& other) noexcept 280   25 tcp_socket& operator=(tcp_socket&& other) noexcept
281   { 281   {
HITCBC 282   25 if (this != &other) 282   25 if (this != &other)
283   { 283   {
HITCBC 284   25 close(); 284   25 close();
HITCBC 285   25 h_ = std::move(other.h_); 285   25 h_ = std::move(other.h_);
286   } 286   }
HITCBC 287   25 return *this; 287   25 return *this;
288   } 288   }
289   289  
290   tcp_socket(tcp_socket const&) = delete; 290   tcp_socket(tcp_socket const&) = delete;
291   tcp_socket& operator=(tcp_socket const&) = delete; 291   tcp_socket& operator=(tcp_socket const&) = delete;
292   292  
293   /** Open the socket. 293   /** Open the socket.
294   294  
295   Creates a TCP socket and associates it with the platform 295   Creates a TCP socket and associates it with the platform
296   reactor (IOCP on Windows). Calling @ref connect on a closed 296   reactor (IOCP on Windows). Calling @ref connect on a closed
297   socket opens it automatically with the endpoint's address family, 297   socket opens it automatically with the endpoint's address family,
298   so explicit `open()` is only needed when socket options must be 298   so explicit `open()` is only needed when socket options must be
299   set before connecting. 299   set before connecting.
300   300  
301   Failures such as descriptor exhaustion are normal runtime 301   Failures such as descriptor exhaustion are normal runtime
302   conditions and are reported through the returned error code. 302   conditions and are reported through the returned error code.
303   Opening an already-open socket is a no-op that reports 303   Opening an already-open socket is a no-op that reports
304   success. 304   success.
305   305  
306   @param f The address family (IPv4 or IPv6). Defaults to 306   @param f The address family (IPv4 or IPv6). Defaults to
307   `family::v4`. 307   `family::v4`.
308   308  
309   @return The error code, empty on success. 309   @return The error code, empty on success.
310   */ 310   */
311   [[nodiscard]] std::error_code open(family f = family::v4) noexcept; 311   [[nodiscard]] std::error_code open(family f = family::v4) noexcept;
312   312  
313   /** Bind the socket to a local endpoint. 313   /** Bind the socket to a local endpoint.
314   314  
315   Associates the socket with a local address and port before 315   Associates the socket with a local address and port before
316   connecting. Useful for multi-homed hosts or source-port 316   connecting. Useful for multi-homed hosts or source-port
317   pinning. 317   pinning.
318   318  
319   @param ep The local endpoint to bind to. 319   @param ep The local endpoint to bind to.
320   320  
321   @return An error code indicating success or the reason for 321   @return An error code indicating success or the reason for
322   failure. 322   failure.
323   323  
324   @par Error Conditions 324   @par Error Conditions
325   @li `errc::address_in_use`: The endpoint is already in use. 325   @li `errc::address_in_use`: The endpoint is already in use.
326   @li `errc::address_not_available`: The address is not 326   @li `errc::address_not_available`: The address is not
327   available on any local interface. 327   available on any local interface.
328   @li `errc::permission_denied`: Insufficient privileges to 328   @li `errc::permission_denied`: Insufficient privileges to
329   bind to the endpoint (e.g., privileged port). 329   bind to the endpoint (e.g., privileged port).
330   330  
331   A closed socket reports `errc::bad_file_descriptor`. 331   A closed socket reports `errc::bad_file_descriptor`.
332   */ 332   */
333   [[nodiscard]] std::error_code bind(endpoint ep) noexcept; 333   [[nodiscard]] std::error_code bind(endpoint ep) noexcept;
334   334  
335   /** Close the socket. 335   /** Close the socket.
336   336  
337   Releases socket resources. Any pending operations complete 337   Releases socket resources. Any pending operations complete
338   with `errc::operation_canceled`. 338   with `errc::operation_canceled`.
339   */ 339   */
340   void close() noexcept; 340   void close() noexcept;
341   341  
342   /** Check if the socket is open. 342   /** Check if the socket is open.
343   343  
344   @return `true` if the socket is open and ready for operations. 344   @return `true` if the socket is open and ready for operations.
345   */ 345   */
HITCBC 346   28572 bool is_open() const noexcept 346   28659 bool is_open() const noexcept
347   { 347   {
348   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS) 348   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
349   return h_ && get().native_handle() != ~native_handle_type(0); 349   return h_ && get().native_handle() != ~native_handle_type(0);
350   #else 350   #else
HITCBC 351   28572 return h_ && get().native_handle() >= 0; 351   28659 return h_ && get().native_handle() >= 0;
352   #endif 352   #endif
353   } 353   }
354   354  
355   /** Initiate an asynchronous connect operation. 355   /** Initiate an asynchronous connect operation.
356   356  
357   If the socket is not already open, it is opened automatically 357   If the socket is not already open, it is opened automatically
358   using the address family of @p ep (IPv4 or IPv6). If the socket 358   using the address family of @p ep (IPv4 or IPv6). If the socket
359   is already open, the existing file descriptor is used as-is. 359   is already open, the existing file descriptor is used as-is.
360   360  
361   The operation supports cancellation via `std::stop_token` through 361   The operation supports cancellation via `std::stop_token` through
362   the affine awaitable protocol. If the associated stop token is 362   the affine awaitable protocol. If the associated stop token is
363   triggered, the operation completes immediately with 363   triggered, the operation completes immediately with
364   `errc::operation_canceled`. 364   `errc::operation_canceled`.
365   365  
366   @param ep The remote endpoint to connect to. 366   @param ep The remote endpoint to connect to.
367   367  
368   @return An awaitable that completes with `io_result<>`. 368   @return An awaitable that completes with `io_result<>`.
369   Returns success (default error_code) on successful connection, 369   Returns success (default error_code) on successful connection,
370   or an error code on failure including: 370   or an error code on failure including:
371   - connection_refused: No server listening at endpoint 371   - connection_refused: No server listening at endpoint
372   - timed_out: Connection attempt timed out 372   - timed_out: Connection attempt timed out
373   - network_unreachable: No route to host 373   - network_unreachable: No route to host
374   - operation_canceled: Cancelled via stop_token or cancel(). 374   - operation_canceled: Cancelled via stop_token or cancel().
375   Check `ec == cond::canceled` for portable comparison. 375   Check `ec == cond::canceled` for portable comparison.
376   376  
377   If the socket needs to be opened and the open fails, the 377   If the socket needs to be opened and the open fails, the
378   awaitable completes immediately with that error. 378   awaitable completes immediately with that error.
379   379  
380   @par Preconditions 380   @par Preconditions
381   This socket must outlive the returned awaitable. 381   This socket must outlive the returned awaitable.
382   382  
383   @par Example 383   @par Example
384   @par !example connect 384   @par !example connect
385   */ 385   */
HITCBC 386   4471 [[nodiscard]] auto connect(endpoint ep) 386   4485 [[nodiscard]] auto connect(endpoint ep)
387   { 387   {
HITCBC 388   4471 connect_awaitable aw(*this, ep); 388   4485 connect_awaitable aw(*this, ep);
HITCBC 389   4471 if (!is_open()) 389   4485 if (!is_open())
HITCBC 390   87 aw.ec_ = open(ep.address().family()); 390   87 aw.ec_ = open(ep.address().family());
HITCBC 391   4471 return aw; 391   4485 return aw;
392   } 392   }
393   393  
394   /** Wait for the socket to become ready in a given direction. 394   /** Wait for the socket to become ready in a given direction.
395   395  
396   Suspends until the socket is ready for the requested 396   Suspends until the socket is ready for the requested
397   direction, or an error condition is reported. No bytes 397   direction, or an error condition is reported. No bytes
398   are transferred — useful for integrating with C libraries 398   are transferred — useful for integrating with C libraries
399   that own the I/O on a nonblocking fd and only need 399   that own the I/O on a nonblocking fd and only need
400   readiness notification (e.g. libpq async, libssh). 400   readiness notification (e.g. libpq async, libssh).
401   401  
402   The operation supports cancellation via `std::stop_token` 402   The operation supports cancellation via `std::stop_token`
403   through the affine awaitable protocol. If the associated 403   through the affine awaitable protocol. If the associated
404   stop token is triggered, the operation completes 404   stop token is triggered, the operation completes
405   immediately with `errc::operation_canceled`. 405   immediately with `errc::operation_canceled`.
406   406  
407   @param w The wait direction (read, write, or error). 407   @param w The wait direction (read, write, or error).
408   408  
409   @return An awaitable that completes with `io_result<>`. 409   @return An awaitable that completes with `io_result<>`.
410   On success, no bytes have been consumed from the 410   On success, no bytes have been consumed from the
411   stream; a subsequent `read_some` (for read waits) 411   stream; a subsequent `read_some` (for read waits)
412   returns the available data. 412   returns the available data.
413   413  
414   A closed socket completes with `errc::bad_file_descriptor`. 414   A closed socket completes with `errc::bad_file_descriptor`.
415   415  
416   @par Preconditions 416   @par Preconditions
417   This socket must outlive the returned awaitable. 417   This socket must outlive the returned awaitable.
418   */ 418   */
HITCBC 419   68 [[nodiscard]] auto wait(wait_type w) 419   68 [[nodiscard]] auto wait(wait_type w)
420   { 420   {
HITCBC 421   68 return wait_awaitable(*this, w); 421   68 return wait_awaitable(*this, w);
422   } 422   }
423   423  
424   /** Cancel any pending asynchronous operations. 424   /** Cancel any pending asynchronous operations.
425   425  
426   Operations still in flight complete with `errc::operation_canceled`; 426   Operations still in flight complete with `errc::operation_canceled`;
427   an operation whose result is already decided reports that result. 427   an operation whose result is already decided reports that result.
428   Check `ec == cond::canceled` for portable comparison. 428   Check `ec == cond::canceled` for portable comparison.
429   */ 429   */
430   void cancel() noexcept; 430   void cancel() noexcept;
431   431  
432   /** Get the native socket handle. 432   /** Get the native socket handle.
433   433  
434   Returns the underlying platform-specific socket descriptor. 434   Returns the underlying platform-specific socket descriptor.
435   On POSIX systems this is an `int` file descriptor. 435   On POSIX systems this is an `int` file descriptor.
436   On Windows this is a `SOCKET` handle. 436   On Windows this is a `SOCKET` handle.
437   437  
438   @return The native socket handle, or -1/INVALID_SOCKET if not open. 438   @return The native socket handle, or -1/INVALID_SOCKET if not open.
439   439  
440   @par Preconditions 440   @par Preconditions
441   None. May be called on closed sockets. 441   None. May be called on closed sockets.
442   */ 442   */
443   native_handle_type native_handle() const noexcept; 443   native_handle_type native_handle() const noexcept;
444   444  
445   /** Assign an existing native socket to this object. 445   /** Assign an existing native socket to this object.
446   446  
447   Adopts a TCP socket created outside the library — received 447   Adopts a TCP socket created outside the library — received
448   from another process, inherited, or made natively — and 448   from another process, inherited, or made natively — and
449   registers it with the backend. The socket must be a stream 449   registers it with the backend. The socket must be a stream
450   socket in the `AF_INET` or `AF_INET6` family. Adoption never 450   socket in the `AF_INET` or `AF_INET6` family. Adoption never
451   alters the descriptor's flags or options: on POSIX the fd 451   alters the descriptor's flags or options: on POSIX the fd
452   must already be non-blocking, and on Windows the socket must 452   must already be non-blocking, and on Windows the socket must
453   be overlapped-capable. 453   be overlapped-capable.
454   454  
455   If this object is already open, pending operations complete 455   If this object is already open, pending operations complete
456   with `errc::operation_canceled` and the held socket is 456   with `errc::operation_canceled` and the held socket is
457   closed before the new one is adopted. 457   closed before the new one is adopted.
458   458  
459   @par Exception Safety 459   @par Exception Safety
460   Strong guarantee on validation failure: the object is 460   Strong guarantee on validation failure: the object is
461   unchanged. If backend registration fails, the object either 461   unchanged. If backend registration fails, the object either
462   retains its previous socket or is left closed, depending on 462   retains its previous socket or is left closed, depending on
463   the backend. In all failure cases the caller retains 463   the backend. In all failure cases the caller retains
464   ownership of `fd`. 464   ownership of `fd`.
465   465  
466   @param fd The native socket to adopt. On success the object 466   @param fd The native socket to adopt. On success the object
467   owns it and will close it. 467   owns it and will close it.
468   468  
469   @return The error code, empty on success. Validation and 469   @return The error code, empty on success. Validation and
470   registration failures are normal runtime conditions when 470   registration failures are normal runtime conditions when
471   adopting foreign descriptors. 471   adopting foreign descriptors.
472   */ 472   */
473   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept; 473   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
474   474  
475   /** Release ownership of the native socket handle. 475   /** Release ownership of the native socket handle.
476   476  
477   Deregisters the socket from the backend and cancels pending 477   Deregisters the socket from the backend and cancels pending
478   operations without closing the descriptor. The caller takes 478   operations without closing the descriptor. The caller takes
479   ownership of the returned handle. 479   ownership of the returned handle.
480   480  
481   @return The native handle. 481   @return The native handle.
482   482  
483   @throws std::system_error `errc::bad_file_descriptor` if the 483   @throws std::system_error `errc::bad_file_descriptor` if the
484   socket is not open. 484   socket is not open.
485   485  
486   @post is_open() == false 486   @post is_open() == false
487   */ 487   */
488   native_handle_type release(); 488   native_handle_type release();
489   489  
490   /** Disable sends or receives on the socket. 490   /** Disable sends or receives on the socket.
491   491  
492   TCP connections are full-duplex: each direction (send and receive) 492   TCP connections are full-duplex: each direction (send and receive)
493   operates independently. This function allows you to close one or 493   operates independently. This function allows you to close one or
494   both directions without destroying the socket. 494   both directions without destroying the socket.
495   495  
496   @li @ref shutdown_send sends a TCP FIN packet to the peer, 496   @li @ref shutdown_send sends a TCP FIN packet to the peer,
497   signaling that you have no more data to send. You can still 497   signaling that you have no more data to send. You can still
498   receive data until the peer also closes their send direction. 498   receive data until the peer also closes their send direction.
499   This is the most common use case, typically called before 499   This is the most common use case, typically called before
500   close() to ensure graceful connection termination. 500   close() to ensure graceful connection termination.
501   501  
502   @li @ref shutdown_receive disables reading on the socket. This 502   @li @ref shutdown_receive disables reading on the socket. This
503   does NOT send anything to the peer - they are not informed 503   does NOT send anything to the peer - they are not informed
504   and may continue sending data. Subsequent reads will fail 504   and may continue sending data. Subsequent reads will fail
505   or return end-of-file. Incoming data may be discarded or 505   or return end-of-file. Incoming data may be discarded or
506   buffered depending on the operating system. 506   buffered depending on the operating system.
507   507  
508   @li @ref shutdown_both combines both effects: sends a FIN and 508   @li @ref shutdown_both combines both effects: sends a FIN and
509   disables reading. 509   disables reading.
510   510  
511   When the peer shuts down their send direction (sends a FIN), 511   When the peer shuts down their send direction (sends a FIN),
512   subsequent read operations will complete with `capy::cond::eof`. 512   subsequent read operations will complete with `capy::cond::eof`.
513   Use the portable condition test rather than comparing error 513   Use the portable condition test rather than comparing error
514   codes directly: 514   codes directly:
515   515  
516   @par !example shutdown 516   @par !example shutdown
517   517  
518   Failures such as a peer that already disconnected are 518   Failures such as a peer that already disconnected are
519   normal runtime conditions and are reported through the 519   normal runtime conditions and are reported through the
520   returned error code. A closed socket reports 520   returned error code. A closed socket reports
521   `errc::bad_file_descriptor`. 521   `errc::bad_file_descriptor`.
522   522  
523   @param what Determines what operations will no longer be allowed. 523   @param what Determines what operations will no longer be allowed.
524   524  
525   @return The error code, empty on success. 525   @return The error code, empty on success.
526   */ 526   */
527   [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept; 527   [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept;
528   528  
529   /** Set a socket option. 529   /** Set a socket option.
530   530  
531   Applies a type-safe socket option to the underlying socket. 531   Applies a type-safe socket option to the underlying socket.
532   The option type encodes the protocol level and option name. 532   The option type encodes the protocol level and option name.
533   533  
534   @par Example 534   @par Example
535   @par !example set_option 535   @par !example set_option
536   536  
537   @param opt The option to set. 537   @param opt The option to set.
538   538  
539   @throws std::system_error `errc::bad_file_descriptor` if the 539   @throws std::system_error `errc::bad_file_descriptor` if the
540   socket is not open; otherwise thrown on failure. 540   socket is not open; otherwise thrown on failure.
541   */ 541   */
542   template<class Option> 542   template<class Option>
HITCBC 543   288 void set_option(Option const& opt) 543   288 void set_option(Option const& opt)
544   { 544   {
HITCBC 545   288 if (!is_open()) 545   288 if (!is_open())
HITCBC 546   2 detail::throw_system_error( 546   2 detail::throw_system_error(
HITCBC 547   4 make_error_code(std::errc::bad_file_descriptor), 547   4 make_error_code(std::errc::bad_file_descriptor),
548   "tcp_socket::set_option"); 548   "tcp_socket::set_option");
HITCBC 549   286 auto const fam = get().family(); 549   286 auto const fam = get().family();
HITCBC 550   286 std::error_code ec = get().set_option( 550   286 std::error_code ec = get().set_option(
551   opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam)); 551   opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
HITCBC 552   286 if (ec) 552   286 if (ec)
HITCBC 553   7 detail::throw_system_error(ec, "tcp_socket::set_option"); 553   7 detail::throw_system_error(ec, "tcp_socket::set_option");
HITCBC 554   279 } 554   279 }
555   555  
556   /** Get a socket option. 556   /** Get a socket option.
557   557  
558   Retrieves the current value of a type-safe socket option. 558   Retrieves the current value of a type-safe socket option.
559   559  
560   @par Example 560   @par Example
561   @par !example get_option 561   @par !example get_option
562   562  
563   @return The current option value. 563   @return The current option value.
564   564  
565   @throws std::system_error `errc::bad_file_descriptor` if the 565   @throws std::system_error `errc::bad_file_descriptor` if the
566   socket is not open; otherwise thrown on failure. 566   socket is not open; otherwise thrown on failure.
567   */ 567   */
568   template<class Option> 568   template<class Option>
HITCBC 569   97 Option get_option() const 569   97 Option get_option() const
570   { 570   {
HITCBC 571   97 if (!is_open()) 571   97 if (!is_open())
HITCBC 572   2 detail::throw_system_error( 572   2 detail::throw_system_error(
HITCBC 573   4 make_error_code(std::errc::bad_file_descriptor), 573   4 make_error_code(std::errc::bad_file_descriptor),
574   "tcp_socket::get_option"); 574   "tcp_socket::get_option");
HITCBC 575   95 Option opt{}; 575   95 Option opt{};
HITCBC 576   95 auto const fam = get().family(); 576   95 auto const fam = get().family();
HITCBC 577   95 std::size_t sz = opt.size(fam); 577   95 std::size_t sz = opt.size(fam);
578   std::error_code ec = 578   std::error_code ec =
HITCBC 579   95 get().get_option(opt.level(fam), opt.name(fam), opt.data(fam), &sz); 579   95 get().get_option(opt.level(fam), opt.name(fam), opt.data(fam), &sz);
HITCBC 580   95 if (ec) 580   95 if (ec)
HITCBC 581   7 detail::throw_system_error(ec, "tcp_socket::get_option"); 581   7 detail::throw_system_error(ec, "tcp_socket::get_option");
HITCBC 582   88 opt.resize(fam, sz); 582   88 opt.resize(fam, sz);
HITCBC 583   88 return opt; 583   88 return opt;
584   } 584   }
585   585  
586   /** Get the local endpoint of the socket. 586   /** Get the local endpoint of the socket.
587   587  
588   Returns the local address and port to which the socket is bound. 588   Returns the local address and port to which the socket is bound.
589   For a connected socket, this is the local side of the connection. 589   For a connected socket, this is the local side of the connection.
590   The endpoint is cached when the connection is established. 590   The endpoint is cached when the connection is established.
591   591  
592   @return The local endpoint, or a default endpoint (0.0.0.0:0) if 592   @return The local endpoint, or a default endpoint (0.0.0.0:0) if
593   the socket is not connected. 593   the socket is not connected.
594   594  
595   @par Thread Safety 595   @par Thread Safety
596   The cached endpoint value is set during connect/accept completion 596   The cached endpoint value is set during connect/accept completion
597   and cleared during close(). This function may be called concurrently 597   and cleared during close(). This function may be called concurrently
598   with I/O operations, but must not be called concurrently with 598   with I/O operations, but must not be called concurrently with
599   connect(), accept(), or close(). 599   connect(), accept(), or close().
600   */ 600   */
601   endpoint local_endpoint() const noexcept; 601   endpoint local_endpoint() const noexcept;
602   602  
603   /** Get the remote endpoint of the socket. 603   /** Get the remote endpoint of the socket.
604   604  
605   Returns the remote address and port to which the socket is connected. 605   Returns the remote address and port to which the socket is connected.
606   The endpoint is cached when the connection is established. 606   The endpoint is cached when the connection is established.
607   607  
608   @return The remote endpoint, or a default endpoint (0.0.0.0:0) if 608   @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
609   the socket is not connected. 609   the socket is not connected.
610   610  
611   @par Thread Safety 611   @par Thread Safety
612   The cached endpoint value is set during connect/accept completion 612   The cached endpoint value is set during connect/accept completion
613   and cleared during close(). This function may be called concurrently 613   and cleared during close(). This function may be called concurrently
614   with I/O operations, but must not be called concurrently with 614   with I/O operations, but must not be called concurrently with
615   connect(), accept(), or close(). 615   connect(), accept(), or close().
616   */ 616   */
617   endpoint remote_endpoint() const noexcept; 617   endpoint remote_endpoint() const noexcept;
618   618  
619   protected: 619   protected:
HITCBC 620   55 tcp_socket() noexcept = default; 620   55 tcp_socket() noexcept = default;
621   621  
622   explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {} 622   explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
623   623  
624   private: 624   private:
625   friend class tcp_acceptor; 625   friend class tcp_acceptor;
626   626  
627   /// Open the socket for the given protocol triple. 627   /// Open the socket for the given protocol triple.
628   [[nodiscard]] std::error_code 628   [[nodiscard]] std::error_code
629   open_for_family(int family, int type, int protocol) noexcept; 629   open_for_family(int family, int type, int protocol) noexcept;
630   630  
HITCBC 631   33531 inline implementation& get() const noexcept 631   33633 inline implementation& get() const noexcept
632   { 632   {
HITCBC 633   33531 return *static_cast<implementation*>(h_.get()); 633   33633 return *static_cast<implementation*>(h_.get());
634   } 634   }
635   }; 635   };
636   636  
637   } // namespace boost::corosio 637   } // namespace boost::corosio
638   638  
639   #endif 639   #endif