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