1use crate::Error;
12
13pub const HEADER_LEN: usize = 9;
17
18pub const DEFAULT_MAX_FRAME: usize = 16_384;
21
22pub const CLIENT_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum FrameType {
30 Data,
31 Headers,
32 Priority,
33 RstStream,
34 Settings,
35 PushPromise,
36 Ping,
37 GoAway,
38 WindowUpdate,
39 Continuation,
40 Unknown(u8),
44}
45
46impl FrameType {
47 pub fn from_byte(byte: u8) -> Self {
48 match byte {
49 0x0 => Self::Data,
50 0x1 => Self::Headers,
51 0x2 => Self::Priority,
52 0x3 => Self::RstStream,
53 0x4 => Self::Settings,
54 0x5 => Self::PushPromise,
55 0x6 => Self::Ping,
56 0x7 => Self::GoAway,
57 0x8 => Self::WindowUpdate,
58 0x9 => Self::Continuation,
59 other => Self::Unknown(other),
60 }
61 }
62
63 pub fn to_byte(self) -> u8 {
64 match self {
65 Self::Data => 0x0,
66 Self::Headers => 0x1,
67 Self::Priority => 0x2,
68 Self::RstStream => 0x3,
69 Self::Settings => 0x4,
70 Self::PushPromise => 0x5,
71 Self::Ping => 0x6,
72 Self::GoAway => 0x7,
73 Self::WindowUpdate => 0x8,
74 Self::Continuation => 0x9,
75 Self::Unknown(other) => other,
76 }
77 }
78}
79
80pub mod flags {
81 pub const END_STREAM: u8 = 0x1;
82 pub const ACK: u8 = 0x1;
83 pub const END_HEADERS: u8 = 0x4;
84 pub const PADDED: u8 = 0x8;
85 pub const PRIORITY: u8 = 0x20;
86}
87
88pub mod settings {
89 pub const HEADER_TABLE_SIZE: u16 = 0x1;
90 pub const ENABLE_PUSH: u16 = 0x2;
91 pub const MAX_CONCURRENT_STREAMS: u16 = 0x3;
92 pub const INITIAL_WINDOW_SIZE: u16 = 0x4;
93 pub const MAX_FRAME_SIZE: u16 = 0x5;
94 pub const MAX_HEADER_LIST_SIZE: u16 = 0x6;
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub struct FrameHeader {
99 pub length: usize,
100 pub kind: FrameType,
101 pub flags: u8,
102 pub stream: u32,
103}
104
105impl FrameHeader {
106 pub fn parse(bytes: &[u8]) -> Result<Self, Error> {
108 let bytes = bytes.get(..HEADER_LEN).ok_or(Error::Incomplete)?;
109 Ok(Self {
110 length: u32::from_be_bytes([0, bytes[0], bytes[1], bytes[2]]) as usize,
111 kind: FrameType::from_byte(bytes[3]),
112 flags: bytes[4],
113 stream: u32::from_be_bytes([bytes[5], bytes[6], bytes[7], bytes[8]]) & 0x7fff_ffff,
116 })
117 }
118
119 pub fn write(&self, out: &mut [u8]) -> Result<usize, Error> {
120 let out = out.get_mut(..HEADER_LEN).ok_or(Error::BufferTooSmall)?;
121 let length = (self.length as u32).to_be_bytes();
122 out[0..3].copy_from_slice(&length[1..4]);
123 out[3] = self.kind.to_byte();
124 out[4] = self.flags;
125 out[5..9].copy_from_slice(&(self.stream & 0x7fff_ffff).to_be_bytes());
126 Ok(HEADER_LEN)
127 }
128
129 pub fn has(&self, flag: u8) -> bool {
130 self.flags & flag != 0
131 }
132}
133
134pub fn write_frame(
136 kind: FrameType,
137 flags: u8,
138 stream: u32,
139 payload: &[u8],
140 out: &mut [u8],
141) -> Result<usize, Error> {
142 let header = FrameHeader {
143 length: payload.len(),
144 kind,
145 flags,
146 stream,
147 };
148 header.write(out)?;
149 let end = HEADER_LEN + payload.len();
150 out.get_mut(HEADER_LEN..end)
151 .ok_or(Error::BufferTooSmall)?
152 .copy_from_slice(payload);
153 Ok(end)
154}
155
156pub fn strip_padding(payload: &[u8], flags: u8) -> Result<&[u8], Error> {
162 if flags & flags::PADDED == 0 {
163 return Ok(payload);
164 }
165 let pad_length = *payload.first().ok_or(Error::Protocol)? as usize;
166 let body = payload.get(1..).ok_or(Error::Protocol)?;
167 let end = body.len().checked_sub(pad_length).ok_or(Error::Protocol)?;
168 Ok(&body[..end])
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn parses_the_settings_frame_a_server_opens_with() {
177 let bytes = [0x00, 0x00, 0x12, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00];
179 let header = FrameHeader::parse(&bytes).unwrap();
180 assert_eq!(header.length, 18);
181 assert_eq!(header.kind, FrameType::Settings);
182 assert_eq!(header.stream, 0);
183 assert!(!header.has(flags::ACK));
184 }
185
186 #[test]
187 fn the_reserved_bit_of_a_stream_id_is_ignored() {
188 let bytes = [0x00, 0x00, 0x00, 0x01, 0x04, 0x80, 0x00, 0x00, 0x01];
190 assert_eq!(FrameHeader::parse(&bytes).unwrap().stream, 1);
191 }
192
193 #[test]
194 fn a_header_round_trips() {
195 let header = FrameHeader {
196 length: 16_383,
197 kind: FrameType::Data,
198 flags: flags::END_STREAM,
199 stream: 3,
200 };
201 let mut out = [0u8; HEADER_LEN];
202 header.write(&mut out).unwrap();
203 assert_eq!(FrameHeader::parse(&out).unwrap(), header);
204 }
205
206 #[test]
207 fn an_unknown_frame_type_is_carried_rather_than_rejected() {
208 let bytes = [0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00];
211 assert_eq!(
212 FrameHeader::parse(&bytes).unwrap().kind,
213 FrameType::Unknown(0x63)
214 );
215 }
216
217 #[test]
218 fn a_short_buffer_is_incomplete_rather_than_a_guess() {
219 assert_eq!(FrameHeader::parse(&[0x00, 0x00]), Err(Error::Incomplete));
220 }
221
222 #[test]
223 fn padding_is_stripped_from_both_ends() {
224 let payload = [0x02, b'h', b'i', 0x00, 0x00];
226 assert_eq!(strip_padding(&payload, flags::PADDED).unwrap(), b"hi");
227 assert_eq!(strip_padding(&payload, 0).unwrap(), &payload);
229 assert_eq!(
231 strip_padding(&[0x09, b'h'], flags::PADDED),
232 Err(Error::Protocol)
233 );
234 }
235}