Skip to main content

sozu_lib/protocol/
rustls.rs

1//! Rustls handshake driver.
2//!
3//! Owns the per-session `rustls::ServerConnection` during the TLS
4//! handshake: pumps `read_tls`/`write_tls`, surfaces handshake completion
5//! to the parent state, and emits handshake-completion metrics. Cipher /
6//! ALPN / SNI binding decisions live in `lib/src/https.rs`; certificate
7//! resolution and dynamic cert reload live in `lib/src/tls.rs`.
8
9use std::{
10    cell::RefCell,
11    io::{ErrorKind, Read, Write},
12    net::SocketAddr,
13    rc::Rc,
14    time::Instant,
15};
16
17use mio::{Token, net::TcpStream};
18use rustls::{Error as RustlsError, ServerConnection};
19use rusty_ulid::Ulid;
20use sozu_command::{
21    config::MAX_LOOP_ITERATIONS,
22    logging::{LogContext, ansi_palette},
23};
24
25use crate::metrics::names;
26use crate::{
27    Readiness, Ready, SessionMetrics, SessionResult, StateResult, protocol::SessionState,
28    socket::ShortReadProbe, timer::TimeoutContainer,
29};
30
31/// This macro is defined uniquely in this module to help the tracking of tls
32/// issues inside Sōzu. When the logger emits to a TTY the protocol label is
33/// bold bright-white (uniform across every protocol), the `Session` keyword is
34/// light grey, attribute keys are gray and values are bright white. ANSI codes
35/// are skipped when output goes to a file or otherwise non-colored sink. The
36/// `[ulid - - -]` context prefix comes first to keep column alignment with
37/// `MUX-*` and `SOCKET` logs.
38macro_rules! log_context {
39    ($self:expr) => {{
40        let (open, reset, grey, gray, white) = ansi_palette();
41        format!(
42            "{gray}{ctx}{reset}\t{open}RUSTLS{reset}\t{grey}Session{reset}({gray}sni_bytes{reset}={white}{sni_bytes:?}{reset}, {gray}alpn_bytes{reset}={white}{alpn_bytes:?}{reset}, {gray}version{reset}={white}{version:?}{reset}, {gray}source{reset}={white}{source:?}{reset}, {gray}frontend{reset}={white}{frontend}{reset}, {gray}readiness{reset}={white}{readiness}{reset})\t >>>",
43            open = open,
44            reset = reset,
45            grey = grey,
46            gray = gray,
47            white = white,
48            ctx = $self.log_context(),
49            sni_bytes = $self.session.server_name().map(str::len),
50            alpn_bytes = $self.session.alpn_protocol().map(|bytes| bytes.len()),
51            version = $self.session.protocol_version(),
52            source = $self
53                .peer_address
54                .map(|addr| addr.to_string())
55                .unwrap_or_else(|| "<none>".to_string()),
56            frontend = $self.frontend_token.0,
57            readiness = $self.frontend_readiness,
58        )
59    }};
60}
61
62/// Why [`handshake_read`] ended the handshake, handed back rather than logged
63/// so [`TlsHandshake::readable`] can render it with its session context.
64#[derive(Debug)]
65enum HandshakeReadFault {
66    /// `read_tls` answered `Ok(0)`: the peer closed during the handshake.
67    Closed,
68    /// `read_tls` failed with something other than `WouldBlock` or
69    /// `Interrupted`.
70    ReadTls(std::io::Error),
71    /// `process_new_packets` rejected what `read_tls` delivered.
72    ProcessPackets(RustlsError),
73}
74
75/// Read half of [`TlsHandshake::readable`], generic over the transport so
76/// tests can count every `recv(2)` it issues.
77///
78/// Calls `read_tls` (one `recv`) and `process_new_packets` while rustls
79/// `wants_read()`, and stops on the first of:
80///
81/// - EAGAIN;
82/// - a short read: a `recv` that answered fewer bytes than the 4096 rustls
83///   offered (`rustls-0.23.45/src/msgs/deframer/buffers.rs:208,220`) emptied
84///   the receive queue, exactly as in [`crate::socket::ShortReadProbe`]'s
85///   other caller, so a second `recv` could only answer EAGAIN. What it
86///   delivered is still processed;
87/// - `wants_read()` turning false: rustls holds plaintext, or has a flight to
88///   send first (`rustls-0.23.45/src/common_state.rs:674-684`).
89///
90/// A `recv` the kernel interrupted (`EINTR`) is none of these: rustls hands
91/// it back from `read_tls`, and it says nothing about the socket, so it is
92/// retried at once, as `flush_tls` (`lib/src/socket.rs`) retries an
93/// interrupted write. The probe only records a successful read, so the retry
94/// leaves `was_short` exact.
95///
96/// A TLS 1.3 server `wants_read()` again as soon as its own flight is queued,
97/// because it may already send application data; without the stop on a short
98/// read the ClientHello and the client `Finished` were each followed by a
99/// `recv` that answered EAGAIN.
100///
101/// Readiness contract, the one `rustls_socket_read` (`lib/src/socket.rs`)
102/// follows:
103///
104/// - EAGAIN and a short read both drop READABLE from `event`. mio registers
105///   every socket edge-triggered (`mio/src/sys/unix/selector/epoll.rs`,
106///   `EPOLLET`), and "an event will be generated upon each receipt of a chunk
107///   of data" (`man 7 epoll`), so the next segment of a ClientHello split
108///   across several raises the next event and the next call reads it. The
109///   handshake never waits for bytes that are already queued;
110/// - a read that fills what rustls offered is not a short read: the loop
111///   reads again;
112/// - HUP needs no exception here, unlike the mux (`update_readiness_after_read`
113///   in `lib/src/protocol/mux/mod.rs`): a client whose FIN arrived before its
114///   handshake completed can never send its `Finished`, so
115///   `TlsHandshake::readable` closes such a handshake instead of reading to
116///   the EOF;
117/// - TCP urgent data is the exception to "a short read empties the queue":
118///   `recv` stops before an urgent mark with bytes queued behind it (see
119///   `plain_socket_read` in `lib/src/socket.rs`), so a peer that sends OOB
120///   data inside its handshake stalls until its next send, as it does after
121///   the handshake since #1606. TLS never uses urgent data.
122///
123/// HAProxy drives the handshake the same way: `ssl_sock_handshake`
124/// (`src/ssl_sock.c:6462`) returns on `SSL_ERROR_WANT_READ` and subscribes
125/// for the next receive event (`src/ssl_sock.c:6676-6682`), and its BIO reads
126/// through `raw_sock_to_buf`, which stops on `ret < try` (`src/raw_sock.c:315-317`).
127/// tokio-rustls loops `read_tls` until `Pending`
128/// (`tokio-rustls-0.26.4/src/common/mod.rs:161-170`), and that `Pending` costs
129/// no `recv` after a short read because tokio clears the readiness when
130/// `0 < n < len` (`tokio-1.53.1/src/io/poll_evented.rs:211-213`).
131fn handshake_read<R: Read>(
132    session: &mut ServerConnection,
133    stream: &mut R,
134    event: &mut Ready,
135) -> Result<(), HandshakeReadFault> {
136    let mut can_read = true;
137    while can_read && session.wants_read() {
138        let mut probe = ShortReadProbe::new(&mut *stream);
139        let read = loop {
140            match session.read_tls(&mut probe) {
141                Err(e) if e.kind() == ErrorKind::Interrupted => continue,
142                read => break read,
143            }
144        };
145        match read {
146            Ok(0) => return Err(HandshakeReadFault::Closed),
147            Ok(_) => {
148                if probe.was_short() {
149                    event.remove(Ready::READABLE);
150                    can_read = false;
151                }
152            }
153            Err(e) if e.kind() == ErrorKind::WouldBlock => {
154                event.remove(Ready::READABLE);
155                can_read = false;
156            }
157            Err(e) => {
158                // A reset during the handshake is counted with the ones after
159                // it (sozu-proxy/sozu#434); the session closes either way.
160                if matches!(
161                    e.kind(),
162                    ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted
163                ) {
164                    incr!(names::rustls::READ_RESET);
165                }
166                return Err(HandshakeReadFault::ReadTls(e));
167            }
168        }
169        session
170            .process_new_packets()
171            .map_err(HandshakeReadFault::ProcessPackets)?;
172    }
173    // Every exit that stops reading while rustls still wants to proved the
174    // queue empty, and dropped READABLE with it.
175    debug_assert!(
176        can_read || !event.is_readable(),
177        "the handshake stopped reading on an empty queue but kept READABLE"
178    );
179    Ok(())
180}
181
182/// One `write_tls` of [`TlsHandshake::writable`], generic over the transport
183/// so tests can interrupt it.
184///
185/// A write the kernel interrupted (`EINTR`) is retried at once, as
186/// `flush_tls` (`lib/src/socket.rs`) does after the handshake: it says nothing
187/// about the socket, so it must neither stop the pump as `WouldBlock`, which
188/// no edge would follow, nor close the session as an error.
189fn handshake_write<W: Write>(
190    session: &mut ServerConnection,
191    stream: &mut W,
192) -> std::io::Result<usize> {
193    loop {
194        match session.write_tls(stream) {
195            Err(e) if e.kind() == ErrorKind::Interrupted => continue,
196            written => break written,
197        }
198    }
199}
200
201pub enum TlsState {
202    Initial,
203    Handshake,
204    Established,
205    Error,
206}
207
208pub struct TlsHandshake {
209    pub container_frontend_timeout: TimeoutContainer,
210    pub frontend_readiness: Readiness,
211    frontend_token: Token,
212    pub peer_address: Option<SocketAddr>,
213    pub request_id: Ulid,
214    pub session: ServerConnection,
215    pub stream: TcpStream,
216    /// Wall-clock anchor for the `tls.handshake_ms` histogram. Captured the
217    /// first time the handshake state actually does I/O (not at construction,
218    /// because the session may sit in the accept queue or in expect-proxy for
219    /// an unbounded amount of time before the TLS bytes start flowing).
220    handshake_started_at: Option<Instant>,
221}
222
223impl TlsHandshake {
224    /// Instantiate a new TlsHandshake SessionState with:
225    ///
226    /// - frontend_interest: READABLE | HUP | ERROR
227    /// - frontend_event: EMPTY
228    ///
229    /// Remember to set the events from the previous State!
230    pub fn new(
231        container_frontend_timeout: TimeoutContainer,
232        session: ServerConnection,
233        stream: TcpStream,
234        frontend_token: Token,
235        request_id: Ulid,
236        peer_address: Option<SocketAddr>,
237    ) -> TlsHandshake {
238        TlsHandshake {
239            container_frontend_timeout,
240            frontend_readiness: Readiness {
241                interest: Ready::READABLE | Ready::HUP | Ready::ERROR,
242                event: Ready::EMPTY,
243            },
244            frontend_token,
245            peer_address,
246            request_id,
247            session,
248            stream,
249            handshake_started_at: None,
250        }
251    }
252
253    /// Returns the elapsed handshake duration in milliseconds and clears the
254    /// captured start instant so the histogram is only recorded once. Returns
255    /// `None` when no I/O happened (e.g. the connection closed mid-handshake
256    /// before any bytes were exchanged); callers should not emit
257    /// `tls.handshake_ms` in that case.
258    fn record_handshake_duration_ms(&mut self) -> Option<u128> {
259        let was_anchored = self.handshake_started_at.is_some();
260        let elapsed = self
261            .handshake_started_at
262            .take()
263            .map(|t| t.elapsed().as_millis());
264        // `take()` is idempotent-disarming: the anchor is always cleared so the
265        // histogram is recorded at most once, and a duration is returned iff an
266        // anchor existed.
267        debug_assert!(
268            self.handshake_started_at.is_none(),
269            "handshake anchor must be cleared after recording the duration"
270        );
271        debug_assert_eq!(
272            elapsed.is_some(),
273            was_anchored,
274            "a duration is returned iff the handshake had been anchored"
275        );
276        elapsed
277    }
278
279    pub fn readable(&mut self) -> SessionResult {
280        // Anchor the handshake duration the first time we observe TLS bytes
281        // moving in either direction. Using `get_or_insert_with` keeps the
282        // anchor sticky across `WouldBlock` retries and across the
283        // readable/writable boundary.
284        self.handshake_started_at.get_or_insert_with(Instant::now);
285        // The anchor is sticky once set: this method must never run unanchored.
286        debug_assert!(
287            self.handshake_started_at.is_some(),
288            "handshake anchor must be set before driving TLS I/O"
289        );
290
291        // rustls handshake completion is monotonic (`true → false`, never
292        // back). Snapshot it so the exit assertions can prove we never resurrect
293        // a finished handshake.
294        let was_handshaking = self.session.is_handshaking();
295
296        match handshake_read(
297            &mut self.session,
298            &mut self.stream,
299            &mut self.frontend_readiness.event,
300        ) {
301            Ok(()) => {}
302            // A FIN or a reset before the handshake completes is routine
303            // (health checks, scanners): not an error.
304            Err(HandshakeReadFault::Closed) => {
305                debug!("{} Connection closed during handshake", log_context!(self));
306                return SessionResult::Close;
307            }
308            Err(HandshakeReadFault::ReadTls(e))
309                if matches!(
310                    e.kind(),
311                    ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted
312                ) =>
313            {
314                debug!(
315                    "{} Connection reset during handshake: {:?}",
316                    log_context!(self),
317                    e
318                );
319                return SessionResult::Close;
320            }
321            Err(HandshakeReadFault::ReadTls(e)) => {
322                error!(
323                    "{} Could not perform handshake: {:?}",
324                    log_context!(self),
325                    e
326                );
327                return SessionResult::Close;
328            }
329            Err(HandshakeReadFault::ProcessPackets(e)) => {
330                self.log_handshake_error(&e);
331                return SessionResult::Close;
332            }
333        }
334
335        // Handshake completion is monotonic: a handshake that had already
336        // finished at entry cannot become unfinished by pumping `read_tls`.
337        debug_assert!(
338            was_handshaking || !self.session.is_handshaking(),
339            "rustls handshake must not regress from finished back to handshaking"
340        );
341
342        // The client sent its FIN before its handshake completed: it can
343        // never send its `Finished`, so nothing it sent can be answered. A
344        // FIN after the `Finished` is a half-close the mux serves (the HUP
345        // survives the upgrade, `upgraded_frontend_events` in
346        // `lib/src/https.rs`).
347        if self.frontend_readiness.event.is_hup() && self.session.is_handshaking() {
348            debug!(
349                "{} Connection closed before the handshake completed",
350                log_context!(self)
351            );
352            return SessionResult::Close;
353        }
354
355        // Readiness must mirror rustls's own wants: we only drop READABLE
356        // interest when the session no longer wants to read.
357        if !self.session.wants_read() {
358            self.frontend_readiness.interest.remove(Ready::READABLE);
359        }
360        debug_assert!(
361            self.session.wants_read() || !self.frontend_readiness.interest.is_readable(),
362            "READABLE interest must be cleared once rustls stops wanting reads"
363        );
364
365        if self.session.wants_write() {
366            self.frontend_readiness.interest.insert(Ready::WRITABLE);
367        }
368
369        if self.session.is_handshaking() {
370            SessionResult::Continue
371        } else {
372            // handshake might be finished, but we still have something to send
373            if self.session.wants_write() {
374                SessionResult::Continue
375            } else {
376                // Upgrade is only signalled once the handshake is complete and
377                // there is nothing left to flush to the peer.
378                debug_assert!(
379                    !self.session.is_handshaking() && !self.session.wants_write(),
380                    "Upgrade requires a completed handshake with no pending output"
381                );
382                // No `event.insert(READABLE)` here: whether the upgraded
383                // session must read at once is decided by
384                // `upgraded_frontend_events` (`lib/src/https.rs`) from what is
385                // left in `event` — READABLE survives only when no read proved
386                // the socket empty since the last edge — and from the
387                // plaintext rustls already holds. Forcing it here made every
388                // upgraded session issue one `recv` that answered EAGAIN.
389                self.frontend_readiness.interest.insert(Ready::READABLE);
390                self.frontend_readiness.interest.insert(Ready::WRITABLE);
391                if let Some(elapsed_ms) = self.record_handshake_duration_ms() {
392                    time!(names::tls::HANDSHAKE_MS, elapsed_ms);
393                }
394                SessionResult::Upgrade
395            }
396        }
397    }
398
399    pub fn writable(&mut self) -> SessionResult {
400        // Same anchor logic as `readable()` — see the comment there.
401        self.handshake_started_at.get_or_insert_with(Instant::now);
402        debug_assert!(
403            self.handshake_started_at.is_some(),
404            "handshake anchor must be set before driving TLS I/O"
405        );
406
407        // Snapshot handshake completion for the monotonicity post-condition.
408        let was_handshaking = self.session.is_handshaking();
409
410        let mut can_write = true;
411
412        loop {
413            let mut can_work = false;
414
415            if self.session.wants_write() && can_write {
416                can_work = true;
417
418                match handshake_write(&mut self.session, &mut self.stream) {
419                    Ok(_) => {}
420                    Err(e) => match e.kind() {
421                        ErrorKind::WouldBlock => {
422                            self.frontend_readiness.event.remove(Ready::WRITABLE);
423                            can_write = false
424                        }
425                        _ => {
426                            if matches!(
427                                e.kind(),
428                                ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted
429                            ) {
430                                incr!(names::rustls::WRITE_RESET);
431                            }
432                            error!(
433                                "{} Could not perform handshake: {:?}",
434                                log_context!(self),
435                                e
436                            );
437                            return SessionResult::Close;
438                        }
439                    },
440                }
441
442                if let Err(e) = self.session.process_new_packets() {
443                    self.log_handshake_error(&e);
444                    return SessionResult::Close;
445                }
446            }
447
448            if !can_work {
449                break;
450            }
451        }
452
453        // Handshake completion is monotonic: pumping `write_tls` can finish a
454        // handshake but never un-finish one.
455        debug_assert!(
456            was_handshaking || !self.session.is_handshaking(),
457            "rustls handshake must not regress from finished back to handshaking"
458        );
459
460        // Readiness mirrors rustls's wants: WRITABLE interest is only dropped
461        // once the session no longer wants to write.
462        if !self.session.wants_write() {
463            self.frontend_readiness.interest.remove(Ready::WRITABLE);
464        }
465        debug_assert!(
466            self.session.wants_write() || !self.frontend_readiness.interest.is_writable(),
467            "WRITABLE interest must be cleared once rustls stops wanting writes"
468        );
469
470        if self.session.wants_read() {
471            self.frontend_readiness.interest.insert(Ready::READABLE);
472        }
473
474        if self.session.is_handshaking() {
475            SessionResult::Continue
476        } else if self.session.wants_read() {
477            // Upgrade after a completed handshake; the session still wants to
478            // read application data, which the upgraded state will drive.
479            debug_assert!(
480                !self.session.is_handshaking(),
481                "Upgrade requires a completed handshake"
482            );
483            self.frontend_readiness.interest.insert(Ready::READABLE);
484            if let Some(elapsed_ms) = self.record_handshake_duration_ms() {
485                time!(names::tls::HANDSHAKE_MS, elapsed_ms);
486            }
487            SessionResult::Upgrade
488        } else {
489            debug_assert!(
490                !self.session.is_handshaking(),
491                "Upgrade requires a completed handshake"
492            );
493            self.frontend_readiness.interest.insert(Ready::WRITABLE);
494            self.frontend_readiness.interest.insert(Ready::READABLE);
495            if let Some(elapsed_ms) = self.record_handshake_duration_ms() {
496                time!(names::tls::HANDSHAKE_MS, elapsed_ms);
497            }
498            SessionResult::Upgrade
499        }
500    }
501
502    pub fn log_context(&self) -> LogContext<'_> {
503        LogContext {
504            session_id: self.request_id,
505            request_id: None,
506            cluster_id: None,
507            backend_id: None,
508        }
509    }
510
511    pub fn front_socket(&self) -> &TcpStream {
512        &self.stream
513    }
514
515    /// Tiered logging for TLS handshake errors surfaced by `process_new_packets`.
516    ///
517    /// - `AlertReceived(_)`: remote peer rejected our cert/config (e.g. old
518    ///   CA bundle, scanner, cert-pinning client). Not actionable per-connection
519    ///   on a public endpoint, so log at `debug!`.
520    /// - Peer protocol violations (`PeerIncompatible`, `PeerMisbehaved`,
521    ///   `InvalidMessage`, inappropriate message / handshake message,
522    ///   oversized record, ALPN mismatch, bad client cert, `DecryptError`,
523    ///   `NoCertificatesPresented`): occasionally useful to spot buggy
524    ///   clients or stale roots, so log at `warn!`.
525    /// - Everything else (local/config/provider failures like `EncryptError`,
526    ///   `General`, `Other`, CRL issues, missing entropy): genuine server-side
527    ///   problems, stay at `error!`.
528    ///
529    /// Each tier additionally bumps `tls.handshake.failed.<reason>` so dashboards
530    /// can split spikes by category without having to grep logs.
531    fn log_handshake_error(&self, err: &RustlsError) {
532        let reason = handshake_failure_reason(err);
533        // Every reason must stay inside the bounded `tls.handshake.failed.*`
534        // namespace so statsd cardinality is predictable — unknown variants
535        // collapse to `.other`, never an unnamespaced key.
536        debug_assert!(
537            reason.starts_with("tls.handshake.failed."),
538            "handshake failure metric {reason} escaped the tls.handshake.failed. namespace"
539        );
540        match err {
541            RustlsError::AlertReceived(_) => debug!(
542                "{} Could not perform handshake: {:?}",
543                log_context!(self),
544                err
545            ),
546            RustlsError::PeerIncompatible(_)
547            | RustlsError::PeerMisbehaved(_)
548            | RustlsError::InvalidMessage(_)
549            | RustlsError::InappropriateMessage { .. }
550            | RustlsError::InappropriateHandshakeMessage { .. }
551            | RustlsError::PeerSentOversizedRecord
552            | RustlsError::NoApplicationProtocol
553            | RustlsError::InvalidCertificate(_)
554            | RustlsError::DecryptError
555            | RustlsError::NoCertificatesPresented => warn!(
556                "{} Could not perform handshake: {:?}",
557                log_context!(self),
558                err
559            ),
560            _ => error!(
561                "{} Could not perform handshake: {:?}",
562                log_context!(self),
563                err
564            ),
565        }
566        count!(reason, 1);
567    }
568}
569
570/// Compile-time literal `tls.handshake.failed.<reason>` keys for every variant
571/// the proxy can observe. Free function (rather than a method) so unit tests
572/// can drive it without constructing a real `ServerConnection`. The set of
573/// suffixes is bounded — anything outside the explicit `match` arms collapses
574/// to `tls.handshake.failed.other` so statsd cardinality stays predictable.
575fn handshake_failure_reason(err: &RustlsError) -> &'static str {
576    match err {
577        RustlsError::AlertReceived(_) => "tls.handshake.failed.alert_received",
578        RustlsError::PeerIncompatible(_) => "tls.handshake.failed.peer_incompatible",
579        RustlsError::PeerMisbehaved(_) => "tls.handshake.failed.peer_misbehaved",
580        RustlsError::InvalidMessage(_) => "tls.handshake.failed.invalid_message",
581        RustlsError::InappropriateMessage { .. } => "tls.handshake.failed.inappropriate_message",
582        RustlsError::InappropriateHandshakeMessage { .. } => {
583            "tls.handshake.failed.inappropriate_handshake_message"
584        }
585        RustlsError::PeerSentOversizedRecord => "tls.handshake.failed.oversized_record",
586        RustlsError::NoApplicationProtocol => "tls.handshake.failed.no_alpn",
587        RustlsError::InvalidCertificate(_) => "tls.handshake.failed.invalid_certificate",
588        RustlsError::DecryptError => "tls.handshake.failed.decrypt_error",
589        RustlsError::NoCertificatesPresented => "tls.handshake.failed.no_certificates_present",
590        _ => "tls.handshake.failed.other",
591    }
592}
593
594#[cfg(test)]
595thread_local! {
596    /// Passes of `TlsHandshake::drive`'s loop, so a test can prove one event
597    /// settles in a few passes rather than at `MAX_LOOP_ITERATIONS`.
598    static HANDSHAKE_PASSES: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
599}
600
601impl TlsHandshake {
602    /// One readiness event's worth of handshake work; `SessionState::ready`
603    /// is this with arguments the handshake does not use.
604    fn drive(&mut self) -> SessionResult {
605        let mut counter = 0;
606
607        // A HUP with no read due closes the handshake. With a read due it may
608        // be a client's half-close (`EPOLLRDHUP`, which `Ready::from(&Event)`
609        // in `command/src/ready.rs` reports as HUP): with TLS 1.3 its
610        // `Finished`, its request and its FIN may arrive together. Read them
611        // first: `TlsHandshake::readable` closes the handshake if they did not
612        // complete it.
613        if self.frontend_readiness.event.is_hup()
614            && !self.frontend_readiness.filter_interest().is_readable()
615        {
616            return SessionResult::Close;
617        }
618
619        while counter < MAX_LOOP_ITERATIONS {
620            #[cfg(test)]
621            HANDSHAKE_PASSES.with(|passes| passes.set(passes.get() + 1));
622            let frontend_interest = self.frontend_readiness.filter_interest();
623
624            trace!("{} Interest({:?})", log_context!(self), frontend_interest);
625            // HUP alone is no work this loop can do: nothing here acts on it.
626            if !frontend_interest.is_readable()
627                && !frontend_interest.is_writable()
628                && !frontend_interest.is_error()
629            {
630                break;
631            }
632
633            if frontend_interest.is_readable() {
634                let protocol_result = self.readable();
635                if protocol_result != SessionResult::Continue {
636                    return protocol_result;
637                }
638            }
639
640            if frontend_interest.is_writable() {
641                let protocol_result = self.writable();
642                if protocol_result != SessionResult::Continue {
643                    return protocol_result;
644                }
645            }
646
647            if frontend_interest.is_error() {
648                error!("{} Front socket error, disconnecting", log_context!(self));
649                self.frontend_readiness.interest = Ready::EMPTY;
650                return SessionResult::Close;
651            }
652
653            counter += 1;
654        }
655
656        if counter >= MAX_LOOP_ITERATIONS {
657            error!(
658                "{}\tHandling session went through {} iterations, there's a probable infinite loop bug, closing the connection",
659                log_context!(self),
660                MAX_LOOP_ITERATIONS
661            );
662
663            incr!(names::http::INFINITE_LOOP_ERROR);
664            self.print_state("HTTPS");
665
666            return SessionResult::Close;
667        }
668
669        SessionResult::Continue
670    }
671}
672
673impl SessionState for TlsHandshake {
674    fn ready(
675        &mut self,
676        _session: Rc<RefCell<dyn crate::ProxySession>>,
677        _proxy: Rc<RefCell<dyn crate::L7Proxy>>,
678        _metrics: &mut SessionMetrics,
679    ) -> SessionResult {
680        self.drive()
681    }
682
683    fn update_readiness(&mut self, token: Token, events: Ready) {
684        if self.frontend_token == token {
685            self.frontend_readiness.event |= events;
686        }
687    }
688
689    fn timeout(&mut self, token: Token, _metrics: &mut SessionMetrics) -> StateResult {
690        // relevant timeout is still stored in the Session as front_timeout.
691        if self.frontend_token == token {
692            self.container_frontend_timeout.triggered();
693            return StateResult::CloseSession;
694        }
695
696        error!(
697            "{}, Expect state: got timeout for an invalid token: {:?}",
698            log_context!(self),
699            token
700        );
701        StateResult::CloseSession
702    }
703
704    fn cancel_timeouts(&mut self) {
705        self.container_frontend_timeout.cancel();
706    }
707
708    fn print_state(&self, context: &str) {
709        error!(
710            "{} Session(Handshake)\n\tFrontend:\n\t\ttoken: {:?}\treadiness: {:?}",
711            context, self.frontend_token, self.frontend_readiness
712        );
713    }
714}
715
716// -----------------------------------------------------------------------------
717// Unit tests
718
719#[cfg(test)]
720mod tests {
721    use std::collections::HashSet;
722
723    use rustls::{
724        AlertDescription, CertificateError, ContentType, Error as RustlsError, HandshakeType,
725        InvalidMessage, PeerIncompatible, PeerMisbehaved,
726    };
727
728    use mio::{Token, net::TcpStream};
729    use rusty_ulid::Ulid;
730
731    use super::{
732        HandshakeReadFault, TlsHandshake, handshake_failure_reason, handshake_read, handshake_write,
733    };
734    use crate::{
735        Ready, SessionResult, metrics::names, protocol::SessionState, timer::TimeoutContainer,
736    };
737    use std::io::ErrorKind;
738
739    /// Every rustls error variant the proxy can observe must map to a distinct,
740    /// compile-time literal `tls.handshake.failed.<reason>` key. Unknown
741    /// variants (future rustls additions, `General`, `Other`, CRL errors, etc.)
742    /// collapse to `tls.handshake.failed.other` so statsd cardinality stays
743    /// bounded. This test also guards against accidental duplicate keys.
744    #[test]
745    fn handshake_failure_reason_maps_every_variant_to_unique_namespaced_key() {
746        let cases: &[(RustlsError, &str)] = &[
747            (
748                RustlsError::AlertReceived(AlertDescription::HandshakeFailure),
749                "tls.handshake.failed.alert_received",
750            ),
751            (
752                RustlsError::PeerIncompatible(PeerIncompatible::NoCipherSuitesInCommon),
753                "tls.handshake.failed.peer_incompatible",
754            ),
755            (
756                RustlsError::PeerMisbehaved(PeerMisbehaved::IllegalMiddleboxChangeCipherSpec),
757                "tls.handshake.failed.peer_misbehaved",
758            ),
759            (
760                RustlsError::InvalidMessage(InvalidMessage::InvalidContentType),
761                "tls.handshake.failed.invalid_message",
762            ),
763            (
764                RustlsError::InappropriateMessage {
765                    expect_types: vec![ContentType::Handshake],
766                    got_type: ContentType::ApplicationData,
767                },
768                "tls.handshake.failed.inappropriate_message",
769            ),
770            (
771                RustlsError::InappropriateHandshakeMessage {
772                    expect_types: vec![HandshakeType::ClientHello],
773                    got_type: HandshakeType::Finished,
774                },
775                "tls.handshake.failed.inappropriate_handshake_message",
776            ),
777            (
778                RustlsError::PeerSentOversizedRecord,
779                "tls.handshake.failed.oversized_record",
780            ),
781            (
782                RustlsError::NoApplicationProtocol,
783                "tls.handshake.failed.no_alpn",
784            ),
785            (
786                RustlsError::InvalidCertificate(CertificateError::Expired),
787                "tls.handshake.failed.invalid_certificate",
788            ),
789            (
790                RustlsError::DecryptError,
791                "tls.handshake.failed.decrypt_error",
792            ),
793            (
794                RustlsError::NoCertificatesPresented,
795                "tls.handshake.failed.no_certificates_present",
796            ),
797            // `Other` bucket — any variant not in the explicit list collapses here.
798            (
799                RustlsError::General("test".to_owned()),
800                "tls.handshake.failed.other",
801            ),
802            (RustlsError::EncryptError, "tls.handshake.failed.other"),
803            (
804                RustlsError::FailedToGetCurrentTime,
805                "tls.handshake.failed.other",
806            ),
807            (
808                RustlsError::HandshakeNotComplete,
809                "tls.handshake.failed.other",
810            ),
811        ];
812
813        let mut seen = HashSet::new();
814        for (err, expected) in cases {
815            let got = handshake_failure_reason(err);
816            assert_eq!(got, *expected, "variant {err:?} → {got}, want {expected}");
817            assert!(
818                got.starts_with("tls.handshake.failed."),
819                "reason {got} missing tls.handshake.failed. namespace"
820            );
821            seen.insert(got);
822        }
823
824        // 11 explicit buckets + 1 shared `other` bucket = 12 distinct keys.
825        assert_eq!(seen.len(), 12, "unexpected key set: {seen:?}");
826    }
827
828    /// Everything `conn` has queued for the wire.
829    fn flight<D>(conn: &mut rustls::ConnectionCommon<D>) -> Vec<u8> {
830        let mut wire = Vec::new();
831        while conn.wants_write() {
832            conn.write_tls(&mut wire)
833                .expect("a TLS flight must serialize into memory");
834        }
835        wire
836    }
837
838    /// Feed a whole server flight to the client, in memory.
839    fn deliver(client: &mut rustls::ClientConnection, wire: &[u8]) {
840        let mut cursor = std::io::Cursor::new(wire);
841        while (cursor.position() as usize) < wire.len() {
842            client
843                .read_tls(&mut cursor)
844                .expect("the server flight must be readable from memory");
845            client
846                .process_new_packets()
847                .expect("the server flight must process cleanly");
848        }
849    }
850
851    /// The ClientHello and the client `Finished` each fit in one `recv`
852    /// shorter than the 4096 bytes rustls offers: each costs that one `recv`
853    /// and drops READABLE, with no second `recv` answering EAGAIN (#1609).
854    #[test]
855    fn a_short_handshake_read_stops_without_an_eagain() {
856        use crate::socket::rustls_read_tests::{CountingTransport, fresh_pair};
857
858        let (mut server, mut client) = fresh_pair(Vec::new());
859        let hello = flight(&mut client);
860        assert!(
861            hello.len() < 4096,
862            "premise: the ClientHello is a short read"
863        );
864        let mut transport = CountingTransport {
865            wire: hello.into_iter().collect(),
866            ..Default::default()
867        };
868
869        let mut event = Ready::READABLE;
870        handshake_read(&mut server, &mut transport, &mut event)
871            .expect("the ClientHello must process");
872        assert_eq!((transport.reads, transport.eagains), (1, 0));
873        assert!(
874            !event.is_readable(),
875            "a short read proved the queue empty: READABLE must go"
876        );
877        assert!(server.is_handshaking());
878
879        deliver(&mut client, &flight(&mut server));
880        transport.wire.extend(flight(&mut client));
881        event = Ready::READABLE;
882        handshake_read(&mut server, &mut transport, &mut event)
883            .expect("the client Finished must process");
884        assert_eq!(
885            (transport.reads, transport.eagains),
886            (2, 0),
887            "the Finished costs one recv and no EAGAIN"
888        );
889        assert!(!event.is_readable());
890        assert!(!server.is_handshaking(), "the handshake is complete");
891    }
892
893    /// A peer that resets the connection during the handshake closes the
894    /// session, as it always did, and counts `rustls.read.reset` once, like
895    /// a reset after the handshake (sozu-proxy/sozu#434).
896    ///
897    /// To SEE THIS RED: drop `incr!(names::rustls::READ_RESET)` from
898    /// `handshake_read`.
899    #[test]
900    fn a_reset_during_the_handshake_counts_a_read_reset() {
901        use crate::socket::{rustls_read_tests::fresh_pair, tests};
902
903        let (mut server, _client) = fresh_pair(Vec::new());
904        let mut stream = tests::reset_stream();
905        let before = tests::proxy_count(names::rustls::READ_RESET);
906
907        let mut event = Ready::READABLE;
908        let fault = handshake_read(&mut server, &mut stream, &mut event)
909            .expect_err("a reset socket cannot carry a handshake");
910        assert!(
911            matches!(&fault, HandshakeReadFault::ReadTls(e) if e.kind() == ErrorKind::ConnectionReset),
912            "the reset surfaces as a read fault, got {fault:?}"
913        );
914        assert_eq!(tests::proxy_count(names::rustls::READ_RESET) - before, 1);
915    }
916
917    /// A read that fills the 4096 bytes rustls offered is not a short read:
918    /// the loop reads again. A ClientHello padded past 4096 bytes by a long
919    /// ALPN list takes one full read and one short one, still no EAGAIN.
920    #[test]
921    fn a_full_handshake_read_reads_again() {
922        use crate::socket::rustls_read_tests::{CountingTransport, fresh_pair};
923
924        let alpn = (0..64u8)
925            .map(|index| vec![b'a' + index % 26; 100])
926            .collect();
927        let (mut server, mut client) = fresh_pair(alpn);
928        let hello = flight(&mut client);
929        assert!(
930            4096 < hello.len() && hello.len() < 8192,
931            "premise: one full read and one short one, got {} bytes",
932            hello.len()
933        );
934        let mut transport = CountingTransport {
935            wire: hello.into_iter().collect(),
936            ..Default::default()
937        };
938        let mut event = Ready::READABLE;
939        handshake_read(&mut server, &mut transport, &mut event)
940            .expect("the padded ClientHello must process");
941        assert_eq!(
942            (transport.reads, transport.eagains),
943            (2, 0),
944            "a full read is followed by one more recv, which is short"
945        );
946        assert!(!event.is_readable());
947        assert!(server.wants_write(), "the whole ClientHello was processed");
948    }
949
950    /// A transport whose first call, read or write, is interrupted (`EINTR`)
951    /// and whose later calls reach `inner`.
952    struct InterruptedOnce<T> {
953        inner: T,
954        calls: usize,
955    }
956
957    impl<T> InterruptedOnce<T> {
958        fn new(inner: T) -> Self {
959            InterruptedOnce { inner, calls: 0 }
960        }
961
962        fn interrupt_first(&mut self) -> std::io::Result<()> {
963            self.calls += 1;
964            if self.calls == 1 {
965                return Err(ErrorKind::Interrupted.into());
966            }
967            Ok(())
968        }
969    }
970
971    impl<T: std::io::Read> std::io::Read for InterruptedOnce<T> {
972        fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
973            self.interrupt_first()?;
974            self.inner.read(buf)
975        }
976    }
977
978    impl<T: std::io::Write> std::io::Write for InterruptedOnce<T> {
979        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
980            self.interrupt_first()?;
981            self.inner.write(buf)
982        }
983
984        fn flush(&mut self) -> std::io::Result<()> {
985            self.inner.flush()
986        }
987    }
988
989    /// An interrupted `recv` during the handshake is retried, not reported as
990    /// a `ReadTls` fault that closes the session: it says nothing about the
991    /// socket. The retry reads the ClientHello short, which still drops
992    /// READABLE.
993    ///
994    /// TO SEE THIS RED: remove the `Interrupted` arm of the `read_tls` retry
995    /// loop in `handshake_read`.
996    #[test]
997    fn an_interrupted_handshake_read_is_retried() {
998        use crate::socket::rustls_read_tests::{CountingTransport, fresh_pair};
999
1000        let (mut server, mut client) = fresh_pair(Vec::new());
1001        let mut transport = InterruptedOnce::new(CountingTransport {
1002            wire: flight(&mut client).into_iter().collect(),
1003            ..Default::default()
1004        });
1005
1006        let mut event = Ready::READABLE;
1007        handshake_read(&mut server, &mut transport, &mut event)
1008            .expect("an interrupted read must be retried, not reported");
1009
1010        assert_eq!(transport.calls, 2, "one interrupted read, one retry");
1011        assert_eq!(
1012            (transport.inner.reads, transport.inner.eagains),
1013            (1, 0),
1014            "the retry is the only recv to reach the socket"
1015        );
1016        assert!(
1017            transport.inner.wire.is_empty(),
1018            "the retry read the ClientHello"
1019        );
1020        assert!(
1021            !event.is_readable(),
1022            "the retried read was short: READABLE must go"
1023        );
1024        assert!(server.wants_write(), "the whole ClientHello was processed");
1025    }
1026
1027    /// An interrupted handshake write is retried, not reported as an error
1028    /// that closes the session.
1029    ///
1030    /// TO SEE THIS RED: remove the `Interrupted` arm of the retry loop in
1031    /// `handshake_write`.
1032    #[test]
1033    fn an_interrupted_handshake_write_is_retried() {
1034        use crate::socket::rustls_read_tests::fresh_pair;
1035
1036        let (mut server, mut client) = fresh_pair(Vec::new());
1037        let hello = flight(&mut client);
1038        server
1039            .read_tls(&mut std::io::Cursor::new(hello))
1040            .expect("the ClientHello must be readable from memory");
1041        server
1042            .process_new_packets()
1043            .expect("the ClientHello must process cleanly");
1044        assert!(server.wants_write(), "premise: the server flight is queued");
1045        let mut transport = InterruptedOnce::new(Vec::new());
1046
1047        let written = handshake_write(&mut server, &mut transport)
1048            .expect("an interrupted write must be retried, not reported");
1049
1050        assert_eq!(transport.calls, 2, "one interrupted write, one retry");
1051        assert!(written > 0 && transport.inner.len() == written);
1052        let mut wire = transport.inner;
1053        wire.extend(flight(&mut server));
1054        deliver(&mut client, &wire);
1055        assert!(
1056            client.wants_write(),
1057            "the client got the whole server flight and answers it"
1058        );
1059    }
1060
1061    /// A client sends `hello_len` bytes of its ClientHello, then its FIN, and
1062    /// the handshake gets one event carrying `event`. Returns what the pass
1063    /// decided and how many loop passes it took.
1064    fn hello_then_fin(hello_part: impl Fn(usize) -> usize, event: Ready) -> (SessionResult, usize) {
1065        use std::io::Write as _;
1066
1067        use crate::socket::rustls_read_tests::fresh_pair;
1068
1069        let listener = std::net::TcpListener::bind("127.0.0.1:0")
1070            .expect("test listener must bind to a loopback port");
1071        let mut peer = std::net::TcpStream::connect(
1072            listener
1073                .local_addr()
1074                .expect("test listener must report its local address"),
1075        )
1076        .expect("loopback connect must complete");
1077        let (accepted, _) = listener.accept().expect("the connection must be accepted");
1078        accepted
1079            .set_nonblocking(true)
1080            .expect("mio requires a nonblocking stream");
1081        let (server, mut client) = fresh_pair(Vec::new());
1082        let hello = flight(&mut client);
1083        peer.write_all(&hello[..hello_part(hello.len())])
1084            .expect("the client must send");
1085        peer.shutdown(std::net::Shutdown::Write)
1086            .expect("the client must send its FIN");
1087
1088        let token = Token(7);
1089        let mut handshake = TlsHandshake::new(
1090            TimeoutContainer::new_empty(std::time::Duration::from_secs(10)),
1091            server,
1092            TcpStream::from_std(accepted),
1093            token,
1094            Ulid::generate(),
1095            None,
1096        );
1097        handshake.update_readiness(token, event);
1098        super::HANDSHAKE_PASSES.with(|passes| passes.set(0));
1099        let result = handshake.drive();
1100        (result, super::HANDSHAKE_PASSES.with(|passes| passes.get()))
1101    }
1102
1103    /// A ClientHello cut short by the client's FIN: the short read stops
1104    /// before the EOF, and the client can never complete the handshake, so
1105    /// the handshake closes. Waiting for a read that never comes left the HUP
1106    /// spinning the loop to `MAX_LOOP_ITERATIONS`.
1107    ///
1108    /// TO SEE THIS RED, for this test and the two below: drop the
1109    /// `is_hup() && is_handshaking()` close from `TlsHandshake::readable` and
1110    /// the HUP-alone break from `TlsHandshake::drive`.
1111    #[test]
1112    fn a_partial_client_hello_then_fin_closes_without_spinning() {
1113        let (result, passes) = hello_then_fin(|len| len / 2, Ready::READABLE | Ready::HUP);
1114        assert_eq!(result, SessionResult::Close, "the EOF closes the handshake");
1115        assert!(
1116            passes < 4,
1117            "one event settles in a few passes, took {passes}"
1118        );
1119    }
1120
1121    /// A whole ClientHello then the FIN: rustls wants to answer, but the
1122    /// client can never finish the handshake, and its EOF closes it.
1123    #[test]
1124    fn a_full_client_hello_then_fin_closes_without_spinning() {
1125        let (result, passes) = hello_then_fin(|len| len, Ready::READABLE | Ready::HUP);
1126        assert_eq!(result, SessionResult::Close, "the EOF closes the handshake");
1127        assert!(
1128            passes < 4,
1129            "one event settles in a few passes, took {passes}"
1130        );
1131    }
1132
1133    /// The same with WRITABLE in the event, as the first edge of a fresh
1134    /// socket reports it.
1135    #[test]
1136    fn a_full_client_hello_then_fin_with_writable_closes_without_spinning() {
1137        let (result, passes) =
1138            hello_then_fin(|len| len, Ready::READABLE | Ready::WRITABLE | Ready::HUP);
1139        assert_eq!(result, SessionResult::Close, "the EOF closes the handshake");
1140        assert!(
1141            passes < 4,
1142            "one event settles in a few passes, took {passes}"
1143        );
1144    }
1145
1146    /// Drive a handshake the way `TlsHandshake::ready` does, for one event.
1147    fn drive(handshake: &mut TlsHandshake) -> SessionResult {
1148        for _ in 0..16 {
1149            let interest = handshake.frontend_readiness.filter_interest();
1150            if interest.is_empty() {
1151                return SessionResult::Continue;
1152            }
1153            if interest.is_readable() {
1154                let result = handshake.readable();
1155                if result != SessionResult::Continue {
1156                    return result;
1157                }
1158            }
1159            if interest.is_writable() {
1160                let result = handshake.writable();
1161                if result != SessionResult::Continue {
1162                    return result;
1163                }
1164            }
1165        }
1166        panic!("the handshake did not settle within 16 passes");
1167    }
1168
1169    /// A ClientHello split over three TCP segments completes on a real
1170    /// loopback socket registered edge-triggered with mio. After each short
1171    /// read the handshake drops READABLE, no event is pending, and the next
1172    /// segment raises the edge that resumes it: stopping on a short read never
1173    /// leaves the handshake waiting for bytes already queued. The upgrade that
1174    /// follows the client `Finished` leaves READABLE unset, because the last
1175    /// read proved the socket empty (#1609).
1176    #[test]
1177    fn a_client_hello_in_three_segments_completes_on_a_loopback_socket() {
1178        use std::io::{Read as _, Write as _};
1179
1180        use crate::socket::rustls_read_tests::fresh_pair;
1181
1182        let token = Token(7);
1183        let mut poll = mio::Poll::new().expect("a poll instance must open");
1184        let mut events = mio::Events::with_capacity(8);
1185        let listener = std::net::TcpListener::bind("127.0.0.1:0")
1186            .expect("test listener must bind to a loopback port");
1187        let mut peer = std::net::TcpStream::connect(
1188            listener
1189                .local_addr()
1190                .expect("test listener must report its local address"),
1191        )
1192        .expect("loopback connect must complete");
1193        peer.set_nodelay(true)
1194            .expect("the client must disable Nagle to send each segment at once");
1195        let (accepted, _) = listener.accept().expect("the connection must be accepted");
1196        accepted
1197            .set_nonblocking(true)
1198            .expect("mio requires a nonblocking stream");
1199        let mut stream = TcpStream::from_std(accepted);
1200        poll.registry()
1201            .register(&mut stream, token, mio::Interest::READABLE)
1202            .expect("the stream must register");
1203
1204        let (server, mut client) = fresh_pair(Vec::new());
1205        let mut handshake = TlsHandshake::new(
1206            TimeoutContainer::new_empty(std::time::Duration::from_secs(10)),
1207            server,
1208            stream,
1209            token,
1210            Ulid::generate(),
1211            None,
1212        );
1213        let mut edge_within = |poll: &mut mio::Poll, timeout: std::time::Duration| {
1214            poll.poll(&mut events, Some(timeout))
1215                .expect("poll must succeed");
1216            events
1217                .iter()
1218                .any(|event| event.token() == token && event.is_readable())
1219        };
1220        let edge = std::time::Duration::from_secs(5);
1221        let quiet = std::time::Duration::from_millis(50);
1222
1223        let hello = flight(&mut client);
1224        let third = hello.len() / 3;
1225        let segments = [
1226            &hello[..third],
1227            &hello[third..2 * third],
1228            &hello[2 * third..],
1229        ];
1230        for (index, segment) in segments.iter().enumerate() {
1231            peer.write_all(segment).expect("the client must send");
1232            assert!(
1233                edge_within(&mut poll, edge),
1234                "segment {index} must raise an edge"
1235            );
1236            handshake.update_readiness(token, Ready::READABLE);
1237            assert_eq!(drive(&mut handshake), SessionResult::Continue);
1238            assert!(
1239                !handshake.frontend_readiness.event.is_readable(),
1240                "segment {index}: the short read must drop READABLE"
1241            );
1242            if index + 1 < segments.len() {
1243                assert!(handshake.session.is_handshaking());
1244                assert!(
1245                    !edge_within(&mut poll, quiet),
1246                    "segment {index}: edge-triggered, nothing new, no event"
1247                );
1248                let mut probe = [0u8; 1];
1249                assert!(
1250                    matches!(
1251                        handshake.stream.read(&mut probe),
1252                        Err(ref e) if e.kind() == ErrorKind::WouldBlock
1253                    ),
1254                    "segment {index}: nothing is left queued"
1255                );
1256            }
1257        }
1258        assert!(
1259            handshake.session.wants_write(),
1260            "the whole ClientHello was read and processed"
1261        );
1262
1263        // The server flight: write it as `ready` would once WRITABLE fires.
1264        handshake.update_readiness(token, Ready::WRITABLE);
1265        assert_eq!(drive(&mut handshake), SessionResult::Continue);
1266        assert!(!handshake.session.wants_write(), "the server flight is out");
1267
1268        peer.set_read_timeout(Some(std::time::Duration::from_secs(5)))
1269            .expect("the client read timeout must set");
1270        while client.is_handshaking() {
1271            client
1272                .read_tls(&mut peer)
1273                .expect("the client must read the server flight");
1274            client
1275                .process_new_packets()
1276                .expect("the server flight must process cleanly");
1277        }
1278        peer.write_all(&flight(&mut client))
1279            .expect("the client must send its Finished");
1280        assert!(
1281            edge_within(&mut poll, edge),
1282            "the Finished must raise an edge"
1283        );
1284        handshake.update_readiness(token, Ready::READABLE);
1285        assert_eq!(drive(&mut handshake), SessionResult::Upgrade);
1286        assert!(!handshake.session.is_handshaking());
1287        assert!(
1288            !handshake.frontend_readiness.event.is_readable(),
1289            "the socket is empty: the upgrade must not be handed a READABLE"
1290        );
1291    }
1292}