1use crate::{Error, connection::HttpStream};
2use std::collections::HashMap;
3use std::{fmt::Display, str};
4use tokio::io::{AsyncBufReadExt, AsyncReadExt, BufReader};
5const BACKING_READ_BUFFER_LENGTH: usize = 16 * 1024;
6
7#[derive(Clone, PartialEq, Eq, Debug)]
20pub struct Response {
21 pub status_code: i32,
23 pub reason_phrase: String,
25 pub headers: HashMap<String, String>,
28 pub url: String,
33 pub download_size: u64,
34 body: Vec<u8>,
35}
36impl Display for Response {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 let len = self.body_len();
39 let len = match len {
40 0 => self.download_size,
41 _ => len
42 };
43 writeln!(
44 f,
45 "Response {{ Status: {} {}, body len: {} }}",
46 self.status_code,
47 self.reason_phrase,
48 len
49 )
50 }
51}
52impl Response {
53 pub(crate) async fn create(mut parent: ResponseLazy, is_head: bool) -> Result<Response, Error> {
54 let mut body = Vec::new();
55 if !is_head && parent.status_code != 204 && parent.status_code != 304 {
56 loop {
57 let d = parent.next().await;
58 match d {
59 Some(byte) => {
60 body.push(byte);
61 }
62 None => break,
63 }
64 }
65 }
66
67 let ResponseLazy {
68 status_code,
69 reason_phrase,
70 headers,
71 url,
72 ..
73 } = parent;
74
75 Ok(Response {
76 status_code,
77 reason_phrase,
78 headers,
79 url,
80 body,
81 download_size: 0
82 })
83 }
84
85 pub fn as_str(&self) -> Result<&str, Error> {
105 match str::from_utf8(&self.body) {
106 Ok(s) => Ok(s),
107 Err(err) => Err(Error::InvalidUtf8InBody(err)),
108 }
109 }
110
111 pub fn as_bytes(&self) -> &[u8] {
126 &self.body
127 }
128
129 pub fn into_bytes(self) -> Vec<u8> {
146 self.body
147 }
148 pub fn body_len(&self) -> u64{
149 let default_content_lenth = String::from("0");
150 let content_lenth = self.headers.get("content-length").unwrap_or(&default_content_lenth);
151 u64::from_str_radix(content_lenth.trim(), 10).unwrap_or(0)
152 }
153}
154
155pub struct ResponseLazy {
192 pub status_code: i32,
194 pub reason_phrase: String,
196 pub headers: HashMap<String, String>,
199 pub url: String,
204
205 pub stream: HttpStreamBytes,
206 state: HttpStreamState,
207 max_trailing_headers_size: Option<usize>,
208}
209
210pub type HttpStreamBytes = BufReader<HttpStream>;
211
212impl ResponseLazy {
213 pub(crate) async fn from_stream(
214 stream: HttpStream,
215 max_headers_size: Option<usize>,
216 max_status_line_len: Option<usize>,
217 ) -> Result<ResponseLazy, Error> {
218 let mut stream = BufReader::with_capacity(BACKING_READ_BUFFER_LENGTH, stream);
219 let ResponseMetadata {
220 status_code,
221 reason_phrase,
222 headers,
223 state,
224 max_trailing_headers_size,
225 } = read_metadata(&mut stream, max_headers_size, max_status_line_len).await?;
226
227 Ok(ResponseLazy {
228 status_code,
229 reason_phrase,
230 headers,
231 url: String::new(),
232 stream,
233 state,
234 max_trailing_headers_size,
235 })
236 }
237 async fn next(&mut self) -> Option<u8> {
238 use HttpStreamState::*;
239 match self.state {
240 EndOnClose => read_until_closed(&mut self.stream).await,
241 ContentLength(ref mut length) => {
242 read_with_content_length(&mut self.stream, length).await
243 }
244 Chunked(ref mut expecting_chunks, ref mut length, ref mut content_length) => {
245 read_chunked(
246 &mut self.stream,
247 &mut self.headers,
248 expecting_chunks,
249 length,
250 content_length,
251 self.max_trailing_headers_size,
252 )
253 .await
254 }
255 }
256 }
257}
258
259async fn read_until_closed(bytes: &mut HttpStreamBytes) -> Option<u8> {
260 let mut buf = [0u8];
261 if let Ok(len) = bytes.read(&mut buf).await {
262 if len != 0 {
263 return Some(buf[0]);
264 }
265 }
266 None
267}
268
269async fn read_with_content_length(
270 bytes: &mut HttpStreamBytes,
271 content_length: &mut usize,
272) -> Option<u8> {
273 if *content_length > 0 {
274 *content_length -= 1;
275 let mut buf = [0u8];
276 if let Ok(len) = bytes.read(&mut buf).await {
277 if len != 0 {
278 return Some(buf[0]);
279 }
280 }
281 }
282 None
283}
284
285async fn read_trailers(
286 bytes: &mut HttpStreamBytes,
287 headers: &mut HashMap<String, String>,
288 mut max_headers_size: Option<usize>,
289) -> Result<(), Error> {
290 loop {
291 let trailer_line = read_line(bytes).await?;
292 if let Some(ref mut max_headers_size) = max_headers_size {
293 *max_headers_size -= trailer_line.len() + 2;
294 }
295 if let Some((header, value)) = parse_header(trailer_line) {
296 headers.insert(header, value);
297 } else {
298 break;
299 }
300 }
301 Ok(())
302}
303
304async fn read_chunked(
305 bytes: &mut HttpStreamBytes,
306 headers: &mut HashMap<String, String>,
307 expecting_more_chunks: &mut bool,
308 chunk_length: &mut usize,
309 content_length: &mut usize,
310 max_trailing_headers_size: Option<usize>,
311) -> Option<u8> {
312 if !*expecting_more_chunks && *chunk_length == 0 {
313 return None;
314 }
315
316 if *chunk_length == 0 {
317 let length_line = match read_line(bytes).await {
323 Ok(line) => line,
324 Err(_) => return None,
325 };
326
327 let incoming_length = if length_line.is_empty() {
331 0
332 } else {
333 let length = if let Some(i) = length_line.find(';') {
334 length_line[..i].trim()
335 } else {
336 length_line.trim()
337 };
338 match usize::from_str_radix(length, 16) {
339 Ok(length) => length,
340 Err(_) => return None,
341 }
342 };
343
344 if incoming_length == 0 {
345 if let Err(_) = read_trailers(bytes, headers, max_trailing_headers_size).await {
346 return None;
347 }
348
349 *expecting_more_chunks = false;
350 headers.insert("content-length".to_string(), (*content_length).to_string());
351 headers.remove("transfer-encoding");
352 return None;
353 }
354 *chunk_length = incoming_length;
355 *content_length += incoming_length;
356 }
357
358 if *chunk_length > 0 {
359 *chunk_length -= 1;
360 let mut buf = [0u8];
361 if let Ok(len) = bytes.read(&mut buf).await {
362 if len == 1 {
363 if *chunk_length == 0 {
365 if let Err(_) = read_line(bytes).await {
372 return None;
373 }
374 }
375
376 return Some(buf[0]);
377 } else {
378 return None;
379 }
380 }
381 }
382
383 None
384}
385
386enum HttpStreamState {
387 EndOnClose,
391 ContentLength(usize),
393 Chunked(bool, usize, usize),
399}
400
401struct ResponseMetadata {
406 status_code: i32,
407 reason_phrase: String,
408 headers: HashMap<String, String>,
409 state: HttpStreamState,
410 max_trailing_headers_size: Option<usize>,
411}
412
413async fn read_metadata(
414 stream: &mut HttpStreamBytes,
415 mut max_headers_size: Option<usize>,
416 _max_status_line_len: Option<usize>,
417) -> Result<ResponseMetadata, Error> {
418 let line = read_line(stream).await?;
419 let (status_code, reason_phrase) = parse_status_line(&line);
420
421 let mut headers = HashMap::new();
422 loop {
423 let line = read_line(stream).await?;
424 if line.is_empty() || line == "\r\n" {
425 break;
427 }
428 let line = line.trim().to_string();
429 if let Some(ref mut max_headers_size) = max_headers_size {
430 *max_headers_size -= line.len() + 2;
431 }
432 if let Some(header) = parse_header(line) {
433 headers.insert(header.0.to_lowercase(), header.1);
434 }
435 }
436
437 let mut chunked = false;
438 let mut content_length = None;
439 for (header, value) in &headers {
440 if header.to_lowercase().trim() == "transfer-encoding"
442 && value.to_lowercase().trim() == "chunked"
443 {
444 chunked = true;
445 }
446
447 if header.to_lowercase().trim() == "content-length" {
449 match str::parse::<usize>(value.trim()) {
450 Ok(length) => content_length = Some(length),
451 Err(_) => return Err(Error::MalformedContentLength),
452 }
453 }
454 }
455
456 let state = if chunked {
457 HttpStreamState::Chunked(true, 0, 0)
458 } else if let Some(length) = content_length {
459 HttpStreamState::ContentLength(length)
460 } else {
461 HttpStreamState::EndOnClose
462 };
463
464 Ok(ResponseMetadata {
465 status_code,
466 reason_phrase,
467 headers,
468 state,
469 max_trailing_headers_size: max_headers_size,
470 })
471}
472
473pub async fn read_line<R: AsyncBufReadExt + Unpin>(reader: &mut R) -> Result<String, Error> {
474 let mut buffer = String::new();
475 match reader
476 .read_line(&mut buffer)
477 .await
478 .map_err(|_| Error::HeadersOverflow)
479 {
480 Ok(_) => Ok(buffer),
481 Err(_) => Err(Error::HeadersOverflow),
482 }
483}
484
485fn parse_status_line(line: &str) -> (i32, String) {
486 let mut status_code = String::with_capacity(3);
489 let mut reason_phrase = String::with_capacity(2);
490
491 let mut spaces = 0;
492
493 for c in line.chars() {
494 if spaces >= 2 {
495 reason_phrase.push(c);
496 }
497
498 if c == ' ' {
499 spaces += 1;
500 } else if spaces == 1 {
501 status_code.push(c);
502 }
503 }
504
505 if let Ok(status_code) = status_code.parse::<i32>() {
506 return (status_code, reason_phrase.trim().to_string());
507 }
508
509 (503, "Server did not provide a status line".to_string())
510}
511
512fn parse_header(mut line: String) -> Option<(String, String)> {
513 if let Some(location) = line.find(':') {
514 let value = if let Some(sp) = line.get(location + 1..location + 2) {
520 if sp == " " {
521 line[location + 2..].to_string()
522 } else {
523 line[location + 1..].to_string()
524 }
525 } else {
526 line[location + 1..].to_string()
527 };
528
529 line.truncate(location);
530 line.make_ascii_lowercase();
532 return Some((line, value));
533 }
534 None
535}