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