0.00% Lines (0/4) 0.00% Functions (0/2)
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 Michael Vandeberg 3   // Copyright (c) 2026 Michael Vandeberg
4   // Copyright (c) 2026 Steve Gerbino 4   // Copyright (c) 2026 Steve Gerbino
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_TLS_STREAM_HPP 12   #ifndef BOOST_COROSIO_TLS_STREAM_HPP
13   #define BOOST_COROSIO_TLS_STREAM_HPP 13   #define BOOST_COROSIO_TLS_STREAM_HPP
14   14  
15   #include <boost/corosio/detail/config.hpp> 15   #include <boost/corosio/detail/config.hpp>
16   #include <boost/capy/buffers.hpp> 16   #include <boost/capy/buffers.hpp>
17   #include <boost/capy/detail/buffer_array.hpp> 17   #include <boost/capy/detail/buffer_array.hpp>
18   #include <boost/capy/io/any_stream.hpp> 18   #include <boost/capy/io/any_stream.hpp>
19   #include <boost/capy/results.hpp> 19   #include <boost/capy/results.hpp>
20   #include <boost/capy/task.hpp> 20   #include <boost/capy/task.hpp>
21   21  
22   #include <cstddef> 22   #include <cstddef>
23   #include <string_view> 23   #include <string_view>
24   24  
25   namespace boost::corosio { 25   namespace boost::corosio {
26   26  
27   namespace detail { 27   namespace detail {
28   28  
29   /** A transport a TLS stream owns, held apart from its `any_stream`. 29   /** A transport a TLS stream owns, held apart from its `any_stream`.
30   30  
31   Destroying the stream cancels the transport's pending operations 31   Destroying the stream cancels the transport's pending operations
32   while the TLS state they complete into stays alive; the transport 32   while the TLS state they complete into stays alive; the transport
33   itself outlives every operation that could be parked on it. 33   itself outlives every operation that could be parked on it.
34   */ 34   */
35   struct tls_owned_transport 35   struct tls_owned_transport
36   { 36   {
37   /// The owned transport, or null in reference mode. 37   /// The owned transport, or null in reference mode.
38   void* p = nullptr; 38   void* p = nullptr;
39   39  
40   /// Destroy the transport `p` points to. 40   /// Destroy the transport `p` points to.
41   void (*destroy)(void*) noexcept = nullptr; 41   void (*destroy)(void*) noexcept = nullptr;
42   42  
43   /// Cancel the transport's pending operations, or null if it can't. 43   /// Cancel the transport's pending operations, or null if it can't.
44   void (*cancel)(void*) noexcept = nullptr; 44   void (*cancel)(void*) noexcept = nullptr;
45   }; 45   };
46   46  
47   /// Destroy an owned transport of type `S`. 47   /// Destroy an owned transport of type `S`.
48   template<class S> 48   template<class S>
49   void 49   void
50   destroy_owned_transport(void* p) noexcept 50   destroy_owned_transport(void* p) noexcept
51   { 51   {
52   delete static_cast<S*>(p); 52   delete static_cast<S*>(p);
53   } 53   }
54   54  
55   /// Cancel an owned transport of type `S`'s pending operations. 55   /// Cancel an owned transport of type `S`'s pending operations.
56   template<class S> 56   template<class S>
57   void 57   void
58   cancel_owned_transport(void* p) noexcept 58   cancel_owned_transport(void* p) noexcept
59   { 59   {
60   static_cast<S*>(p)->cancel(); 60   static_cast<S*>(p)->cancel();
61   } 61   }
62   62  
63   /// Make the owned-transport record for a transport of type `S`. 63   /// Make the owned-transport record for a transport of type `S`.
64   template<class S> 64   template<class S>
65   tls_owned_transport 65   tls_owned_transport
66   make_owned_transport(S* p) noexcept 66   make_owned_transport(S* p) noexcept
67   { 67   {
68   tls_owned_transport t{p, &destroy_owned_transport<S>, nullptr}; 68   tls_owned_transport t{p, &destroy_owned_transport<S>, nullptr};
69   if constexpr (requires(S& s) { 69   if constexpr (requires(S& s) {
70   { s.cancel() } noexcept; 70   { s.cancel() } noexcept;
71   }) 71   })
72   t.cancel = &cancel_owned_transport<S>; 72   t.cancel = &cancel_owned_transport<S>;
73   return t; 73   return t;
74   } 74   }
75   75  
76   } // namespace detail 76   } // namespace detail
77   77  
78   /** TLS handshake role. 78   /** TLS handshake role.
79   79  
80   Specifies whether to perform the TLS handshake as a client or server. 80   Specifies whether to perform the TLS handshake as a client or server.
81   81  
82   @see tls_stream::handshake 82   @see tls_stream::handshake
83   */ 83   */
84   enum class tls_role 84   enum class tls_role
85   { 85   {
86   /// Perform handshake as the connecting client. 86   /// Perform handshake as the connecting client.
87   client, 87   client,
88   88  
89   /// Perform handshake as the accepting server. 89   /// Perform handshake as the accepting server.
90   server 90   server
91   }; 91   };
92   92  
93   /** Reads, writes, and manages the handshake lifecycle of a TLS 93   /** Reads, writes, and manages the handshake lifecycle of a TLS
94   session over an underlying stream. 94   session over an underlying stream.
95   95  
96   This class provides a runtime-polymorphic interface for TLS 96   This class provides a runtime-polymorphic interface for TLS
97   implementations. Derived classes (`openssl_stream`, `wolfssl_stream`) 97   implementations. Derived classes (`openssl_stream`, `wolfssl_stream`)
98   implement the virtual functions to provide backend-specific 98   implement the virtual functions to provide backend-specific
99   TLS functionality. 99   TLS functionality.
100   100  
101   An @ref io_stream represents OS-level I/O completed by the kernel. 101   An @ref io_stream represents OS-level I/O completed by the kernel.
102   TLS streams are coroutine-based instead: their operations are 102   TLS streams are coroutine-based instead: their operations are
103   coroutines that orchestrate sub-operations on the underlying stream. 103   coroutines that orchestrate sub-operations on the underlying stream.
104   104  
105   The non-virtual template wrappers (`read_some`, `write_some`) 105   The non-virtual template wrappers (`read_some`, `write_some`)
106   satisfy the `capy::Stream` concept, enabling TLS streams to 106   satisfy the `capy::Stream` concept, enabling TLS streams to
107   be used anywhere a Stream is expected. 107   be used anywhere a Stream is expected.
108   108  
109   @par Thread Safety 109   @par Thread Safety
110   Distinct objects: Safe.@n 110   Distinct objects: Safe.@n
111   Shared objects: Unsafe, with one exception: one read operation and 111   Shared objects: Unsafe, with one exception: one read operation and
112   one write operation may be in flight simultaneously. `shutdown()` 112   one write operation may be in flight simultaneously. `shutdown()`
113   may overlap a pending read. On a multi-threaded execution context, 113   may overlap a pending read. On a multi-threaded execution context,
114   all operations on one stream must run within the same 114   all operations on one stream must run within the same
115   `capy::strand`, or must otherwise never run concurrently. A 115   `capy::strand`, or must otherwise never run concurrently. A
116   single-threaded context needs no strand. 116   single-threaded context needs no strand.
117   117  
118   @see openssl_stream, wolfssl_stream 118   @see openssl_stream, wolfssl_stream
119   */ 119   */
120   class BOOST_COROSIO_DECL tls_stream 120   class BOOST_COROSIO_DECL tls_stream
121   { 121   {
122   public: 122   public:
123   /// Destroy the TLS stream. 123   /// Destroy the TLS stream.
124   virtual ~tls_stream() = default; 124   virtual ~tls_stream() = default;
125   125  
126   /// Copy construction is disabled; copying a stream would slice the derived session. 126   /// Copy construction is disabled; copying a stream would slice the derived session.
127   tls_stream(tls_stream const&) = delete; 127   tls_stream(tls_stream const&) = delete;
128   /// Copy assignment is disabled; copying a stream would slice the derived session. 128   /// Copy assignment is disabled; copying a stream would slice the derived session.
129   tls_stream& operator=(tls_stream const&) = delete; 129   tls_stream& operator=(tls_stream const&) = delete;
130   130  
131   /** Initiate an asynchronous read operation. 131   /** Initiate an asynchronous read operation.
132   132  
133   Reads decrypted data into the provided buffer sequence. The 133   Reads decrypted data into the provided buffer sequence. The
134   operation completes when it reads at least one byte, 134   operation completes when it reads at least one byte,
135   or an error occurs. 135   or an error occurs.
136   136  
137   This non-virtual template wrapper satisfies the `capy::Stream` 137   This non-virtual template wrapper satisfies the `capy::Stream`
138   concept by delegating to the virtual `do_read_some`. 138   concept by delegating to the virtual `do_read_some`.
139   139  
140   @par Thread Safety 140   @par Thread Safety
141   May run concurrently with one operation in the other 141   May run concurrently with one operation in the other
142   direction, subject to the class-level threading contract. 142   direction, subject to the class-level threading contract.
143   Two concurrent operations in the same direction are 143   Two concurrent operations in the same direction are
144   undefined. 144   undefined.
145   145  
146   @param buffers The buffer sequence to read data into. 146   @param buffers The buffer sequence to read data into.
147   147  
148   @return An awaitable yielding `(error_code,std::size_t)`. 148   @return An awaitable yielding `(error_code,std::size_t)`.
149   */ 149   */
150   template<capy::MutableBufferSequence Buffers> 150   template<capy::MutableBufferSequence Buffers>
MISUBC 151   ✗ [[nodiscard]] auto read_some(Buffers const& buffers) 151   ✗ [[nodiscard]] auto read_some(Buffers const& buffers)
152   { 152   {
MISUBC 153   ✗ return do_read_some(buffers); 153   ✗ return do_read_some(buffers);
154   } 154   }
155   155  
156   /** Initiate an asynchronous write operation. 156   /** Initiate an asynchronous write operation.
157   157  
158   Encrypts and writes data from the provided buffer sequence. 158   Encrypts and writes data from the provided buffer sequence.
159   The operation completes when it writes at least one byte, 159   The operation completes when it writes at least one byte,
160   or an error occurs. 160   or an error occurs.
161   161  
162   This non-virtual template wrapper satisfies the `capy::Stream` 162   This non-virtual template wrapper satisfies the `capy::Stream`
163   concept by delegating to the virtual `do_write_some`. 163   concept by delegating to the virtual `do_write_some`.
164   164  
165   @par Thread Safety 165   @par Thread Safety
166   May run concurrently with one operation in the other 166   May run concurrently with one operation in the other
167   direction, subject to the class-level threading contract. 167   direction, subject to the class-level threading contract.
168   Two concurrent operations in the same direction are 168   Two concurrent operations in the same direction are
169   undefined. 169   undefined.
170   170  
171   @param buffers The buffer sequence containing data to write. 171   @param buffers The buffer sequence containing data to write.
172   172  
173   @return An awaitable yielding `(error_code,std::size_t)`. 173   @return An awaitable yielding `(error_code,std::size_t)`.
174   */ 174   */
175   template<capy::ConstBufferSequence Buffers> 175   template<capy::ConstBufferSequence Buffers>
MISUBC 176   ✗ [[nodiscard]] auto write_some(Buffers const& buffers) 176   ✗ [[nodiscard]] auto write_some(Buffers const& buffers)
177   { 177   {
MISUBC 178   ✗ return do_write_some(buffers); 178   ✗ return do_write_some(buffers);
179   } 179   }
180   180  
181   /** Asynchronously perform the TLS handshake. 181   /** Asynchronously perform the TLS handshake.
182   182  
183   Initiates the TLS handshake process. For client connections, 183   Initiates the TLS handshake process. For client connections,
184   this sends the ClientHello and processes the server's response. 184   this sends the ClientHello and processes the server's response.
185   For server connections, this waits for the ClientHello and 185   For server connections, this waits for the ClientHello and
186   sends the server's response. 186   sends the server's response.
187   187  
188   A handshake attempt consumes the stream state, whether it 188   A handshake attempt consumes the stream state, whether it
189   succeeds or not. A subsequent call behaves as if `reset()` ran 189   succeeds or not. A subsequent call behaves as if `reset()` ran
190   first, and performs a fresh handshake using the current 190   first, and performs a fresh handshake using the current
191   configuration. 191   configuration.
192   192  
193   @pre The underlying stream must be connected. No other TLS 193   @pre The underlying stream must be connected. No other TLS
194   operation may be in progress on this stream. 194   operation may be in progress on this stream.
195   195  
196   @param role The handshake role, client or server. 196   @param role The handshake role, client or server.
197   197  
198   @return An awaitable yielding `(error_code)`. 198   @return An awaitable yielding `(error_code)`.
199   */ 199   */
200   [[nodiscard]] virtual capy::task<capy::results<>> handshake(tls_role role) = 0; 200   [[nodiscard]] virtual capy::task<capy::results<>> handshake(tls_role role) = 0;
201   201  
202   /** Asynchronously perform a graceful TLS shutdown. 202   /** Asynchronously perform a graceful TLS shutdown.
203   203  
204   Initiates the TLS shutdown sequence by sending a close_notify 204   Initiates the TLS shutdown sequence by sending a close_notify
205   alert and waiting for the peer's close_notify response. 205   alert and waiting for the peer's close_notify response.
206   206  
207   @pre A handshake must have completed successfully. May overlap 207   @pre A handshake must have completed successfully. May overlap
208   a pending read. No concurrent write may be in progress. 208   a pending read. No concurrent write may be in progress.
209   209  
210   @par Postconditions 210   @par Postconditions
211   If the transport ends before the peer's close_notify arrives, 211   If the transport ends before the peer's close_notify arrives,
212   the result is `capy::error::stream_truncated`, not success. An 212   the result is `capy::error::stream_truncated`, not success. An
213   unannounced close is indistinguishable from a truncation attack, 213   unannounced close is indistinguishable from a truncation attack,
214   so it must not be reported as a clean shutdown. A shutdown 214   so it must not be reported as a clean shutdown. A shutdown
215   stopped mid-flight reports canceled. Any other transport 215   stopped mid-flight reports canceled. Any other transport
216   error propagates unchanged. 216   error propagates unchanged.
217   217  
218   @return An awaitable yielding `(error_code)`. 218   @return An awaitable yielding `(error_code)`.
219   */ 219   */
220   [[nodiscard]] virtual capy::task<capy::results<>> shutdown() = 0; 220   [[nodiscard]] virtual capy::task<capy::results<>> shutdown() = 0;
221   221  
222   /** Cancel operations pending on the owned underlying stream. 222   /** Cancel operations pending on the owned underlying stream.
223   223  
224   Operations parked on the underlying stream complete with 224   Operations parked on the underlying stream complete with
225   `capy::error::canceled`. A stream that owns another TLS stream 225   `capy::error::canceled`. A stream that owns another TLS stream
226   reaches the innermost transport through this, so destroying 226   reaches the innermost transport through this, so destroying
227   the outer stream cancels a read parked through both. 227   the outer stream cancels a read parked through both.
228   228  
229   In reference mode, and when the owned stream has no `cancel()` 229   In reference mode, and when the owned stream has no `cancel()`
230   member, this does nothing; cancel the underlying stream 230   member, this does nothing; cancel the underlying stream
231   directly instead. 231   directly instead.
232   232  
233   @par Thread Safety 233   @par Thread Safety
234   Not thread safe with respect to destroying or moving this 234   Not thread safe with respect to destroying or moving this
235   stream. 235   stream.
236   */ 236   */
237   virtual void cancel() noexcept = 0; 237   virtual void cancel() noexcept = 0;
238   238  
239   /** Reset TLS session state for reuse. 239   /** Reset TLS session state for reuse.
240   240  
241   Releases TLS session state including session keys and peer 241   Releases TLS session state including session keys and peer
242   certificates, returning the stream to a state where 242   certificates, returning the stream to a state where
243   `handshake()` can be called again. Internal memory 243   `handshake()` can be called again. Internal memory
244   allocations (I/O buffers) are preserved. 244   allocations (I/O buffers) are preserved.
245   245  
246   Calling `handshake()` on a previously-used stream 246   Calling `handshake()` on a previously-used stream
247   implicitly performs a reset first, so explicit calls 247   implicitly performs a reset first, so explicit calls
248   are only needed to eagerly release session state. 248   are only needed to eagerly release session state.
249   249  
250   @pre No TLS operation (handshake, read, write, shutdown) is 250   @pre No TLS operation (handshake, read, write, shutdown) is
251   in progress. 251   in progress.
252   252  
253   @par Thread Safety 253   @par Thread Safety
254   Not thread safe. The caller must ensure no concurrent 254   Not thread safe. The caller must ensure no concurrent
255   operations are in progress on this stream. 255   operations are in progress on this stream.
256   256  
257   @note If called mid-session before `shutdown()`, pending 257   @note If called mid-session before `shutdown()`, pending
258   TLS data is discarded and the peer observes a 258   TLS data is discarded and the peer observes a
259   truncated stream. 259   truncated stream.
260   */ 260   */
261   virtual void reset() = 0; 261   virtual void reset() = 0;
262   262  
263   /** Set the peer hostname for SNI and certificate verification. 263   /** Set the peer hostname for SNI and certificate verification.
264   264  
265   Configures the hostname sent in the TLS Server Name 265   Configures the hostname sent in the TLS Server Name
266   Indication extension and matched against the peer 266   Indication extension and matched against the peer
267   certificate during verification. The value takes effect 267   certificate during verification. The value takes effect
268   at the next `handshake()`; an established session is not 268   at the next `handshake()`; an established session is not
269   affected. It persists across `reset()`, so a stream reused 269   affected. It persists across `reset()`, so a stream reused
270   to reach a different host must set the new name before 270   to reach a different host must set the new name before
271   handshaking again. 271   handshaking again.
272   272  
273   An empty hostname (the default) disables SNI and hostname 273   An empty hostname (the default) disables SNI and hostname
274   verification. 274   verification.
275   275  
276   If `hostname` is an IP literal (IPv4 or IPv6), it is matched 276   If `hostname` is an IP literal (IPv4 or IPv6), it is matched
277   against the certificate's iPAddress entries instead of its DNS 277   against the certificate's iPAddress entries instead of its DNS
278   names. No SNI is sent, because RFC 6066 excludes literals. 278   names. No SNI is sent, because RFC 6066 excludes literals.
279   A backend build that cannot match iPAddress entries fails the 279   A backend build that cannot match iPAddress entries fails the
280   handshake with `std::errc::function_not_supported` rather 280   handshake with `std::errc::function_not_supported` rather
281   than skip verification. 281   than skip verification.
282   282  
283   @par Postconditions 283   @par Postconditions
284   The next `handshake()` uses `hostname` for SNI and 284   The next `handshake()` uses `hostname` for SNI and
285   certificate verification, or neither if it is empty. 285   certificate verification, or neither if it is empty.
286   286  
287   @note The hostname is used for client handshakes only; 287   @note The hostname is used for client handshakes only;
288   it is ignored when handshaking as a server. 288   it is ignored when handshaking as a server.
289   289  
290   @param hostname The peer hostname, or empty to disable. 290   @param hostname The peer hostname, or empty to disable.
291   */ 291   */
292   virtual void set_hostname(std::string_view hostname) = 0; 292   virtual void set_hostname(std::string_view hostname) = 0;
293   293  
294   /** Return a reference to the underlying stream. 294   /** Return a reference to the underlying stream.
295   295  
296   Provides access to the type-erased underlying stream for 296   Provides access to the type-erased underlying stream for
297   operations like cancellation or accessing native handles. 297   operations like cancellation or accessing native handles.
298   298  
299   @warning Do not reseat (assign to) the returned reference. 299   @warning Do not reseat (assign to) the returned reference.
300   The TLS implementation holds internal state bound to 300   The TLS implementation holds internal state bound to
301   the original stream. Replacing it causes undefined 301   the original stream. Replacing it causes undefined
302   behavior. 302   behavior.
303   303  
304   @return Reference to the wrapped stream. 304   @return Reference to the wrapped stream.
305   */ 305   */
306   virtual capy::any_stream& next_layer() noexcept = 0; 306   virtual capy::any_stream& next_layer() noexcept = 0;
307   307  
308   /** Return a const reference to the underlying stream. 308   /** Return a const reference to the underlying stream.
309   309  
310   @return Const reference to the wrapped stream. 310   @return Const reference to the wrapped stream.
311   */ 311   */
312   virtual capy::any_stream const& next_layer() const noexcept = 0; 312   virtual capy::any_stream const& next_layer() const noexcept = 0;
313   313  
314   /** Return the name of the TLS backend. 314   /** Return the name of the TLS backend.
315   315  
316   @return A string identifying the TLS implementation, 316   @return A string identifying the TLS implementation,
317   such as "openssl" or "wolfssl". 317   such as "openssl" or "wolfssl".
318   */ 318   */
319   virtual std::string_view name() const noexcept = 0; 319   virtual std::string_view name() const noexcept = 0;
320   320  
321   /** Return the ALPN protocol negotiated during the handshake. 321   /** Return the ALPN protocol negotiated during the handshake.
322   322  
323   Application-Layer Protocol Negotiation selects a single 323   Application-Layer Protocol Negotiation selects a single
324   application protocol (for example `"h2"` or `"http/1.1"`) 324   application protocol (for example `"h2"` or `"http/1.1"`)
325   during the TLS handshake, from the list supplied via 325   during the TLS handshake, from the list supplied via
326   @ref tls_context::set_alpn. 326   @ref tls_context::set_alpn.
327   327  
328   @return The negotiated protocol, or an empty view. It is empty 328   @return The negotiated protocol, or an empty view. It is empty
329   if no protocol was negotiated or ALPN was not offered. It is 329   if no protocol was negotiated or ALPN was not offered. It is
330   also empty if the handshake has not completed, or if the build 330   also empty if the handshake has not completed, or if the build
331   lacks ALPN support. 331   lacks ALPN support.
332   332  
333   @par Thread Safety 333   @par Thread Safety
334   Safe to call after the handshake completes; not safe to call 334   Safe to call after the handshake completes; not safe to call
335   concurrently with a handshake or reset. 335   concurrently with a handshake or reset.
336   */ 336   */
337   virtual std::string_view alpn_protocol() const noexcept 337   virtual std::string_view alpn_protocol() const noexcept
338   { 338   {
339   return {}; 339   return {};
340   } // LCOV_EXCL_LINE every concrete stream overrides this; the base default is never called 340   } // LCOV_EXCL_LINE every concrete stream overrides this; the base default is never called
341   341  
342   protected: 342   protected:
343   /// Default construct; a derived class supplies the session. 343   /// Default construct; a derived class supplies the session.
344   tls_stream() = default; 344   tls_stream() = default;
345   345  
346   /** Perform the backend-specific decrypted read. 346   /** Perform the backend-specific decrypted read.
347   347  
348   Derived classes override this to perform TLS decryption 348   Derived classes override this to perform TLS decryption
349   and read operations. 349   and read operations.
350   350  
351   @param buffers Buffer sequence to read into. 351   @param buffers Buffer sequence to read into.
352   352  
353   @return An awaitable yielding `(error_code,std::size_t)`. 353   @return An awaitable yielding `(error_code,std::size_t)`.
354   */ 354   */
355   virtual capy::task<capy::results<std::size_t>> do_read_some( 355   virtual capy::task<capy::results<std::size_t>> do_read_some(
356   capy::detail::mutable_buffer_array<capy::detail::max_iovec_> 356   capy::detail::mutable_buffer_array<capy::detail::max_iovec_>
357   buffers) = 0; 357   buffers) = 0;
358   358  
359   /** Perform the backend-specific encrypted write. 359   /** Perform the backend-specific encrypted write.
360   360  
361   Derived classes override this to perform TLS encryption 361   Derived classes override this to perform TLS encryption
362   and write operations. 362   and write operations.
363   363  
364   @param buffers Buffer sequence to write from. 364   @param buffers Buffer sequence to write from.
365   365  
366   @return An awaitable yielding `(error_code,std::size_t)`. 366   @return An awaitable yielding `(error_code,std::size_t)`.
367   */ 367   */
368   virtual capy::task<capy::results<std::size_t>> do_write_some( 368   virtual capy::task<capy::results<std::size_t>> do_write_some(
369   capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers) = 0; 369   capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers) = 0;
370   }; 370   };
371   371  
372   } // namespace boost::corosio 372   } // namespace boost::corosio
373   373  
374   #endif 374   #endif