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)
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 |