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