src/corosio/src/tls/detail/engine_driver.hpp

89.0% Lines (325 / 365, 1 excl) 51.5% Branches (525 / 1020, 2 excl) 100.0% Functions (34 / 34, 1 excl)
engine_driver.hpp
f(x) Functions (35)
Function Calls Lines Branches Blocks
<unknown function 163> :163 – – – – boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::flush_output() :215 137332x 88.4% 53.9% 58.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::best_effort_flush() :298 20x 100.0% 50.0% 48.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::fill_input(unsigned long long) :306 17780x 86.7% 51.0% 53.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::destroy_transport() :371 5636x 100.0% 100.0% 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::begin_transport_call() :383 137279x 71.4% 50.0% 75.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::end_transport_call() :392 140097x 100.0% 100.0% 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::transport_call::~transport_call() :406 274554x 100.0% – 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::transport_call::transport_call(boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>&) :411 274554x 100.0% 50.0% 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::transport_call::operator bool() const :419 137277x 100.0% – 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::transport_call::end() :424 274553x 100.0% 100.0% 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::take_pending_flush_ec() :434 233074x 100.0% – 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::engine_driver(boost::capy::any_stream, boost::corosio::detail::tls_owned_transport, boost::corosio::tls_context) :452 2818x 100.0% 50.0% 50.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::~engine_driver() :461 2818x 100.0% – 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::add_ref() :470 235178x 100.0% – 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::release_ref() :476 237996x 100.0% – 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::orphan() :489 2818x 100.0% 100.0% 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::cancel_transport() :501 2x 85.7% 75.0% 83.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::orphaned() const :511 155056x 100.0% – 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::stream() :517 2x 100.0% – 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::set_hostname(std::__1::basic_string_view<char, std::__1::char_traits<char>>) :541 15x 100.0% – 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::alpn_protocol() const :547 3x 100.0% – 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::reset() :553 67x 100.0% – 100.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::do_read_some(boost::capy::detail::buffer_array<16ul, false>) :564 116496x 82.0% 51.9% 47.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::do_write_some(boost::capy::detail::buffer_array<16ul, true>) :655 116432x 70.0% 33.1% 34.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::do_handshake(boost::corosio::tls_role) :750 2102x 94.9% 61.3% 58.0% boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::do_shutdown() :825 146x 93.5% 52.3% 51.0% boost::corosio::detail::driver_ref<boost::corosio::openssl_stream::implementation>::driver_ref(boost::corosio::openssl_stream::implementation*) :897 470356x 100.0% – 100.0% boost::corosio::detail::driver_ref<boost::corosio::openssl_stream::implementation>::driver_ref(boost::corosio::detail::driver_ref<boost::corosio::openssl_stream::implementation>&&) :903 470356x 100.0% – 100.0% boost::corosio::detail::driver_ref<boost::corosio::openssl_stream::implementation>::~driver_ref() :913 940712x 100.0% 83.3% 100.0% boost::corosio::detail::driver_ref<boost::corosio::openssl_stream::implementation>::operator->() const :920 235176x 100.0% – 100.0% boost::capy::task<std::__1::tuple<std::__1::error_code, unsigned long>> boost::corosio::detail::driver_read_some<boost::corosio::openssl_stream::implementation>(boost::corosio::detail::driver_ref<boost::corosio::openssl_stream::implementation>, boost::capy::detail::buffer_array<16ul, false>) :929 116498x 100.0% 60.0% 55.0% boost::capy::task<std::__1::tuple<std::__1::error_code, unsigned long>> boost::corosio::detail::driver_write_some<boost::corosio::openssl_stream::implementation>(boost::corosio::detail::driver_ref<boost::corosio::openssl_stream::implementation>, boost::capy::detail::buffer_array<16ul, true>) :939 116432x 100.0% 50.0% 48.0% boost::capy::task<std::__1::tuple<std::__1::error_code>> boost::corosio::detail::driver_handshake<boost::corosio::openssl_stream::implementation>(boost::corosio::detail::driver_ref<boost::corosio::openssl_stream::implementation>, boost::corosio::tls_role) :949 2102x 100.0% 50.0% 48.0% boost::capy::task<std::__1::tuple<std::__1::error_code>> boost::corosio::detail::driver_shutdown<boost::corosio::openssl_stream::implementation>(boost::corosio::detail::driver_ref<boost::corosio::openssl_stream::implementation>) :957 146x 100.0% 50.0% 48.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Steve Gerbino
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #ifndef SRC_TLS_DETAIL_ENGINE_DRIVER_HPP
11 #define SRC_TLS_DETAIL_ENGINE_DRIVER_HPP
12
13 #include <boost/corosio/tls_context.hpp>
14 #include <boost/corosio/tls_stream.hpp>
15
16 #include "src/tls/detail/engine_types.hpp"
17
18 #include <boost/capy/buffers.hpp>
19 #include <boost/capy/detail/buffer_array.hpp>
20 #include <boost/capy/ex/async_mutex.hpp>
21 #include <boost/capy/io/any_stream.hpp>
22 #include <boost/capy/results.hpp>
23 #include <boost/capy/task.hpp>
24 #include <boost/capy/write.hpp>
25
26 #include <atomic>
27 #include <concepts>
28 #include <cstddef>
29 #include <cstdint>
30 #include <cstring>
31 #include <limits>
32 #include <string>
33 #include <string_view>
34 #include <system_error>
35 #include <tuple>
36 #include <utility>
37 #include <vector>
38
39 // This header is instantiated in both backends' TUs, whose vendor
40 // headers cannot coexist (WolfSSL's OpenSSL-compat layer clashes
41 // with genuine OpenSSL declarations on some builds); it must stay
42 // vendor-free.
43 #ifdef OPENSSL_VERSION_NUMBER
44 #error engine_driver.hpp must stay vendor-header-free
45 #endif
46 #ifdef WOLFSSL_VERSION
47 #error engine_driver.hpp must stay vendor-header-free
48 #endif
49
50 /*
51 TLS Driver Architecture
52
53 TLS layer wrapping an underlying stream (via any_stream). One
54 read_some and one write_some may be in flight concurrently.
55
56 Engine / Driver Split. The engine (each backend's detail/engine.hpp)
57 owns the TLS session, its byte interface, and every library-result-
58 to-error-code decision; it is synchronous and transport-free. This
59 driver owns the transport, the per-direction claims, and all
60 buffering, and loops on engine verdicts:
61
62 1. Call eng_.perform(op, data, len)
63 2. output_then_retry: flush pending ciphertext, then retry
64 3. input: feed transport bytes into the engine, then retry
65 4. done / output_then_done: flush if pending, report the
66 (already mapped) ec
67
68 Renegotiation causes cross-direction I/O: a read may need to
69 write handshake data, a write may need to read. Each operation
70 services whatever direction the engine requests.
71
72 Full-Duplex Concurrency. rd_cm_ and wr_cm_ are separate transport
73 claims: a read parked on the network cannot stall a concurrent
74 write's flush, and vice versa. A cross-direction request (e.g. a
75 read needing to flush queued handshake output) takes the other
76 direction's claim itself.
77
78 read_gen_ counts successful deposits into the engine. Each engine
79 call snapshots it before running; if fill_input later finds the
80 generation has moved (a concurrent operation's flush suspension let
81 a deposit land in the meantime) it retries the engine instead of
82 issuing a redundant transport read.
83
84 The transport reads ciphertext straight into the engine's input
85 staging via input_area(), so no driver-side buffer or deposit copy
86 sits on the read path. The write path stays copy-based on purpose:
87 ciphertext drained from the engine into out_buf_[0, out_len_) is
88 decoupled from the engine's shared output staging, so a concurrent
89 reader can still emit its own records (an alert, a key update) while
90 a write is in flight. out_len_ holds only the abandoned tail a failed
91 or canceled write kept for retry; the length being written is a local,
92 never published, so a concurrent flush probe cannot mistake in-flight
93 bytes for a tail and park on the write claim behind a healthy write.
94
95 A read or write that fully satisfies the caller's buffer still owes
96 a trailing flush (queued handshake or session-ticket output must
97 reach the peer before the call can report success). If that flush
98 fails, the transfer already succeeded and the stream contract
99 forbids reporting both a full count and an error in the same
100 result, so the error is stashed in pending_flush_ec_ and surfaced
101 by the next read, write, or shutdown call instead.
102 */
103
104 namespace boost::corosio {
105
106 namespace detail {
107
108 /** The engine surface `engine_driver` drives.
109
110 A synchronous, transport-free TLS record engine: `perform`
111 advances one operation and returns a fully mapped verdict, while
112 `put_input` / `get_output` shuttle wire bytes. The handshake
113 hooks (`check_context`, `check_session`, `prepare`) let each
114 backend gate and configure a handshake at the protocol-mandated
115 points without the driver knowing the backend's session model.
116 */
117 template<class Engine>
118 concept tls_engine = requires(
119 Engine& e,
120 Engine const& ce,
121 engine_op op,
122 void* buf,
123 unsigned char const* in,
124 unsigned char* out,
125 std::size_t n,
126 tls_context const& ctx,
127 tls_role role,
128 std::string const& hostname,
129 std::string& alpn) {
130 { e.perform(op, buf, n) } -> std::same_as<engine_result>;
131 { e.put_input(in, n) } -> std::same_as<std::size_t>;
132 { e.input_area() } -> std::same_as<std::pair<unsigned char*, std::size_t>>;
133 { e.input_committed(n) };
134 { ce.pending_output() } -> std::same_as<std::size_t>;
135 { e.get_output(out, n) } -> std::same_as<std::size_t>;
136 { ce.received_shutdown() } -> std::same_as<bool>;
137 { ce.capture_alpn(alpn) };
138 { e.reset() };
139 { ce.check_context() } -> std::same_as<std::error_code>;
140 { ce.check_session() } -> std::same_as<std::error_code>;
141 { e.prepare(ctx, role, hostname) } -> std::same_as<std::error_code>;
142 };
143
144 /** Coroutine driver shared by every TLS backend.
145
146 Hosts the transport, the per-direction claims, the buffering, and
147 the four operation loops; the engine type supplies all TLS
148 mechanics. Each backend's stream impl instantiates this template
149 with its own engine so the driver logic exists exactly once.
150
151 @par Thread Safety
152 Distinct objects: Safe.@n
153 Shared objects: Unsafe.
154 */
155 template<tls_engine Engine>
156 class engine_driver
157 {
158 // Large enough to hold the largest possible TLS record.
159 static constexpr std::size_t buffer_size_ = std::size_t{17} * 1024;
160
161 // Operations hold a reference across their suspensions, so the
162 // driver outlives the stream that created it until they finish.
163 2818x std::atomic<std::size_t> refs_{1};
164
165 // Set when the stream is destroyed: no transport call may follow.
166 2818x std::atomic<bool> orphaned_{false};
167
168 // Transport calls in flight. Once orphaned, the owned transport is
169 // destroyed when this reaches zero: right away for a stream with no
170 // call parked, so it never outlives its context waiting on an
171 // operation nobody awaits.
172 2818x std::atomic<std::size_t> calls_{0};
173 2818x std::atomic<bool> transport_gone_{false};
174
175 // The transport in owning mode; s_ refers to it without owning.
176 tls_owned_transport owned_;
177 capy::any_stream s_;
178 tls_context ctx_;
179 Engine eng_;
180
181 // A handshake was attempted (successfully or not); the stream
182 // must be reset before the next handshake.
183 2818x bool used_ = false;
184
185 // Per-stream SNI/verification hostname, set via set_hostname().
186 std::string hostname_;
187
188 // ALPN protocol negotiated during the handshake (empty if none).
189 std::string alpn_selected_;
190
191 std::vector<char> out_buf_;
192
193 // One transport claim per direction so a parked reader cannot block
194 // a writer's flush. read_gen_ counts deposits into the engine: each
195 // operation captures it at its engine call and passes it to
196 // fill_input, which retries the engine instead of issuing a stale
197 // transport read when a deposit landed anywhere after the engine's
198 // verdict (including during an intervening flush suspension).
199 capy::async_mutex rd_cm_;
200 capy::async_mutex wr_cm_;
201 2818x std::uint64_t read_gen_ = 0;
202
203 // Engine ciphertext drained but not yet written: the abandoned
204 // tail a failed or canceled transport write kept for retry.
205 // Zero while a write is in flight (that length is a local).
206 2818x std::size_t out_len_ = 0;
207
208 // A trailing flush that fails after the engine already accepted a
209 // full payload cannot be reported alongside n == size (the stream
210 // contract forbids error + full transfer, and capy::write's
211 // composed loop discards ec exactly in that case); the error is
212 // held here and surfaced by the next operation instead.
213 std::error_code pending_flush_ec_;
214
215
10/24
✓ Branch 0 taken 137332 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 137332 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 137332 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 137332 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 137332 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 137332 times.
✓ Branch 15 taken 137332 times.
✓ Branch 16 taken 137332 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 238998 times.
✓ Branch 19 taken 137332 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
889992x capy::task<std::error_code> flush_output()
216 137332x {
217
5/6
✓ Branch 0 taken 137332 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17834 times.
✓ Branch 3 taken 119498 times.
✓ Branch 4 taken 17833 times.
✓ Branch 5 taken 1 time.
137332x if (eng_.pending_output() == 0 && out_len_ == 0)
218
1/2
✓ Branch 0 taken 17833 times.
✗ Branch 1 not taken.
17833x co_return std::error_code{};
219
220
5/12
✓ Branch 0 taken 119499 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 119499 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 119499 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 238998 times.
✓ Branch 11 taken 358497 times.
119499x auto [lec] = co_await wr_cm_.lock();
221
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 358497 times.
358497x if (lec)
222
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ co_return lec;
223 358497x capy::async_mutex::lock_guard wr_guard(&wr_cm_);
224
225 // Drain under the claim so records reach the wire in engine
226 // order even when both directions produce output.
227
5/6
✓ Branch 0 taken 477797 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 358496 times.
✓ Branch 3 taken 119301 times.
✓ Branch 4 taken 119300 times.
✓ Branch 5 taken 358497 times.
477797x while (eng_.pending_output() > 0 || out_len_ > 0)
228 {
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 358497 times.
358497x if (orphaned())
230
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ co_return make_error_code(capy::error::canceled);
231
232 // Take the abandoned tail (from an earlier failed write in
233 // this loop or a prior flush) into a local and append fresh
234 // engine ciphertext behind it, preserving record order.
235 // out_len_ drops to zero for the duration of the write: the
236 // in-flight length lives only in `n`, so a concurrent flush
237 // probe never sees these bytes as a tail and never parks on
238 // the write claim behind a healthy write.
239 358497x std::size_t n = out_len_;
240 358497x out_len_ = 0;
241
2/2
✓ Branch 0 taken 238998 times.
✓ Branch 1 taken 119499 times.
358497x if (n < out_buf_.size())
242
2/4
✓ Branch 0 taken 119499 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 119499 times.
✗ Branch 3 not taken.
238998x n += eng_.get_output(
243 119499x reinterpret_cast<unsigned char*>(out_buf_.data()) + n,
244 119499x out_buf_.size() - n);
245 // The loop guard just confirmed pending bytes exist, so a
246 // drain failure here is unreachable in practice; fail loudly
247 // rather than silently drop already-accepted ciphertext.
248 − if (n == 0) // LCOV_EXCL_LINE unreachable: pending bytes confirmed
249
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ co_return make_error_code(
250 std::errc::
251 no_buffer_space); // LCOV_EXCL_LINE unreachable: transport returned 0 with no error unreachable: pending bytes confirmed
252 // capy::write's loop, with the stream's destruction checked
253 // between partial writes: once orphaned, the transport is
254 // no longer this driver's to write to.
255 358497x std::error_code ec;
256 358497x std::size_t wn = 0;
257
2/2
✓ Branch 0 taken 119300 times.
✓ Branch 1 taken 358497 times.
477797x while (wn < n)
258 {
259 358497x transport_call call(*this);
260
1/2
✓ Branch 0 taken 358497 times.
✗ Branch 1 not taken.
358497x if (!call)
261 {
262 ✗ ec = make_error_code(capy::error::canceled);
263 ✗ break;
264 }
265
9/16
✓ Branch 0 taken 358497 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 358497 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 358497 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 119499 times.
✓ Branch 7 taken 238998 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 119499 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 119499 times.
✓ Branch 13 taken 119499 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 119499 times.
716994x auto [wec, k] = co_await s_.write_some(
266 358497x capy::const_buffer(out_buf_.data() + wn, n - wn));
267 119499x call.end();
268 119499x wn += k;
269
4/4
✓ Branch 0 taken 199 times.
✓ Branch 1 taken 119300 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 198 times.
119499x if (wn < n && wec)
270 {
271 198x ec = wec;
272 198x break;
273 }
274
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 119300 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
119301x if (wn < n && orphaned())
275 {
276 1x ec = make_error_code(capy::error::canceled);
277 1x break;
278 }
279 119499x }
280
2/2
✓ Branch 0 taken 199 times.
✓ Branch 1 taken 119300 times.
119499x if (ec)
281 {
282 // wn bytes already reached the peer; keep only the unsent
283 // remainder so a post-cancellation flush retry resends
284 // neither the delivered prefix nor loses the rest.
285 199x std::memmove(out_buf_.data(), out_buf_.data() + wn, n - wn);
286 199x out_len_ = n - wn;
287
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 199 times.
199x co_return ec;
288 }
289 119499x }
290
1/2
✓ Branch 0 taken 119300 times.
✗ Branch 1 not taken.
119300x co_return std::error_code{};
291 597495x }
292
293 // Flushes on a fatal/alert exit path: the engine may have queued a
294 // close_notify or alert that should still reach the peer, but a
295 // caller already reporting the triggering error cannot also report
296 // this flush's own failure. flush_output() itself no-ops when
297 // nothing is pending.
298
12/26
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 40 times.
✓ Branch 7 taken 60 times.
✓ Branch 8 taken 20 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 20 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 16 taken 20 times.
✓ Branch 17 taken 20 times.
✓ Branch 18 taken 20 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 40 times.
✓ Branch 21 taken 20 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
100x capy::task<void> best_effort_flush()
299 20x {
300
8/14
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 20 times.
✓ Branch 5 taken 40 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 20 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 20 times.
✓ Branch 11 taken 20 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 20 times.
60x std::ignore = co_await flush_output();
301 40x }
302
303 // gen is the caller's read_gen_ snapshot from its engine call; a
304 // later snapshot would miss deposits landing while the caller's
305 // flush_output was suspended.
306
10/24
✓ Branch 0 taken 17780 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17780 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 17780 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 17780 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 17779 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 17779 times.
✓ Branch 15 taken 17779 times.
✓ Branch 16 taken 17779 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 35554 times.
✓ Branch 19 taken 17780 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
124448x capy::task<std::error_code> fill_input(std::uint64_t gen)
307 17780x {
308 // Input already arrived since the engine's verdict; retry the
309 // engine rather than queue behind a re-parked reader.
310
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17780 times.
17780x if (read_gen_ != gen)
311
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ co_return std::error_code{};
312
313
4/10
✓ Branch 0 taken 17780 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17780 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 35554 times.
✓ Branch 9 taken 53334 times.
17780x auto [lec] = co_await rd_cm_.lock();
314
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53334 times.
53334x if (lec)
315
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ co_return lec;
316 53334x capy::async_mutex::lock_guard rd_guard(&rd_cm_);
317
318 // Input arrived while we queued for the claim; the caller's
319 // engine retry consumes it.
320
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 53333 times.
53334x if (read_gen_ != gen)
321
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x co_return std::error_code{};
322
323
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53333 times.
53333x if (orphaned())
324
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ co_return make_error_code(capy::error::canceled);
325
326 // Read the transport straight into the engine's input staging:
327 // input_area() hands back the contiguous writable run, so no
328 // staging buffer or deposit copy sits between the socket and the
329 // engine.
330
1/2
✓ Branch 0 taken 53333 times.
✗ Branch 1 not taken.
159997x auto [dst, cap] = eng_.input_area();
331 // The staging is full while the caller still wants input: its own
332 // engine retry must decrypt the staged record to free space, so
333 // report success to re-run it rather than issue a zero-length read.
334
2/2
✓ Branch 0 taken 53332 times.
✓ Branch 1 taken 1 time.
53333x if (cap == 0)
335
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x co_return std::error_code{};
336
337 53332x transport_call call(*this);
338
1/2
✓ Branch 0 taken 53332 times.
✗ Branch 1 not taken.
53332x if (!call)
339
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ co_return make_error_code(capy::error::canceled);
340
13/18
✓ Branch 0 taken 53332 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 53332 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 53332 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 53332 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 17778 times.
✓ Branch 9 taken 35554 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 17778 times.
✓ Branch 12 taken 1 time.
✓ Branch 13 taken 1 time.
✓ Branch 14 taken 17777 times.
✓ Branch 15 taken 17778 times.
✓ Branch 16 taken 1 time.
✓ Branch 17 taken 17777 times.
159996x auto [ec, n] = co_await s_.read_some(capy::mutable_buffer(dst, cap));
341 17777x call.end();
342
343 // The stream went away while the read was parked; its result
344 // belongs to no one.
345
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 17774 times.
17777x if (orphaned())
346
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3x co_return make_error_code(capy::error::canceled);
347
348 // ReadStream permits n>0 alongside ec (IOCP forwards
349 // bytes_transferred on failed completions; a canceled read can
350 // likewise deliver bytes); commit whatever arrived before
351 // surfacing ec, or the record stream loses wire bytes the
352 // transport already handed over.
353
2/2
✓ Branch 0 taken 16695 times.
✓ Branch 1 taken 1079 times.
17774x if (n > 0)
354 {
355
2/4
✓ Branch 0 taken 16695 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16695 times.
✗ Branch 3 not taken.
33390x eng_.input_committed(n);
356 16695x ++read_gen_;
357
2/4
✓ Branch 0 taken 16695 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16695 times.
✗ Branch 3 not taken.
33390x co_return ec;
358 }
359
360
2/2
✓ Branch 0 taken 1078 times.
✓ Branch 1 taken 1 time.
1079x if (ec)
361
2/4
✓ Branch 0 taken 1078 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1078 times.
✗ Branch 3 not taken.
2156x co_return ec;
362
363 // The transport delivered nothing without an error, so it cannot
364 // make progress: fail loudly rather than spin the engine's input
365 // retry against a staging that will never fill.
366
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x co_return make_error_code(
367 std::errc::
368 no_buffer_space); // LCOV_EXCL_LINE unreachable: staging cannot stay empty
369 88890x }
370
371 5636x void destroy_transport() noexcept
372 {
373
2/2
✓ Branch 0 taken 2818 times.
✓ Branch 1 taken 2818 times.
5636x if (transport_gone_.exchange(true, std::memory_order_acq_rel))
374 2818x return;
375 2818x auto const owned = std::exchange(owned_, tls_owned_transport{});
376
2/2
✓ Branch 0 taken 2812 times.
✓ Branch 1 taken 6 times.
2818x if (owned.destroy)
377 6x owned.destroy(owned.p);
378 }
379
380 // Return false once orphaned: the transport may already be gone.
381 // The orphan check follows the count's increment, pairing with
382 // orphan()'s store-then-load, so one side always sees the other.
383 137279x bool begin_transport_call() noexcept
384 {
385 137279x calls_.fetch_add(1, std::memory_order_seq_cst);
386
1/2
✓ Branch 0 taken 137279 times.
✗ Branch 1 not taken.
137279x if (!orphaned_.load(std::memory_order_seq_cst))
387 137279x return true;
388 ✗ end_transport_call();
389 ✗ return false;
390 137279x }
391
392 140097x void end_transport_call() noexcept
393 {
394
4/4
✓ Branch 0 taken 139965 times.
✓ Branch 1 taken 132 times.
✓ Branch 2 taken 137147 times.
✓ Branch 3 taken 2818 times.
140097x if (calls_.fetch_sub(1, std::memory_order_seq_cst) == 1 &&
395 139965x orphaned_.load(std::memory_order_seq_cst))
396 2818x destroy_transport();
397 140097x }
398
399 // A transport call that also ends when its frame is unwound while
400 // parked, so the count never pins an orphaned transport.
401 class transport_call
402 {
403 engine_driver* d_;
404
405 public:
406 274554x ~transport_call()
407 137277x {
408 137277x end();
409 274554x }
410
411 274554x explicit transport_call(engine_driver& d) noexcept
412
1/2
✓ Branch 0 taken 137277 times.
✗ Branch 1 not taken.
137277x : d_(d.begin_transport_call() ? &d : nullptr)
413 137277x {
414 274554x }
415
416 transport_call(transport_call const&) = delete;
417 transport_call& operator=(transport_call const&) = delete;
418
419 137277x explicit operator bool() const noexcept
420 {
421 137277x return d_ != nullptr;
422 }
423
424 274553x void end() noexcept
425 {
426
2/2
✓ Branch 0 taken 137276 times.
✓ Branch 1 taken 137277 times.
274553x if (d_)
427 137277x std::exchange(d_, nullptr)->end_transport_call();
428 274553x }
429 };
430
431 // A prior read/write already reported its full transfer as success;
432 // the trailing flush error it deferred surfaces on the next call
433 // instead. Each entry point takes it exactly once.
434 233074x std::error_code take_pending_flush_ec() noexcept
435 {
436 233074x std::error_code ec = pending_flush_ec_;
437 233074x pending_flush_ec_ = {};
438 233074x return ec;
439 }
440
441 public:
442 /** Construct a driver over a transport stream.
443
444 The driver starts with one reference, the creating stream's.
445
446 @param s The transport.
447 @param owned The transport `s` refers to, when the stream owns
448 it; the driver destroys it on @ref orphan.
449 @param ctx The TLS context handed to the engine's handshake
450 preparation.
451 */
452 11272x engine_driver(
453 capy::any_stream s, tls_owned_transport owned, tls_context ctx)
454 2818x : owned_(owned)
455 2818x , s_(std::move(s))
456 2818x , ctx_(std::move(ctx))
457 {
458
1/2
✓ Branch 0 taken 2818 times.
✗ Branch 1 not taken.
2818x out_buf_.resize(buffer_size_);
459 2818x }
460
461 2818x ~engine_driver()
462 {
463 2818x destroy_transport();
464 2818x }
465
466 engine_driver(engine_driver const&) = delete;
467 engine_driver& operator=(engine_driver const&) = delete;
468
469 /// Add a reference for an operation in flight.
470 235178x void add_ref() noexcept
471 {
472 235178x refs_.fetch_add(1, std::memory_order_relaxed);
473 235178x }
474
475 /// Drop a reference; return true if it was the last.
476 237996x bool release_ref() noexcept
477 {
478 237996x return refs_.fetch_sub(1, std::memory_order_acq_rel) == 1;
479 }
480
481 /** Detach the driver from its destroyed stream.
482
483 Operations still in flight finish as canceled without touching
484 the transport again. An owned transport is destroyed now when
485 no transport call is parked on it; otherwise it is told to
486 cancel, if it can, and destroyed when the last parked call
487 returns.
488 */
489 2818x void orphan() noexcept
490 {
491 // Counted as a call itself, so a parked call returning meanwhile
492 // cannot destroy the transport under the cancel.
493 2818x calls_.fetch_add(1, std::memory_order_seq_cst);
494 2818x orphaned_.store(true, std::memory_order_seq_cst);
495
2/2
✓ Branch 0 taken 2814 times.
✓ Branch 1 taken 4 times.
2818x if (owned_.cancel)
496 4x owned_.cancel(owned_.p);
497 2818x end_transport_call();
498 2818x }
499
500 /// Cancel the owned transport's pending operations, if it can.
501 2x void cancel_transport() noexcept
502 {
503
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2x if (!begin_transport_call())
504 ✗ return;
505
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2x if (owned_.cancel)
506 1x owned_.cancel(owned_.p);
507 2x end_transport_call();
508 2x }
509
510 /// Return true once the stream has been destroyed.
511 155056x bool orphaned() const noexcept
512 {
513 155056x return orphaned_.load(std::memory_order_acquire);
514 }
515
516 /// Return the transport.
517 2x capy::any_stream& stream() noexcept
518 {
519 2x return s_;
520 }
521
522 /// Return the transport.
523 capy::any_stream const& stream() const noexcept
524 {
525 return s_;
526 }
527
528 /// Return the engine for backend-specific setup.
529 Engine& engine() noexcept
530 {
531 return eng_;
532 }
533
534 /// Return the TLS context this driver was constructed with.
535 tls_context const& context() const noexcept
536 {
537 return ctx_;
538 }
539
540 /// Set the hostname applied to the next client handshake.
541 15x void set_hostname(std::string_view hostname)
542 {
543 15x hostname_ = hostname;
544 15x }
545
546 /// Return the ALPN protocol negotiated by the last handshake.
547 3x std::string_view alpn_protocol() const noexcept
548 {
549 3x return alpn_selected_;
550 }
551
552 /// Reset the engine and every per-connection driver state.
553 67x void reset()
554 {
555 67x eng_.reset();
556
557 67x out_len_ = 0;
558
559 67x alpn_selected_.clear();
560 67x pending_flush_ec_ = {};
561 67x used_ = false;
562 67x }
563
564
10/24
✓ Branch 0 taken 116496 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 116496 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 116496 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 116496 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 116495 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 116495 times.
✓ Branch 15 taken 116495 times.
✓ Branch 16 taken 116495 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 116496 times.
✓ Branch 19 taken 4 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
349496x capy::task<capy::results<std::size_t>> do_read_some(
565 capy::detail::mutable_buffer_array<capy::detail::max_iovec_> buffers)
566 116496x {
567
4/4
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 116483 times.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 116483 times.
116509x if (auto ec = take_pending_flush_ec())
568
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13x co_return {ec, 0};
569
570 116483x std::error_code ec;
571 116483x std::size_t total_read = 0;
572 116483x std::size_t const bufs_size = capy::buffer_size(buffers);
573
574
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 116482 times.
✓ Branch 2 taken 116476 times.
✓ Branch 3 taken 1 time.
232959x for (auto& buf : buffers)
575 {
576 116482x char* const dest = static_cast<char*>(buf.data());
577 // Clamp for the engines' int-sized transfer API: a >=2 GiB
578 // buffer becomes a partial read, never a truncated length.
579 116482x int const remaining = static_cast<int>(
580
2/4
✓ Branch 0 taken 116482 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 116482 times.
✗ Branch 3 not taken.
232964x (std::min)(buf.size(),
581 116482x std::size_t((std::numeric_limits<int>::max)())));
582
1/2
✓ Branch 0 taken 116482 times.
✗ Branch 1 not taken.
116482x if (remaining == 0)
583 ✗ continue;
584
585 // Exits by co_return: success after the first transferred
586 // chunk, or any terminal error.
587 161864x while (true)
588 {
589 161864x auto const gen = read_gen_;
590
4/4
✓ Branch 0 taken 131584 times.
✓ Branch 1 taken 30280 times.
✓ Branch 2 taken 131584 times.
✓ Branch 3 taken 30280 times.
323728x auto r = eng_.perform(
591 161864x engine_op::read, dest, static_cast<std::size_t>(remaining));
592
593
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 131520 times.
131584x if (r.ec)
594 {
595 // Terminal, already mapped by the engine. eof (a
596 // received close_notify) arrives as a plain done:
597 // it queues no output, so no flush is needed.
598
2/2
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 3 times.
64x if (r.want == engine_want::output_then_done)
599
8/14
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 time.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 time.
✓ Branch 11 taken 1 time.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 time.
3x co_await best_effort_flush();
600
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 2 times.
62x co_return {r.ec, total_read};
601 }
602
603
4/4
✓ Branch 0 taken 15139 times.
✓ Branch 1 taken 116381 times.
✓ Branch 2 taken 30278 times.
✓ Branch 3 taken 45417 times.
131520x if (r.want == engine_want::done ||
604 15139x r.want == engine_want::output_then_done)
605 {
606 146659x total_read += r.bytes;
607
608 // r.bytes > 0 already satisfies ReadStream's "at
609 // least one byte transferred" success condition;
610 // report now rather than loop for more (another
611 // engine call could park on input). out_len_ > 0
612 // covers a retained tail the engine cannot see.
613
4/4
✓ Branch 0 taken 116381 times.
✓ Branch 1 taken 30278 times.
✓ Branch 2 taken 30278 times.
✓ Branch 3 taken 86103 times.
146659x if (r.want == engine_want::output_then_done || out_len_ > 0)
614
0/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
60556x ec = co_await flush_output();
615
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 86103 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
86103x if (ec && total_read == bufs_size)
616 {
617 // First failure wins: concurrent directions share
618 // one transport failure domain; the earliest
619 // error is the meaningful one.
620
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (!pending_flush_ec_)
621 ✗ pending_flush_ec_ = ec;
622 ✗ ec = {};
623 ✗ }
624
2/2
✓ Branch 0 taken 116381 times.
✓ Branch 1 taken 30278 times.
86103x co_return {ec, total_read};
625 }
626
627
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45417 times.
45417x if (r.want == engine_want::output_then_retry)
628 {
629
0/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ ec = co_await flush_output();
630
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (ec)
631
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ co_return {ec, total_read};
632 ✗ continue;
633 }
634
635 // want == input. Flush first: a retained tail (an
636 // earlier failed write) may hold bytes the peer needs
637 // before it will send more; no-op when nothing pends.
638
9/14
✗ Branch 0 not taken.
✓ Branch 1 taken 45417 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 45417 times.
✓ Branch 4 taken 15139 times.
✓ Branch 5 taken 30278 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 15139 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 15139 times.
✓ Branch 11 taken 15139 times.
✓ Branch 12 taken 30274 times.
✓ Branch 13 taken 45413 times.
45417x ec = co_await flush_output();
639
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 45412 times.
45413x if (ec)
640
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x co_return {ec, total_read};
641
642
11/14
✗ Branch 0 not taken.
✓ Branch 1 taken 45412 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 45412 times.
✓ Branch 4 taken 15138 times.
✓ Branch 5 taken 30274 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 15138 times.
✓ Branch 8 taken 1 time.
✓ Branch 9 taken 1 time.
✓ Branch 10 taken 15137 times.
✓ Branch 11 taken 15138 times.
✓ Branch 12 taken 1 time.
✓ Branch 13 taken 15137 times.
45412x ec = co_await fill_input(gen);
643
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 15102 times.
15137x if (ec)
644 {
645 35x ec = map_fill_error(
646
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35x engine_op::read, ec, eng_.received_shutdown());
647
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35x co_return {ec, total_read};
648 }
649 161858x }
650 116476x }
651
652
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x co_return {std::error_code{}, total_read};
653 237593x }
654
655
10/24
✓ Branch 0 taken 116432 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 116432 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 116432 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 116432 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 116432 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 116432 times.
✓ Branch 15 taken 116432 times.
✓ Branch 16 taken 116432 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 116432 times.
✓ Branch 19 taken 4 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
349304x capy::task<capy::results<std::size_t>> do_write_some(
656 capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers)
657 116432x {
658
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 116430 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 116430 times.
116434x if (auto ec = take_pending_flush_ec())
659
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x co_return {ec, 0};
660
661 116430x std::error_code ec;
662 116430x std::size_t total_written = 0;
663 116430x std::size_t const bufs_size = capy::buffer_size(buffers);
664
665
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 116429 times.
✓ Branch 2 taken 116425 times.
✓ Branch 3 taken 1 time.
232855x for (auto const& buf : buffers)
666 {
667 // The engine only reads through this pointer for a write
668 // op; the cast satisfies perform's single transfer
669 // signature.
670 116429x void* const src = const_cast<void*>(buf.data());
671 // Clamp for the engines' int-sized transfer API: a >=2 GiB
672 // buffer becomes a partial write, never a truncated length.
673 116429x int const remaining = static_cast<int>(
674
2/4
✓ Branch 0 taken 116429 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 116429 times.
✗ Branch 3 not taken.
232858x (std::min)(buf.size(),
675 116429x std::size_t((std::numeric_limits<int>::max)())));
676
1/2
✓ Branch 0 taken 116429 times.
✗ Branch 1 not taken.
116429x if (remaining == 0)
677 ✗ continue;
678
679 // Exits by co_return: success after the first transferred
680 // chunk, or any terminal error.
681 349283x while (true)
682 {
683 349283x auto const gen = read_gen_;
684
4/4
✓ Branch 0 taken 116429 times.
✓ Branch 1 taken 232854 times.
✓ Branch 2 taken 116429 times.
✓ Branch 3 taken 232854 times.
698566x auto r = eng_.perform(
685 349283x engine_op::write, src, static_cast<std::size_t>(remaining));
686
687
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 116427 times.
116429x if (r.ec)
688 {
689 // Terminal, already mapped. eof means the peer's
690 // close_notify WAS received (an announced close);
691 // it arrives as a plain done and needs no flush.
692
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x if (r.want == engine_want::output_then_done)
693
0/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ co_await best_effort_flush();
694
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x co_return {r.ec, total_written};
695 }
696
697
2/4
✓ Branch 0 taken 116427 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 116427 times.
✗ Branch 3 not taken.
116427x if (r.want == engine_want::done ||
698 116427x r.want == engine_want::output_then_done)
699 {
700 116427x total_written += r.bytes;
701
702 // r.bytes > 0 already satisfies WriteStream's "at
703 // least one byte transferred" success condition;
704 // report now rather than loop for more. out_len_ > 0
705 // covers a retained tail the engine cannot see.
706
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 116427 times.
✓ Branch 2 taken 232854 times.
✓ Branch 3 taken 232854 times.
116427x if (r.want == engine_want::output_then_done || out_len_ > 0)
707
8/14
✓ Branch 0 taken 349281 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 349281 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 116427 times.
✓ Branch 5 taken 232854 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 116427 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 116427 times.
✓ Branch 11 taken 116427 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 116427 times.
349281x ec = co_await flush_output();
708
4/4
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 349263 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 17 times.
349281x if (ec && total_written == bufs_size)
709 {
710 // First failure wins: concurrent directions share
711 // one transport failure domain; the earliest
712 // error is the meaningful one.
713
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17x if (!pending_flush_ec_)
714 17x pending_flush_ec_ = ec;
715 17x ec = {};
716 17x }
717
2/2
✓ Branch 0 taken 116427 times.
✓ Branch 1 taken 232854 times.
349281x co_return {ec, total_written};
718 }
719
720
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (r.want == engine_want::output_then_retry)
721 {
722
0/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ ec = co_await flush_output();
723
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (ec)
724
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ co_return {ec, total_written};
725 ✗ continue;
726 }
727
728 // want == input: a write can need a read mid-rekey; the
729 // transport eof this surfaces means the same thing it
730 // means on the read path, so map it the same way. Flush
731 // first for the same retained-tail reason as the read
732 // path.
733
0/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ ec = co_await flush_output();
734
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (ec)
735
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ co_return {ec, total_written};
736
737
0/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ ec = co_await fill_input(gen);
738
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (ec)
739 {
740 ✗ ec = map_fill_error(
741
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ engine_op::write, ec, eng_.received_shutdown());
742
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ co_return {ec, total_written};
743 }
744 116429x }
745 116425x }
746
747
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x co_return {std::error_code{}, total_written};
748 814988x }
749
750
10/24
✓ Branch 0 taken 2102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2102 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2102 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2102 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2102 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 2102 times.
✓ Branch 15 taken 2102 times.
✓ Branch 16 taken 2102 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 2102 times.
✓ Branch 19 taken 274 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
6854x capy::task<capy::results<>> do_handshake(tls_role role)
751 2102x {
752 // Refuse the handshake while the engine's configuration is
753 // unusable, before consuming any per-connection state.
754
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2102 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2102 times.
2102x if (auto cec = eng_.check_context())
755
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ co_return {cec};
756
757
2/2
✓ Branch 0 taken 2095 times.
✓ Branch 1 taken 7 times.
2102x if (used_)
758
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7x reset();
759
760 // reset() may not have restored a usable session; refuse to
761 // hand out a handshake on it.
762
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2100 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2100 times.
2104x if (auto sec = eng_.check_session())
763
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x co_return {sec};
764
765 // A failed attempt leaves the session in a dead state; any
766 // attempt, not just a completed handshake, consumes the stream
767 // so the next handshake() starts fresh.
768 2100x used_ = true;
769
770
5/6
✓ Branch 0 taken 2100 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 2078 times.
✓ Branch 4 taken 296 times.
✓ Branch 5 taken 2396 times.
2122x if (auto pec = eng_.prepare(ctx_, role, hostname_))
771
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22x co_return {pec};
772
773 2396x auto const op = role == tls_role::client ? engine_op::handshake_client
774 : engine_op::handshake_server;
775
776 2396x std::error_code ec;
777
778 10794x while (true)
779 {
780 10794x auto const gen = read_gen_;
781
2/2
✓ Branch 0 taken 5446 times.
✓ Branch 1 taken 5348 times.
10794x auto r = eng_.perform(op, nullptr, 0);
782
783
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 5426 times.
5446x if (r.ec)
784 {
785
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 57 times.
20x if (r.want == engine_want::output_then_done)
786
8/14
✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 57 times.
✓ Branch 4 taken 19 times.
✓ Branch 5 taken 38 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 19 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 19 times.
✓ Branch 11 taken 19 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 19 times.
57x co_await best_effort_flush();
787
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 36 times.
56x co_return {r.ec};
788 }
789
790
4/4
✓ Branch 0 taken 5424 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 8082 times.
✓ Branch 3 taken 13506 times.
5426x if (r.want == engine_want::done ||
791 5424x r.want == engine_want::output_then_done)
792 {
793
2/2
✓ Branch 0 taken 2772 times.
✓ Branch 1 taken 5312 times.
8084x eng_.capture_alpn(alpn_selected_);
794
8/14
✗ Branch 0 not taken.
✓ Branch 1 taken 2772 times.
✓ Branch 2 taken 2772 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 924 times.
✓ Branch 5 taken 1848 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 924 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 924 times.
✓ Branch 11 taken 924 times.
✓ Branch 12 taken 924 times.
✗ Branch 13 not taken.
2772x ec = co_await flush_output();
795
1/2
✓ Branch 0 taken 924 times.
✗ Branch 1 not taken.
924x co_return {ec};
796 }
797
798
2/2
✓ Branch 0 taken 5961 times.
✓ Branch 1 taken 7545 times.
13506x if (r.want == engine_want::output_then_retry)
799 {
800 // Must flush (e.g. ClientHello) before reading the
801 // peer's reply.
802
8/14
✗ Branch 0 not taken.
✓ Branch 1 taken 5961 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5961 times.
✓ Branch 4 taken 1987 times.
✓ Branch 5 taken 3974 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1987 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1987 times.
✓ Branch 11 taken 1987 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1987 times.
5961x ec = co_await flush_output();
803
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 1870 times.
1987x if (ec)
804
1/2
✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
117x co_return {ec};
805 1870x continue;
806 }
807
808 // want == input. The flush is a no-op unless a retained
809 // tail pends; the fill error passes through map_fill_error
810 // unchanged (no close is clean before the session is
811 // established).
812
9/14
✓ Branch 0 taken 7545 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7545 times.
✓ Branch 4 taken 2515 times.
✓ Branch 5 taken 5030 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2515 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2515 times.
✓ Branch 11 taken 2515 times.
✓ Branch 12 taken 5030 times.
✓ Branch 13 taken 7545 times.
7545x ec = co_await flush_output();
813
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7545 times.
7545x if (ec)
814
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ co_return {ec};
815
816
8/14
✓ Branch 0 taken 7545 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7545 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2515 times.
✓ Branch 5 taken 5030 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2515 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2515 times.
✓ Branch 11 taken 2515 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 2515 times.
7545x ec = co_await fill_input(gen);
817
2/2
✓ Branch 0 taken 1017 times.
✓ Branch 1 taken 1498 times.
2515x if (ec)
818 {
819
1/2
✓ Branch 0 taken 1017 times.
✗ Branch 1 not taken.
1017x ec = map_fill_error(op, ec, eng_.received_shutdown());
820
1/2
✓ Branch 0 taken 1017 times.
✗ Branch 1 not taken.
1017x co_return {ec};
821 }
822 10476x }
823 28694x }
824
825
10/24
✓ Branch 0 taken 146 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 146 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 146 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 146 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 146 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 146 times.
✓ Branch 15 taken 146 times.
✓ Branch 16 taken 146 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 146 times.
✓ Branch 19 taken 76 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
590x capy::task<capy::results<>> do_shutdown()
826 146x {
827
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 145 times.
✓ Branch 2 taken 77 times.
✓ Branch 3 taken 69 times.
147x if (auto ec = take_pending_flush_ec())
828
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1x co_return {ec};
829
830 69x std::error_code ec;
831
832 500x while (true)
833 {
834 362x auto const gen = read_gen_;
835
1/2
✓ Branch 0 taken 362 times.
✗ Branch 1 not taken.
362x auto r = eng_.perform(engine_op::shutdown, nullptr, 0);
836
837
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 360 times.
362x if (r.ec)
838 {
839
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x if (r.want == engine_want::output_then_done)
840
0/14
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ co_await best_effort_flush();
841
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2x co_return {r.ec};
842 }
843
844
4/4
✓ Branch 0 taken 269 times.
✓ Branch 1 taken 91 times.
✓ Branch 2 taken 391 times.
✓ Branch 3 taken 660 times.
360x if (r.want == engine_want::done ||
845 269x r.want == engine_want::output_then_done)
846 {
847 // Covers both the bidirectional-complete result and the
848 // engine's shutdown-after-close success mapping.
849
8/14
✓ Branch 0 taken 300 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 300 times.
✓ Branch 4 taken 100 times.
✓ Branch 5 taken 200 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 100 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 100 times.
✓ Branch 11 taken 100 times.
✓ Branch 12 taken 100 times.
✗ Branch 13 not taken.
482x ec = co_await flush_output();
850
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100x co_return {ec};
851 }
852
853
2/2
✓ Branch 0 taken 279 times.
✓ Branch 1 taken 381 times.
660x if (r.want == engine_want::output_then_retry)
854 {
855 // Sends our close_notify before parking for the peer's.
856
8/14
✗ Branch 0 not taken.
✓ Branch 1 taken 279 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 279 times.
✓ Branch 4 taken 93 times.
✓ Branch 5 taken 186 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 93 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 93 times.
✓ Branch 11 taken 93 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 93 times.
279x ec = co_await flush_output();
857
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 81 times.
93x if (ec)
858
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12x co_return {ec};
859 81x continue;
860 }
861
862 // want == input: awaiting the peer's close_notify. It may
863 // already have been deposited (and consumed by a concurrent
864 // reader) during a flush; fill_input then returns without
865 // reading and the loop retries the engine instead of parking.
866
9/14
✗ Branch 0 not taken.
✓ Branch 1 taken 381 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 381 times.
✓ Branch 4 taken 127 times.
✓ Branch 5 taken 254 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 127 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 127 times.
✓ Branch 11 taken 127 times.
✓ Branch 12 taken 254 times.
✓ Branch 13 taken 381 times.
381x ec = co_await flush_output();
867
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 381 times.
381x if (ec)
868
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ co_return {ec};
869
870
8/14
✓ Branch 0 taken 381 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 381 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 127 times.
✓ Branch 5 taken 254 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 127 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 127 times.
✓ Branch 11 taken 127 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 127 times.
381x ec = co_await fill_input(gen);
871
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 96 times.
127x if (ec)
872 {
873 31x ec = map_fill_error(
874
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31x engine_op::shutdown, ec, eng_.received_shutdown());
875
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31x co_return {ec};
876 }
877 576x }
878 1039x }
879 };
880
881 /** A reference to a TLS stream's driver held by an operation.
882
883 Taken when the operation is created, so a stream destroyed or
884 moved before the operation is awaited or finishes leaves its
885 driver alive until the operation lets go.
886
887 @tparam Impl The stream's implementation, derived from
888 @ref engine_driver.
889 */
890 template<class Impl>
891 class driver_ref
892 {
893 Impl* p_;
894
895 public:
896 /// Construct, adding a reference to @p p.
897 470356x explicit driver_ref(Impl* p) noexcept : p_(p)
898 235178x {
899 235178x p_->add_ref();
900 470356x }
901
902 /// Construct by taking @p other's reference.
903 470356x driver_ref(driver_ref&& other) noexcept
904 235178x : p_(std::exchange(other.p_, nullptr))
905 235178x {
906 470356x }
907
908 driver_ref(driver_ref const&) = delete;
909 driver_ref& operator=(driver_ref const&) = delete;
910 driver_ref& operator=(driver_ref&&) = delete;
911
912 /// Destroy, deleting the driver if this was its last reference.
913 940712x ~driver_ref()
914 470356x {
915
4/4
✓ Branch 0 taken 235178 times.
✓ Branch 1 taken 235178 times.
✓ Branch 2 taken 235172 times.
✓ Branch 3 taken 6 times.
470356x if (p_ && p_->release_ref())
916
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6x delete p_;
917 940712x }
918
919 /// Return the driver.
920 235176x Impl* operator->() const noexcept
921 {
922 235176x return p_;
923 }
924 };
925
926 /// Read through a driver an operation keeps alive.
927 template<class Impl>
928 capy::task<capy::results<std::size_t>>
929
12/24
✓ Branch 0 taken 116498 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 116498 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 116496 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 232988 times.
✓ Branch 7 taken 349486 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 116495 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 116495 times.
✓ Branch 15 taken 116495 times.
✓ Branch 16 taken 116495 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 232986 times.
✓ Branch 19 taken 116498 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
582478x driver_read_some(
930 driver_ref<Impl> d,
931 capy::detail::mutable_buffer_array<capy::detail::max_iovec_> buffers)
932 116498x {
933
12/16
✓ Branch 0 taken 349486 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 349486 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 116496 times.
✓ Branch 5 taken 232990 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 116496 times.
✓ Branch 8 taken 1 time.
✓ Branch 9 taken 1 time.
✓ Branch 10 taken 116495 times.
✓ Branch 11 taken 116496 times.
✓ Branch 12 taken 116495 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 1 time.
✓ Branch 15 taken 116495 times.
349486x co_return co_await d->do_read_some(buffers);
934 232992x }
935
936 /// Write through a driver an operation keeps alive.
937 template<class Impl>
938 capy::task<capy::results<std::size_t>>
939
11/24
✓ Branch 0 taken 116432 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 116432 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 116432 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 232864 times.
✓ Branch 7 taken 349296 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 116432 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 116432 times.
✓ Branch 15 taken 116432 times.
✓ Branch 16 taken 116432 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 232864 times.
✓ Branch 19 taken 116432 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
582160x driver_write_some(
940 driver_ref<Impl> d,
941 capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers)
942 116432x {
943
9/16
✓ Branch 0 taken 349296 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 349296 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 116432 times.
✓ Branch 5 taken 232864 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 116432 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 116432 times.
✓ Branch 11 taken 116432 times.
✓ Branch 12 taken 116432 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 116432 times.
349296x co_return co_await d->do_write_some(buffers);
944 232864x }
945
946 /// Handshake through a driver an operation keeps alive.
947 template<class Impl>
948 capy::task<capy::results<>>
949
11/24
✓ Branch 0 taken 2102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2102 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2102 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4204 times.
✓ Branch 7 taken 6306 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2102 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 2102 times.
✓ Branch 15 taken 2102 times.
✓ Branch 16 taken 2102 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 4204 times.
✓ Branch 19 taken 2102 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
10510x driver_handshake(driver_ref<Impl> d, tls_role role)
950 2102x {
951
9/16
✓ Branch 0 taken 6306 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6306 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2102 times.
✓ Branch 5 taken 4204 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2102 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2102 times.
✓ Branch 11 taken 2102 times.
✓ Branch 12 taken 2102 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 2102 times.
6306x co_return co_await d->do_handshake(role);
952 4204x }
953
954 /// Shut down through a driver an operation keeps alive.
955 template<class Impl>
956 capy::task<capy::results<>>
957
11/24
✓ Branch 0 taken 146 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 146 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 146 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 292 times.
✓ Branch 7 taken 438 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 146 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 146 times.
✓ Branch 15 taken 146 times.
✓ Branch 16 taken 146 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 292 times.
✓ Branch 19 taken 146 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
730x driver_shutdown(driver_ref<Impl> d)
958 146x {
959
9/16
✓ Branch 0 taken 438 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 438 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 146 times.
✓ Branch 5 taken 292 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 146 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 146 times.
✓ Branch 11 taken 146 times.
✓ Branch 12 taken 146 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 146 times.
438x co_return co_await d->do_shutdown();
960 292x }
961
962 } // namespace detail
963
964 } // namespace boost::corosio
965
966 #endif
967