1use core::{fmt, 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!("prepares HTTP/1.0 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 trace!("http/1.0 send: {}", self.state);
164
165 if let Some(bytes) = self.wants_write.take() {
166 return HttpCoroutineState::Yielded(HttpSendYield::WantsWrite(bytes));
167 }
168
169 match &mut self.state {
170 State::ReadHeaders(rh) => match rh.resume(arg.take()) {
171 HttpCoroutineState::Yielded(HttpYield::WantsRead) => {
172 return HttpCoroutineState::Yielded(HttpSendYield::WantsRead);
173 }
174 HttpCoroutineState::Yielded(HttpYield::WantsWrite(_)) => {
175 unreachable!("Http11ReadHeaders never writes");
176 }
177 HttpCoroutineState::Complete(Err(err)) => {
178 return HttpCoroutineState::Complete(Err(err.into()));
179 }
180 HttpCoroutineState::Complete(Ok(out)) => {
181 let response = out.response;
182 self.keep_alive = out.keep_alive;
183 let status = *response.status;
184
185 if status == 204 || status == 304 {
186 return self.finish(response, out.remaining);
187 }
188
189 if let Some(len_str) = response.header(CONTENT_LENGTH) {
190 let len_str = len_str.trim();
191 let Ok(len) = len_str.parse::<usize>() else {
192 let err = Http10SendError::InvalidContentLength(len_str.to_owned());
193 return HttpCoroutineState::Complete(Err(err));
194 };
195 self.buf = out.remaining;
196 self.response = Some(response);
197 self.state = State::BodyLength(len);
198 continue;
199 }
200
201 self.buf = out.remaining;
202 self.response = Some(response);
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
249impl fmt::Display for State {
250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251 match self {
252 Self::ReadHeaders(_) => f.write_str("read headers"),
253 Self::BodyLength(_) => f.write_str("read body length"),
254 Self::BodyEof => f.write_str("read body until eof"),
255 }
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn body_length_completes() {
265 let req = HttpRequest::get("http://example.com".try_into().unwrap());
266 let mut coroutine = Http10Send::new(req);
267
268 let bytes = expect_wants_write(&mut coroutine, None);
269 assert_eq!(bytes, b"GET / HTTP/1.0\r\ncontent-length: 0\r\n\r\n");
270
271 expect_wants_read(&mut coroutine, None);
272
273 let reply = b"HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\nhello";
274 let out = expect_complete_ok(&mut coroutine, Some(reply));
275 assert_eq!(out.response.version, "HTTP/1.0");
276 assert_eq!(*out.response.status, 200);
277 assert_eq!(out.response.body, b"hello");
278 assert!(!out.keep_alive);
279 }
280
281 #[test]
282 fn body_eof_completes() {
283 let req = HttpRequest::get("http://example.com".try_into().unwrap());
284 let mut coroutine = Http10Send::new(req);
285
286 expect_wants_write(&mut coroutine, None);
287 expect_wants_read(&mut coroutine, None);
288 expect_wants_read(&mut coroutine, Some(b"HTTP/1.0 200 OK\r\n\r\nhello "));
289 expect_wants_read(&mut coroutine, Some(b"world"));
290
291 let out = expect_complete_ok(&mut coroutine, Some(b""));
292 assert_eq!(out.response.body, b"hello world");
293 assert!(!out.keep_alive);
294 }
295
296 #[test]
297 fn keep_alive_when_server_says_so() {
298 let req = HttpRequest::get("http://example.com".try_into().unwrap());
299 let mut coroutine = Http10Send::new(req);
300
301 expect_wants_write(&mut coroutine, None);
302 expect_wants_read(&mut coroutine, None);
303
304 let reply = b"HTTP/1.0 200 OK\r\nConnection: keep-alive\r\nContent-Length: 0\r\n\r\n";
305 let out = expect_complete_ok(&mut coroutine, Some(reply));
306 assert!(out.keep_alive);
307 }
308
309 #[test]
310 fn invalid_content_length_errors() {
311 let req = HttpRequest::get("http://example.com".try_into().unwrap());
312 let mut coroutine = Http10Send::new(req);
313
314 expect_wants_write(&mut coroutine, None);
315 expect_wants_read(&mut coroutine, None);
316
317 let reply = b"HTTP/1.0 200 OK\r\nContent-Length: notanumber\r\n\r\n";
318 let err = expect_complete_err(&mut coroutine, Some(reply));
319 let Http10SendError::InvalidContentLength(s) = err else {
320 panic!("expected InvalidContentLength, got {err:?}");
321 };
322 assert_eq!(s, "notanumber");
323 }
324
325 fn expect_wants_write(cor: &mut Http10Send, arg: Option<&[u8]>) -> Vec<u8> {
328 match cor.resume(arg) {
329 HttpCoroutineState::Yielded(HttpSendYield::WantsWrite(bytes)) => bytes,
330 state => panic!("expected WantsWrite, got {state:?}"),
331 }
332 }
333
334 fn expect_wants_read(cor: &mut Http10Send, arg: Option<&[u8]>) {
335 match cor.resume(arg) {
336 HttpCoroutineState::Yielded(HttpSendYield::WantsRead) => {}
337 state => panic!("expected WantsRead, got {state:?}"),
338 }
339 }
340
341 fn expect_complete_ok(cor: &mut Http10Send, arg: Option<&[u8]>) -> HttpSendOutput {
342 match cor.resume(arg) {
343 HttpCoroutineState::Complete(Ok(out)) => out,
344 state => panic!("expected Complete(Ok), got {state:?}"),
345 }
346 }
347
348 fn expect_complete_err(cor: &mut Http10Send, arg: Option<&[u8]>) -> Http10SendError {
349 match cor.resume(arg) {
350 HttpCoroutineState::Complete(Err(err)) => err,
351 state => panic!("expected Complete(Err), got {state:?}"),
352 }
353 }
354}