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