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