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