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