Coverage Report

Created: 2026-03-20 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h2/src/proto/settings.rs
Line
Count
Source
1
use crate::codec::UserError;
2
use crate::error::Reason;
3
use crate::proto::*;
4
use std::task::{Context, Poll};
5
6
#[derive(Debug)]
7
pub(crate) struct Settings {
8
    /// Our local SETTINGS sync state with the remote.
9
    local: Local,
10
    /// Received SETTINGS frame pending processing. The ACK must be written to
11
    /// the socket first then the settings applied **before** receiving any
12
    /// further frames.
13
    remote: Option<frame::Settings>,
14
    /// Whether the connection has received the initial SETTINGS frame from the
15
    /// remote peer.
16
    has_received_remote_initial_settings: bool,
17
}
18
19
#[derive(Debug)]
20
enum Local {
21
    /// We want to send these SETTINGS to the remote when the socket is ready.
22
    ToSend(frame::Settings),
23
    /// We have sent these SETTINGS and are waiting for the remote to ACK
24
    /// before we apply them.
25
    WaitingAck(frame::Settings),
26
    /// Our local settings are in sync with the remote.
27
    Synced,
28
}
29
30
impl Settings {
31
14.5k
    pub(crate) fn new(local: frame::Settings) -> Self {
32
14.5k
        Settings {
33
14.5k
            // We assume the initial local SETTINGS were flushed during
34
14.5k
            // the handshake process.
35
14.5k
            local: Local::WaitingAck(local),
36
14.5k
            remote: None,
37
14.5k
            has_received_remote_initial_settings: false,
38
14.5k
        }
39
14.5k
    }
40
41
6.64k
    pub(crate) fn recv_settings<T, B, C, P>(
42
6.64k
        &mut self,
43
6.64k
        frame: frame::Settings,
44
6.64k
        codec: &mut Codec<T, B>,
45
6.64k
        streams: &mut Streams<C, P>,
46
6.64k
    ) -> Result<(), Error>
47
6.64k
    where
48
6.64k
        T: AsyncWrite + Unpin,
49
6.64k
        B: Buf,
50
6.64k
        C: Buf,
51
6.64k
        P: Peer,
52
    {
53
6.64k
        if frame.is_ack() {
54
18
            match &self.local {
55
17
                Local::WaitingAck(local) => {
56
17
                    tracing::debug!("received settings ACK; applying {:?}", local);
57
58
17
                    if let Some(max) = local.max_frame_size() {
59
0
                        codec.set_max_recv_frame_size(max as usize);
60
17
                    }
61
62
17
                    if let Some(max) = local.max_header_list_size() {
63
0
                        codec.set_max_recv_header_list_size(max as usize);
64
17
                    }
65
66
17
                    if let Some(val) = local.header_table_size() {
67
0
                        codec.set_recv_header_table_size(val as usize);
68
17
                    }
69
70
17
                    streams.apply_local_settings(local)?;
71
17
                    self.local = Local::Synced;
72
17
                    Ok(())
73
                }
74
                Local::ToSend(..) | Local::Synced => {
75
                    // We haven't sent any SETTINGS frames to be ACKed, so
76
                    // this is very bizarre! Remote is either buggy or malicious.
77
1
                    proto_err!(conn: "received unexpected settings ack");
78
1
                    Err(Error::library_go_away(Reason::PROTOCOL_ERROR))
79
                }
80
            }
81
        } else {
82
            // We always ACK before reading more frames, so `remote` should
83
            // always be none!
84
6.62k
            assert!(self.remote.is_none());
85
6.62k
            self.remote = Some(frame);
86
6.62k
            Ok(())
87
        }
88
6.64k
    }
89
90
0
    pub(crate) fn send_settings(&mut self, frame: frame::Settings) -> Result<(), UserError> {
91
0
        assert!(!frame.is_ack());
92
0
        match &self.local {
93
0
            Local::ToSend(..) | Local::WaitingAck(..) => Err(UserError::SendSettingsWhilePending),
94
            Local::Synced => {
95
0
                tracing::trace!("queue to send local settings: {:?}", frame);
96
0
                self.local = Local::ToSend(frame);
97
0
                Ok(())
98
            }
99
        }
100
0
    }
101
102
    /// Sets `true` to `self.has_received_remote_initial_settings`.
103
    /// Returns `true` if this method is called for the first time.
104
    /// (i.e. it is the initial SETTINGS frame from the remote peer)
105
6.54k
    fn mark_remote_initial_settings_as_received(&mut self) -> bool {
106
6.54k
        let has_received = self.has_received_remote_initial_settings;
107
6.54k
        self.has_received_remote_initial_settings = true;
108
6.54k
        !has_received
109
6.54k
    }
110
111
590k
    pub(crate) fn poll_send<T, B, C, P>(
112
590k
        &mut self,
113
590k
        cx: &mut Context,
114
590k
        dst: &mut Codec<T, B>,
115
590k
        streams: &mut Streams<C, P>,
116
590k
    ) -> Poll<Result<(), Error>>
117
590k
    where
118
590k
        T: AsyncWrite + Unpin,
119
590k
        B: Buf,
120
590k
        C: Buf,
121
590k
        P: Peer,
122
    {
123
590k
        if let Some(settings) = self.remote.clone() {
124
9.02k
            if !dst.poll_ready(cx)?.is_ready() {
125
2.41k
                return Poll::Pending;
126
6.54k
            }
127
128
            // Create an ACK settings frame
129
6.54k
            let frame = frame::Settings::ack();
130
131
            // Buffer the settings frame
132
6.54k
            dst.buffer(frame.into()).expect("invalid settings frame");
133
134
6.54k
            tracing::trace!("ACK sent; applying settings");
135
136
6.54k
            let is_initial = self.mark_remote_initial_settings_as_received();
137
6.54k
            streams.apply_remote_settings(&settings, is_initial)?;
138
139
6.53k
            if let Some(val) = settings.header_table_size() {
140
1.53k
                dst.set_send_header_table_size(val as usize);
141
5.00k
            }
142
143
6.53k
            if let Some(val) = settings.max_frame_size() {
144
336
                dst.set_max_send_frame_size(val as usize);
145
6.20k
            }
146
581k
        }
147
148
588k
        self.remote = None;
149
150
588k
        match &self.local {
151
0
            Local::ToSend(settings) => {
152
0
                if !dst.poll_ready(cx)?.is_ready() {
153
0
                    return Poll::Pending;
154
0
                }
155
156
                // Buffer the settings frame
157
0
                dst.buffer(settings.clone().into())
158
0
                    .expect("invalid settings frame");
159
0
                tracing::trace!("local settings sent; waiting for ack: {:?}", settings);
160
161
0
                self.local = Local::WaitingAck(settings.clone());
162
            }
163
588k
            Local::WaitingAck(..) | Local::Synced => {}
164
        }
165
166
588k
        Poll::Ready(Ok(()))
167
590k
    }
168
}