src/corosio/src/tls/detail/engine_driver.hpp
85.3% Lines (214/251)
100.0% List of functions (14/14)
47.1% Branches (379/804)
Functions (14)
Function
Calls
Lines
Branches
Blocks
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::~engine_driver()
:157
3071x
100.0%
–
100.0%
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::flush_output()
:200
168953x
92.6%
51.1%
55.0%
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::best_effort_flush()
:253
19x
100.0%
50.0%
48.0%
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::fill_input(unsigned long long)
:261
22471x
82.6%
44.4%
47.0%
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::take_pending_flush_ec()
:316
286307x
100.0%
–
100.0%
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::engine_driver(boost::capy::any_stream&, boost::corosio::tls_context)
:331
3071x
100.0%
50.0%
50.0%
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::rebind_stream(boost::capy::any_stream&)
:354
7x
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>>)
:361
13x
100.0%
–
100.0%
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::alpn_protocol() const
:368
3x
100.0%
–
100.0%
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::reset()
:374
65x
100.0%
–
100.0%
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::do_read_some(boost::capy::detail::buffer_array<16ul, false>)
:386
143113x
80.0%
43.3%
41.0%
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::do_write_some(boost::capy::detail::buffer_array<16ul, true>)
:476
143061x
68.0%
32.7%
34.0%
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::do_handshake(boost::corosio::tls_role)
:569
2286x
92.3%
59.3%
56.0%
boost::corosio::detail::engine_driver<boost::corosio::detail::openssl::engine>::do_shutdown()
:645
133x
93.5%
52.3%
51.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/io_task.hpp> | |||
| 23 | #include <boost/capy/task.hpp> | |||
| 24 | #include <boost/capy/write.hpp> | |||
| 25 | ||||
| 26 | #include <concepts> | |||
| 27 | #include <cstddef> | |||
| 28 | #include <cstdint> | |||
| 29 | #include <cstring> | |||
| 30 | #include <string> | |||
| 31 | #include <string_view> | |||
| 32 | #include <system_error> | |||
| 33 | #include <tuple> | |||
| 34 | #include <utility> | |||
| 35 | #include <vector> | |||
| 36 | ||||
| 37 | // This header is instantiated in both backends' TUs, whose vendor | |||
| 38 | // headers cannot coexist (WolfSSL's OpenSSL-compat layer clashes | |||
| 39 | // with genuine OpenSSL declarations on some builds); it must stay | |||
| 40 | // vendor-free. | |||
| 41 | #ifdef OPENSSL_VERSION_NUMBER | |||
| 42 | #error engine_driver.hpp must stay vendor-header-free | |||
| 43 | #endif | |||
| 44 | #ifdef WOLFSSL_VERSION | |||
| 45 | #error engine_driver.hpp must stay vendor-header-free | |||
| 46 | #endif | |||
| 47 | ||||
| 48 | /* | |||
| 49 | TLS Driver Architecture | |||
| 50 | ||||
| 51 | TLS layer wrapping an underlying stream (via any_stream). One | |||
| 52 | read_some and one write_some may be in flight concurrently. | |||
| 53 | ||||
| 54 | Engine / Driver Split. The engine (each backend's detail/engine.hpp) | |||
| 55 | owns the TLS session, its byte interface, and every library-result- | |||
| 56 | to-error-code decision; it is synchronous and transport-free. This | |||
| 57 | driver owns the transport, the per-direction claims, and all | |||
| 58 | buffering, and loops on engine verdicts: | |||
| 59 | ||||
| 60 | 1. Call eng_.perform(op, data, len) | |||
| 61 | 2. output_then_retry: flush pending ciphertext, then retry | |||
| 62 | 3. input: feed transport bytes into the engine, then retry | |||
| 63 | 4. done / output_then_done: flush if pending, report the | |||
| 64 | (already mapped) ec | |||
| 65 | ||||
| 66 | Renegotiation causes cross-direction I/O: a read may need to | |||
| 67 | write handshake data, a write may need to read. Each operation | |||
| 68 | services whatever direction the engine requests. | |||
| 69 | ||||
| 70 | Full-Duplex Concurrency. rd_cm_ and wr_cm_ are separate transport | |||
| 71 | claims: a read parked on the network cannot stall a concurrent | |||
| 72 | write's flush, and vice versa. A cross-direction request (e.g. a | |||
| 73 | read needing to flush queued handshake output) takes the other | |||
| 74 | direction's claim itself. | |||
| 75 | ||||
| 76 | read_gen_ counts successful deposits into the engine. Each engine | |||
| 77 | call snapshots it before running; if fill_input later finds the | |||
| 78 | generation has moved (a concurrent operation's flush suspension let | |||
| 79 | a deposit land in the meantime) it retries the engine instead of | |||
| 80 | issuing a redundant transport read. | |||
| 81 | ||||
| 82 | The transport reads ciphertext straight into the engine's input | |||
| 83 | staging via input_area(), so no driver-side buffer or deposit copy | |||
| 84 | sits on the read path. The write path stays copy-based on purpose: | |||
| 85 | ciphertext drained from the engine into out_buf_[0, out_len_) is | |||
| 86 | decoupled from the engine's shared output staging, so a concurrent | |||
| 87 | reader can still emit its own records (an alert, a key update) while | |||
| 88 | a write is in flight. out_len_ holds only the abandoned tail a failed | |||
| 89 | or canceled write kept for retry; the length being written is a local, | |||
| 90 | never published, so a concurrent flush probe cannot mistake in-flight | |||
| 91 | bytes for a tail and park on the write claim behind a healthy write. | |||
| 92 | ||||
| 93 | A read or write that fully satisfies the caller's buffer still owes | |||
| 94 | a trailing flush (queued handshake or session-ticket output must | |||
| 95 | reach the peer before the call can report success). If that flush | |||
| 96 | fails, the transfer already succeeded and the stream contract | |||
| 97 | forbids reporting both a full count and an error in the same | |||
| 98 | result, so the error is stashed in pending_flush_ec_ and surfaced | |||
| 99 | by the next read, write, or shutdown call instead. | |||
| 100 | */ | |||
| 101 | ||||
| 102 | namespace boost::corosio { | |||
| 103 | ||||
| 104 | namespace detail { | |||
| 105 | ||||
| 106 | /** The engine surface `engine_driver` drives. | |||
| 107 | ||||
| 108 | A synchronous, transport-free TLS record engine: `perform` | |||
| 109 | advances one operation and returns a fully mapped verdict, while | |||
| 110 | `put_input` / `get_output` shuttle wire bytes. The handshake | |||
| 111 | hooks (`check_context`, `check_session`, `prepare`) let each | |||
| 112 | backend gate and configure a handshake at the protocol-mandated | |||
| 113 | points without the driver knowing the backend's session model. | |||
| 114 | */ | |||
| 115 | template<class Engine> | |||
| 116 | concept tls_engine = | |||
| 117 | requires( | |||
| 118 | Engine& e, | |||
| 119 | Engine const& ce, | |||
| 120 | engine_op op, | |||
| 121 | void* buf, | |||
| 122 | unsigned char const* in, | |||
| 123 | unsigned char* out, | |||
| 124 | std::size_t n, | |||
| 125 | tls_context const& ctx, | |||
| 126 | tls_role role, | |||
| 127 | std::string const& hostname, | |||
| 128 | std::string& alpn) | |||
| 129 | { | |||
| 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() } | |||
| 133 | -> std::same_as<std::pair<unsigned char*, std::size_t>>; | |||
| 134 | { e.input_committed(n) }; | |||
| 135 | { ce.pending_output() } -> std::same_as<std::size_t>; | |||
| 136 | { e.get_output(out, n) } -> std::same_as<std::size_t>; | |||
| 137 | { ce.received_shutdown() } -> std::same_as<bool>; | |||
| 138 | { ce.capture_alpn(alpn) }; | |||
| 139 | { e.reset() }; | |||
| 140 | { ce.check_context() } -> std::same_as<std::error_code>; | |||
| 141 | { ce.check_session() } -> std::same_as<std::error_code>; | |||
| 142 | { e.prepare(ctx, role, hostname) } -> std::same_as<std::error_code>; | |||
| 143 | }; | |||
| 144 | ||||
| 145 | /** Coroutine driver shared by every TLS backend. | |||
| 146 | ||||
| 147 | Hosts the transport, the per-direction claims, the buffering, and | |||
| 148 | the four operation loops; the engine type supplies all TLS | |||
| 149 | mechanics. Each backend's stream impl instantiates this template | |||
| 150 | with its own engine so the driver logic exists exactly once. | |||
| 151 | ||||
| 152 | @par Thread Safety | |||
| 153 | Distinct objects: Safe.@n | |||
| 154 | Shared objects: Unsafe. | |||
| 155 | */ | |||
| 156 | template<tls_engine Engine> | |||
| 157 | class engine_driver | |||
| 158 | { | |||
| 159 | // Large enough to hold the largest possible TLS record. | |||
| 160 | static constexpr std::size_t buffer_size_ = std::size_t{17} * 1024; | |||
| 161 | ||||
| 162 | capy::any_stream* s_; | |||
| 163 | tls_context ctx_; | |||
| 164 | Engine eng_; | |||
| 165 | ||||
| 166 | // A handshake was attempted (successfully or not); the stream | |||
| 167 | // must be reset before the next handshake. | |||
| 168 | 3071x | bool used_ = false; | ||
| 169 | ||||
| 170 | // Per-stream SNI/verification hostname, set via set_hostname(). | |||
| 171 | std::string hostname_; | |||
| 172 | ||||
| 173 | // ALPN protocol negotiated during the handshake (empty if none). | |||
| 174 | std::string alpn_selected_; | |||
| 175 | ||||
| 176 | std::vector<char> out_buf_; | |||
| 177 | ||||
| 178 | // One transport claim per direction so a parked reader cannot block | |||
| 179 | // a writer's flush. read_gen_ counts deposits into the engine: each | |||
| 180 | // operation captures it at its engine call and passes it to | |||
| 181 | // fill_input, which retries the engine instead of issuing a stale | |||
| 182 | // transport read when a deposit landed anywhere after the engine's | |||
| 183 | // verdict (including during an intervening flush suspension). | |||
| 184 | capy::async_mutex rd_cm_; | |||
| 185 | capy::async_mutex wr_cm_; | |||
| 186 | 3071x | std::uint64_t read_gen_ = 0; | ||
| 187 | ||||
| 188 | // Engine ciphertext drained but not yet written: the abandoned | |||
| 189 | // tail a failed or canceled transport write kept for retry. | |||
| 190 | // Zero while a write is in flight (that length is a local). | |||
| 191 | 3071x | std::size_t out_len_ = 0; | ||
| 192 | ||||
| 193 | // A trailing flush that fails after the engine already accepted a | |||
| 194 | // full payload cannot be reported alongside n == size (the stream | |||
| 195 | // contract forbids error + full transfer, and capy::write's | |||
| 196 | // composed loop discards ec exactly in that case); the error is | |||
| 197 | // held here and surfaced by the next operation instead. | |||
| 198 | std::error_code pending_flush_ec_; | |||
| 199 | ||||
| 200 |
10/24✓ Branch 0 taken 168953 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 168953 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 168953 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 168953 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 168953 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 168953 times.
✓ Branch 15 taken 168953 times.
✓ Branch 16 taken 168953 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 292864 times.
✓ Branch 19 taken 168953 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
|
1092587x | capy::task<std::error_code> flush_output() | |
| 201 | 168953x | { | ||
| 202 |
5/6✓ Branch 0 taken 168953 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22522 times.
✓ Branch 3 taken 146431 times.
✓ Branch 4 taken 22521 times.
✓ Branch 5 taken 1 time.
|
168953x | if (eng_.pending_output() == 0 && out_len_ == 0) | |
| 203 |
1/2✓ Branch 0 taken 22521 times.
✗ Branch 1 not taken.
|
22521x | co_return std::error_code{}; | |
| 204 | ||||
| 205 |
5/12✓ Branch 0 taken 146432 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 146432 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 146432 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 292864 times.
✓ Branch 11 taken 439296 times.
|
146432x | auto [lec] = co_await wr_cm_.lock(); | |
| 206 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 439296 times.
|
439296x | if (lec) | |
| 207 | ✗ | co_return lec; | ||
| 208 | 439296x | capy::async_mutex::lock_guard wr_guard(&wr_cm_); | ||
| 209 | ||||
| 210 | // Drain under the claim so records reach the wire in engine | |||
| 211 | // order even when both directions produce output. | |||
| 212 |
5/6✓ Branch 0 taken 585579 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 439295 times.
✓ Branch 3 taken 146284 times.
✓ Branch 4 taken 146283 times.
✓ Branch 5 taken 439296 times.
|
585579x | while (eng_.pending_output() > 0 || out_len_ > 0) | |
| 213 | { | |||
| 214 | // Take the abandoned tail (from an earlier failed write in | |||
| 215 | // this loop or a prior flush) into a local and append fresh | |||
| 216 | // engine ciphertext behind it, preserving record order. | |||
| 217 | // out_len_ drops to zero for the duration of the write: the | |||
| 218 | // in-flight length lives only in `n`, so a concurrent flush | |||
| 219 | // probe never sees these bytes as a tail and never parks on | |||
| 220 | // the write claim behind a healthy write. | |||
| 221 | 439296x | std::size_t n = out_len_; | ||
| 222 | 439296x | out_len_ = 0; | ||
| 223 |
2/2✓ Branch 0 taken 292864 times.
✓ Branch 1 taken 146432 times.
|
439296x | if (n < out_buf_.size()) | |
| 224 |
2/4✓ Branch 0 taken 146432 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 146432 times.
✗ Branch 3 not taken.
|
292864x | n += eng_.get_output( | |
| 225 | 146432x | reinterpret_cast<unsigned char*>(out_buf_.data()) + n, | ||
| 226 | 146432x | out_buf_.size() - n); | ||
| 227 | // The loop guard just confirmed pending bytes exist, so a | |||
| 228 | // drain failure here is unreachable in practice; fail loudly | |||
| 229 | // rather than silently drop already-accepted ciphertext. | |||
| 230 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 439296 times.
|
439296x | if (n == 0) | |
| 231 | ✗ | co_return make_error_code(std::errc::no_buffer_space); | ||
| 232 |
10/18✓ Branch 0 taken 439296 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 439296 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 439296 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 439296 times.
✓ Branch 8 taken 146432 times.
✓ Branch 9 taken 292864 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 146432 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 146432 times.
✓ Branch 15 taken 146432 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 146432 times.
|
1317888x | auto [ec, wn] = co_await capy::write( | |
| 233 | 878592x | *s_, capy::const_buffer(out_buf_.data(), n)); | ||
| 234 |
2/2✓ Branch 0 taken 149 times.
✓ Branch 1 taken 146283 times.
|
146432x | if (ec) | |
| 235 | { | |||
| 236 | // wn bytes already reached the peer; keep only the unsent | |||
| 237 | // remainder so a post-cancellation flush retry resends | |||
| 238 | // neither the delivered prefix nor loses the rest. | |||
| 239 | 149x | std::memmove( | ||
| 240 | 447x | out_buf_.data(), out_buf_.data() + wn, n - wn); | ||
| 241 | 298x | out_len_ = n - wn; | ||
| 242 |
2/4✓ Branch 0 taken 149 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 149 times.
✗ Branch 3 not taken.
|
298x | co_return ec; | |
| 243 | } | |||
| 244 | 146432x | } | ||
| 245 |
1/2✓ Branch 0 taken 146283 times.
✗ Branch 1 not taken.
|
146283x | co_return std::error_code{}; | |
| 246 | 732160x | } | ||
| 247 | ||||
| 248 | // Flushes on a fatal/alert exit path: the engine may have queued a | |||
| 249 | // close_notify or alert that should still reach the peer, but a | |||
| 250 | // caller already reporting the triggering error cannot also report | |||
| 251 | // this flush's own failure. flush_output() itself no-ops when | |||
| 252 | // nothing is pending. | |||
| 253 |
12/26✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 38 times.
✓ Branch 7 taken 57 times.
✓ Branch 8 taken 19 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 19 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 16 taken 19 times.
✓ Branch 17 taken 19 times.
✓ Branch 18 taken 19 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 38 times.
✓ Branch 21 taken 19 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
|
95x | capy::task<void> best_effort_flush() | |
| 254 | 19x | { | ||
| 255 |
8/14✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 57 times.
✗ Branch 3 not taken.
✓ 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 | std::ignore = co_await flush_output(); | |
| 256 | 38x | } | ||
| 257 | ||||
| 258 | // gen is the caller's read_gen_ snapshot from its engine call; a | |||
| 259 | // later snapshot would miss deposits landing while the caller's | |||
| 260 | // flush_output was suspended. | |||
| 261 |
10/24✓ Branch 0 taken 22471 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22471 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 22471 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 22471 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 22471 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 22471 times.
✓ Branch 15 taken 22471 times.
✓ Branch 16 taken 22471 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 44792 times.
✓ Branch 19 taken 22471 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
|
156997x | capy::task<std::error_code> fill_input(std::uint64_t gen) | |
| 262 | 22471x | { | ||
| 263 | // Input already arrived since the engine's verdict; retry the | |||
| 264 | // engine rather than queue behind a re-parked reader. | |||
| 265 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22471 times.
|
22471x | if (read_gen_ != gen) | |
| 266 | ✗ | co_return std::error_code{}; | ||
| 267 | ||||
| 268 |
4/10✓ Branch 0 taken 22471 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22471 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 44792 times.
✓ Branch 9 taken 67263 times.
|
22471x | auto [lec] = co_await rd_cm_.lock(); | |
| 269 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 67263 times.
|
67263x | if (lec) | |
| 270 | ✗ | co_return lec; | ||
| 271 | 67263x | capy::async_mutex::lock_guard rd_guard(&rd_cm_); | ||
| 272 | ||||
| 273 | // Input arrived while we queued for the claim; the caller's | |||
| 274 | // engine retry consumes it. | |||
| 275 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 67262 times.
|
67263x | if (read_gen_ != gen) | |
| 276 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1x | co_return std::error_code{}; | |
| 277 | ||||
| 278 | // Read the transport straight into the engine's input staging: | |||
| 279 | // input_area() hands back the contiguous writable run, so no | |||
| 280 | // staging buffer or deposit copy sits between the socket and the | |||
| 281 | // engine. | |||
| 282 |
1/2✓ Branch 0 taken 67262 times.
✗ Branch 1 not taken.
|
67262x | auto [dst, cap] = eng_.input_area(); | |
| 283 | // The staging is full while the caller still wants input: its own | |||
| 284 | // engine retry must decrypt the staged record to free space, so | |||
| 285 | // report success to re-run it rather than issue a zero-length read. | |||
| 286 |
1/2✓ Branch 0 taken 67262 times.
✗ Branch 1 not taken.
|
67262x | if (cap == 0) | |
| 287 | ✗ | co_return std::error_code{}; | ||
| 288 | ||||
| 289 | 89732x | auto [ec, n] = | ||
| 290 |
10/18✓ Branch 0 taken 67262 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67262 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 67262 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 67262 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 22396 times.
✓ Branch 9 taken 44866 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 22396 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 22470 times.
✓ Branch 15 taken 22396 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 22470 times.
|
201786x | co_await s_->read_some(capy::mutable_buffer(dst, cap)); | |
| 291 | ||||
| 292 | // ReadStream permits n>0 alongside ec (IOCP forwards | |||
| 293 | // bytes_transferred on failed completions; a canceled read can | |||
| 294 | // likewise deliver bytes); commit whatever arrived before | |||
| 295 | // surfacing ec, or the record stream loses wire bytes the | |||
| 296 | // transport already handed over. | |||
| 297 |
2/2✓ Branch 0 taken 21340 times.
✓ Branch 1 taken 1130 times.
|
22470x | if (n > 0) | |
| 298 | { | |||
| 299 |
2/4✓ Branch 0 taken 21340 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21340 times.
✗ Branch 3 not taken.
|
42680x | eng_.input_committed(n); | |
| 300 | 21340x | ++read_gen_; | ||
| 301 |
2/4✓ Branch 0 taken 21340 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21340 times.
✗ Branch 3 not taken.
|
42680x | co_return ec; | |
| 302 | } | |||
| 303 | ||||
| 304 |
1/2✓ Branch 0 taken 1130 times.
✗ Branch 1 not taken.
|
1130x | if (ec) | |
| 305 |
2/4✓ Branch 0 taken 1130 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1130 times.
✗ Branch 3 not taken.
|
2260x | co_return ec; | |
| 306 | ||||
| 307 | // The transport delivered nothing without an error, so it cannot | |||
| 308 | // make progress: fail loudly rather than spin the engine's input | |||
| 309 | // retry against a staging that will never fill. | |||
| 310 | ✗ | co_return make_error_code(std::errc::no_buffer_space); | ||
| 311 | 112055x | } | ||
| 312 | ||||
| 313 | // A prior read/write already reported its full transfer as success; | |||
| 314 | // the trailing flush error it deferred surfaces on the next call | |||
| 315 | // instead. Each entry point takes it exactly once. | |||
| 316 | 286307x | std::error_code take_pending_flush_ec() noexcept | ||
| 317 | { | |||
| 318 | 286307x | std::error_code ec = pending_flush_ec_; | ||
| 319 | 286307x | pending_flush_ec_ = {}; | ||
| 320 | 286307x | return ec; | ||
| 321 | } | |||
| 322 | ||||
| 323 | public: | |||
| 324 | /** Construct a driver over a transport stream. | |||
| 325 | ||||
| 326 | @param s The transport; the caller keeps it alive and repoints | |||
| 327 | it on move via `rebind_stream`. | |||
| 328 | @param ctx The TLS context handed to the engine's handshake | |||
| 329 | preparation. | |||
| 330 | */ | |||
| 331 | 12284x | engine_driver(capy::any_stream& s, tls_context ctx) | ||
| 332 | 3071x | : s_(&s) | ||
| 333 | 3071x | , ctx_(std::move(ctx)) | ||
| 334 | { | |||
| 335 |
1/2✓ Branch 0 taken 3071 times.
✗ Branch 1 not taken.
|
3071x | out_buf_.resize(buffer_size_); | |
| 336 | 3071x | } | ||
| 337 | ||||
| 338 | /// Return the engine for backend-specific setup. | |||
| 339 | Engine& | |||
| 340 | engine() noexcept | |||
| 341 | { | |||
| 342 | return eng_; | |||
| 343 | } | |||
| 344 | ||||
| 345 | /// Return the TLS context this driver was constructed with. | |||
| 346 | tls_context const& | |||
| 347 | context() const noexcept | |||
| 348 | { | |||
| 349 | return ctx_; | |||
| 350 | } | |||
| 351 | ||||
| 352 | /// Point the driver at the transport's post-move location. | |||
| 353 | void | |||
| 354 | 7x | rebind_stream(capy::any_stream& s) noexcept | ||
| 355 | { | |||
| 356 | 7x | s_ = &s; | ||
| 357 | 7x | } | ||
| 358 | ||||
| 359 | /// Set the hostname applied to the next client handshake. | |||
| 360 | void | |||
| 361 | 13x | set_hostname(std::string_view hostname) | ||
| 362 | { | |||
| 363 | 13x | hostname_ = hostname; | ||
| 364 | 13x | } | ||
| 365 | ||||
| 366 | /// Return the ALPN protocol negotiated by the last handshake. | |||
| 367 | std::string_view | |||
| 368 | 3x | alpn_protocol() const noexcept | ||
| 369 | { | |||
| 370 | 3x | return alpn_selected_; | ||
| 371 | } | |||
| 372 | ||||
| 373 | /// Reset the engine and every per-connection driver state. | |||
| 374 | 65x | void reset() | ||
| 375 | { | |||
| 376 | 65x | eng_.reset(); | ||
| 377 | ||||
| 378 | 65x | out_len_ = 0; | ||
| 379 | ||||
| 380 | 65x | alpn_selected_.clear(); | ||
| 381 | 65x | pending_flush_ec_ = {}; | ||
| 382 | 65x | used_ = false; | ||
| 383 | 65x | } | ||
| 384 | ||||
| 385 | capy::io_task<std::size_t> | |||
| 386 |
10/24✓ Branch 0 taken 143113 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 143113 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 143113 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 143113 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 143113 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 143113 times.
✓ Branch 15 taken 143113 times.
✓ Branch 16 taken 143113 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 2 times.
✓ Branch 19 taken 143113 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
|
429343x | do_read_some( | |
| 387 | capy::detail::mutable_buffer_array<capy::detail::max_iovec_> buffers) | |||
| 388 | 143113x | { | ||
| 389 |
4/4✓ Branch 0 taken 13 times.
✓ Branch 1 taken 143100 times.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 143100 times.
|
143126x | if (auto ec = take_pending_flush_ec()) | |
| 390 |
1/2✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
|
13x | co_return {ec, 0}; | |
| 391 | ||||
| 392 | 143100x | std::error_code ec; | ||
| 393 | 143100x | std::size_t total_read = 0; | ||
| 394 | 143100x | std::size_t const bufs_size = capy::buffer_size(buffers); | ||
| 395 | ||||
| 396 |
4/4✓ Branch 0 taken 1 time.
✓ Branch 1 taken 143099 times.
✓ Branch 2 taken 143097 times.
✓ Branch 3 taken 1 time.
|
286197x | for (auto& buf : buffers) | |
| 397 | { | |||
| 398 | 143099x | char* const dest = static_cast<char*>(buf.data()); | ||
| 399 | 143099x | int const remaining = static_cast<int>(buf.size()); | ||
| 400 |
1/2✓ Branch 0 taken 143099 times.
✗ Branch 1 not taken.
|
143099x | if (remaining == 0) | |
| 401 | ✗ | continue; | ||
| 402 | ||||
| 403 | // Exits by co_return: success after the first transferred | |||
| 404 | // chunk, or any terminal error. | |||
| 405 | 201722x | while (true) | ||
| 406 | { | |||
| 407 | 201722x | auto const gen = read_gen_; | ||
| 408 |
4/4✓ Branch 0 taken 162612 times.
✓ Branch 1 taken 39110 times.
✓ Branch 2 taken 162612 times.
✓ Branch 3 taken 39110 times.
|
403444x | auto r = eng_.perform( | |
| 409 | 201722x | engine_op::read, dest, | ||
| 410 | 201722x | static_cast<std::size_t>(remaining)); | ||
| 411 | ||||
| 412 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 162558 times.
|
162612x | if (r.ec) | |
| 413 | { | |||
| 414 | // Terminal, already mapped by the engine. eof (a | |||
| 415 | // received close_notify) arrives as a plain done: | |||
| 416 | // it queues no output, so no flush is needed. | |||
| 417 |
1/2✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
|
54x | if (r.want == engine_want::output_then_done) | |
| 418 | ✗ | co_await best_effort_flush(); | ||
| 419 |
1/2✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
|
54x | co_return {r.ec, total_read}; | |
| 420 | } | |||
| 421 | ||||
| 422 |
4/4✓ Branch 0 taken 19555 times.
✓ Branch 1 taken 143003 times.
✓ Branch 2 taken 39110 times.
✓ Branch 3 taken 58665 times.
|
162558x | if (r.want == engine_want::done || | |
| 423 | 19555x | r.want == engine_want::output_then_done) | ||
| 424 | { | |||
| 425 | 182113x | total_read += r.bytes; | ||
| 426 | ||||
| 427 | // r.bytes > 0 already satisfies ReadStream's "at | |||
| 428 | // least one byte transferred" success condition; | |||
| 429 | // report now rather than loop for more (another | |||
| 430 | // engine call could park on input). out_len_ > 0 | |||
| 431 | // covers a retained tail the engine cannot see. | |||
| 432 |
4/4✓ Branch 0 taken 143003 times.
✓ Branch 1 taken 39110 times.
✓ Branch 2 taken 39110 times.
✓ Branch 3 taken 103893 times.
|
182113x | if (r.want == engine_want::output_then_done || | |
| 433 | 143003x | out_len_ > 0) | ||
| 434 |
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.
|
78220x | ec = co_await flush_output(); | |
| 435 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 103893 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
103893x | if (ec && total_read == bufs_size) | |
| 436 | { | |||
| 437 | // First failure wins: concurrent directions share | |||
| 438 | // one transport failure domain; the earliest | |||
| 439 | // error is the meaningful one. | |||
| 440 | ✗ | if (!pending_flush_ec_) | ||
| 441 | ✗ | pending_flush_ec_ = ec; | ||
| 442 | ✗ | ec = {}; | ||
| 443 | ✗ | } | ||
| 444 |
2/2✓ Branch 0 taken 143003 times.
✓ Branch 1 taken 39110 times.
|
103893x | co_return {ec, total_read}; | |
| 445 | } | |||
| 446 | ||||
| 447 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 58665 times.
|
58665x | if (r.want == engine_want::output_then_retry) | |
| 448 | { | |||
| 449 | ✗ | ec = co_await flush_output(); | ||
| 450 | ✗ | if (ec) | ||
| 451 | ✗ | co_return {ec, total_read}; | ||
| 452 | ✗ | continue; | ||
| 453 | } | |||
| 454 | ||||
| 455 | // want == input. Flush first: a retained tail (an | |||
| 456 | // earlier failed write) may hold bytes the peer needs | |||
| 457 | // before it will send more; no-op when nothing pends. | |||
| 458 |
9/14✗ Branch 0 not taken.
✓ Branch 1 taken 58665 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 58665 times.
✓ Branch 4 taken 19555 times.
✓ Branch 5 taken 39110 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 19555 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 19555 times.
✓ Branch 11 taken 19555 times.
✓ Branch 12 taken 39108 times.
✓ Branch 13 taken 58663 times.
|
58665x | ec = co_await flush_output(); | |
| 459 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 58662 times.
|
58663x | if (ec) | |
| 460 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1x | co_return {ec, total_read}; | |
| 461 | ||||
| 462 |
8/14✗ Branch 0 not taken.
✓ Branch 1 taken 58662 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 58662 times.
✓ Branch 4 taken 19554 times.
✓ Branch 5 taken 39108 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 19554 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 19554 times.
✓ Branch 11 taken 19554 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 19554 times.
|
58662x | ec = co_await fill_input(gen); | |
| 463 |
2/2✓ Branch 0 taken 41 times.
✓ Branch 1 taken 19513 times.
|
19554x | if (ec) | |
| 464 | { | |||
| 465 | 41x | ec = map_fill_error( | ||
| 466 |
1/2✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
|
41x | engine_op::read, ec, eng_.received_shutdown()); | |
| 467 |
1/2✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
|
41x | co_return {ec, total_read}; | |
| 468 | } | |||
| 469 | 201720x | } | ||
| 470 | 143097x | } | ||
| 471 | ||||
| 472 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1x | co_return {std::error_code{}, total_read}; | |
| 473 | 299536x | } | ||
| 474 | ||||
| 475 | capy::io_task<std::size_t> | |||
| 476 |
10/24✓ Branch 0 taken 143061 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 143061 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 143061 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 143061 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 143061 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 143061 times.
✓ Branch 15 taken 143061 times.
✓ Branch 16 taken 143061 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 4 times.
✓ Branch 19 taken 143061 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
|
429191x | do_write_some( | |
| 477 | capy::detail::const_buffer_array<capy::detail::max_iovec_> buffers) | |||
| 478 | 143061x | { | ||
| 479 |
4/4✓ Branch 0 taken 1 time.
✓ Branch 1 taken 143060 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 143060 times.
|
143062x | if (auto ec = take_pending_flush_ec()) | |
| 480 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1x | co_return {ec, 0}; | |
| 481 | ||||
| 482 | 143060x | std::error_code ec; | ||
| 483 | 143060x | std::size_t total_written = 0; | ||
| 484 | 143060x | std::size_t const bufs_size = capy::buffer_size(buffers); | ||
| 485 | ||||
| 486 |
4/4✓ Branch 0 taken 1 time.
✓ Branch 1 taken 143059 times.
✓ Branch 2 taken 143055 times.
✓ Branch 3 taken 1 time.
|
286115x | for (auto const& buf : buffers) | |
| 487 | { | |||
| 488 | // The engine only reads through this pointer for a write | |||
| 489 | // op; the cast satisfies perform's single transfer | |||
| 490 | // signature. | |||
| 491 | 143059x | void* const src = const_cast<void*>(buf.data()); | ||
| 492 | 143059x | int const remaining = static_cast<int>(buf.size()); | ||
| 493 |
1/2✓ Branch 0 taken 143059 times.
✗ Branch 1 not taken.
|
143059x | if (remaining == 0) | |
| 494 | ✗ | continue; | ||
| 495 | ||||
| 496 | // Exits by co_return: success after the first transferred | |||
| 497 | // chunk, or any terminal error. | |||
| 498 | 429173x | while (true) | ||
| 499 | { | |||
| 500 | 429173x | auto const gen = read_gen_; | ||
| 501 |
4/4✓ Branch 0 taken 143059 times.
✓ Branch 1 taken 286114 times.
✓ Branch 2 taken 143059 times.
✓ Branch 3 taken 286114 times.
|
858346x | auto r = eng_.perform( | |
| 502 | 429173x | engine_op::write, src, | ||
| 503 | 429173x | static_cast<std::size_t>(remaining)); | ||
| 504 | ||||
| 505 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 143057 times.
|
143059x | if (r.ec) | |
| 506 | { | |||
| 507 | // Terminal, already mapped. eof means the peer's | |||
| 508 | // close_notify WAS received (an announced close); | |||
| 509 | // it arrives as a plain done and needs no flush. | |||
| 510 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2x | if (r.want == engine_want::output_then_done) | |
| 511 | ✗ | co_await best_effort_flush(); | ||
| 512 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2x | co_return {r.ec, total_written}; | |
| 513 | } | |||
| 514 | ||||
| 515 |
2/4✓ Branch 0 taken 143057 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 143057 times.
✗ Branch 3 not taken.
|
143057x | if (r.want == engine_want::done || | |
| 516 | 143057x | r.want == engine_want::output_then_done) | ||
| 517 | { | |||
| 518 | 143057x | total_written += r.bytes; | ||
| 519 | ||||
| 520 | // r.bytes > 0 already satisfies WriteStream's "at | |||
| 521 | // least one byte transferred" success condition; | |||
| 522 | // report now rather than loop for more. out_len_ > 0 | |||
| 523 | // covers a retained tail the engine cannot see. | |||
| 524 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 143057 times.
✓ Branch 2 taken 286114 times.
✓ Branch 3 taken 286114 times.
|
143057x | if (r.want == engine_want::output_then_done || | |
| 525 | ✗ | out_len_ > 0) | ||
| 526 |
8/14✓ Branch 0 taken 429171 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 429171 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 143057 times.
✓ Branch 5 taken 286114 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 143057 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 143057 times.
✓ Branch 11 taken 143057 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 143057 times.
|
429171x | ec = co_await flush_output(); | |
| 527 |
4/4✓ Branch 0 taken 16 times.
✓ Branch 1 taken 429155 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 15 times.
|
429171x | if (ec && total_written == bufs_size) | |
| 528 | { | |||
| 529 | // First failure wins: concurrent directions share | |||
| 530 | // one transport failure domain; the earliest | |||
| 531 | // error is the meaningful one. | |||
| 532 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15x | if (!pending_flush_ec_) | |
| 533 | 15x | pending_flush_ec_ = ec; | ||
| 534 | 15x | ec = {}; | ||
| 535 | 15x | } | ||
| 536 |
2/2✓ Branch 0 taken 143057 times.
✓ Branch 1 taken 286114 times.
|
429171x | co_return {ec, total_written}; | |
| 537 | } | |||
| 538 | ||||
| 539 | ✗ | if (r.want == engine_want::output_then_retry) | ||
| 540 | { | |||
| 541 | ✗ | ec = co_await flush_output(); | ||
| 542 | ✗ | if (ec) | ||
| 543 | ✗ | co_return {ec, total_written}; | ||
| 544 | ✗ | continue; | ||
| 545 | } | |||
| 546 | ||||
| 547 | // want == input: a write can need a read mid-rekey; the | |||
| 548 | // transport eof this surfaces means the same thing it | |||
| 549 | // means on the read path, so map it the same way. Flush | |||
| 550 | // first for the same retained-tail reason as the read | |||
| 551 | // path. | |||
| 552 | ✗ | ec = co_await flush_output(); | ||
| 553 | ✗ | if (ec) | ||
| 554 | ✗ | co_return {ec, total_written}; | ||
| 555 | ||||
| 556 | ✗ | ec = co_await fill_input(gen); | ||
| 557 | ✗ | if (ec) | ||
| 558 | { | |||
| 559 | ✗ | ec = map_fill_error( | ||
| 560 | ✗ | engine_op::write, ec, eng_.received_shutdown()); | ||
| 561 | ✗ | co_return {ec, total_written}; | ||
| 562 | } | |||
| 563 | 143059x | } | ||
| 564 | 143055x | } | ||
| 565 | ||||
| 566 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1x | co_return {std::error_code{}, total_written}; | |
| 567 | 1001398x | } | ||
| 568 | ||||
| 569 |
10/24✓ Branch 0 taken 2286 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2286 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2286 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2286 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2286 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 2286 times.
✓ Branch 15 taken 2286 times.
✓ Branch 16 taken 2286 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 2286 times.
✓ Branch 19 taken 34 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
|
6926x | capy::io_task<> do_handshake(tls_role role) | |
| 570 | 2286x | { | ||
| 571 | // Refuse the handshake while the engine's configuration is | |||
| 572 | // unusable, before consuming any per-connection state. | |||
| 573 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 2286 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2286 times.
|
2286x | if (auto cec = eng_.check_context()) | |
| 574 | ✗ | co_return {cec}; | ||
| 575 | ||||
| 576 |
2/2✓ Branch 0 taken 2281 times.
✓ Branch 1 taken 5 times.
|
2286x | if (used_) | |
| 577 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5x | reset(); | |
| 578 | ||||
| 579 | // reset() may not have restored a usable session; refuse to | |||
| 580 | // hand out a handshake on it. | |||
| 581 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 2286 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2286 times.
|
2286x | if (auto sec = eng_.check_session()) | |
| 582 | ✗ | co_return {sec}; | ||
| 583 | ||||
| 584 | // A failed attempt leaves the session in a dead state; any | |||
| 585 | // attempt, not just a completed handshake, consumes the stream | |||
| 586 | // so the next handshake() starts fresh. | |||
| 587 | 2286x | used_ = true; | ||
| 588 | ||||
| 589 |
5/6✓ Branch 0 taken 2286 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 2280 times.
✓ Branch 4 taken 40 times.
✓ Branch 5 taken 2326 times.
|
2292x | if (auto pec = eng_.prepare(ctx_, role, hostname_)) | |
| 590 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6x | co_return {pec}; | |
| 591 | ||||
| 592 | 2326x | auto const op = role == tls_role::client | ||
| 593 | ? engine_op::handshake_client | |||
| 594 | : engine_op::handshake_server; | |||
| 595 | ||||
| 596 | 2326x | std::error_code ec; | ||
| 597 | ||||
| 598 | 11668x | while (true) | ||
| 599 | { | |||
| 600 | 11668x | auto const gen = read_gen_; | ||
| 601 |
2/2✓ Branch 0 taken 6038 times.
✓ Branch 1 taken 5630 times.
|
11668x | auto r = eng_.perform(op, nullptr, 0); | |
| 602 | ||||
| 603 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 6018 times.
|
6038x | if (r.ec) | |
| 604 | { | |||
| 605 |
2/2✓ Branch 0 taken 37 times.
✓ Branch 1 taken 57 times.
|
20x | if (r.want == engine_want::output_then_done) | |
| 606 |
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(); | |
| 607 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 36 times.
|
56x | co_return {r.ec}; | |
| 608 | } | |||
| 609 | ||||
| 610 |
4/4✓ Branch 0 taken 6017 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 8815 times.
✓ Branch 3 taken 14832 times.
|
6018x | if (r.want == engine_want::done || | |
| 611 | 6017x | r.want == engine_want::output_then_done) | ||
| 612 | { | |||
| 613 |
2/2✓ Branch 0 taken 3222 times.
✓ Branch 1 taken 5594 times.
|
8816x | eng_.capture_alpn(alpn_selected_); | |
| 614 |
8/14✗ Branch 0 not taken.
✓ Branch 1 taken 3222 times.
✓ Branch 2 taken 3222 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1074 times.
✓ Branch 5 taken 2148 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1074 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1074 times.
✓ Branch 11 taken 1074 times.
✓ Branch 12 taken 1074 times.
✗ Branch 13 not taken.
|
3222x | ec = co_await flush_output(); | |
| 615 |
1/2✓ Branch 0 taken 1074 times.
✗ Branch 1 not taken.
|
1074x | co_return {ec}; | |
| 616 | } | |||
| 617 | ||||
| 618 |
2/2✓ Branch 0 taken 6456 times.
✓ Branch 1 taken 8376 times.
|
14832x | if (r.want == engine_want::output_then_retry) | |
| 619 | { | |||
| 620 | // Must flush (e.g. ClientHello) before reading the | |||
| 621 | // peer's reply. | |||
| 622 |
8/14✗ Branch 0 not taken.
✓ Branch 1 taken 6456 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6456 times.
✓ Branch 4 taken 2152 times.
✓ Branch 5 taken 4304 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2152 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2152 times.
✓ Branch 11 taken 2152 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 2152 times.
|
6456x | ec = co_await flush_output(); | |
| 623 |
2/2✓ Branch 0 taken 78 times.
✓ Branch 1 taken 2074 times.
|
2152x | if (ec) | |
| 624 |
1/2✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
|
78x | co_return {ec}; | |
| 625 | 2074x | continue; | ||
| 626 | } | |||
| 627 | ||||
| 628 | // want == input. The flush is a no-op unless a retained | |||
| 629 | // tail pends; the fill error passes through map_fill_error | |||
| 630 | // unchanged (no close is clean before the session is | |||
| 631 | // established). | |||
| 632 |
9/14✓ Branch 0 taken 8376 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8376 times.
✓ Branch 4 taken 2792 times.
✓ Branch 5 taken 5584 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2792 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2792 times.
✓ Branch 11 taken 2792 times.
✓ Branch 12 taken 5584 times.
✓ Branch 13 taken 8376 times.
|
8376x | ec = co_await flush_output(); | |
| 633 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8376 times.
|
8376x | if (ec) | |
| 634 | ✗ | co_return {ec}; | ||
| 635 | ||||
| 636 |
8/14✓ Branch 0 taken 8376 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8376 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2792 times.
✓ Branch 5 taken 5584 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2792 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2792 times.
✓ Branch 11 taken 2792 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 2792 times.
|
8376x | ec = co_await fill_input(gen); | |
| 637 |
2/2✓ Branch 0 taken 1108 times.
✓ Branch 1 taken 1684 times.
|
2792x | if (ec) | |
| 638 | { | |||
| 639 |
1/2✓ Branch 0 taken 1108 times.
✗ Branch 1 not taken.
|
1108x | ec = map_fill_error(op, ec, eng_.received_shutdown()); | |
| 640 |
1/2✓ Branch 0 taken 1108 times.
✗ Branch 1 not taken.
|
1108x | co_return {ec}; | |
| 641 | } | |||
| 642 | 11622x | } | ||
| 643 | 31198x | } | ||
| 644 | ||||
| 645 |
10/24✓ Branch 0 taken 133 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 133 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 133 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 133 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 133 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 133 times.
✓ Branch 15 taken 133 times.
✓ Branch 16 taken 133 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 133 times.
✓ Branch 19 taken 88 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
|
575x | capy::io_task<> do_shutdown() | |
| 646 | 133x | { | ||
| 647 |
4/4✓ Branch 0 taken 1 time.
✓ Branch 1 taken 132 times.
✓ Branch 2 taken 89 times.
✓ Branch 3 taken 44 times.
|
134x | if (auto ec = take_pending_flush_ec()) | |
| 648 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1x | co_return {ec}; | |
| 649 | ||||
| 650 | 44x | std::error_code ec; | ||
| 651 | ||||
| 652 | 467x | while (true) | ||
| 653 | { | |||
| 654 | 379x | auto const gen = read_gen_; | ||
| 655 |
1/2✓ Branch 0 taken 379 times.
✗ Branch 1 not taken.
|
379x | auto r = eng_.perform(engine_op::shutdown, nullptr, 0); | |
| 656 | ||||
| 657 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 378 times.
|
379x | if (r.ec) | |
| 658 | { | |||
| 659 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1x | if (r.want == engine_want::output_then_done) | |
| 660 | ✗ | co_await best_effort_flush(); | ||
| 661 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1x | co_return {r.ec}; | |
| 662 | } | |||
| 663 | ||||
| 664 |
4/4✓ Branch 0 taken 255 times.
✓ Branch 1 taken 123 times.
✓ Branch 2 taken 390 times.
✓ Branch 3 taken 645 times.
|
378x | if (r.want == engine_want::done || | |
| 665 | 255x | r.want == engine_want::output_then_done) | ||
| 666 | { | |||
| 667 | // Covers both the bidirectional-complete result and the | |||
| 668 | // engine's shutdown-after-close success mapping. | |||
| 669 |
8/14✓ Branch 0 taken 267 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 267 times.
✓ Branch 4 taken 89 times.
✓ Branch 5 taken 178 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 89 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 89 times.
✓ Branch 11 taken 89 times.
✓ Branch 12 taken 89 times.
✗ Branch 13 not taken.
|
513x | ec = co_await flush_output(); | |
| 670 |
1/2✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
|
89x | co_return {ec}; | |
| 671 | } | |||
| 672 | ||||
| 673 |
2/2✓ Branch 0 taken 270 times.
✓ Branch 1 taken 375 times.
|
645x | if (r.want == engine_want::output_then_retry) | |
| 674 | { | |||
| 675 | // Sends our close_notify before parking for the peer's. | |||
| 676 |
8/14✗ Branch 0 not taken.
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 270 times.
✓ Branch 4 taken 90 times.
✓ Branch 5 taken 180 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 90 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 90 times.
✓ Branch 11 taken 90 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 90 times.
|
270x | ec = co_await flush_output(); | |
| 677 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 78 times.
|
90x | if (ec) | |
| 678 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12x | co_return {ec}; | |
| 679 | 78x | continue; | ||
| 680 | } | |||
| 681 | ||||
| 682 | // want == input: awaiting the peer's close_notify. It may | |||
| 683 | // already have been deposited (and consumed by a concurrent | |||
| 684 | // reader) during a flush; fill_input then returns without | |||
| 685 | // reading and the loop retries the engine instead of parking. | |||
| 686 |
9/14✗ Branch 0 not taken.
✓ Branch 1 taken 375 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 375 times.
✓ Branch 4 taken 125 times.
✓ Branch 5 taken 250 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 125 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 125 times.
✓ Branch 11 taken 125 times.
✓ Branch 12 taken 250 times.
✓ Branch 13 taken 375 times.
|
375x | ec = co_await flush_output(); | |
| 687 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 375 times.
|
375x | if (ec) | |
| 688 | ✗ | co_return {ec}; | ||
| 689 | ||||
| 690 |
8/14✓ Branch 0 taken 375 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 375 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 125 times.
✓ Branch 5 taken 250 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 125 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 125 times.
✓ Branch 11 taken 125 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 125 times.
|
375x | ec = co_await fill_input(gen); | |
| 691 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 95 times.
|
125x | if (ec) | |
| 692 | { | |||
| 693 | 30x | ec = map_fill_error( | ||
| 694 |
1/2✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
|
30x | engine_op::shutdown, ec, eng_.received_shutdown()); | |
| 695 |
1/2✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
|
30x | co_return {ec}; | |
| 696 | } | |||
| 697 | 555x | } | ||
| 698 | 990x | } | ||
| 699 | }; | |||
| 700 | ||||
| 701 | } // namespace detail | |||
| 702 | ||||
| 703 | } // namespace boost::corosio | |||
| 704 | ||||
| 705 | #endif | |||
| 706 |