1use core::mem;
55
56use alloc::{borrow::ToOwned, string::String, vec::Vec};
57
58use httparse::Error as HttparseError;
59use log::trace;
60use thiserror::Error;
61use url::Url;
62
63use crate::{
64 coroutine::*,
65 rfc9110::{
66 headers::{CONTENT_LENGTH, LOCATION},
67 request::HttpRequest,
68 response::HttpResponse,
69 send::{HttpSendOutput, HttpSendYield},
70 },
71 rfc9112::read_headers::{Http11ReadHeaders, Http11ReadHeadersError},
72};
73
74#[derive(Debug, Error)]
76pub enum Http10SendError {
77 #[error("HTTP/1.0 send failed: reached unexpected EOF")]
78 Eof,
79 #[error("HTTP/1.0 send failed: parse response headers: {0}")]
80 ParseResponseHeaders(HttparseError),
81 #[error("HTTP/1.0 send failed: invalid content length `{0}`")]
82 InvalidContentLength(String),
83}
84
85impl From<Http11ReadHeadersError> for Http10SendError {
86 fn from(err: Http11ReadHeadersError) -> Self {
87 match err {
88 Http11ReadHeadersError::Eof => Self::Eof,
89 Http11ReadHeadersError::ParseResponseHeaders(e) => Self::ParseResponseHeaders(e),
90 }
91 }
92}
93
94#[derive(Debug)]
96pub struct Http10Send {
97 request_url: Url,
98 state: State,
99 wants_write: Option<Vec<u8>>,
100 keep_alive: bool,
101 response: Option<HttpResponse>,
102 buf: Vec<u8>,
103}
104
105impl Http10Send {
106 pub fn new(req: HttpRequest) -> Self {
109 trace!("prepare request to be sent: {req:?}");
110
111 let request_url = req.url.clone();
112 let bytes = req.to_http_10_vec();
113
114 Self {
115 request_url,
116 state: State::ReadHeaders(Http11ReadHeaders::default()),
117 wants_write: Some(bytes),
118 keep_alive: false,
119 response: None,
120 buf: Vec::new(),
121 }
122 }
123
124 fn finish(
125 &self,
126 response: HttpResponse,
127 remaining: Vec<u8>,
128 ) -> HttpCoroutineState<HttpSendYield, Result<HttpSendOutput, Http10SendError>> {
129 let keep_alive = self.keep_alive;
130
131 if response.status.is_redirection() {
132 if let Some(location) = response.header(LOCATION) {
133 if let Ok(url) = self.request_url.join(location) {
134 let same_scheme = self.request_url.scheme() == url.scheme();
135 let same_host = self.request_url.host() == url.host()
136 && self.request_url.port() == url.port();
137 let same_origin = same_scheme && same_host;
138
139 return HttpCoroutineState::Yielded(HttpSendYield::WantsRedirect {
140 url,
141 response,
142 keep_alive,
143 same_origin,
144 });
145 }
146 }
147 }
148
149 HttpCoroutineState::Complete(Ok(HttpSendOutput {
150 response,
151 remaining,
152 keep_alive,
153 }))
154 }
155}
156
157impl HttpCoroutine for Http10Send {
158 type Yield = HttpSendYield;
159 type Return = Result<HttpSendOutput, Http10SendError>;
160
161 fn resume(&mut self, mut arg: Option<&[u8]>) -> HttpCoroutineState<Self::Yield, Self::Return> {
162 loop {
163 if let Some(bytes) = self.wants_write.take() {
164 return HttpCoroutineState::Yielded(HttpSendYield::WantsWrite(bytes));
165 }
166
167 match &mut self.state {
168 State::ReadHeaders(rh) => match rh.resume(arg.take()) {
169 HttpCoroutineState::Yielded(HttpYield::WantsRead) => {
170 return HttpCoroutineState::Yielded(HttpSendYield::WantsRead);
171 }
172 HttpCoroutineState::Yielded(HttpYield::WantsWrite(_)) => {
173 unreachable!("Http11ReadHeaders never writes");
174 }
175 HttpCoroutineState::Complete(Err(err)) => {
176 return HttpCoroutineState::Complete(Err(err.into()));
177 }
178 HttpCoroutineState::Complete(Ok(out)) => {
179 let response = out.response;
180 self.keep_alive = out.keep_alive;
181 let status = *response.status;
182
183 if status == 204 || status == 304 {
184 return self.finish(response, out.remaining);
185 }
186
187 if let Some(len_str) = response.header(CONTENT_LENGTH) {
188 let len_str = len_str.trim();
189 let Ok(len) = len_str.parse::<usize>() else {
190 let err = Http10SendError::InvalidContentLength(len_str.to_owned());
191 return HttpCoroutineState::Complete(Err(err));
192 };
193 self.buf = out.remaining;
194 self.response = Some(response);
195 trace!("reading body of length {len}");
196 self.state = State::BodyLength(len);
197 continue;
198 }
199
200 self.buf = out.remaining;
201 self.response = Some(response);
202 trace!("reading body until connection closes");
203 self.state = State::BodyEof;
204 }
205 },
206 State::BodyLength(len) => {
207 if let Some(data) = arg.take() {
208 self.buf.extend_from_slice(data);
209 }
210
211 if *len > self.buf.len() {
212 trace!("received incomplete body {len}/{}", self.buf.len());
213 return HttpCoroutineState::Yielded(HttpSendYield::WantsRead);
214 }
215
216 let body = self.buf.drain(..*len).collect();
217 let remaining = mem::take(&mut self.buf);
218 let mut response = self.response.take().expect("response missing");
219 response.body = body;
220 return self.finish(response, remaining);
221 }
222 State::BodyEof => match arg.take() {
223 Some(&[]) => {
224 let buf = mem::take(&mut self.buf);
225 let mut response = self.response.take().expect("response missing");
226 response.body = buf;
227 return self.finish(response, Vec::new());
228 }
229 Some(data) => {
230 self.buf.extend_from_slice(data);
231 return HttpCoroutineState::Yielded(HttpSendYield::WantsRead);
232 }
233 None => {
234 return HttpCoroutineState::Yielded(HttpSendYield::WantsRead);
235 }
236 },
237 }
238 }
239 }
240}
241
242#[derive(Debug)]
243enum State {
244 ReadHeaders(Http11ReadHeaders),
245 BodyLength(usize),
246 BodyEof,
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[test]
254 fn body_length_completes() {
255 let req = HttpRequest::get("http://example.com".try_into().unwrap());
256 let mut coroutine = Http10Send::new(req);
257
258 let bytes = expect_wants_write(&mut coroutine, None);
259 assert_eq!(bytes, b"GET / HTTP/1.0\r\ncontent-length: 0\r\n\r\n");
260
261 expect_wants_read(&mut coroutine, None);
262
263 let reply = b"HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\nhello";
264 let out = expect_complete_ok(&mut coroutine, Some(reply));
265 assert_eq!(out.response.version, "HTTP/1.0");
266 assert_eq!(*out.response.status, 200);
267 assert_eq!(out.response.body, b"hello");
268 assert!(!out.keep_alive);
269 }
270
271 #[test]
272 fn body_eof_completes() {
273 let req = HttpRequest::get("http://example.com".try_into().unwrap());
274 let mut coroutine = Http10Send::new(req);
275
276 expect_wants_write(&mut coroutine, None);
277 expect_wants_read(&mut coroutine, None);
278 expect_wants_read(&mut coroutine, Some(b"HTTP/1.0 200 OK\r\n\r\nhello "));
279 expect_wants_read(&mut coroutine, Some(b"world"));
280
281 let out = expect_complete_ok(&mut coroutine, Some(b""));
282 assert_eq!(out.response.body, b"hello world");
283 assert!(!out.keep_alive);
284 }
285
286 #[test]
287 fn keep_alive_when_server_says_so() {
288 let req = HttpRequest::get("http://example.com".try_into().unwrap());
289 let mut coroutine = Http10Send::new(req);
290
291 expect_wants_write(&mut coroutine, None);
292 expect_wants_read(&mut coroutine, None);
293
294 let reply = b"HTTP/1.0 200 OK\r\nConnection: keep-alive\r\nContent-Length: 0\r\n\r\n";
295 let out = expect_complete_ok(&mut coroutine, Some(reply));
296 assert!(out.keep_alive);
297 }
298
299 #[test]
300 fn invalid_content_length_errors() {
301 let req = HttpRequest::get("http://example.com".try_into().unwrap());
302 let mut coroutine = Http10Send::new(req);
303
304 expect_wants_write(&mut coroutine, None);
305 expect_wants_read(&mut coroutine, None);
306
307 let reply = b"HTTP/1.0 200 OK\r\nContent-Length: notanumber\r\n\r\n";
308 let err = expect_complete_err(&mut coroutine, Some(reply));
309 let Http10SendError::InvalidContentLength(s) = err else {
310 panic!("expected InvalidContentLength, got {err:?}");
311 };
312 assert_eq!(s, "notanumber");
313 }
314
315 fn expect_wants_write(cor: &mut Http10Send, arg: Option<&[u8]>) -> Vec<u8> {
318 match cor.resume(arg) {
319 HttpCoroutineState::Yielded(HttpSendYield::WantsWrite(bytes)) => bytes,
320 state => panic!("expected WantsWrite, got {state:?}"),
321 }
322 }
323
324 fn expect_wants_read(cor: &mut Http10Send, arg: Option<&[u8]>) {
325 match cor.resume(arg) {
326 HttpCoroutineState::Yielded(HttpSendYield::WantsRead) => {}
327 state => panic!("expected WantsRead, got {state:?}"),
328 }
329 }
330
331 fn expect_complete_ok(cor: &mut Http10Send, arg: Option<&[u8]>) -> HttpSendOutput {
332 match cor.resume(arg) {
333 HttpCoroutineState::Complete(Ok(out)) => out,
334 state => panic!("expected Complete(Ok), got {state:?}"),
335 }
336 }
337
338 fn expect_complete_err(cor: &mut Http10Send, arg: Option<&[u8]>) -> Http10SendError {
339 match cor.resume(arg) {
340 HttpCoroutineState::Complete(Err(err)) => err,
341 state => panic!("expected Complete(Err), got {state:?}"),
342 }
343 }
344}