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}
34
35#[derive(Debug)]
38pub struct Http11HeadersReadOutput {
39 pub response: HttpResponse,
41 pub remaining: Vec<u8>,
43 pub keep_alive: bool,
45}
46
47#[derive(Debug, Default)]
49pub struct Http11HeadersRead {
50 buf: Vec<u8>,
51}
52
53impl HttpCoroutine for Http11HeadersRead {
54 type Yield = HttpYield;
55 type Return = Result<Http11HeadersReadOutput, Http11HeadersReadError>;
56
57 fn resume(&mut self, arg: Option<&[u8]>) -> HttpCoroutineState<Self::Yield, Self::Return> {
58 match arg {
59 Some(&[]) => {
60 return HttpCoroutineState::Complete(Err(Http11HeadersReadError::Eof));
61 }
62 Some(data) => self.buf.extend_from_slice(data),
63 None => {}
64 }
65
66 let mut headers = [EMPTY_HEADER; 64];
67 let mut parsed = Response::new(&mut headers);
68
69 let header_end = match parsed.parse(&self.buf) {
70 Ok(Status::Complete(n)) => n,
71 Ok(Status::Partial) => {
72 trace!("received incomplete headers");
73 return HttpCoroutineState::Yielded(HttpYield::WantsRead);
74 }
75 Err(err) => {
76 return HttpCoroutineState::Complete(Err(
77 Http11HeadersReadError::ParseResponseHeaders(err),
78 ));
79 }
80 };
81
82 let mut builder = HttpResponseBuilder::default();
83 let is_http10 = matches!(parsed.version, Some(0));
84 builder.version = if is_http10 { HTTP_10 } else { HTTP_11 }.into();
85 if let Some(code) = parsed.code {
86 builder.status = Some(HttpStatusCode(code));
87 }
88 for header in parsed.headers.iter() {
89 builder.header(header.name, header.value);
90 }
91
92 let keep_alive = match builder.get_header(HTTP_CONNECTION) {
93 Some(conn) => !conn.eq_ignore_ascii_case("close"),
94 None => !is_http10,
95 };
96
97 debug!("received complete headers");
98 trace!("{builder:?}");
99
100 let response = builder.build(Vec::new());
101 let remaining = self.buf.split_off(header_end);
102
103 HttpCoroutineState::Complete(Ok(Http11HeadersReadOutput {
104 response,
105 remaining,
106 keep_alive,
107 }))
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use crate::{coroutine::*, rfc9112::read_headers::*};
114
115 #[test]
116 fn parses_complete_head() {
117 let mut coroutine = Http11HeadersRead::default();
118 let reply = b"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nbody";
119
120 let out = expect_complete_ok(&mut coroutine, Some(reply));
121 assert_eq!(*out.response.status, 200);
122 assert_eq!(out.response.version, "HTTP/1.1");
123 assert_eq!(out.response.header("content-type"), Some("text/plain"));
124 assert_eq!(out.remaining, b"body");
125 }
126
127 #[test]
128 fn incomplete_head_wants_read() {
129 let mut coroutine = Http11HeadersRead::default();
130 expect_wants_read(&mut coroutine, Some(b"HTTP/1.1 200 OK\r\n"));
131 }
132
133 #[test]
134 fn eof_returns_eof_error() {
135 let mut coroutine = Http11HeadersRead::default();
136 let err = expect_complete_err(&mut coroutine, Some(b""));
137 assert!(matches!(err, Http11HeadersReadError::Eof));
138 }
139
140 #[test]
141 fn http10_keep_alive_defaults_false() {
142 let mut coroutine = Http11HeadersRead::default();
143 let reply = b"HTTP/1.0 200 OK\r\n\r\n";
144 let out = expect_complete_ok(&mut coroutine, Some(reply));
145 assert!(!out.keep_alive);
146 assert_eq!(out.response.version, "HTTP/1.0");
147 }
148
149 #[test]
150 fn http11_keep_alive_defaults_true() {
151 let mut coroutine = Http11HeadersRead::default();
152 let reply = b"HTTP/1.1 200 OK\r\n\r\n";
153 let out = expect_complete_ok(&mut coroutine, Some(reply));
154 assert!(out.keep_alive);
155 }
156
157 #[test]
158 fn connection_close_overrides_default() {
159 let mut coroutine = Http11HeadersRead::default();
160 let reply = b"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
161 let out = expect_complete_ok(&mut coroutine, Some(reply));
162 assert!(!out.keep_alive);
163 }
164
165 fn expect_wants_read(cor: &mut Http11HeadersRead, arg: Option<&[u8]>) {
166 match cor.resume(arg) {
167 HttpCoroutineState::Yielded(HttpYield::WantsRead) => {}
168 state => panic!("expected WantsRead, got {state:?}"),
169 }
170 }
171
172 fn expect_complete_ok(
173 cor: &mut Http11HeadersRead,
174 arg: Option<&[u8]>,
175 ) -> Http11HeadersReadOutput {
176 match cor.resume(arg) {
177 HttpCoroutineState::Complete(Ok(out)) => out,
178 state => panic!("expected Complete(Ok), got {state:?}"),
179 }
180 }
181
182 fn expect_complete_err(
183 cor: &mut Http11HeadersRead,
184 arg: Option<&[u8]>,
185 ) -> Http11HeadersReadError {
186 match cor.resume(arg) {
187 HttpCoroutineState::Complete(Err(err)) => err,
188 state => panic!("expected Complete(Err), got {state:?}"),
189 }
190 }
191}