Skip to main content

vibeio_http/h2/codec/
mod.rs

1//! HTTP/2 frame codec (RFC 9113 Section 6).
2//!
3//! Parses and writes the 9-octet frame header and every frame type:
4//! DATA, HEADERS, PRIORITY, RST_STREAM, SETTINGS, PUSH_PROMISE, PING,
5//! GOAWAY, WINDOW_UPDATE and CONTINUATION, including PADDED and
6//! PRIORITY flags. Validation covers frame-size limits, stream-id rules,
7//! settings values and field-block continuation discipline. Unknown
8//! frame types are ignored (RFC 9113 Section 4.1).
9//!
10//! The decoder is incremental: feed it bytes with [`FrameDecoder::extend`]
11//! and call [`FrameDecoder::next_frame`]; it returns `Ok(None)` until a
12//! complete frame is buffered.
13//!
14//! This module is the frame layer of the native HTTP/2 implementation
15//! (see CUSTOM_HTTP2_IMPL.md); stream and connection semantics
16//! (flow control, settings tracking, lifecycle) live in later steps on
17//! top of it.
18
19use super::error::{H2Error, Reason};
20use bytes::{Bytes, BytesMut};
21
22/// Length of the fixed frame header (RFC 9113 Section 4.1).
23pub const FRAME_HEADER_LEN: usize = 9;
24/// The initial maximum frame payload size (RFC 9113 Section 4.2).
25pub const DEFAULT_MAX_FRAME_SIZE: usize = 16_384;
26/// The largest settable frame payload size (2^24-1).
27pub const MAX_FRAME_SIZE_LIMIT: usize = 16_777_215;
28/// The initial connection flow-control window (RFC 9113 Section 5.2.1).
29pub const DEFAULT_INITIAL_WINDOW_SIZE: u32 = 1_048_576;
30/// The largest legal flow-control window (2^31-1).
31pub const MAX_WINDOW_SIZE: u32 = 2_147_483_647;
32/// The HTTP/2 connection preface (RFC 9113 Section 3.5).
33pub const CLIENT_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
34
35const DATA_TYPE: u8 = 0x00;
36const HEADERS_TYPE: u8 = 0x01;
37const PRIORITY_TYPE: u8 = 0x02;
38const RST_STREAM_TYPE: u8 = 0x03;
39const SETTINGS_TYPE: u8 = 0x04;
40const PUSH_PROMISE_TYPE: u8 = 0x05;
41const PING_TYPE: u8 = 0x06;
42const GOAWAY_TYPE: u8 = 0x07;
43const WINDOW_UPDATE_TYPE: u8 = 0x08;
44const CONTINUATION_TYPE: u8 = 0x09;
45
46/// Flag bits shared across frame types (RFC 9113 Section 6).
47const FLAG_END_STREAM: u8 = 0x01;
48const FLAG_ACK: u8 = 0x01;
49const FLAG_END_HEADERS: u8 = 0x04;
50const FLAG_PADDED: u8 = 0x08;
51const FLAG_PRIORITY: u8 = 0x20;
52
53/// Stream priority (RFC 9113 Section 6.3).
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub struct Priority {
56    pub exclusive: bool,
57    pub dependency: u32,
58    pub weight: u8,
59}
60
61/// A SETTINGS parameter (RFC 9113 Section 6.5.2).
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub struct Setting {
64    pub id: u16,
65    pub value: u32,
66}
67
68/// A parsed frame. Payloads are zero-copy views over the decoder's
69/// buffer (kept alive by `Bytes` refcounting until the frame is
70/// dropped); the connection layer reassembles field blocks from the
71/// `block` fragments and applies stream semantics. Padding, when
72/// present, is validated and stripped.
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub enum Frame {
75    Data {
76        stream_id: u32,
77        end_stream: bool,
78        data: Bytes,
79    },
80    Headers {
81        stream_id: u32,
82        end_stream: bool,
83        end_headers: bool,
84        priority: Option<Priority>,
85        block: Bytes,
86    },
87    Priority {
88        stream_id: u32,
89        priority: Priority,
90    },
91    Reset {
92        stream_id: u32,
93        error_code: u32,
94    },
95    Settings {
96        ack: bool,
97        settings: Vec<Setting>,
98    },
99    PushPromise {
100        stream_id: u32,
101        end_headers: bool,
102        promised_stream_id: u32,
103        block: Bytes,
104    },
105    Ping {
106        ack: bool,
107        payload: [u8; 8],
108    },
109    GoAway {
110        last_stream_id: u32,
111        error_code: u32,
112        debug: Bytes,
113    },
114    WindowUpdate {
115        stream_id: u32,
116        increment: u32,
117    },
118    Continuation {
119        stream_id: u32,
120        end_headers: bool,
121        block: Bytes,
122    },
123    /// A frame type this implementation does not understand; ignored by
124    /// the connection (RFC 9113 Section 4.1).
125    Unknown {
126        typ: u8,
127        flags: u8,
128        stream_id: u32,
129        payload: Bytes,
130    },
131}
132
133/// Incremental frame decoder over a growing buffer.
134#[derive(Debug)]
135pub struct FrameDecoder {
136    buf: BytesMut,
137    max_frame_size: usize,
138    /// The stream whose field block is open (HEADERS/PUSH_PROMISE
139    /// without END_HEADERS was seen); only CONTINUATION frames for that
140    /// stream may follow.
141    block_stream: Option<u32>,
142}
143
144impl FrameDecoder {
145    /// Creates a decoder enforcing the given maximum frame payload
146    /// size.
147    #[inline]
148    pub fn new(max_frame_size: usize) -> FrameDecoder {
149        FrameDecoder {
150            buf: BytesMut::new(),
151            max_frame_size,
152            block_stream: None,
153        }
154    }
155
156    /// Appends bytes to the decode buffer.
157    #[inline]
158    pub fn extend(&mut self, bytes: &[u8]) {
159        self.buf.extend_from_slice(bytes);
160    }
161
162    /// The maximum frame payload size the peer may send.
163    #[inline]
164    pub fn max_frame_size(&self) -> usize {
165        self.max_frame_size
166    }
167
168    /// Adjusts the frame-size limit after SETTINGS_MAX_FRAME_SIZE.
169    #[inline]
170    pub fn set_max_frame_size(&mut self, max_frame_size: usize) {
171        self.max_frame_size = max_frame_size;
172    }
173
174    /// The stream with an open field block, if any.
175    #[inline]
176    pub fn block_stream(&self) -> Option<u32> {
177        self.block_stream
178    }
179
180    /// Forgets any open field block. Used when a stream is reset mid-block
181    /// (e.g. a CONTINUATION flood) so subsequent frames are not all treated
182    /// as protocol violations for the now-gone stream.
183    #[inline]
184    pub fn clear_block(&mut self) {
185        self.block_stream = None;
186    }
187
188    /// Parses the next complete frame. Returns `Ok(None)` when more
189    /// bytes are needed.
190    #[inline]
191    pub fn next_frame(&mut self) -> Result<Option<Frame>, H2Error> {
192        if self.buf.len() < FRAME_HEADER_LEN {
193            return Ok(None);
194        }
195
196        // 24-bit big-endian payload length. The zero padding leads the
197        // three length octets.
198        let payload_len = u32::from_be_bytes([0, self.buf[0], self.buf[1], self.buf[2]]) as usize;
199        let typ = self.buf[3];
200        let flags = self.buf[4];
201        let raw_stream_id =
202            u32::from_be_bytes([self.buf[5], self.buf[6], self.buf[7], self.buf[8]]);
203        // RFC 9113 Section 4.1: the high bit of the stream identifier
204        // field is reserved and MUST be ignored, not treated as an
205        // error.
206        let stream_id = raw_stream_id & 0x7fff_ffff;
207
208        if payload_len > self.max_frame_size {
209            return Err(H2Error::frame_size(
210                "frame payload exceeds SETTINGS_MAX_FRAME_SIZE",
211            ));
212        }
213
214        let total = FRAME_HEADER_LEN + payload_len;
215        if self.buf.len() < total {
216            return Ok(None);
217        }
218
219        let _header = self.buf.split_to(FRAME_HEADER_LEN);
220        let payload = self.buf.split_to(payload_len).freeze();
221        let frame = parse_frame(typ, flags, stream_id, payload, self)?;
222        Ok(Some(frame))
223    }
224}
225
226/// Parses one complete frame payload (after the 9-octet header).
227#[inline]
228fn parse_frame(
229    typ: u8,
230    flags: u8,
231    stream_id: u32,
232    payload: Bytes,
233    decoder: &mut FrameDecoder,
234) -> Result<Frame, H2Error> {
235    let mut body = &payload[..];
236
237    // Field-block continuation discipline (RFC 9113 Section 6.10).
238    if let Some(open_stream) = decoder.block_stream {
239        if typ != CONTINUATION_TYPE {
240            return Err(H2Error::protocol(
241                "frame received while field block was open",
242            ));
243        }
244        if stream_id != open_stream {
245            return Err(H2Error::protocol(
246                "CONTINUATION frame on a different stream than the open field block",
247            ));
248        }
249    } else if typ == CONTINUATION_TYPE {
250        return Err(H2Error::protocol(
251            "CONTINUATION frame without a preceding HEADERS or PUSH_PROMISE",
252        ));
253    }
254
255    let frame = match typ {
256        DATA_TYPE => {
257            require_stream(stream_id)?;
258            let end_stream = flags & FLAG_END_STREAM != 0;
259            let pad_len = take_padding(&mut body, flags & FLAG_PADDED != 0)?;
260            Frame::Data {
261                stream_id,
262                end_stream,
263                data: payload_slice(&payload, body, pad_len),
264            }
265        }
266        HEADERS_TYPE => {
267            require_stream(stream_id)?;
268            let end_stream = flags & FLAG_END_STREAM != 0;
269            let end_headers = flags & FLAG_END_HEADERS != 0;
270            let pad_len = take_padding(&mut body, flags & FLAG_PADDED != 0)?;
271            let priority = if flags & FLAG_PRIORITY != 0 {
272                Some(read_priority(&mut body, stream_id)?)
273            } else {
274                None
275            };
276            if !end_headers {
277                decoder.block_stream = Some(stream_id);
278            }
279            Frame::Headers {
280                stream_id,
281                end_stream,
282                end_headers,
283                priority,
284                block: payload_slice(&payload, body, pad_len),
285            }
286        }
287        PRIORITY_TYPE => {
288            require_stream(stream_id)?;
289            if body.len() != 5 {
290                return Err(H2Error::frame_size(
291                    "PRIORITY frame payload must be exactly 5 octets",
292                ));
293            }
294            let priority = read_priority(&mut body, stream_id)?;
295            Frame::Priority {
296                stream_id,
297                priority,
298            }
299        }
300        RST_STREAM_TYPE => {
301            require_stream(stream_id)?;
302            if body.len() != 4 {
303                return Err(H2Error::frame_size(
304                    "RST_STREAM frame payload must be exactly 4 octets",
305                ));
306            }
307            Frame::Reset {
308                stream_id,
309                error_code: read_u32(body),
310            }
311        }
312        SETTINGS_TYPE => {
313            if stream_id != 0 {
314                return Err(H2Error::protocol(
315                    "SETTINGS frame must use stream identifier 0",
316                ));
317            }
318            let ack = flags & FLAG_ACK != 0;
319            if ack && !body.is_empty() {
320                return Err(H2Error::frame_size(
321                    "SETTINGS frame with ACK flag must have an empty payload",
322                ));
323            }
324            if !body.len().is_multiple_of(6) {
325                return Err(H2Error::frame_size(
326                    "SETTINGS frame payload must be a multiple of 6 octets",
327                ));
328            }
329            let mut settings = Vec::with_capacity(body.len() / 6);
330            while !body.is_empty() {
331                let id = u16::from_be_bytes([body[0], body[1]]);
332                let value = u32::from_be_bytes([body[2], body[3], body[4], body[5]]);
333                validate_setting(id, value)?;
334                settings.push(Setting { id, value });
335                body = &body[6..];
336            }
337            // The peer's announced SETTINGS_MAX_FRAME_SIZE governs what it
338            // may send; adopt it immediately so later frames in this
339            // buffer are checked against it.
340            if !ack {
341                for setting in &settings {
342                    if setting.id == 0x05 {
343                        decoder.set_max_frame_size(setting.value as usize);
344                    }
345                }
346            }
347            Frame::Settings { ack, settings }
348        }
349        PUSH_PROMISE_TYPE => {
350            require_stream(stream_id)?;
351            let end_headers = flags & FLAG_END_HEADERS != 0;
352            let pad_len = take_padding(&mut body, flags & FLAG_PADDED != 0)?;
353            if body.len() < 4 {
354                return Err(H2Error::frame_size(
355                    "PUSH_PROMISE frame payload must be at least 4 octets",
356                ));
357            }
358            let promised_stream_id = read_u32(&body[..4]) & 0x7fff_ffff;
359            if promised_stream_id == 0 {
360                return Err(H2Error::protocol(
361                    "PUSH_PROMISE promised stream identifier is 0",
362                ));
363            }
364            if !end_headers {
365                decoder.block_stream = Some(stream_id);
366            }
367            body = &body[4..];
368            Frame::PushPromise {
369                stream_id,
370                end_headers,
371                promised_stream_id,
372                block: payload_slice(&payload, body, pad_len),
373            }
374        }
375        PING_TYPE => {
376            if stream_id != 0 {
377                return Err(H2Error::protocol("PING frame must use stream identifier 0"));
378            }
379            if body.len() != 8 {
380                return Err(H2Error::frame_size(
381                    "PING frame payload must be exactly 8 octets",
382                ));
383            }
384            Frame::Ping {
385                ack: flags & FLAG_ACK != 0,
386                payload: body[..8]
387                    .try_into()
388                    .expect("PING payload is exactly 8 octets (validated above)"),
389            }
390        }
391        GOAWAY_TYPE => {
392            if stream_id != 0 {
393                return Err(H2Error::protocol(
394                    "GOAWAY frame must use stream identifier 0",
395                ));
396            }
397            if body.len() < 8 {
398                return Err(H2Error::frame_size(
399                    "GOAWAY frame payload must be at least 8 octets",
400                ));
401            }
402            let last_stream_id = read_u32(&body[..4]) & 0x7fff_ffff;
403            let error_code = read_u32(&body[4..8]);
404            Frame::GoAway {
405                last_stream_id,
406                error_code,
407                debug: payload.slice(8..),
408            }
409        }
410        WINDOW_UPDATE_TYPE => {
411            if body.len() != 4 {
412                return Err(H2Error::frame_size(
413                    "WINDOW_UPDATE frame payload must be exactly 4 octets",
414                ));
415            }
416            let increment = read_u32(body) & 0x7fff_ffff;
417            if increment == 0 {
418                return Err(H2Error::protocol("WINDOW_UPDATE frame with zero increment"));
419            }
420            Frame::WindowUpdate {
421                stream_id,
422                increment,
423            }
424        }
425        CONTINUATION_TYPE => {
426            let end_headers = flags & FLAG_END_HEADERS != 0;
427            if end_headers {
428                decoder.block_stream = None;
429            }
430            Frame::Continuation {
431                stream_id,
432                end_headers,
433                block: payload,
434            }
435        }
436        _ => Frame::Unknown {
437            typ,
438            flags,
439            stream_id,
440            payload,
441        },
442    };
443
444    Ok(frame)
445}
446
447/// A zero-copy view of `payload` covering exactly the range `body`
448/// points at: the leading octets were trimmed by padding/priority
449/// fields and `pad_len` trailing octets by padding.
450#[inline]
451fn payload_slice(payload: &Bytes, body: &[u8], pad_len: usize) -> Bytes {
452    let start = payload.len() - pad_len - body.len();
453    payload.slice(start..start + body.len())
454}
455
456/// Reads the pad-length octet when `padded` is set and strips that many
457/// trailing octets from `body`, returning the padding length.
458///
459/// The padding must be strictly shorter than the payload (RFC 9113
460/// Section 6.1).
461#[inline]
462fn take_padding(body: &mut &[u8], padded: bool) -> Result<usize, H2Error> {
463    if !padded {
464        return Ok(0);
465    }
466    let pad_len = *body
467        .first()
468        .ok_or_else(|| H2Error::frame_size("PADDED frame payload too short for pad length"))?
469        as usize;
470    if pad_len >= body.len() {
471        return Err(H2Error::protocol(
472            "padding length is the length of the frame payload or greater",
473        ));
474    }
475    *body = &body[1..body.len() - pad_len];
476    Ok(pad_len)
477}
478
479/// Reads the 5-octet priority fields (Exclusive + Stream Dependency +
480/// Weight).
481#[inline]
482fn read_priority(body: &mut &[u8], stream_id: u32) -> Result<Priority, H2Error> {
483    if body.len() < 5 {
484        return Err(H2Error::frame_size(
485            "frame payload too short for priority fields",
486        ));
487    }
488    let exclusive = body[0] & 0x80 != 0;
489    let dependency = u32::from_be_bytes([body[0], body[1], body[2], body[3]]) & 0x7fff_ffff;
490    if dependency == stream_id {
491        return Err(H2Error::protocol(
492            "stream priority depends on its own stream identifier",
493        ));
494    }
495    let weight = body[4];
496    *body = &body[5..];
497    Ok(Priority {
498        exclusive,
499        dependency,
500        weight,
501    })
502}
503
504#[inline]
505fn require_stream(stream_id: u32) -> Result<(), H2Error> {
506    if stream_id == 0 {
507        Err(H2Error::new(
508            Reason::ProtocolError,
509            "frame must use a non-zero stream identifier",
510        ))
511    } else {
512        Ok(())
513    }
514}
515
516/// Validates the value of a known SETTINGS parameter (RFC 9113
517/// Section 6.5.2). Unknown identifiers are accepted and ignored by the
518/// caller.
519#[inline]
520fn validate_setting(id: u16, value: u32) -> Result<(), H2Error> {
521    match id {
522        0x02 => {
523            // SETTINGS_ENABLE_PUSH.
524            if value > 1 {
525                return Err(H2Error::protocol("SETTINGS_ENABLE_PUSH must be 0 or 1"));
526            }
527        }
528        0x04 => {
529            // SETTINGS_INITIAL_WINDOW_SIZE.
530            if value > MAX_WINDOW_SIZE {
531                return Err(H2Error::new(
532                    Reason::FlowControlError,
533                    "SETTINGS_INITIAL_WINDOW_SIZE exceeds 2^31-1",
534                ));
535            }
536        }
537        0x05 if !(DEFAULT_MAX_FRAME_SIZE..=MAX_FRAME_SIZE_LIMIT).contains(&(value as usize)) => {
538            // SETTINGS_MAX_FRAME_SIZE.
539            return Err(H2Error::protocol(
540                "SETTINGS_MAX_FRAME_SIZE outside 16384..16777215",
541            ));
542        }
543        _ => {}
544    }
545    Ok(())
546}
547
548#[inline]
549fn read_u32(bytes: &[u8]) -> u32 {
550    u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
551}
552
553/// Writes frames to an output buffer. The field-block writer
554/// ([`FrameWriter::write_field_block`]) splits the block across
555/// HEADERS/CONTINUATION frames at the peer's frame-size limit.
556#[derive(Debug, Clone, Copy, Default)]
557pub struct FrameWriter {
558    /// The maximum payload size the peer accepts (the peer's
559    /// SETTINGS_MAX_FRAME_SIZE; 16384 by default).
560    pub max_frame_size: usize,
561}
562
563impl FrameWriter {
564    pub const fn new(max_frame_size: usize) -> FrameWriter {
565        FrameWriter { max_frame_size }
566    }
567
568    #[inline]
569    fn header(out: &mut Vec<u8>, payload_len: usize, typ: u8, flags: u8, stream_id: u32) {
570        out.push(((payload_len >> 16) & 0xff) as u8);
571        out.push(((payload_len >> 8) & 0xff) as u8);
572        out.push((payload_len & 0xff) as u8);
573        out.push(typ);
574        out.push(flags);
575        out.push(((stream_id >> 24) & 0x7f) as u8);
576        out.push(((stream_id >> 16) & 0xff) as u8);
577        out.push(((stream_id >> 8) & 0xff) as u8);
578        out.push((stream_id & 0xff) as u8);
579    }
580
581    #[inline]
582    pub fn write_data(&self, out: &mut Vec<u8>, stream_id: u32, end_stream: bool, data: &[u8]) {
583        let flags = if end_stream { FLAG_END_STREAM } else { 0 };
584        FrameWriter::header(out, data.len(), DATA_TYPE, flags, stream_id);
585        out.extend_from_slice(data);
586    }
587
588    /// Writes a HEADERS frame (single frame, no field-block splitting).
589    #[inline]
590    pub fn write_headers(
591        &self,
592        out: &mut Vec<u8>,
593        stream_id: u32,
594        end_stream: bool,
595        end_headers: bool,
596        priority: Option<Priority>,
597        block: &[u8],
598    ) {
599        let mut flags = 0;
600        if end_stream {
601            flags |= FLAG_END_STREAM;
602        }
603        if end_headers {
604            flags |= FLAG_END_HEADERS;
605        }
606        let extra = if priority.is_some() { 5 } else { 0 };
607        if extra != 0 {
608            flags |= FLAG_PRIORITY;
609        }
610        FrameWriter::header(out, block.len() + extra, HEADERS_TYPE, flags, stream_id);
611        if let Some(p) = priority {
612            let dep = p.dependency & 0x7fff_ffff | if p.exclusive { 0x8000_0000 } else { 0 };
613            out.extend_from_slice(&dep.to_be_bytes());
614            out.push(p.weight);
615        }
616        out.extend_from_slice(block);
617    }
618
619    /// Writes a field block as a HEADERS frame (optionally with
620    /// END_STREAM) followed by as many CONTINUATION frames as needed to
621    /// stay within the peer's frame-size limit.
622    #[inline]
623    pub fn write_field_block(
624        &self,
625        out: &mut Vec<u8>,
626        stream_id: u32,
627        end_stream: bool,
628        block: &[u8],
629    ) {
630        let capacity = self.max_frame_size;
631        if block.len() <= capacity {
632            self.write_headers(out, stream_id, end_stream, true, None, block);
633            return;
634        }
635        let first = &block[..capacity];
636        self.write_headers(out, stream_id, end_stream, false, None, first);
637        let mut rest = &block[capacity..];
638        while rest.len() > capacity {
639            self.write_continuation(out, stream_id, false, &rest[..capacity]);
640            rest = &rest[capacity..];
641        }
642        self.write_continuation(out, stream_id, true, rest);
643    }
644
645    #[inline]
646    pub fn write_priority(&self, out: &mut Vec<u8>, stream_id: u32, priority: Priority) {
647        let dep =
648            priority.dependency & 0x7fff_ffff | if priority.exclusive { 0x8000_0000 } else { 0 };
649        FrameWriter::header(out, 5, PRIORITY_TYPE, 0, stream_id);
650        out.extend_from_slice(&dep.to_be_bytes());
651        out.push(priority.weight);
652    }
653
654    #[inline]
655    pub fn write_reset(&self, out: &mut Vec<u8>, stream_id: u32, error_code: u32) {
656        FrameWriter::header(out, 4, RST_STREAM_TYPE, 0, stream_id);
657        out.extend_from_slice(&error_code.to_be_bytes());
658    }
659
660    #[inline]
661    pub fn write_settings(&self, out: &mut Vec<u8>, settings: &[Setting]) {
662        FrameWriter::header(out, settings.len() * 6, SETTINGS_TYPE, 0, 0);
663        for setting in settings {
664            out.extend_from_slice(&setting.id.to_be_bytes());
665            out.extend_from_slice(&setting.value.to_be_bytes());
666        }
667    }
668
669    #[inline]
670    pub fn write_settings_ack(&self, out: &mut Vec<u8>) {
671        FrameWriter::header(out, 0, SETTINGS_TYPE, FLAG_ACK, 0);
672    }
673
674    #[inline]
675    pub fn write_push_promise(
676        &self,
677        out: &mut Vec<u8>,
678        stream_id: u32,
679        promised_stream_id: u32,
680        block: &[u8],
681    ) {
682        FrameWriter::header(
683            out,
684            4 + block.len(),
685            PUSH_PROMISE_TYPE,
686            FLAG_END_HEADERS,
687            stream_id,
688        );
689        out.extend_from_slice(&(promised_stream_id & 0x7fff_ffff).to_be_bytes());
690        out.extend_from_slice(block);
691    }
692
693    #[inline]
694    pub fn write_ping(&self, out: &mut Vec<u8>, payload: &[u8; 8]) {
695        FrameWriter::header(out, 8, PING_TYPE, 0, 0);
696        out.extend_from_slice(payload);
697    }
698
699    #[inline]
700    pub fn write_ping_ack(&self, out: &mut Vec<u8>, payload: &[u8; 8]) {
701        FrameWriter::header(out, 8, PING_TYPE, FLAG_ACK, 0);
702        out.extend_from_slice(payload);
703    }
704
705    #[inline]
706    pub fn write_goaway(
707        &self,
708        out: &mut Vec<u8>,
709        last_stream_id: u32,
710        error_code: u32,
711        debug: &[u8],
712    ) {
713        FrameWriter::header(out, 8 + debug.len(), GOAWAY_TYPE, 0, 0);
714        out.extend_from_slice(&(last_stream_id & 0x7fff_ffff).to_be_bytes());
715        out.extend_from_slice(&error_code.to_be_bytes());
716        out.extend_from_slice(debug);
717    }
718
719    #[inline]
720    pub fn write_window_update(&self, out: &mut Vec<u8>, stream_id: u32, increment: u32) {
721        FrameWriter::header(out, 4, WINDOW_UPDATE_TYPE, 0, stream_id);
722        out.extend_from_slice(&(increment & 0x7fff_ffff).to_be_bytes());
723    }
724
725    #[inline]
726    pub fn write_continuation(
727        &self,
728        out: &mut Vec<u8>,
729        stream_id: u32,
730        end_headers: bool,
731        block: &[u8],
732    ) {
733        let flags = if end_headers { FLAG_END_HEADERS } else { 0 };
734        FrameWriter::header(out, block.len(), CONTINUATION_TYPE, flags, stream_id);
735        out.extend_from_slice(block);
736    }
737}
738
739#[cfg(test)]
740mod tests;