io_http/rfc9112/
read_headers.rs1use alloc::vec::Vec;
8
9use httparse::{EMPTY_HEADER, Error as HttparseError, Response, Status};
10use log::trace;
11use thiserror::Error;
12
13use crate::{
14 coroutine::*,
15 rfc1945::version::HTTP_10,
16 rfc9110::{
17 headers::CONNECTION,
18 response::{HttpResponse, ResponseBuilder},
19 status::StatusCode,
20 },
21 rfc9112::version::HTTP_11,
22};
23
24#[derive(Debug, Error)]
26pub enum Http11ReadHeadersError {
27 #[error("HTTP/1.X read headers failed: reached unexpected EOF before headers were complete")]
28 Eof,
29 #[error("HTTP/1.X read headers failed: parse response headers: {0}")]
30 ParseResponseHeaders(HttparseError),
31}
32
33#[derive(Debug)]
35pub struct Http11ReadHeadersOutput {
36 pub response: HttpResponse,
37 pub remaining: Vec<u8>,
38 pub keep_alive: bool,
39}
40
41#[derive(Debug, Default)]
43pub struct Http11ReadHeaders {
44 buf: Vec<u8>,
45}
46
47impl HttpCoroutine for Http11ReadHeaders {
48 type Yield = HttpYield;
49 type Return = Result<Http11ReadHeadersOutput, Http11ReadHeadersError>;
50
51 fn resume(&mut self, arg: Option<&[u8]>) -> HttpCoroutineState<Self::Yield, Self::Return> {
52 match arg {
53 Some(&[]) => {
54 return HttpCoroutineState::Complete(Err(Http11ReadHeadersError::Eof));
55 }
56 Some(data) => self.buf.extend_from_slice(data),
57 None => {}
58 }
59
60 let mut headers = [EMPTY_HEADER; 64];
61 let mut parsed = Response::new(&mut headers);
62
63 let header_end = match parsed.parse(&self.buf) {
64 Ok(Status::Complete(n)) => n,
65 Ok(Status::Partial) => {
66 trace!("received incomplete headers");
67 return HttpCoroutineState::Yielded(HttpYield::WantsRead);
68 }
69 Err(err) => {
70 return HttpCoroutineState::Complete(Err(
71 Http11ReadHeadersError::ParseResponseHeaders(err),
72 ));
73 }
74 };
75
76 let mut builder = ResponseBuilder::default();
77 let is_http10 = matches!(parsed.version, Some(0));
78 builder.version = if is_http10 { HTTP_10 } else { HTTP_11 }.into();
79 if let Some(code) = parsed.code {
80 builder.status = Some(StatusCode(code));
81 }
82 for header in parsed.headers.iter() {
83 builder.header(header.name, header.value);
84 }
85
86 let keep_alive = match builder.get_header(CONNECTION) {
87 Some(conn) => !conn.eq_ignore_ascii_case("close"),
88 None => !is_http10,
89 };
90
91 trace!("received complete headers: {builder:?}");
92
93 let response = builder.build(Vec::new());
94 let remaining = self.buf.split_off(header_end);
95
96 HttpCoroutineState::Complete(Ok(Http11ReadHeadersOutput {
97 response,
98 remaining,
99 keep_alive,
100 }))
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn parses_complete_head() {
110 let mut coroutine = Http11ReadHeaders::default();
111 let reply = b"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nbody";
112
113 let out = expect_complete_ok(&mut coroutine, Some(reply));
114 assert_eq!(*out.response.status, 200);
115 assert_eq!(out.response.version, "HTTP/1.1");
116 assert_eq!(out.response.header("content-type"), Some("text/plain"));
117 assert_eq!(out.remaining, b"body");
118 }
119
120 #[test]
121 fn incomplete_head_wants_read() {
122 let mut coroutine = Http11ReadHeaders::default();
123 expect_wants_read(&mut coroutine, Some(b"HTTP/1.1 200 OK\r\n"));
124 }
125
126 #[test]
127 fn eof_returns_eof_error() {
128 let mut coroutine = Http11ReadHeaders::default();
129 let err = expect_complete_err(&mut coroutine, Some(b""));
130 assert!(matches!(err, Http11ReadHeadersError::Eof));
131 }
132
133 #[test]
134 fn http10_keep_alive_defaults_false() {
135 let mut coroutine = Http11ReadHeaders::default();
136 let reply = b"HTTP/1.0 200 OK\r\n\r\n";
137 let out = expect_complete_ok(&mut coroutine, Some(reply));
138 assert!(!out.keep_alive);
139 assert_eq!(out.response.version, "HTTP/1.0");
140 }
141
142 #[test]
143 fn http11_keep_alive_defaults_true() {
144 let mut coroutine = Http11ReadHeaders::default();
145 let reply = b"HTTP/1.1 200 OK\r\n\r\n";
146 let out = expect_complete_ok(&mut coroutine, Some(reply));
147 assert!(out.keep_alive);
148 }
149
150 #[test]
151 fn connection_close_overrides_default() {
152 let mut coroutine = Http11ReadHeaders::default();
153 let reply = b"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
154 let out = expect_complete_ok(&mut coroutine, Some(reply));
155 assert!(!out.keep_alive);
156 }
157
158 fn expect_wants_read(cor: &mut Http11ReadHeaders, arg: Option<&[u8]>) {
161 match cor.resume(arg) {
162 HttpCoroutineState::Yielded(HttpYield::WantsRead) => {}
163 state => panic!("expected WantsRead, got {state:?}"),
164 }
165 }
166
167 fn expect_complete_ok(
168 cor: &mut Http11ReadHeaders,
169 arg: Option<&[u8]>,
170 ) -> Http11ReadHeadersOutput {
171 match cor.resume(arg) {
172 HttpCoroutineState::Complete(Ok(out)) => out,
173 state => panic!("expected Complete(Ok), got {state:?}"),
174 }
175 }
176
177 fn expect_complete_err(
178 cor: &mut Http11ReadHeaders,
179 arg: Option<&[u8]>,
180 ) -> Http11ReadHeadersError {
181 match cor.resume(arg) {
182 HttpCoroutineState::Complete(Err(err)) => err,
183 state => panic!("expected Complete(Err), got {state:?}"),
184 }
185 }
186}