Skip to main content

specter/transport/h2/
frame.rs

1//! HTTP/2 frame types and binary serialization.
2//!
3//! Implements RFC 9113 frame format with full control over frame ordering
4//! and content for fingerprint accuracy.
5
6use bytes::{Buf, BufMut, Bytes, BytesMut};
7
8/// Frame header size (9 bytes per RFC 9113).
9pub const FRAME_HEADER_SIZE: usize = 9;
10
11/// Default maximum frame size (16KB per RFC 9113).
12pub const DEFAULT_MAX_FRAME_SIZE: u32 = 16384;
13
14/// HTTP/2 connection preface (client must send this first).
15pub const CONNECTION_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
16
17/// Frame type identifiers per RFC 9113.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19#[repr(u8)]
20pub enum FrameType {
21    Data = 0x0,
22    Headers = 0x1,
23    Priority = 0x2,
24    RstStream = 0x3,
25    Settings = 0x4,
26    PushPromise = 0x5,
27    Ping = 0x6,
28    GoAway = 0x7,
29    WindowUpdate = 0x8,
30    Continuation = 0x9,
31    Unknown(u8),
32}
33
34impl From<u8> for FrameType {
35    fn from(v: u8) -> Self {
36        match v {
37            0x0 => Self::Data,
38            0x1 => Self::Headers,
39            0x2 => Self::Priority,
40            0x3 => Self::RstStream,
41            0x4 => Self::Settings,
42            0x5 => Self::PushPromise,
43            0x6 => Self::Ping,
44            0x7 => Self::GoAway,
45            0x8 => Self::WindowUpdate,
46            0x9 => Self::Continuation,
47            other => Self::Unknown(other),
48        }
49    }
50}
51
52impl From<FrameType> for u8 {
53    fn from(ft: FrameType) -> u8 {
54        match ft {
55            FrameType::Data => 0x0,
56            FrameType::Headers => 0x1,
57            FrameType::Priority => 0x2,
58            FrameType::RstStream => 0x3,
59            FrameType::Settings => 0x4,
60            FrameType::PushPromise => 0x5,
61            FrameType::Ping => 0x6,
62            FrameType::GoAway => 0x7,
63            FrameType::WindowUpdate => 0x8,
64            FrameType::Continuation => 0x9,
65            FrameType::Unknown(v) => v,
66        }
67    }
68}
69
70/// Frame flags.
71pub mod flags {
72    pub const END_STREAM: u8 = 0x1;
73    pub const ACK: u8 = 0x1; // Same value, different context (SETTINGS/PING)
74    pub const END_HEADERS: u8 = 0x4;
75    pub const PADDED: u8 = 0x8;
76    pub const PRIORITY: u8 = 0x20;
77}
78
79/// SETTINGS frame parameter identifiers.
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81#[repr(u16)]
82pub enum SettingsId {
83    HeaderTableSize = 0x1,
84    EnablePush = 0x2,
85    MaxConcurrentStreams = 0x3,
86    InitialWindowSize = 0x4,
87    MaxFrameSize = 0x5,
88    MaxHeaderListSize = 0x6,
89}
90
91impl From<u16> for SettingsId {
92    fn from(v: u16) -> Self {
93        match v {
94            0x1 => Self::HeaderTableSize,
95            0x2 => Self::EnablePush,
96            0x3 => Self::MaxConcurrentStreams,
97            0x4 => Self::InitialWindowSize,
98            0x5 => Self::MaxFrameSize,
99            0x6 => Self::MaxHeaderListSize,
100            _ => Self::HeaderTableSize, // Unknown settings are ignored per spec
101        }
102    }
103}
104
105impl From<SettingsId> for u16 {
106    fn from(id: SettingsId) -> Self {
107        id as u16
108    }
109}
110
111/// HTTP/2 error codes per RFC 9113 Section 7.
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113#[repr(u32)]
114pub enum ErrorCode {
115    NoError = 0x0,
116    ProtocolError = 0x1,
117    InternalError = 0x2,
118    FlowControlError = 0x3,
119    SettingsTimeout = 0x4,
120    StreamClosed = 0x5,
121    FrameSizeError = 0x6,
122    RefusedStream = 0x7,
123    Cancel = 0x8,
124    CompressionError = 0x9,
125    ConnectError = 0xa,
126    EnhanceYourCalm = 0xb,
127    InadequateSecurity = 0xc,
128    Http11Required = 0xd,
129}
130
131/// Parsed frame header.
132#[derive(Debug, Clone)]
133pub struct FrameHeader {
134    pub length: u32,
135    pub frame_type: FrameType,
136    pub flags: u8,
137    pub stream_id: u32,
138}
139
140impl FrameHeader {
141    /// Parse a frame header from bytes.
142    /// Returns None if header is invalid (e.g., reserved bits set incorrectly).
143    pub fn parse(buf: &[u8]) -> Option<Self> {
144        if buf.len() < FRAME_HEADER_SIZE {
145            return None;
146        }
147
148        let length = ((buf[0] as u32) << 16) | ((buf[1] as u32) << 8) | (buf[2] as u32);
149        let frame_type = FrameType::from(buf[3]);
150        let flags = buf[4];
151
152        // RFC 9113 Section 4.1: Stream ID is 31 bits, high bit (bit 0 of first byte) is reserved
153        // Check reserved bit - must be 0
154        if (buf[5] & 0x80) != 0 {
155            return None; // Reserved bit set - invalid frame
156        }
157
158        let stream_id = ((buf[5] as u32 & 0x7f) << 24)
159            | ((buf[6] as u32) << 16)
160            | ((buf[7] as u32) << 8)
161            | (buf[8] as u32);
162
163        Some(Self {
164            length,
165            frame_type,
166            flags,
167            stream_id,
168        })
169    }
170
171    /// Serialize frame header to bytes.
172    pub fn serialize(&self, buf: &mut BytesMut) {
173        // Length (24 bits)
174        buf.put_u8((self.length >> 16) as u8);
175        buf.put_u8((self.length >> 8) as u8);
176        buf.put_u8(self.length as u8);
177        // Type (8 bits)
178        buf.put_u8(self.frame_type.into());
179        // Flags (8 bits)
180        buf.put_u8(self.flags);
181        // Stream ID (31 bits, high bit reserved and must be 0)
182        // RFC 9113 Section 4.1: Stream ID is 31-bit unsigned integer
183        buf.put_u32(self.stream_id & 0x7fffffff);
184    }
185}
186
187/// SETTINGS frame payload.
188#[derive(Debug, Clone)]
189pub struct SettingsFrame {
190    /// Settings to send, in order.
191    /// Each tuple is (id, value).
192    /// Order matters for fingerprinting!
193    pub settings: Vec<(u16, u32)>,
194    pub ack: bool,
195}
196
197impl SettingsFrame {
198    /// Create a new SETTINGS frame.
199    pub fn new() -> Self {
200        Self {
201            settings: Vec::new(),
202            ack: false,
203        }
204    }
205
206    /// Create a SETTINGS ACK frame.
207    pub fn ack() -> Self {
208        Self {
209            settings: Vec::new(),
210            ack: true,
211        }
212    }
213
214    /// Add a setting. Order of calls determines wire order.
215    pub fn set<T: Into<u16>>(&mut self, id: T, value: u32) -> &mut Self {
216        self.settings.push((id.into(), value));
217        self
218    }
219
220    /// Serialize to bytes (including frame header).
221    pub fn serialize(&self) -> BytesMut {
222        let payload_len = if self.ack { 0 } else { self.settings.len() * 6 };
223        let mut buf = BytesMut::with_capacity(FRAME_HEADER_SIZE + payload_len);
224
225        // Frame header
226        let header = FrameHeader {
227            length: payload_len as u32,
228            frame_type: FrameType::Settings,
229            flags: if self.ack { flags::ACK } else { 0 },
230            stream_id: 0, // SETTINGS always on stream 0
231        };
232        header.serialize(&mut buf);
233
234        // Payload (only if not ACK)
235        if !self.ack {
236            for (id, value) in &self.settings {
237                buf.put_u16(*id);
238                buf.put_u32(*value);
239            }
240        }
241
242        buf
243    }
244
245    /// Parse a SETTINGS frame payload.
246    pub fn parse(flags: u8, mut payload: Bytes) -> Self {
247        let ack = (flags & flags::ACK) != 0;
248        let mut settings = Vec::new();
249
250        while payload.remaining() >= 6 {
251            let id = payload.get_u16();
252            let value = payload.get_u32();
253            settings.push((id, value));
254        }
255
256        Self { settings, ack }
257    }
258}
259
260impl Default for SettingsFrame {
261    fn default() -> Self {
262        Self::new()
263    }
264}
265
266/// WINDOW_UPDATE frame.
267#[derive(Debug, Clone)]
268pub struct WindowUpdateFrame {
269    pub stream_id: u32,
270    pub increment: u32,
271}
272
273impl WindowUpdateFrame {
274    /// Create a new WINDOW_UPDATE frame.
275    pub fn new(stream_id: u32, increment: u32) -> Self {
276        Self {
277            stream_id,
278            increment,
279        }
280    }
281
282    /// Serialize to bytes (including frame header).
283    pub fn serialize(&self) -> BytesMut {
284        let mut buf = BytesMut::with_capacity(FRAME_HEADER_SIZE + 4);
285
286        let header = FrameHeader {
287            length: 4,
288            frame_type: FrameType::WindowUpdate,
289            flags: 0,
290            stream_id: self.stream_id,
291        };
292        header.serialize(&mut buf);
293
294        // Window size increment (31 bits, high bit reserved)
295        buf.put_u32(self.increment & 0x7fffffff);
296
297        buf
298    }
299
300    /// Parse from payload.
301    /// Returns None if frame is invalid (e.g., increment is 0).
302    pub fn parse(stream_id: u32, mut payload: Bytes) -> Option<Self> {
303        if payload.remaining() < 4 {
304            return None;
305        }
306        let increment_raw = payload.get_u32();
307        let increment = increment_raw & 0x7fffffff;
308
309        // RFC 9113 Section 6.9.1: Window size increment MUST be between 1 and 2^31-1
310        // A value of 0 is invalid and MUST be treated as a connection error (FLOW_CONTROL_ERROR)
311        if increment == 0 {
312            return None;
313        }
314
315        Some(Self {
316            stream_id,
317            increment,
318        })
319    }
320}
321
322/// HEADERS frame.
323#[derive(Debug, Clone)]
324pub struct HeadersFrame {
325    pub stream_id: u32,
326    pub header_block: Bytes,
327    pub end_stream: bool,
328    pub end_headers: bool,
329    pub priority: Option<PriorityData>,
330}
331
332/// Priority data (optional in HEADERS frame).
333#[derive(Debug, Clone, Copy)]
334pub struct PriorityData {
335    pub exclusive: bool,
336    pub stream_dependency: u32,
337    pub weight: u8,
338}
339
340impl HeadersFrame {
341    /// Create a new HEADERS frame.
342    pub fn new(stream_id: u32, header_block: Bytes) -> Self {
343        Self {
344            stream_id,
345            header_block,
346            end_stream: false,
347            end_headers: true,
348            priority: None,
349        }
350    }
351
352    /// Set end_stream flag.
353    pub fn end_stream(mut self, end: bool) -> Self {
354        self.end_stream = end;
355        self
356    }
357
358    /// Set priority data.
359    pub fn with_priority(mut self, priority: PriorityData) -> Self {
360        self.priority = Some(priority);
361        self
362    }
363
364    /// Set end_headers flag.
365    pub fn end_headers(mut self, end: bool) -> Self {
366        self.end_headers = end;
367        self
368    }
369
370    /// Serialize to bytes (including frame header).
371    /// Padding is not currently supported in serialization as it is not required
372    /// for accurate browser fingerprinting.
373    pub fn serialize(&self) -> BytesMut {
374        let priority_len = if self.priority.is_some() { 5 } else { 0 };
375        let payload_len = priority_len + self.header_block.len();
376        let mut buf = BytesMut::with_capacity(FRAME_HEADER_SIZE + payload_len);
377
378        let mut frame_flags = 0u8;
379        if self.end_stream {
380            frame_flags |= flags::END_STREAM;
381        }
382        if self.end_headers {
383            frame_flags |= flags::END_HEADERS;
384        }
385        if self.priority.is_some() {
386            frame_flags |= flags::PRIORITY;
387        }
388
389        let header = FrameHeader {
390            length: payload_len as u32,
391            frame_type: FrameType::Headers,
392            flags: frame_flags,
393            stream_id: self.stream_id,
394        };
395        header.serialize(&mut buf);
396
397        // Priority data (optional)
398        if let Some(priority) = &self.priority {
399            let dep = if priority.exclusive {
400                priority.stream_dependency | 0x80000000
401            } else {
402                priority.stream_dependency
403            };
404            buf.put_u32(dep);
405            buf.put_u8(priority.weight);
406        }
407
408        // Header block fragment
409        buf.extend_from_slice(&self.header_block);
410
411        buf
412    }
413
414    /// Parse a HEADERS frame from payload (with padding and priority handling).
415    /// Returns None if frame is invalid.
416    pub fn parse(stream_id: u32, flags: u8, mut payload: Bytes) -> Result<Self, String> {
417        if stream_id == 0 {
418            return Err("HEADERS frame must have non-zero stream ID".to_string());
419        }
420
421        let end_stream = (flags & flags::END_STREAM) != 0;
422        let end_headers = (flags & flags::END_HEADERS) != 0;
423        let padded = (flags & flags::PADDED) != 0;
424        let priority_flag = (flags & flags::PRIORITY) != 0;
425
426        // Parse padding length (if PADDED flag set)
427        let pad_len = if padded {
428            if payload.remaining() < 1 {
429                return Err("PADDED HEADERS frame missing padding length".to_string());
430            }
431            let pad_len = payload.get_u8() as usize;
432            if pad_len >= payload.remaining() {
433                return Err("Padding length exceeds payload size".to_string());
434            }
435            pad_len
436        } else {
437            0
438        };
439
440        // Parse priority (if PRIORITY flag set)
441        let priority = if priority_flag {
442            if payload.remaining() < 5 {
443                return Err("HEADERS frame with PRIORITY flag missing priority data".to_string());
444            }
445            let dep_raw = payload.get_u32();
446            let exclusive = (dep_raw & 0x80000000) != 0;
447            let stream_dependency = dep_raw & 0x7fffffff;
448            let weight = payload.get_u8();
449            Some(PriorityData {
450                exclusive,
451                stream_dependency,
452                weight,
453            })
454        } else {
455            None
456        };
457
458        // Extract header block (remaining payload minus padding)
459        let header_block_len = payload.remaining() - pad_len;
460        if header_block_len == 0 {
461            return Err("HEADERS frame header block is empty".to_string());
462        }
463        let header_block = payload.copy_to_bytes(header_block_len);
464        // Skip padding bytes
465        payload.advance(pad_len);
466
467        Ok(Self {
468            stream_id,
469            header_block,
470            end_stream,
471            end_headers,
472            priority,
473        })
474    }
475}
476
477/// CONTINUATION frame (RFC 9113 Section 6.10).
478#[derive(Debug, Clone)]
479pub struct ContinuationFrame {
480    pub stream_id: u32,
481    pub flags: u8,
482    pub header_fragment: Bytes,
483}
484
485impl ContinuationFrame {
486    /// Create a new CONTINUATION frame.
487    pub fn new(stream_id: u32, header_fragment: Bytes, end_headers: bool) -> Self {
488        let flags = if end_headers { flags::END_HEADERS } else { 0 };
489        Self {
490            stream_id,
491            flags,
492            header_fragment,
493        }
494    }
495
496    /// Check if END_HEADERS flag is set.
497    pub fn end_headers(&self) -> bool {
498        self.flags & flags::END_HEADERS != 0
499    }
500
501    /// Serialize to bytes (including frame header).
502    pub fn serialize(&self) -> BytesMut {
503        let payload_len = self.header_fragment.len();
504        let mut buf = BytesMut::with_capacity(FRAME_HEADER_SIZE + payload_len);
505
506        let header = FrameHeader {
507            length: payload_len as u32,
508            frame_type: FrameType::Continuation,
509            flags: self.flags,
510            stream_id: self.stream_id,
511        };
512        header.serialize(&mut buf);
513
514        // Header fragment payload
515        buf.extend_from_slice(&self.header_fragment);
516
517        buf
518    }
519
520    /// Parse a CONTINUATION frame from payload.
521    pub fn parse(stream_id: u32, flags: u8, payload: Bytes) -> Result<Self, String> {
522        if stream_id == 0 {
523            return Err("CONTINUATION frame must have non-zero stream ID".to_string());
524        }
525
526        Ok(Self {
527            stream_id,
528            flags,
529            header_fragment: payload,
530        })
531    }
532}
533
534/// DATA frame.
535#[derive(Debug, Clone)]
536pub struct DataFrame {
537    pub stream_id: u32,
538    pub data: Bytes,
539    pub end_stream: bool,
540    pub padding_len: u8,
541}
542
543impl DataFrame {
544    /// Create a new DATA frame.
545    pub fn new(stream_id: u32, data: Bytes) -> Self {
546        Self {
547            stream_id,
548            data,
549            end_stream: false,
550            padding_len: 0,
551        }
552    }
553
554    /// Set end_stream flag.
555    pub fn end_stream(mut self, end: bool) -> Self {
556        self.end_stream = end;
557        self
558    }
559
560    /// Set padding length (0-255 bytes).
561    pub fn with_padding(mut self, padding_len: u8) -> Self {
562        self.padding_len = padding_len;
563        self
564    }
565
566    /// Serialize to bytes (including frame header).
567    pub fn serialize(&self) -> BytesMut {
568        let payload_len = if self.padding_len > 0 {
569            // Padding length byte + data + padding
570            1 + self.data.len() + self.padding_len as usize
571        } else {
572            self.data.len()
573        };
574
575        let mut buf = BytesMut::with_capacity(FRAME_HEADER_SIZE + payload_len);
576
577        let mut frame_flags = if self.end_stream {
578            flags::END_STREAM
579        } else {
580            0
581        };
582        if self.padding_len > 0 {
583            frame_flags |= flags::PADDED;
584        }
585
586        let header = FrameHeader {
587            length: payload_len as u32,
588            frame_type: FrameType::Data,
589            flags: frame_flags,
590            stream_id: self.stream_id,
591        };
592        header.serialize(&mut buf);
593
594        // Padding length byte (if padded)
595        if self.padding_len > 0 {
596            buf.put_u8(self.padding_len);
597        }
598
599        // Data
600        buf.extend_from_slice(&self.data);
601
602        // Padding bytes (zeros)
603        if self.padding_len > 0 {
604            buf.extend_from_slice(&vec![0u8; self.padding_len as usize]);
605        }
606
607        buf
608    }
609
610    /// Parse a DATA frame from payload (with padding handling).
611    /// Returns None if frame is invalid.
612    pub fn parse(stream_id: u32, flags: u8, mut payload: Bytes) -> Result<Self, String> {
613        if stream_id == 0 {
614            return Err("DATA frame must have non-zero stream ID".to_string());
615        }
616
617        let end_stream = (flags & flags::END_STREAM) != 0;
618        let padded = (flags & flags::PADDED) != 0;
619
620        let (data, padding_len) = if padded {
621            if payload.remaining() < 1 {
622                return Err("PADDED DATA frame missing padding length".to_string());
623            }
624            let pad_len = payload.get_u8() as usize;
625            if pad_len >= payload.remaining() {
626                return Err("Padding length exceeds payload size".to_string());
627            }
628            let data_len = payload.remaining() - pad_len;
629            let data = payload.copy_to_bytes(data_len);
630            // Skip padding bytes
631            payload.advance(pad_len);
632            (data, pad_len as u8)
633        } else {
634            (payload, 0)
635        };
636
637        Ok(Self {
638            stream_id,
639            data,
640            end_stream,
641            padding_len,
642        })
643    }
644}
645
646/// PING frame.
647#[derive(Debug, Clone)]
648pub struct PingFrame {
649    pub ack: bool,
650    pub data: [u8; 8],
651}
652
653impl PingFrame {
654    /// Create a new PING frame.
655    pub fn new(data: [u8; 8]) -> Self {
656        Self { ack: false, data }
657    }
658
659    /// Create a PING ACK frame.
660    pub fn ack(data: [u8; 8]) -> Self {
661        Self { ack: true, data }
662    }
663
664    /// Serialize to bytes.
665    pub fn serialize(&self) -> BytesMut {
666        let mut buf = BytesMut::with_capacity(FRAME_HEADER_SIZE + 8);
667
668        let header = FrameHeader {
669            length: 8,
670            frame_type: FrameType::Ping,
671            flags: if self.ack { flags::ACK } else { 0 },
672            stream_id: 0,
673        };
674        header.serialize(&mut buf);
675        buf.extend_from_slice(&self.data);
676
677        buf
678    }
679
680    /// Parse from payload.
681    pub fn parse(flags: u8, payload: &[u8]) -> Option<Self> {
682        if payload.len() != 8 {
683            return None;
684        }
685        let mut data = [0u8; 8];
686        data.copy_from_slice(payload);
687        Some(Self {
688            ack: (flags & flags::ACK) != 0,
689            data,
690        })
691    }
692}
693
694/// GOAWAY frame.
695#[derive(Debug, Clone)]
696pub struct GoAwayFrame {
697    pub last_stream_id: u32,
698    pub error_code: ErrorCode,
699    pub debug_data: Bytes,
700}
701
702impl GoAwayFrame {
703    /// Create a new GOAWAY frame.
704    pub fn new(last_stream_id: u32, error_code: ErrorCode) -> Self {
705        Self {
706            last_stream_id,
707            error_code,
708            debug_data: Bytes::new(),
709        }
710    }
711
712    /// Serialize to bytes.
713    pub fn serialize(&self) -> BytesMut {
714        let payload_len = 8 + self.debug_data.len();
715        let mut buf = BytesMut::with_capacity(FRAME_HEADER_SIZE + payload_len);
716
717        let header = FrameHeader {
718            length: payload_len as u32,
719            frame_type: FrameType::GoAway,
720            flags: 0,
721            stream_id: 0,
722        };
723        header.serialize(&mut buf);
724        buf.put_u32(self.last_stream_id & 0x7fffffff);
725        buf.put_u32(self.error_code as u32);
726        buf.extend_from_slice(&self.debug_data);
727
728        buf
729    }
730
731    /// Parse from payload.
732    pub fn parse(mut payload: Bytes) -> Option<Self> {
733        if payload.remaining() < 8 {
734            return None;
735        }
736        let last_stream_id = payload.get_u32() & 0x7fffffff;
737        let error_code_raw = payload.get_u32();
738        let error_code = match error_code_raw {
739            0x0 => ErrorCode::NoError,
740            0x1 => ErrorCode::ProtocolError,
741            0x2 => ErrorCode::InternalError,
742            0x3 => ErrorCode::FlowControlError,
743            0x4 => ErrorCode::SettingsTimeout,
744            0x5 => ErrorCode::StreamClosed,
745            0x6 => ErrorCode::FrameSizeError,
746            0x7 => ErrorCode::RefusedStream,
747            0x8 => ErrorCode::Cancel,
748            0x9 => ErrorCode::CompressionError,
749            0xa => ErrorCode::ConnectError,
750            0xb => ErrorCode::EnhanceYourCalm,
751            0xc => ErrorCode::InadequateSecurity,
752            0xd => ErrorCode::Http11Required,
753            _ => ErrorCode::ProtocolError,
754        };
755        let debug_data = payload.copy_to_bytes(payload.remaining());
756
757        Some(Self {
758            last_stream_id,
759            error_code,
760            debug_data,
761        })
762    }
763}
764
765/// PRIORITY frame (RFC 9113 Section 6.3).
766#[derive(Debug, Clone)]
767pub struct PriorityFrame {
768    pub stream_id: u32,
769    pub exclusive: bool,
770    pub stream_dependency: u32,
771    pub weight: u8,
772}
773
774impl PriorityFrame {
775    /// Create a new PRIORITY frame.
776    pub fn new(stream_id: u32, stream_dependency: u32, weight: u8, exclusive: bool) -> Self {
777        Self {
778            stream_id,
779            exclusive,
780            stream_dependency,
781            weight,
782        }
783    }
784
785    /// Serialize to bytes.
786    pub fn serialize(&self) -> BytesMut {
787        let mut buf = BytesMut::with_capacity(FRAME_HEADER_SIZE + 5);
788
789        let header = FrameHeader {
790            length: 5,
791            frame_type: FrameType::Priority,
792            flags: 0,
793            stream_id: self.stream_id,
794        };
795        header.serialize(&mut buf);
796
797        let dep = if self.exclusive {
798            self.stream_dependency | 0x80000000
799        } else {
800            self.stream_dependency
801        };
802        buf.put_u32(dep);
803        buf.put_u8(self.weight);
804
805        buf
806    }
807
808    /// Parse from payload.
809    pub fn parse(stream_id: u32, mut payload: Bytes) -> Result<Self, String> {
810        if stream_id == 0 {
811            return Err("PRIORITY frame must have non-zero stream ID".to_string());
812        }
813        if payload.remaining() < 5 {
814            return Err("PRIORITY frame payload too short".to_string());
815        }
816
817        let dep_raw = payload.get_u32();
818        let exclusive = (dep_raw & 0x80000000) != 0;
819        let stream_dependency = dep_raw & 0x7fffffff;
820        let weight = payload.get_u8();
821
822        // RFC 9113 Section 6.3: Stream cannot depend on itself
823        if stream_dependency == stream_id {
824            return Err("PRIORITY frame stream cannot depend on itself".to_string());
825        }
826
827        Ok(Self {
828            stream_id,
829            exclusive,
830            stream_dependency,
831            weight,
832        })
833    }
834}
835
836/// PUSH_PROMISE frame (RFC 9113 Section 6.6).
837#[derive(Debug, Clone)]
838pub struct PushPromiseFrame {
839    pub stream_id: u32,
840    pub promised_stream_id: u32,
841    pub header_block: Bytes,
842    pub end_headers: bool,
843}
844
845impl PushPromiseFrame {
846    /// Create a new PUSH_PROMISE frame.
847    pub fn new(stream_id: u32, promised_stream_id: u32, header_block: Bytes) -> Self {
848        Self {
849            stream_id,
850            promised_stream_id,
851            header_block,
852            end_headers: true,
853        }
854    }
855
856    /// Set end_headers flag.
857    pub fn end_headers(mut self, end: bool) -> Self {
858        self.end_headers = end;
859        self
860    }
861
862    /// Serialize to bytes.
863    pub fn serialize(&self) -> BytesMut {
864        let payload_len = 4 + self.header_block.len(); // promised_stream_id (4) + header_block
865        let mut buf = BytesMut::with_capacity(FRAME_HEADER_SIZE + payload_len);
866
867        let header = FrameHeader {
868            length: payload_len as u32,
869            frame_type: FrameType::PushPromise,
870            flags: if self.end_headers {
871                flags::END_HEADERS
872            } else {
873                0
874            },
875            stream_id: self.stream_id,
876        };
877        header.serialize(&mut buf);
878
879        // Promised stream ID (31 bits, reserved bit must be 0)
880        buf.put_u32(self.promised_stream_id & 0x7fffffff);
881        buf.extend_from_slice(&self.header_block);
882
883        buf
884    }
885
886    /// Parse from payload (with padding handling).
887    pub fn parse(stream_id: u32, flags: u8, mut payload: Bytes) -> Result<Self, String> {
888        if stream_id == 0 {
889            return Err("PUSH_PROMISE frame must have non-zero stream ID".to_string());
890        }
891
892        let end_headers = (flags & flags::END_HEADERS) != 0;
893        let padded = (flags & flags::PADDED) != 0;
894
895        // Parse padding length (if PADDED flag set)
896        let pad_len = if padded {
897            if payload.remaining() < 1 {
898                return Err("PADDED PUSH_PROMISE frame missing padding length".to_string());
899            }
900            let pad_len = payload.get_u8() as usize;
901            if pad_len >= payload.remaining() {
902                return Err("Padding length exceeds payload size".to_string());
903            }
904            pad_len
905        } else {
906            0
907        };
908
909        // Parse promised stream ID
910        if payload.remaining() < 4 {
911            return Err("PUSH_PROMISE frame missing promised stream ID".to_string());
912        }
913        let promised_stream_id_raw = payload.get_u32();
914        if (promised_stream_id_raw & 0x80000000) != 0 {
915            return Err("PUSH_PROMISE frame reserved bit set in promised stream ID".to_string());
916        }
917        let promised_stream_id = promised_stream_id_raw & 0x7fffffff;
918
919        // Extract header block (remaining payload minus padding)
920        let header_block_len = payload.remaining() - pad_len;
921        if header_block_len == 0 {
922            return Err("PUSH_PROMISE frame header block is empty".to_string());
923        }
924        let header_block = payload.copy_to_bytes(header_block_len);
925        // Skip padding bytes
926        payload.advance(pad_len);
927
928        Ok(Self {
929            stream_id,
930            promised_stream_id,
931            header_block,
932            end_headers,
933        })
934    }
935}
936
937/// RST_STREAM frame.
938#[derive(Debug, Clone)]
939pub struct RstStreamFrame {
940    pub stream_id: u32,
941    pub error_code: ErrorCode,
942}
943
944impl RstStreamFrame {
945    /// Create a new RST_STREAM frame.
946    pub fn new(stream_id: u32, error_code: ErrorCode) -> Self {
947        Self {
948            stream_id,
949            error_code,
950        }
951    }
952
953    /// Serialize to bytes.
954    pub fn serialize(&self) -> BytesMut {
955        let mut buf = BytesMut::with_capacity(FRAME_HEADER_SIZE + 4);
956
957        let header = FrameHeader {
958            length: 4,
959            frame_type: FrameType::RstStream,
960            flags: 0,
961            stream_id: self.stream_id,
962        };
963        header.serialize(&mut buf);
964        buf.put_u32(self.error_code as u32);
965
966        buf
967    }
968
969    /// Parse from payload.
970    pub fn parse(stream_id: u32, mut payload: Bytes) -> Result<Self, String> {
971        if stream_id == 0 {
972            return Err("RST_STREAM frame must have non-zero stream ID".to_string());
973        }
974        if payload.remaining() < 4 {
975            return Err("RST_STREAM frame payload too short".to_string());
976        }
977
978        let error_code_raw = payload.get_u32();
979        let error_code = match error_code_raw {
980            0x0 => ErrorCode::NoError,
981            0x1 => ErrorCode::ProtocolError,
982            0x2 => ErrorCode::InternalError,
983            0x3 => ErrorCode::FlowControlError,
984            0x4 => ErrorCode::SettingsTimeout,
985            0x5 => ErrorCode::StreamClosed,
986            0x6 => ErrorCode::FrameSizeError,
987            0x7 => ErrorCode::RefusedStream,
988            0x8 => ErrorCode::Cancel,
989            0x9 => ErrorCode::CompressionError,
990            0xa => ErrorCode::ConnectError,
991            0xb => ErrorCode::EnhanceYourCalm,
992            0xc => ErrorCode::InadequateSecurity,
993            0xd => ErrorCode::Http11Required,
994            _ => ErrorCode::ProtocolError,
995        };
996
997        Ok(Self {
998            stream_id,
999            error_code,
1000        })
1001    }
1002}
1003
1004#[cfg(test)]
1005mod tests {
1006    use super::*;
1007
1008    #[test]
1009    fn test_settings_frame_serialization() {
1010        let mut settings = SettingsFrame::new();
1011        settings
1012            .set(SettingsId::HeaderTableSize, 65536)
1013            .set(SettingsId::MaxConcurrentStreams, 1000)
1014            .set(SettingsId::InitialWindowSize, 6291456);
1015
1016        let buf = settings.serialize();
1017
1018        // Frame header (9) + 3 settings (3 * 6 = 18) = 27 bytes
1019        assert_eq!(buf.len(), 27);
1020
1021        // Verify frame header
1022        assert_eq!(buf[0..3], [0, 0, 18]); // Length = 18
1023        assert_eq!(buf[3], 0x4); // Type = SETTINGS
1024        assert_eq!(buf[4], 0); // Flags = 0
1025        assert_eq!(buf[5..9], [0, 0, 0, 0]); // Stream ID = 0
1026    }
1027
1028    #[test]
1029    fn test_settings_ack_frame() {
1030        let settings = SettingsFrame::ack();
1031        let buf = settings.serialize();
1032
1033        assert_eq!(buf.len(), 9); // Just header, no payload
1034        assert_eq!(buf[0..3], [0, 0, 0]); // Length = 0
1035        assert_eq!(buf[3], 0x4); // Type = SETTINGS
1036        assert_eq!(buf[4], 0x1); // Flags = ACK
1037    }
1038
1039    #[test]
1040    fn test_window_update_frame() {
1041        let frame = WindowUpdateFrame::new(0, 15663105);
1042        let buf = frame.serialize();
1043
1044        assert_eq!(buf.len(), 13); // 9 header + 4 payload
1045        assert_eq!(buf[3], 0x8); // Type = WINDOW_UPDATE
1046    }
1047
1048    #[test]
1049    fn test_frame_header_parse() {
1050        let bytes = [0, 0, 18, 0x4, 0, 0, 0, 0, 0];
1051        let header = FrameHeader::parse(&bytes).unwrap();
1052
1053        assert_eq!(header.length, 18);
1054        assert_eq!(header.frame_type, FrameType::Settings);
1055        assert_eq!(header.flags, 0);
1056        assert_eq!(header.stream_id, 0);
1057    }
1058}