leyline/core/response/
body.rs1use 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 wait = deadline.error_body_wait();
113 let declared = self.content_length();
114 match within(Some(wait), self.read_decoded(limit, |_, _| false)).await {
115 Ok(Ok((body, true))) => Err(err.with_body(body).without_content_coding()),
116 Ok(Ok((body, false))) if declared != Some(body.len() as u64) => {
117 Err(err.with_body(body).without_content_length())
118 }
119 Ok(Ok((body, false))) => Err(err.with_body(body)),
120 _ => Err(err),
121 }
122 }
123
124 pub async fn read_until<F>(self, limit: usize, done: F) -> Result<Vec<u8>>
125 where
126 F: FnMut(&[u8], usize) -> bool,
127 {
128 self.read_decoded(limit, done).await.map(|(body, _)| body)
129 }
130
131 async fn read_decoded<F>(self, limit: usize, mut done: F) -> Result<(Vec<u8>, bool)>
132 where
133 F: FnMut(&[u8], usize) -> bool,
134 {
135 let limit = limit.min(self.compression.max_body_size);
136 let encoding = content_codings(self.headers.get_all(http::header::CONTENT_ENCODING));
137 let mut decoder = Decoder::truncated(encoding.as_deref(), &self.compression, limit)?;
138 let mut stream = self.into_stream()?;
139 let mut out = Vec::new();
140 while let Some(chunk) = stream.next().await {
141 let chunk = chunk.map_err(Error::from)?;
142 let from = out.len();
143 match decoder.as_mut() {
144 Some(decoder) => decoder.feed(&chunk, &mut out)?,
145 None => out.extend_from_slice(&chunk),
146 }
147 out.truncate(limit);
148 if done(&out, from) || out.len() >= limit {
149 return Ok((out, decoder.is_some()));
150 }
151 }
152 if let Some(decoder) = decoder.as_mut() {
153 decoder.finish(&mut out)?;
154 out.truncate(limit);
155 }
156 Ok((out, decoder.is_some()))
157 }
158}
159
160async fn write_stream<S, W>(
161 mut stream: S,
162 writer: &mut W,
163 map: fn(std::io::Error) -> Error,
164) -> Result<u64>
165where
166 S: Stream<Item = std::io::Result<Bytes>> + Unpin,
167 W: AsyncWrite + Unpin,
168{
169 let mut total: u64 = 0;
170 while let Some(chunk) = stream.next().await {
171 let chunk = chunk.map_err(map)?;
172 writer.write_all(&chunk).await.map_err(Error::from)?;
173 total += chunk.len() as u64;
174 }
175 writer.flush().await.map_err(Error::from)?;
176 Ok(total)
177}