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