Skip to main content

stackforge_core/layer/http2/
builder.rs

1//! HTTP/2 frame and connection builders.
2//!
3//! Provides builder types for constructing HTTP/2 frames and complete
4//! HTTP/2 connections (preface + frames). Follows the "Permissive Builder"
5//! pattern: allows constructing any frame combination including malformed
6//! ones for security testing.
7
8use super::frames::{Http2FrameType, flags};
9use super::hpack::HpackEncoder;
10
11// ============================================================================
12// Core frame building function
13// ============================================================================
14
15/// Build raw bytes for an HTTP/2 frame.
16///
17/// Format: 3 bytes length (big-endian) | 1 byte type | 1 byte flags | 4 bytes `stream_id` | payload
18#[must_use]
19pub fn build_frame(frame_type: u8, flags: u8, stream_id: u32, payload: &[u8]) -> Vec<u8> {
20    let len = payload.len() as u32;
21    let mut out = Vec::with_capacity(9 + payload.len());
22    out.push(((len >> 16) & 0xFF) as u8);
23    out.push(((len >> 8) & 0xFF) as u8);
24    out.push((len & 0xFF) as u8);
25    out.push(frame_type);
26    out.push(flags);
27    out.extend_from_slice(&(stream_id & 0x7FFFFFFF).to_be_bytes());
28    out.extend_from_slice(payload);
29    out
30}
31
32// ============================================================================
33// Http2FrameBuilder
34// ============================================================================
35
36/// Builder for a single HTTP/2 frame.
37///
38/// # Example
39/// ```
40/// use stackforge_core::layer::http2::builder::Http2FrameBuilder;
41/// use stackforge_core::layer::http2::frames::Http2FrameType;
42///
43/// let frame = Http2FrameBuilder::settings_ack().build();
44/// assert_eq!(frame.len(), 9); // 9-byte header, empty payload
45/// ```
46#[derive(Debug, Clone)]
47pub struct Http2FrameBuilder {
48    frame_type: Http2FrameType,
49    flags: u8,
50    stream_id: u32,
51    payload: Vec<u8>,
52}
53
54impl Http2FrameBuilder {
55    /// Create a new frame builder for the given frame type.
56    #[must_use]
57    pub fn new(frame_type: Http2FrameType) -> Self {
58        Http2FrameBuilder {
59            frame_type,
60            flags: 0,
61            stream_id: 0,
62            payload: Vec::new(),
63        }
64    }
65
66    /// Set the flags byte.
67    #[must_use]
68    pub fn flags(mut self, flags: u8) -> Self {
69        self.flags = flags;
70        self
71    }
72
73    /// Set an additional flag bit (OR with existing flags).
74    #[must_use]
75    pub fn add_flag(mut self, flag: u8) -> Self {
76        self.flags |= flag;
77        self
78    }
79
80    /// Set the stream ID.
81    #[must_use]
82    pub fn stream_id(mut self, id: u32) -> Self {
83        self.stream_id = id;
84        self
85    }
86
87    /// Set the frame payload.
88    #[must_use]
89    pub fn payload(mut self, data: Vec<u8>) -> Self {
90        self.payload = data;
91        self
92    }
93
94    /// Set the `END_STREAM` flag (0x1).
95    ///
96    /// Used with DATA and HEADERS frames.
97    #[must_use]
98    pub fn end_stream(mut self) -> Self {
99        self.flags |= 0x01;
100        self
101    }
102
103    /// Set the `END_HEADERS` flag (0x4).
104    ///
105    /// Used with HEADERS, `PUSH_PROMISE`, and CONTINUATION frames.
106    #[must_use]
107    pub fn end_headers(mut self) -> Self {
108        self.flags |= 0x04;
109        self
110    }
111
112    /// Build the frame into bytes.
113    #[must_use]
114    pub fn build(&self) -> Vec<u8> {
115        build_frame(
116            self.frame_type.as_u8(),
117            self.flags,
118            self.stream_id,
119            &self.payload,
120        )
121    }
122
123    // -------------------------------------------------------------------------
124    // Frame factory methods
125    // -------------------------------------------------------------------------
126
127    /// Create an empty SETTINGS frame (stream 0).
128    #[must_use]
129    pub fn settings() -> Self {
130        Http2FrameBuilder::new(Http2FrameType::Settings)
131            .stream_id(0)
132            .flags(0)
133    }
134
135    /// Create a SETTINGS ACK frame (stream 0, ACK flag set).
136    #[must_use]
137    pub fn settings_ack() -> Self {
138        Http2FrameBuilder::new(Http2FrameType::Settings)
139            .stream_id(0)
140            .flags(flags::SETTINGS_ACK)
141    }
142
143    /// Create a SETTINGS frame with parameters.
144    ///
145    /// Each entry is (`settings_id`, value). The payload is 6 bytes per entry.
146    #[must_use]
147    pub fn settings_with_params(params: &[(u16, u32)]) -> Self {
148        let mut payload = Vec::with_capacity(params.len() * 6);
149        for &(id, val) in params {
150            payload.extend_from_slice(&id.to_be_bytes());
151            payload.extend_from_slice(&val.to_be_bytes());
152        }
153        Http2FrameBuilder::new(Http2FrameType::Settings)
154            .stream_id(0)
155            .payload(payload)
156    }
157
158    /// Create a PING frame with 8 bytes of opaque data.
159    #[must_use]
160    pub fn ping(data: [u8; 8]) -> Self {
161        Http2FrameBuilder::new(Http2FrameType::Ping)
162            .stream_id(0)
163            .payload(data.to_vec())
164    }
165
166    /// Create a PING ACK frame (response to a PING).
167    #[must_use]
168    pub fn ping_ack(data: [u8; 8]) -> Self {
169        Http2FrameBuilder::new(Http2FrameType::Ping)
170            .stream_id(0)
171            .flags(flags::PING_ACK)
172            .payload(data.to_vec())
173    }
174
175    /// Create a GOAWAY frame.
176    ///
177    /// # Arguments
178    /// - `last_stream`: the last stream ID processed
179    /// - `error_code`: the error code (see `frames::error_codes`)
180    #[must_use]
181    pub fn goaway(last_stream: u32, error_code: u32) -> Self {
182        let mut payload = Vec::with_capacity(8);
183        payload.extend_from_slice(&(last_stream & 0x7FFFFFFF).to_be_bytes());
184        payload.extend_from_slice(&error_code.to_be_bytes());
185        Http2FrameBuilder::new(Http2FrameType::GoAway)
186            .stream_id(0)
187            .payload(payload)
188    }
189
190    /// Create a `WINDOW_UPDATE` frame.
191    ///
192    /// # Arguments
193    /// - `increment`: window size increment (1-2^31-1)
194    /// - `stream_id`: stream ID (0 for connection-level)
195    #[must_use]
196    pub fn window_update(increment: u32, stream_id: u32) -> Self {
197        let mut payload = Vec::with_capacity(4);
198        payload.extend_from_slice(&(increment & 0x7FFFFFFF).to_be_bytes());
199        Http2FrameBuilder::new(Http2FrameType::WindowUpdate)
200            .stream_id(stream_id)
201            .payload(payload)
202    }
203
204    /// Create a `RST_STREAM` frame.
205    ///
206    /// # Arguments
207    /// - `stream_id`: the stream to reset
208    /// - `error_code`: the error code
209    #[must_use]
210    pub fn rst_stream(stream_id: u32, error_code: u32) -> Self {
211        let mut payload = Vec::with_capacity(4);
212        payload.extend_from_slice(&error_code.to_be_bytes());
213        Http2FrameBuilder::new(Http2FrameType::RstStream)
214            .stream_id(stream_id)
215            .payload(payload)
216    }
217
218    /// Create a HEADERS frame carrying HPACK-encoded header data.
219    ///
220    /// Sets `END_HEADERS` by default. Use `.flags()` to override.
221    #[must_use]
222    pub fn headers_frame(stream_id: u32, hpack_data: Vec<u8>) -> Self {
223        Http2FrameBuilder::new(Http2FrameType::Headers)
224            .stream_id(stream_id)
225            .flags(flags::HEADERS_END_HEADERS)
226            .payload(hpack_data)
227    }
228
229    /// Create a HEADERS frame with `END_STREAM` + `END_HEADERS` flags set.
230    #[must_use]
231    pub fn headers_frame_final(stream_id: u32, hpack_data: Vec<u8>) -> Self {
232        Http2FrameBuilder::new(Http2FrameType::Headers)
233            .stream_id(stream_id)
234            .flags(flags::HEADERS_END_HEADERS | flags::HEADERS_END_STREAM)
235            .payload(hpack_data)
236    }
237
238    /// Create a DATA frame.
239    #[must_use]
240    pub fn data_frame(stream_id: u32, data: Vec<u8>) -> Self {
241        Http2FrameBuilder::new(Http2FrameType::Data)
242            .stream_id(stream_id)
243            .payload(data)
244    }
245
246    /// Create a DATA frame with `END_STREAM` set.
247    #[must_use]
248    pub fn data_frame_final(stream_id: u32, data: Vec<u8>) -> Self {
249        Http2FrameBuilder::new(Http2FrameType::Data)
250            .stream_id(stream_id)
251            .flags(flags::DATA_END_STREAM)
252            .payload(data)
253    }
254
255    /// Create a CONTINUATION frame.
256    #[must_use]
257    pub fn continuation(stream_id: u32, hpack_data: Vec<u8>) -> Self {
258        Http2FrameBuilder::new(Http2FrameType::Continuation)
259            .stream_id(stream_id)
260            .payload(hpack_data)
261    }
262
263    /// Create a CONTINUATION frame with `END_HEADERS` set.
264    #[must_use]
265    pub fn continuation_final(stream_id: u32, hpack_data: Vec<u8>) -> Self {
266        Http2FrameBuilder::new(Http2FrameType::Continuation)
267            .stream_id(stream_id)
268            .flags(flags::CONTINUATION_END_HEADERS)
269            .payload(hpack_data)
270    }
271
272    /// Create a PRIORITY frame.
273    ///
274    /// # Arguments
275    /// - `stream_id`: the stream being prioritized
276    /// - `exclusive`: exclusive dependency flag
277    /// - `stream_dep`: stream dependency
278    /// - `weight`: priority weight (0-255, actual weight = weight + 1)
279    #[must_use]
280    pub fn priority_frame(stream_id: u32, exclusive: bool, stream_dep: u32, weight: u8) -> Self {
281        let dep = if exclusive {
282            stream_dep | 0x80000000
283        } else {
284            stream_dep & 0x7FFFFFFF
285        };
286        let mut payload = Vec::with_capacity(5);
287        payload.extend_from_slice(&dep.to_be_bytes());
288        payload.push(weight);
289        Http2FrameBuilder::new(Http2FrameType::Priority)
290            .stream_id(stream_id)
291            .payload(payload)
292    }
293
294    /// Create a `PUSH_PROMISE` frame.
295    ///
296    /// # Arguments
297    /// - `stream_id`: the associated stream
298    /// - `promised_stream_id`: the stream ID being promised
299    /// - `hpack_data`: HPACK-encoded headers for the promised request
300    #[must_use]
301    pub fn push_promise(stream_id: u32, promised_stream_id: u32, hpack_data: Vec<u8>) -> Self {
302        let mut payload = Vec::with_capacity(4 + hpack_data.len());
303        payload.extend_from_slice(&(promised_stream_id & 0x7FFFFFFF).to_be_bytes());
304        payload.extend_from_slice(&hpack_data);
305        Http2FrameBuilder::new(Http2FrameType::PushPromise)
306            .stream_id(stream_id)
307            .flags(flags::PUSH_PROMISE_END_HEADERS)
308            .payload(payload)
309    }
310}
311
312// ============================================================================
313// Http2Builder — full connection builder
314// ============================================================================
315
316/// Builder for a complete HTTP/2 connection sequence.
317///
318/// Optionally includes the client connection preface, followed by one or
319/// more frames.
320///
321/// # Example
322/// ```
323/// use stackforge_core::layer::http2::builder::{Http2Builder, Http2FrameBuilder};
324///
325/// let bytes = Http2Builder::new()
326///     .frame(Http2FrameBuilder::settings())
327///     .frame(Http2FrameBuilder::settings_ack())
328///     .build();
329/// ```
330#[derive(Debug, Clone)]
331pub struct Http2Builder {
332    /// Whether to prepend the client connection preface.
333    include_preface: bool,
334    /// Ordered list of frames to include.
335    frames: Vec<Http2FrameBuilder>,
336}
337
338impl Default for Http2Builder {
339    fn default() -> Self {
340        Self::new()
341    }
342}
343
344impl Http2Builder {
345    /// Create a builder that includes the HTTP/2 client connection preface.
346    #[must_use]
347    pub fn new() -> Self {
348        Http2Builder {
349            include_preface: true,
350            frames: Vec::new(),
351        }
352    }
353
354    /// Create a builder without the connection preface.
355    #[must_use]
356    pub fn without_preface() -> Self {
357        Http2Builder {
358            include_preface: false,
359            frames: Vec::new(),
360        }
361    }
362
363    /// Add a frame to the sequence.
364    #[must_use]
365    pub fn frame(mut self, f: Http2FrameBuilder) -> Self {
366        self.frames.push(f);
367        self
368    }
369
370    /// Get the total byte size of the built output.
371    #[must_use]
372    pub fn header_size(&self) -> usize {
373        let preface_len = if self.include_preface { 24 } else { 0 };
374        let frames_len: usize = self.frames.iter().map(|f| 9 + f.payload.len()).sum();
375        preface_len + frames_len
376    }
377
378    /// Build the complete HTTP/2 connection sequence into bytes.
379    #[must_use]
380    pub fn build(&self) -> Vec<u8> {
381        let mut out = Vec::with_capacity(self.header_size());
382
383        if self.include_preface {
384            out.extend_from_slice(super::frames::HTTP2_PREFACE);
385        }
386
387        for frame in &self.frames {
388            out.extend_from_slice(&frame.build());
389        }
390
391        out
392    }
393}
394
395// ============================================================================
396// HTTP/2 request/response helpers
397// ============================================================================
398
399/// Build a complete HTTP/2 GET request as bytes.
400///
401/// Produces: preface + SETTINGS frame + HEADERS frame with HPACK-encoded headers.
402///
403/// # Arguments
404/// - `host`: the `:authority` (Host) header value
405/// - `path`: the `:path` value (e.g., "/")
406/// - `stream_id`: stream identifier (must be odd for client-initiated, typically 1)
407#[must_use]
408pub fn build_get_request(host: &str, path: &str, stream_id: u32) -> Vec<u8> {
409    let encoder = HpackEncoder::new();
410    let headers = vec![
411        (":method", "GET"),
412        (":path", path),
413        (":scheme", "https"),
414        (":authority", host),
415        ("accept", "*/*"),
416    ];
417    let hpack_data = encoder.encode(&headers);
418
419    Http2Builder::new()
420        .frame(Http2FrameBuilder::settings())
421        .frame(Http2FrameBuilder::headers_frame_final(
422            stream_id, hpack_data,
423        ))
424        .build()
425}
426
427/// Build a complete HTTP/2 200 OK response as bytes.
428///
429/// Produces: SETTINGS ACK + HEADERS frame + optional DATA frame.
430///
431/// # Arguments
432/// - `stream_id`: the stream ID to respond on
433/// - `body`: optional response body; if `Some`, a DATA frame with `END_STREAM` is appended
434#[must_use]
435pub fn build_response_200(stream_id: u32, body: Option<&[u8]>) -> Vec<u8> {
436    let encoder = HpackEncoder::new();
437    let headers = vec![(":status", "200"), ("content-type", "application/json")];
438    let hpack_data = encoder.encode(&headers);
439
440    let headers_flags = if body.is_none() {
441        // END_HEADERS | END_STREAM if no body
442        flags::HEADERS_END_HEADERS | flags::HEADERS_END_STREAM
443    } else {
444        flags::HEADERS_END_HEADERS
445    };
446
447    let mut builder = Http2Builder::without_preface()
448        .frame(Http2FrameBuilder::settings_ack())
449        .frame(
450            Http2FrameBuilder::new(Http2FrameType::Headers)
451                .stream_id(stream_id)
452                .flags(headers_flags)
453                .payload(hpack_data),
454        );
455
456    if let Some(body_data) = body {
457        builder = builder.frame(Http2FrameBuilder::data_frame_final(
458            stream_id,
459            body_data.to_vec(),
460        ));
461    }
462
463    builder.build()
464}
465
466// ============================================================================
467// Tests
468// ============================================================================
469
470#[cfg(test)]
471mod tests {
472    use super::*;
473    use crate::layer::http2::frames::{
474        Http2Frame, parse_all_frames, parse_goaway, parse_rst_stream, parse_settings,
475        parse_window_update,
476    };
477
478    #[test]
479    fn test_build_settings_frame() {
480        let bytes = Http2FrameBuilder::settings().build();
481        assert_eq!(bytes.len(), 9); // empty payload
482        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
483        assert_eq!(frame.frame_type, Http2FrameType::Settings);
484        assert!(!frame.is_ack());
485        assert_eq!(frame.stream_id, 0);
486    }
487
488    #[test]
489    fn test_build_settings_ack() {
490        let bytes = Http2FrameBuilder::settings_ack().build();
491        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
492        assert_eq!(frame.frame_type, Http2FrameType::Settings);
493        assert!(frame.is_ack());
494    }
495
496    #[test]
497    fn test_build_settings_with_params() {
498        use crate::layer::http2::frames::settings_id;
499
500        let params = [
501            (settings_id::INITIAL_WINDOW_SIZE, 65535u32),
502            (settings_id::MAX_FRAME_SIZE, 16384),
503        ];
504        let bytes = Http2FrameBuilder::settings_with_params(&params).build();
505        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
506
507        let settings = parse_settings(frame.payload(&bytes));
508        assert_eq!(settings.len(), 2);
509        assert_eq!(settings[0], (settings_id::INITIAL_WINDOW_SIZE, 65535));
510        assert_eq!(settings[1], (settings_id::MAX_FRAME_SIZE, 16384));
511    }
512
513    #[test]
514    fn test_build_ping() {
515        let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
516        let bytes = Http2FrameBuilder::ping(data).build();
517        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
518        assert_eq!(frame.frame_type, Http2FrameType::Ping);
519        assert!(!frame.is_ack());
520        assert_eq!(frame.payload(&bytes), &data);
521    }
522
523    #[test]
524    fn test_build_ping_ack() {
525        let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
526        let bytes = Http2FrameBuilder::ping_ack(data).build();
527        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
528        assert!(frame.is_ack());
529        assert_eq!(frame.payload(&bytes), &data);
530    }
531
532    #[test]
533    fn test_build_goaway() {
534        use crate::layer::http2::frames::error_codes;
535        let bytes = Http2FrameBuilder::goaway(3, error_codes::NO_ERROR).build();
536        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
537        assert_eq!(frame.frame_type, Http2FrameType::GoAway);
538        let (last_id, error) = parse_goaway(frame.payload(&bytes)).unwrap();
539        assert_eq!(last_id, 3);
540        assert_eq!(error, error_codes::NO_ERROR);
541    }
542
543    #[test]
544    fn test_build_window_update() {
545        let bytes = Http2FrameBuilder::window_update(65535, 0).build();
546        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
547        assert_eq!(frame.frame_type, Http2FrameType::WindowUpdate);
548        let increment = parse_window_update(frame.payload(&bytes)).unwrap();
549        assert_eq!(increment, 65535);
550    }
551
552    #[test]
553    fn test_build_rst_stream() {
554        use crate::layer::http2::frames::error_codes;
555        let bytes = Http2FrameBuilder::rst_stream(1, error_codes::CANCEL).build();
556        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
557        assert_eq!(frame.frame_type, Http2FrameType::RstStream);
558        assert_eq!(frame.stream_id, 1);
559        let error = parse_rst_stream(frame.payload(&bytes)).unwrap();
560        assert_eq!(error, error_codes::CANCEL);
561    }
562
563    #[test]
564    fn test_build_headers_frame() {
565        let hpack_data = vec![0x82u8]; // ":method: GET"
566        let bytes = Http2FrameBuilder::headers_frame(1, hpack_data.clone()).build();
567        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
568        assert_eq!(frame.frame_type, Http2FrameType::Headers);
569        assert!(frame.is_end_headers());
570        assert!(!frame.is_end_stream());
571        assert_eq!(frame.payload(&bytes), &hpack_data);
572    }
573
574    #[test]
575    fn test_build_headers_final() {
576        let hpack_data = vec![0x82u8];
577        let bytes = Http2FrameBuilder::headers_frame_final(1, hpack_data).build();
578        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
579        assert!(frame.is_end_headers());
580        assert!(frame.is_end_stream());
581    }
582
583    #[test]
584    fn test_build_data_frame() {
585        let data = b"Hello, HTTP/2!";
586        let bytes = Http2FrameBuilder::data_frame(1, data.to_vec()).build();
587        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
588        assert_eq!(frame.frame_type, Http2FrameType::Data);
589        assert!(!frame.is_end_stream());
590        assert_eq!(frame.payload(&bytes), data);
591    }
592
593    #[test]
594    fn test_build_data_frame_final() {
595        let data = b"last chunk";
596        let bytes = Http2FrameBuilder::data_frame_final(1, data.to_vec()).build();
597        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
598        assert!(frame.is_end_stream());
599    }
600
601    #[test]
602    fn test_http2_builder_with_preface() {
603        let bytes = Http2Builder::new()
604            .frame(Http2FrameBuilder::settings())
605            .frame(Http2FrameBuilder::settings_ack())
606            .build();
607
608        // Should start with preface
609        assert!(bytes.starts_with(super::super::frames::HTTP2_PREFACE));
610
611        let frames = parse_all_frames(&bytes);
612        assert_eq!(frames.len(), 2);
613        assert_eq!(frames[0].frame_type, Http2FrameType::Settings);
614        assert_eq!(frames[1].frame_type, Http2FrameType::Settings);
615        assert!(frames[1].is_ack());
616    }
617
618    #[test]
619    fn test_http2_builder_without_preface() {
620        let bytes = Http2Builder::without_preface()
621            .frame(Http2FrameBuilder::settings_ack())
622            .build();
623
624        assert!(!bytes.starts_with(super::super::frames::HTTP2_PREFACE));
625        let frames = parse_all_frames(&bytes);
626        assert_eq!(frames.len(), 1);
627    }
628
629    #[test]
630    fn test_http2_builder_header_size() {
631        let builder = Http2Builder::new()
632            .frame(Http2FrameBuilder::settings()) // 9 bytes
633            .frame(Http2FrameBuilder::settings_ack()); // 9 bytes
634
635        // 24 (preface) + 9 + 9 = 42
636        assert_eq!(builder.header_size(), 42);
637        assert_eq!(builder.build().len(), 42);
638    }
639
640    #[test]
641    fn test_build_get_request() {
642        let bytes = build_get_request("example.com", "/", 1);
643        assert!(bytes.starts_with(super::super::frames::HTTP2_PREFACE));
644
645        let frames = parse_all_frames(&bytes);
646        // Should have at least 2 frames: SETTINGS + HEADERS
647        assert!(frames.len() >= 2);
648        assert_eq!(frames[0].frame_type, Http2FrameType::Settings);
649        assert_eq!(frames[1].frame_type, Http2FrameType::Headers);
650        assert!(frames[1].is_end_headers());
651        assert!(frames[1].is_end_stream());
652        assert_eq!(frames[1].stream_id, 1);
653    }
654
655    #[test]
656    fn test_build_response_200_no_body() {
657        let bytes = build_response_200(1, None);
658        let frames = parse_all_frames(&bytes);
659        assert!(frames.len() >= 2);
660        assert_eq!(frames[0].frame_type, Http2FrameType::Settings);
661        assert!(frames[0].is_ack());
662        assert_eq!(frames[1].frame_type, Http2FrameType::Headers);
663    }
664
665    #[test]
666    fn test_build_response_200_with_body() {
667        let body = b"{\"ok\": true}";
668        let bytes = build_response_200(1, Some(body));
669        let frames = parse_all_frames(&bytes);
670        assert!(frames.len() >= 3);
671        assert_eq!(frames[2].frame_type, Http2FrameType::Data);
672        assert!(frames[2].is_end_stream());
673        assert_eq!(frames[2].payload(&bytes), body);
674    }
675
676    #[test]
677    fn test_end_stream_and_end_headers_helpers() {
678        let builder = Http2FrameBuilder::new(Http2FrameType::Headers)
679            .stream_id(1)
680            .end_stream()
681            .end_headers()
682            .payload(vec![0x82]);
683
684        let bytes = builder.build();
685        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
686        assert!(frame.is_end_stream());
687        assert!(frame.is_end_headers());
688    }
689
690    #[test]
691    fn test_priority_frame() {
692        let bytes = Http2FrameBuilder::priority_frame(1, false, 0, 15).build();
693        let frame = Http2Frame::parse_at(&bytes, 0).unwrap();
694        assert_eq!(frame.frame_type, Http2FrameType::Priority);
695        assert_eq!(frame.stream_id, 1);
696        assert_eq!(frame.length, 5);
697    }
698}