io_http/rfc9112/
read_headers.rs1use alloc::vec::Vec;
8
9use httparse::{EMPTY_HEADER, Error as HttparseError, Response, Status};
10use log::{debug, trace};
11use thiserror::Error;
12
13use crate::{
14 coroutine::*,
15 rfc1945::version::HTTP_10,
16 rfc9110::{
17 headers::HTTP_CONNECTION,
18 response::{HttpResponse, HttpResponseBuilder},
19 status::HttpStatusCode,
20 },
21 rfc9112::version::HTTP_11,
22};
23
24#[derive(Debug, Error)]
26pub enum Http11HeadersReadError {
27 #[error("HTTP/1.X read headers failed: reached unexpected EOF before headers were complete")]
29 Eof,
30 #[error("HTTP/1.X read headers failed: parse response headers: {0}")]
32 ParseResponseHeaders(HttparseError),
33 #[error("HTTP/1.X read headers failed: response head exceeds {0} bytes")]
35 TooLarge(usize),
36}
37
38#[derive(Debug)]
41pub struct Http11HeadersReadOutput {
42 pub response: HttpResponse,
44 pub remaining: Vec<u8>,
46 pub keep_alive: bool,
48}
49
50pub const HTTP_DEFAULT_MAX_HEADERS_LEN: usize = 64 * 1024;
52
53#[derive(Debug)]
55pub struct Http11HeadersRead {
56 pub max_len: usize,
61 buf: Vec<u8>,
62}
63
64impl Default for Http11HeadersRead {
65 fn default() -> Self {
66 Self {
67 max_len: HTTP_DEFAULT_MAX_HEADERS_LEN,
68 buf: Vec::new(),
69 }
70 }
71}
72
73impl HttpCoroutine for Http11HeadersRead {
74 type Yield = HttpYield;
75 type Return = Result<Http11HeadersReadOutput, Http11HeadersReadError>;
76
77 fn resume(&mut self, arg: Option<&[u8]>) -> HttpCoroutineState<Self::Yield, Self::Return> {
78 match arg {
79 Some(&[]) => {
80 return HttpCoroutineState::Complete(Err(Http11HeadersReadError::Eof));
81 }
82 Some(data) => self.buf.extend_from_slice(data),
83 None => {}
84 }
85
86 let mut headers = [EMPTY_HEADER; 64];
87 let mut parsed = Response::new(&mut headers);
88
89 let header_end = match parsed.parse(&self.buf) {
90 Ok(Status::Complete(n)) if n > self.max_len => {
91 let err = Http11HeadersReadError::TooLarge(self.max_len);
92 return HttpCoroutineState::Complete(Err(err));
93 }
94 Ok(Status::Complete(n)) => n,
95 Ok(Status::Partial) if self.buf.len() > self.max_len => {
96 let err = Http11HeadersReadError::TooLarge(self.max_len);
97 return HttpCoroutineState::Complete(Err(err));
98 }
99 Ok(Status::Partial) => {
100 trace!("received incomplete headers");
101 return HttpCoroutineState::Yielded(HttpYield::WantsRead);
102 }
103 Err(err) => {
104 return HttpCoroutineState::Complete(Err(
105 Http11HeadersReadError::ParseResponseHeaders(err),
106 ));
107 }
108 };
109
110 let mut builder = HttpResponseBuilder::default();
111 let is_http10 = matches!(parsed.version, Some(0));
112 builder.version = if is_http10 { HTTP_10 } else { HTTP_11 }.into();
113 if let Some(code) = parsed.code {
114 builder.status = Some(HttpStatusCode(code));
115 }
116 for header in parsed.headers.iter() {
117 builder.header(header.name, header.value);
118 }
119
120 let keep_alive = match builder.get_header(HTTP_CONNECTION) {
121 Some(conn) => !conn.eq_ignore_ascii_case("close"),
122 None => !is_http10,
123 };
124
125 debug!("received complete headers");
126 trace!("{builder:?}");
127
128 let response = builder.build(Vec::new());
129 let remaining = self.buf.split_off(header_end);
130
131 HttpCoroutineState::Complete(Ok(Http11HeadersReadOutput {
132 response,
133 remaining,
134 keep_alive,
135 }))
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use crate::rfc9112::read_headers::*;
142
143 #[test]
144 fn parses_complete_head() {
145 let mut coroutine = Http11HeadersRead::default();
146 let reply = b"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nbody";
147
148 let out = expect_complete_ok(&mut coroutine, Some(reply));
149 assert_eq!(*out.response.status, 200);
150 assert_eq!(out.response.version, "HTTP/1.1");
151 assert_eq!(out.response.header("content-type"), Some("text/plain"));
152 assert_eq!(out.remaining, b"body");
153 }
154
155 #[test]
156 fn incomplete_head_wants_read() {
157 let mut coroutine = Http11HeadersRead::default();
158 expect_wants_read(&mut coroutine, Some(b"HTTP/1.1 200 OK\r\n"));
159 }
160
161 #[test]
162 fn eof_returns_eof_error() {
163 let mut coroutine = Http11HeadersRead::default();
164 let err = expect_complete_err(&mut coroutine, Some(b""));
165 assert!(matches!(err, Http11HeadersReadError::Eof));
166 }
167
168 #[test]
169 fn http10_keep_alive_defaults_false() {
170 let mut coroutine = Http11HeadersRead::default();
171 let reply = b"HTTP/1.0 200 OK\r\n\r\n";
172 let out = expect_complete_ok(&mut coroutine, Some(reply));
173 assert!(!out.keep_alive);
174 assert_eq!(out.response.version, "HTTP/1.0");
175 }
176
177 #[test]
178 fn http11_keep_alive_defaults_true() {
179 let mut coroutine = Http11HeadersRead::default();
180 let reply = b"HTTP/1.1 200 OK\r\n\r\n";
181 let out = expect_complete_ok(&mut coroutine, Some(reply));
182 assert!(out.keep_alive);
183 }
184
185 #[test]
186 fn connection_close_overrides_default() {
187 let mut coroutine = Http11HeadersRead::default();
188 let reply = b"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
189 let out = expect_complete_ok(&mut coroutine, Some(reply));
190 assert!(!out.keep_alive);
191 }
192
193 #[test]
194 fn partial_head_over_limit_errors() {
195 let mut coroutine = Http11HeadersRead {
196 max_len: 32,
197 ..Http11HeadersRead::default()
198 };
199 expect_wants_read(&mut coroutine, Some(b"HTTP/1.1 200 OK\r\n"));
200 let err = expect_complete_err(&mut coroutine, Some(b"X-Filler: aaaaaaaaaaaaaaaa\r\n"));
201 assert!(matches!(err, Http11HeadersReadError::TooLarge(32)));
202 }
203
204 #[test]
205 fn complete_head_over_limit_errors() {
206 let mut coroutine = Http11HeadersRead {
207 max_len: 16,
208 ..Http11HeadersRead::default()
209 };
210 let err = expect_complete_err(&mut coroutine, Some(b"HTTP/1.1 200 OK\r\n\r\n"));
211 assert!(matches!(err, Http11HeadersReadError::TooLarge(16)));
212 }
213
214 fn expect_wants_read(cor: &mut Http11HeadersRead, arg: Option<&[u8]>) {
215 match cor.resume(arg) {
216 HttpCoroutineState::Yielded(HttpYield::WantsRead) => {}
217 state => panic!("expected WantsRead, got {state:?}"),
218 }
219 }
220
221 fn expect_complete_ok(
222 cor: &mut Http11HeadersRead,
223 arg: Option<&[u8]>,
224 ) -> Http11HeadersReadOutput {
225 match cor.resume(arg) {
226 HttpCoroutineState::Complete(Ok(out)) => out,
227 state => panic!("expected Complete(Ok), got {state:?}"),
228 }
229 }
230
231 fn expect_complete_err(
232 cor: &mut Http11HeadersRead,
233 arg: Option<&[u8]>,
234 ) -> Http11HeadersReadError {
235 match cor.resume(arg) {
236 HttpCoroutineState::Complete(Err(err)) => err,
237 state => panic!("expected Complete(Err), got {state:?}"),
238 }
239 }
240}