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