irox_networking/http/
response.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2023 IROX Contributors
3//
4
5use crate::http::headers::HttpHeaders;
6use crate::http::{HeaderResponse, HttpBody};
7use irox_tools::options::MaybeMap;
8use irox_tools::read::{ReadAny, ReadEmpty};
9use std::fmt::{Debug, Formatter};
10use std::io::{BufRead, BufReader, Error, Read, Write};
11use std::str::FromStr;
12
13pub struct HttpResponse {
14    pub(crate) version: String,
15    pub(crate) code: String,
16    pub(crate) status: String,
17    pub(crate) headers: HttpHeaders,
18    pub(crate) body: HttpBody,
19}
20
21impl HttpResponse {
22    pub fn create_from<T: Read + 'static>(input: T) -> Result<HttpResponse, Error> {
23        let mut bufread = BufReader::new(input);
24        let mut line = String::new();
25        let _read = bufread.read_line(&mut line)?;
26        let mut status = line.split(' ');
27        let version = status.next().unwrap_or_default().to_string();
28        let code = status.next().unwrap_or_default().to_string();
29        let status = status.collect::<Vec<&str>>().join(" ");
30
31        let HeaderResponse {
32            headers,
33            mut remaining,
34        } = HttpHeaders::create_from(bufread)?;
35
36        let content_length = headers
37            .get_header("Content-Length")
38            .maybe_map(|v| u64::from_str(v).ok())
39            .unwrap_or(0);
40        let body = if content_length == 0 {
41            HttpBody::Empty
42        } else if content_length < 1_000_000 {
43            let mut buf: Vec<u8> = Vec::with_capacity(1_000_000);
44            remaining.read_to_end(&mut buf)?;
45            HttpBody::Bytes(buf)
46        } else {
47            HttpBody::Read(Box::new(remaining))
48        };
49
50        Ok(HttpResponse {
51            version,
52            code,
53            status,
54            headers,
55            body,
56        })
57    }
58    pub fn body_read(self) -> Box<dyn Read> {
59        match self.body {
60            HttpBody::Read(r) => r,
61            HttpBody::Empty => Box::<ReadEmpty>::default(),
62            HttpBody::String(s) => Box::new(ReadAny::from(s)),
63            HttpBody::Bytes(b) => Box::new(ReadAny::from(b)),
64        }
65    }
66}
67
68impl Debug for HttpResponse {
69    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
70        let mut val: Vec<u8> = Vec::with_capacity(4096);
71        let _ = write!(val, "{} {} {}\r\n", self.version, self.code, self.status);
72        let _ = self.headers.write_to(&mut val);
73        match &self.body {
74            HttpBody::String(s) => {
75                let _ = write!(val, "{s}");
76            }
77            HttpBody::Bytes(b) => val.extend_from_slice(b),
78            _ => {}
79        }
80        write!(f, "{}", String::from_utf8_lossy(&val))?;
81        Ok(())
82    }
83}