Skip to main content

leyline/core/response/
body.rs

1use bytes::Bytes;
2use futures_util::{Stream, StreamExt};
3use tokio::io::{AsyncWrite, AsyncWriteExt};
4
5use super::{Response, ResponseBody};
6use crate::core::body_stream::BodyStream;
7use crate::core::deadline::{Deadline, within};
8use crate::core::error::{Error, Kind, Result};
9use crate::core::session::decompress::{
10    BodyLimit, Decoder, body_read_error, content_codings, decompress_body, drain_stream_into_vec,
11};
12
13impl Response {
14    pub async fn text(self) -> crate::core::Result<String> {
15        self.text_with_charset("utf-8").await
16    }
17
18    #[cfg(feature = "charset")]
19    pub async fn text_with_charset(self, default_encoding: &str) -> crate::core::Result<String> {
20        let label = self.charset_label().map(str::to_owned);
21        let bytes = self.bytes().await?;
22        let encoding = encoding_rs::Encoding::for_label(
23            label.as_deref().unwrap_or(default_encoding).as_bytes(),
24        )
25        .unwrap_or(encoding_rs::UTF_8);
26        Ok(encoding.decode(&bytes).0.into_owned())
27    }
28
29    #[cfg(not(feature = "charset"))]
30    pub async fn text_with_charset(self, _default_encoding: &str) -> crate::core::Result<String> {
31        Ok(String::from_utf8_lossy(&self.bytes().await?).into_owned())
32    }
33
34    #[cfg(feature = "charset")]
35    fn charset_label(&self) -> Option<&str> {
36        let ct = self.header("content-type")?;
37        ct.split(';').skip(1).find_map(|param| {
38            let (k, v) = param.split_once('=')?;
39            k.trim()
40                .eq_ignore_ascii_case("charset")
41                .then(|| v.trim().trim_matches('"'))
42        })
43    }
44
45    pub async fn bytes(self) -> crate::core::Result<Bytes> {
46        let stream = match self.body {
47            ResponseBody::Buffered(b) => return Ok(Bytes::from(b)),
48            ResponseBody::Taken => {
49                return Err(Error::new(Kind::Body).with_message(
50                    "response body stream was taken by `into_stream`; read the bytes from that stream",
51                ));
52            }
53            ResponseBody::Streaming(s) => s,
54        };
55        let buf = drain_stream_into_vec(stream, self.compression.max_body_size).await?;
56        let encoding = content_codings(self.headers.get_all(http::header::CONTENT_ENCODING));
57        let (buf, _) = decompress_body(buf, encoding.as_deref(), &self.compression)?;
58        Ok(Bytes::from(buf))
59    }
60
61    pub async fn json<T: serde::de::DeserializeOwned>(self) -> crate::core::Result<T> {
62        serde_json::from_slice(&self.bytes().await?).map_err(Error::from_json)
63    }
64
65    pub fn into_stream(mut self) -> Result<BodyStream> {
66        match std::mem::replace(&mut self.body, ResponseBody::Taken) {
67            ResponseBody::Streaming(s) => Ok(s),
68            ResponseBody::Buffered(b) => Ok(BodyStream::from_bytes(bytes::Bytes::from(b))),
69            ResponseBody::Taken => Err(Error::new(Kind::Body)
70                .with_message("response body has already been taken as a stream")),
71        }
72    }
73
74    pub async fn copy_to<W>(self, writer: &mut W) -> Result<u64>
75    where
76        W: tokio::io::AsyncWrite + Unpin,
77    {
78        write_stream(self.into_stream()?, writer, Error::from).await
79    }
80
81    pub fn into_decoded_stream(self, limit: Option<u64>) -> Result<BodyStream> {
82        let encoding = content_codings(self.headers.get_all(http::header::CONTENT_ENCODING));
83        if encoding.is_none() {
84            self.refuse_declared_overflow(limit)?;
85        }
86        let decoder = Decoder::capped(encoding.as_deref(), &self.compression, limit)?;
87        Ok(BodyStream::decoded(self.into_stream()?, decoder))
88    }
89
90    fn refuse_declared_overflow(&self, limit: Option<u64>) -> Result<()> {
91        let cap = BodyLimit::session(self.compression.max_body_size).tighter(limit);
92        match self.content_length() {
93            Some(declared) if declared > u64::try_from(cap.bytes).unwrap_or(u64::MAX) => {
94                Err(cap.error())
95            }
96            _ => Ok(()),
97        }
98    }
99
100    pub async fn copy_decoded_to<W>(self, writer: &mut W, limit: Option<u64>) -> Result<u64>
101    where
102        W: AsyncWrite + Unpin,
103    {
104        write_stream(self.into_decoded_stream(limit)?, writer, body_read_error).await
105    }
106
107    pub(crate) async fn error_for_status_with_body(self, deadline: &Deadline) -> Result<Self> {
108        let Some(err) = self.status_error() else {
109            return Ok(self);
110        };
111        let limit = self.compression.max_error_body;
112        let remaining = deadline.remaining();
113        let wait = deadline
114            .error_body()
115            .map_or(remaining, |cap| cap.min(remaining));
116        match within(Some(wait), self.read_until(limit, |_, _| false)).await {
117            Ok(Ok(body)) => Err(err.with_body(body)),
118            _ => Err(err),
119        }
120    }
121
122    pub async fn read_until<F>(self, limit: usize, mut done: F) -> Result<Vec<u8>>
123    where
124        F: FnMut(&[u8], usize) -> bool,
125    {
126        let limit = limit.min(self.compression.max_body_size);
127        let encoding = content_codings(self.headers.get_all(http::header::CONTENT_ENCODING));
128        let mut decoder = Decoder::truncated(encoding.as_deref(), &self.compression, limit)?;
129        let mut stream = self.into_stream()?;
130        let mut out = Vec::new();
131        while let Some(chunk) = stream.next().await {
132            let chunk = chunk.map_err(Error::from)?;
133            let from = out.len();
134            match decoder.as_mut() {
135                Some(decoder) => decoder.feed(&chunk, &mut out)?,
136                None => out.extend_from_slice(&chunk),
137            }
138            out.truncate(limit);
139            if done(&out, from) || out.len() >= limit {
140                return Ok(out);
141            }
142        }
143        if let Some(decoder) = decoder.as_mut() {
144            decoder.finish(&mut out)?;
145            out.truncate(limit);
146        }
147        Ok(out)
148    }
149}
150
151async fn write_stream<S, W>(
152    mut stream: S,
153    writer: &mut W,
154    map: fn(std::io::Error) -> Error,
155) -> Result<u64>
156where
157    S: Stream<Item = std::io::Result<Bytes>> + Unpin,
158    W: AsyncWrite + Unpin,
159{
160    let mut total: u64 = 0;
161    while let Some(chunk) = stream.next().await {
162        let chunk = chunk.map_err(map)?;
163        writer.write_all(&chunk).await.map_err(Error::from)?;
164        total += chunk.len() as u64;
165    }
166    writer.flush().await.map_err(Error::from)?;
167    Ok(total)
168}