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