kutil_http/body/
read.rs

1use super::{super::pieces::*, reader::*};
2
3use {
4    bytes::*,
5    http::*,
6    http_body::*,
7    kutil_io::reader::*,
8    kutil_std::error::*,
9    std::{io, result::Result, string::*},
10    thiserror::*,
11    tokio::io::*,
12};
13
14//
15// ReadBodyIntoBytes
16//
17
18/// Read [Body] into [Bytes].
19///
20/// See also [BodyReader].
21#[allow(async_fn_in_trait)]
22pub trait ReadBodyIntoBytes: Sized {
23    /// Read entire [Body] into [Bytes] and trailers.
24    ///
25    /// If `declared_size` is not [None](Option::None) then that's the size we expect. Otherwise
26    /// we'll try to read up to `max_size` and will expect at least `min_size`.
27    ///
28    /// If we read less than `min_size` *or* we did not read all the way to EOF will return a
29    /// [ReadBodyError] with [FileTooLarge](io::ErrorKind::FileTooLarge) and [BodyPieces], the
30    /// latter of which can be used by the caller to reconstruct the original body, e.g. with
31    /// [BodyReader::new_with_first_bytes](super::reader::BodyReader::new_with_first_bytes).
32    async fn read_into_bytes_or_pieces(
33        self,
34        declared_size: Option<usize>,
35        min_size: usize,
36        max_size: usize,
37    ) -> Result<(Bytes, Vec<HeaderMap>), ErrorWithBodyPieces<ReadBodyError, Self>>;
38
39    /// Read entire [Body] into [Bytes] and trailers.
40    ///
41    /// If we we did not read all the way to EOF will return a
42    /// [FileTooLarge](io::ErrorKind::FileTooLarge) error.
43    async fn read_into_bytes(self, max_size: usize) -> Result<(Bytes, Vec<HeaderMap>), ReadBodyError> {
44        self.read_into_bytes_or_pieces(None, 0, max_size).await.map_err(|error| error.error)
45    }
46
47    /// Read entire [Body] into [String] and trailers.
48    ///
49    /// See [read_into_bytes](ReadBodyIntoBytes::read_into_bytes).
50    async fn read_into_string_or_pieces(
51        self,
52        declared_size: Option<usize>,
53        min_size: usize,
54        max_size: usize,
55    ) -> Result<(String, Vec<HeaderMap>), ErrorWithBodyPieces<ReadBodyError, Self>> {
56        let (bytes, trailers) = self.read_into_bytes_or_pieces(declared_size, min_size, max_size).await?;
57        let string =
58            String::from_utf8(bytes.to_vec()).map_err(|error| ErrorWithBodyPieces::from(ReadBodyError::from(error)))?;
59        Ok((string, trailers))
60    }
61
62    /// Read entire [Body] into [String] and trailers.
63    ///
64    /// If we we did not read all the way to EOF will return a
65    /// [FileTooLarge](io::ErrorKind::FileTooLarge) error.
66    async fn read_into_string(self, max_size: usize) -> Result<(String, Vec<HeaderMap>), ReadBodyError> {
67        self.read_into_string_or_pieces(None, 0, max_size).await.map_err(|error| error.error)
68    }
69}
70
71impl<BodyT> ReadBodyIntoBytes for BodyT
72where
73    BodyT: Body + Unpin,
74    BodyT::Error: Into<CapturedError>,
75{
76    async fn read_into_bytes_or_pieces(
77        self,
78        declared_size: Option<usize>,
79        min_size: usize,
80        max_size: usize,
81    ) -> Result<(Bytes, Vec<HeaderMap>), ErrorWithBodyPieces<ReadBodyError, Self>> {
82        assert!(max_size >= min_size);
83
84        let read_size = match declared_size {
85            Some(declared_size) => {
86                assert!(declared_size >= min_size);
87                assert!(declared_size <= max_size);
88                declared_size
89            }
90
91            None => max_size,
92        };
93
94        let reader = self.into_reader();
95
96        let mut bytes = BytesMut::with_capacity(read_size);
97        let (mut reader, _size) = read_at_most(reader, &mut bytes, read_size as u64)
98            .await
99            .map_err(|error| ErrorWithBodyPieces::from(ReadBodyError::from(error)))?;
100
101        // We'll try to read just one more byte to see if we're complete
102        match reader.read_u8().await {
103            Ok(byte) => {
104                println!("!!!!!!!!!!!!!!!!! {:?} {}", read_size, bytes.len());
105                let (body, remainder, _trailers) = reader.into_inner();
106
107                // Push back the byte we read and the remainder
108                bytes.put_u8(byte);
109                bytes.put(remainder);
110
111                return Err(ErrorWithBodyPieces::new(
112                    io::Error::new(io::ErrorKind::FileTooLarge, format!("body is bigger than {}", read_size)).into(),
113                    Some(BodyPieces::new(body, bytes.into())),
114                ));
115            }
116
117            Err(error) => {
118                // Actually, we *do* expect EOF :)
119                if error.kind() != io::ErrorKind::UnexpectedEof {
120                    let (body, remainder, _trailers) = reader.into_inner();
121                    bytes.put(remainder); // remainder *should* be empty
122                    return Err(ErrorWithBodyPieces::new(error.into(), Some(BodyPieces::new(body, bytes.into()))));
123                }
124            }
125        }
126
127        let fulfilled_size = bytes.len();
128
129        if let Some(declared_size) = declared_size {
130            if declared_size != fulfilled_size {
131                // The declared size is wrong, but that's not in itself an error
132                tracing::warn!("declared size is {} but actual body size is {}", declared_size, fulfilled_size);
133            }
134        }
135
136        if fulfilled_size < min_size {
137            let (body, remainder, _trailers) = reader.into_inner();
138            bytes.put(remainder); // remainder *should* be empty
139            return Err(ErrorWithBodyPieces::new(
140                io::Error::new(
141                    io::ErrorKind::FileTooLarge,
142                    format!("body is too big: {} > {}", fulfilled_size, min_size),
143                )
144                .into(),
145                Some(BodyPieces::new(body, bytes.into())),
146            ));
147        }
148
149        let (_body, remainder, trailers) = reader.into_inner();
150        bytes.put(remainder); // remainder *should* be empty
151        Ok((bytes.into(), trailers))
152    }
153}
154
155//
156// ReadBodyError
157//
158
159/// [ReadBodyIntoBytes] error.
160#[derive(Debug, Error)]
161pub enum ReadBodyError {
162    /// I/O.
163    #[error("I/O: {0}")]
164    IO(#[from] io::Error),
165
166    /// UTF8.
167    #[error("UTF8: {0}")]
168    UTF8(#[from] FromUtf8Error),
169}