Skip to main content

ntex_h2/codec/
mod.rs

1use std::{cell::RefCell, rc::Rc};
2
3use ntex_bytes::{BytePages, Bytes, BytesMut};
4use ntex_codec::{Decoder, Encoder};
5
6mod error;
7mod length_delimited;
8
9pub use self::error::EncoderError;
10
11use self::length_delimited::LengthDelimitedCodec;
12use crate::{consts, frame, frame::Frame, frame::Kind, hpack};
13
14// Push promise frame kind
15const PUSH_PROMISE: u8 = 5;
16
17#[derive(Clone, Debug)]
18pub struct Codec(Rc<RefCell<CodecInner>>);
19
20/// Partially loaded headers frame
21#[derive(Debug)]
22struct Partial {
23    /// Empty frame
24    frame: frame::Headers,
25    /// Partial header payload
26    buf: Bytes,
27    /// Number of continuations
28    count: usize,
29}
30
31#[derive(Debug)]
32struct CodecInner {
33    // encoder state
34    encoder_hpack: hpack::Encoder,
35    encoder_max_frame_size: frame::FrameSize, // Max frame size, this is specified by the peer
36
37    // decoder state
38    decoder: LengthDelimitedCodec,
39    decoder_hpack: hpack::Decoder,
40    decoder_max_headers: usize,
41    decoder_max_header_list_size: usize,
42    decoder_max_header_continuations: usize,
43    partial: Option<Partial>, // Partially loaded headers frame
44}
45
46impl Default for Codec {
47    #[inline]
48    /// Returns a new `Codec` with the default max frame size
49    fn default() -> Self {
50        // Delimit the frames
51        let decoder = self::length_delimited::Builder::new()
52            .length_field_length(3)
53            .length_adjustment(9)
54            .max_frame_length(frame::DEFAULT_MAX_FRAME_SIZE as usize)
55            .num_skip(0) // Don't skip the header
56            .new_codec();
57
58        Codec(Rc::new(RefCell::new(CodecInner {
59            decoder,
60            decoder_hpack: hpack::Decoder::new(frame::DEFAULT_SETTINGS_HEADER_TABLE_SIZE),
61            decoder_max_headers: consts::DEFAULT_MAX_HEADERS,
62            decoder_max_header_list_size: consts::DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE as usize,
63            decoder_max_header_continuations: consts::DEFAULT_MAX_COUNTINUATIONS,
64            partial: None,
65
66            encoder_hpack: hpack::Encoder::default(),
67            encoder_max_frame_size: frame::DEFAULT_MAX_FRAME_SIZE,
68        })))
69    }
70}
71
72impl Codec {
73    /// Updates the max received frame size.
74    ///
75    /// The change takes effect the next time a frame is decoded. In other
76    /// words, if a frame is currently in process of being decoded with a frame
77    /// size greater than `val` but less than the max frame size in effect
78    /// before calling this function, then the frame will be allowed.
79    ///
80    /// # Panics
81    ///
82    /// Panics if size is greater than `16_777_215`.
83    #[inline]
84    pub fn set_recv_frame_size(&self, val: usize) {
85        assert!(
86            frame::DEFAULT_MAX_FRAME_SIZE as usize <= val
87                && val <= frame::MAX_MAX_FRAME_SIZE as usize
88        );
89        self.0.borrow_mut().decoder.set_max_frame_length(val);
90    }
91
92    /// Local max frame size.
93    pub fn recv_frame_size(&self) -> u32 {
94        self.0.borrow_mut().decoder.max_frame_length() as u32
95    }
96
97    /// Set the max header list size that can be received.
98    ///
99    /// By default value is set to 48kb
100    pub fn set_recv_header_list_size(&self, val: usize) {
101        self.0.borrow_mut().decoder_max_header_list_size = val;
102    }
103
104    /// Set the max headers.
105    ///
106    /// By default value is set to 96
107    pub fn set_max_headers(&self, val: usize) {
108        self.0.borrow_mut().decoder_max_headers = val;
109    }
110
111    /// Set the max header continuation frames.
112    ///
113    /// By default value is set to 5
114    pub fn set_max_header_continuations(&self, val: usize) {
115        self.0.borrow_mut().decoder_max_header_continuations = val;
116    }
117
118    /// Set the peer's max frame size.
119    ///
120    /// # Panics
121    ///
122    /// Panics if size is greater than `16_777_215`.
123    pub fn set_send_frame_size(&self, val: usize) {
124        assert!(val <= frame::MAX_MAX_FRAME_SIZE as usize);
125        self.0.borrow_mut().encoder_max_frame_size = val as frame::FrameSize;
126    }
127
128    /// Set the peer's header table size size.
129    pub fn set_send_header_table_size(&self, val: usize) {
130        self.0.borrow_mut().encoder_hpack.update_max_size(val);
131    }
132
133    /// Remote max frame size.
134    pub fn send_frame_size(&self) -> u32 {
135        self.0.borrow_mut().encoder_max_frame_size
136    }
137}
138
139impl Decoder for Codec {
140    type Item = Frame;
141    type Error = frame::FrameError;
142
143    #[allow(clippy::too_many_lines)]
144    /// Decodes a frame.
145    ///
146    /// This method is intentionally de-generified and outlined because it is very large.
147    fn decode(&self, src: &mut BytesMut) -> Result<Option<Frame>, frame::FrameError> {
148        let mut inner = self.0.borrow_mut();
149        loop {
150            let Some(mut bytes) = inner.decoder.decode(src)? else {
151                return Ok(None);
152            };
153
154            // check push promise, we do not support push
155            if bytes[3] == PUSH_PROMISE {
156                return Err(frame::FrameError::UnexpectedPushPromise);
157            }
158
159            // Parse the head
160            let head = frame::Head::parse(&bytes);
161            let kind = head.kind();
162
163            if inner.partial.is_some() && kind != Kind::Continuation {
164                proto_err!(conn: "expected CONTINUATION, got {:?}", kind);
165                return Err(frame::FrameError::Continuation(
166                    frame::FrameContinuationError::Expected,
167                ));
168            }
169
170            log::trace!("decoding {:?} frame, frame buf len {}", kind, bytes.len());
171
172            let frame = match kind {
173                Kind::Settings => frame::Settings::load(head, &bytes[frame::HEADER_LEN..])
174                    .inspect_err(|e| {
175                        proto_err!(conn: "failed to load SETTINGS frame; err={:?}", e);
176                    })?
177                    .into(),
178                Kind::Ping => frame::Ping::load(head, &bytes[frame::HEADER_LEN..])
179                    .inspect_err(|e| {
180                        proto_err!(conn: "failed to load PING frame; err={:?}", e);
181                    })?
182                    .into(),
183                Kind::WindowUpdate => frame::WindowUpdate::load(head, &bytes[frame::HEADER_LEN..])
184                    .inspect_err(|e| {
185                        proto_err!(conn: "failed to load WINDOW_UPDATE frame; err={:?}", e);
186                    })?
187                    .into(),
188                Kind::Data => {
189                    bytes.advance_to(frame::HEADER_LEN);
190
191                    frame::Data::load(head, bytes)
192                        // TODO: Should this always be connection level? Probably not...
193                        .inspect_err(|e| {
194                            proto_err!(conn: "failed to load DATA frame; err={:?}", e);
195                        })?
196                        .into()
197                }
198                Kind::Headers => {
199                    // Drop the frame header
200                    bytes.advance_to(frame::HEADER_LEN);
201
202                    // Parse the header frame w/o parsing the payload
203                    let mut frame = match frame::Headers::load(head, &mut bytes) {
204                        Ok(res) => Ok(res),
205                        Err(frame::FrameError::InvalidDependencyId) => {
206                            proto_err!(stream: "invalid HEADERS dependency ID");
207                            // A stream cannot depend on itself. An endpoint MUST
208                            // treat this as a stream error (Section 5.4.2) of type `PROTOCOL_ERROR`.
209                            Err(frame::FrameError::InvalidDependencyId)
210                        }
211                        Err(e) => {
212                            proto_err!(conn: "failed to load frame; err={:?}", e);
213                            Err(e)
214                        }
215                    }?;
216
217                    if frame.is_end_headers() {
218                        let max_headers = inner.decoder_max_headers;
219
220                        // Load the HPACK encoded headers
221                        match frame.load_hpack(&mut bytes, &mut inner.decoder_hpack, max_headers) {
222                            Ok(()) => {}
223                            Err(frame::FrameError::MalformedMessage) => {
224                                let id = head.stream_id();
225                                proto_err!(stream: "malformed header block; stream={:?}", id);
226                                return Err(frame::FrameError::MalformedMessage);
227                            }
228                            Err(e) => {
229                                proto_err!(conn: "failed HPACK decoding; err={:?}", e);
230                                return Err(e);
231                            }
232                        }
233                        frame.into()
234                    } else {
235                        log::trace!("loaded partial header block");
236                        // Defer returning the frame
237                        inner.partial = Some(Partial {
238                            frame,
239                            buf: bytes,
240                            count: 0,
241                        });
242
243                        continue;
244                    }
245                }
246                Kind::Reset => frame::Reset::load(head, &bytes[frame::HEADER_LEN..])
247                    .inspect_err(|e| {
248                        proto_err!(conn: "failed to load RESET frame; err={:?}", e);
249                    })?
250                    .into(),
251                Kind::GoAway => frame::GoAway::load(&bytes[frame::HEADER_LEN..])
252                    .inspect_err(|e| {
253                        proto_err!(conn: "failed to load GO_AWAY frame; err={:?}", e);
254                    })?
255                    .into(),
256                Kind::Priority => {
257                    if head.stream_id() == 0 {
258                        // Invalid stream identifier
259                        proto_err!(conn: "invalid stream ID 0");
260                        return Err(frame::FrameError::InvalidStreamId);
261                    }
262
263                    match frame::Priority::load(head, &bytes[frame::HEADER_LEN..]) {
264                        Ok(frame) => frame.into(),
265                        Err(frame::FrameError::InvalidDependencyId) => {
266                            // A stream cannot depend on itself. An endpoint MUST
267                            // treat this as a stream error (Section 5.4.2) of type
268                            // `PROTOCOL_ERROR`.
269                            let id = head.stream_id();
270                            proto_err!(stream: "PRIORITY invalid dependency ID; stream={:?}", id);
271                            return Err(frame::FrameError::InvalidDependencyId);
272                        }
273                        Err(e) => {
274                            proto_err!(conn: "failed to load PRIORITY frame; err={:?};", e);
275                            return Err(e);
276                        }
277                    }
278                }
279                Kind::Continuation => {
280                    let mut partial = inner.partial.take().ok_or_else(|| {
281                        proto_err!(conn: "received unexpected CONTINUATION frame");
282                        frame::FrameError::Continuation(frame::FrameContinuationError::Unexpected)
283                    })?;
284
285                    // The stream identifiers must match
286                    if partial.frame.stream_id() != head.stream_id() {
287                        proto_err!(conn: "CONTINUATION frame stream ID does not match previous frame stream ID");
288                        return Err(frame::FrameError::Continuation(
289                            frame::FrameContinuationError::UnknownStreamId,
290                        ));
291                    }
292
293                    if inner.decoder_max_header_continuations > 0 {
294                        // Check count of continuation frames
295                        partial.count += 1;
296                        if partial.count > inner.decoder_max_header_continuations {
297                            proto_err!(conn: "received excessive amount of CONTINUATION frames");
298                            return Err(frame::FrameError::Continuation(
299                                frame::FrameContinuationError::MaxContinuations,
300                            ));
301                        }
302                    }
303
304                    // Extend the buf
305                    if partial.buf.is_empty() {
306                        partial.buf = bytes.split_off(frame::HEADER_LEN);
307                    } else {
308                        // If there was left over bytes previously, they may be
309                        // needed to continue decoding, even though we will
310                        // be ignoring this frame. This is done to keep the HPACK
311                        // decoder state up-to-date.
312                        //
313                        // Still, we need to be careful, because if a malicious
314                        // attacker were to try to send a gigantic string, such
315                        // that it fits over multiple header blocks.
316                        //
317                        // Instead, we use a simple heuristic to determine if
318                        // we should continue to ignore decoding, or to tell
319                        // the attacker to go away.
320                        if partial.buf.len() + bytes.len() > inner.decoder_max_header_list_size {
321                            proto_err!(conn: "CONTINUATION frame header block size over ignorable limit");
322                            return Err(frame::FrameError::Continuation(
323                                frame::FrameContinuationError::MaxLeftoverSize,
324                            ));
325                        }
326                        let mut buf = BytesMut::with_capacity(
327                            partial.buf.len() + bytes.len() - frame::HEADER_LEN,
328                        );
329                        buf.extend_from_slice(&partial.buf);
330                        buf.extend_from_slice(&bytes[frame::HEADER_LEN..]);
331                        partial.buf = buf.into();
332                    }
333
334                    if (head.flag() & 0x4) == 0x4 {
335                        let max_headers = inner.decoder_max_headers;
336                        match partial.frame.load_hpack(
337                            &mut partial.buf,
338                            &mut inner.decoder_hpack,
339                            max_headers,
340                        ) {
341                            Ok(()) => {}
342                            Err(frame::FrameError::MalformedMessage) => {
343                                let id = head.stream_id();
344                                proto_err!(stream: "malformed CONTINUATION frame; stream={:?}", id);
345                                return Err(frame::FrameContinuationError::Malformed.into());
346                            }
347                            Err(e) => {
348                                proto_err!(conn: "failed HPACK decoding; err={:?}", e);
349                                return Err(e);
350                            }
351                        }
352
353                        partial.frame.into()
354                    } else {
355                        inner.partial = Some(partial);
356                        continue;
357                    }
358                }
359                Kind::Unknown => {
360                    // Unknown frames are ignored
361                    continue;
362                }
363            };
364
365            return Ok(Some(frame));
366        }
367    }
368}
369
370impl Encoder for Codec {
371    type Item = Frame;
372    type Error = error::EncoderError;
373
374    fn encodev(&self, item: Frame, buf: &mut BytePages) -> Result<(), error::EncoderError> {
375        // Ensure that we have enough capacity to accept the write.
376        // log::debug!(frame = ?item, "send");
377
378        let mut inner = self.0.borrow_mut();
379
380        match item {
381            Frame::Data(v) => {
382                // Ensure that the payload is not greater than the max frame.
383                let len = v.payload().len();
384                if len > inner.encoder_max_frame_size as usize {
385                    return Err(error::EncoderError::MaxSizeExceeded);
386                }
387                v.encode(buf);
388            }
389            Frame::Headers(v) => {
390                let max_size = inner.encoder_max_frame_size as usize;
391                v.encode(&mut inner.encoder_hpack, buf, max_size);
392            }
393            Frame::Settings(v) => {
394                v.encode(buf);
395            }
396            Frame::GoAway(v) => {
397                v.encode(buf);
398            }
399            Frame::Ping(v) => {
400                v.encode(buf);
401            }
402            Frame::WindowUpdate(v) => {
403                v.encode(buf);
404            }
405
406            Frame::Priority(_) => (),
407            Frame::Reset(v) => {
408                v.encode(buf);
409            }
410        }
411
412        Ok(())
413    }
414}