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