security_client_rs/http/
response.rs1use std::{fmt, marker::PhantomData};
2
3use bytes::Bytes;
4use futures_util::{Stream, StreamExt};
5use http::{HeaderMap, StatusCode};
6use serde::de::DeserializeOwned;
7use serde_json::Value;
8use url::Url;
9
10use crate::{Error, Result, TransportError, error::DecodeError};
11
12pub enum Empty {}
14
15pub enum Raw {}
18
19pub struct Response<T = Value> {
30 inner: reqwest::Response,
31 limit: usize,
32 operation: &'static str,
33 #[cfg(feature = "tracing")]
34 trace: Option<super::trace::Request>,
35 kind: PhantomData<fn() -> T>,
36}
37
38impl<T> Response<T> {
39 pub(crate) fn new(inner: reqwest::Response, limit: usize) -> Self {
40 Self {
41 inner,
42 limit,
43 operation: "request",
44 #[cfg(feature = "tracing")]
45 trace: None,
46 kind: PhantomData,
47 }
48 }
49
50 pub(crate) fn named(mut self, operation: &'static str) -> Self {
51 self.operation = operation;
52 self
53 }
54
55 #[cfg(feature = "tracing")]
56 pub(crate) fn traced(mut self, trace: super::trace::Request) -> Self {
57 self.trace = Some(trace);
58 self
59 }
60
61 pub(crate) fn cast<U>(self) -> Response<U> {
62 Response {
63 inner: self.inner,
64 limit: self.limit,
65 operation: self.operation,
66 #[cfg(feature = "tracing")]
67 trace: self.trace,
68 kind: PhantomData,
69 }
70 }
71
72 pub fn status_code(&self) -> StatusCode {
73 self.inner.status()
74 }
75
76 pub fn headers(&self) -> &HeaderMap {
77 self.inner.headers()
78 }
79
80 pub fn content_length(&self) -> Option<u64> {
81 self.inner.content_length()
82 }
83
84 pub fn url(&self) -> &Url {
86 self.inner.url()
87 }
88
89 pub async fn bytes(mut self) -> Result<Bytes> {
91 let status = self.inner.status();
92 let headers = std::mem::take(self.inner.headers_mut());
93 let body = read_bounded(self.inner, self.limit).await;
94 let result = match body {
95 Bounded {
96 error: Some(source),
97 bytes,
98 ..
99 } => Err(Error::Body {
100 status,
101 headers: Box::new(headers),
102 received: bytes.len(),
103 source,
104 }),
105 Bounded {
106 truncated: true, ..
107 } => Err(Error::ResponseTooLarge {
108 limit: self.limit,
109 status,
110 headers: Box::new(headers),
111 }),
112 Bounded { bytes, .. } => Ok(Bytes::from(bytes)),
113 };
114 #[cfg(feature = "tracing")]
115 if let Some(trace) = &self.trace {
116 trace.body_finished(status, &result);
117 }
118 result
119 }
120
121 pub async fn text(self) -> Result<String> {
123 Ok(String::from_utf8_lossy(&self.bytes().await?).into_owned())
124 }
125
126 pub fn bytes_stream(mut self) -> impl Stream<Item = Result<Bytes>> + Send {
130 let status = self.inner.status();
131 let headers = std::mem::take(self.inner.headers_mut());
132 let mut received = 0_usize;
133 self.inner.bytes_stream().map(move |chunk| match chunk {
134 Ok(bytes) => {
135 received = received.saturating_add(bytes.len());
136 Ok(bytes)
137 }
138 Err(source) => Err(Error::Body {
139 status,
140 headers: Box::new(headers.clone()),
141 received,
142 source: TransportError::from(source),
143 }),
144 })
145 }
146
147 pub async fn json_as<U: DeserializeOwned>(self) -> Result<U> {
149 let status = self.status_code();
150 let headers = Box::new(self.headers().clone());
151 #[cfg(feature = "tracing")]
152 let operation = self.operation;
153 let body = self.bytes().await?;
154 serde_json::from_slice(&body).map_err(|source| {
155 #[cfg(feature = "tracing")]
156 super::trace::decode_failed(operation, status, &source);
157 Error::Decode {
158 status,
159 headers,
160 body: String::from_utf8_lossy(
161 &body[..body.len().min(super::transport::ERROR_LIMIT)],
162 )
163 .into_owned(),
164 source: DecodeError::new(source),
165 }
166 })
167 }
168}
169
170impl<T: DeserializeOwned> Response<T> {
171 pub async fn json(self) -> Result<T> {
175 self.json_as().await
176 }
177}
178
179impl<T> fmt::Debug for Response<T> {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 f.debug_struct("Response")
183 .field("status", &self.status_code())
184 .field("url", &super::redacted(self.url()))
185 .finish_non_exhaustive()
186 }
187}
188
189pub(crate) struct Bounded {
191 pub(crate) bytes: Vec<u8>,
192 pub(crate) truncated: bool,
194 pub(crate) error: Option<TransportError>,
196}
197
198pub(crate) async fn read_bounded(response: reqwest::Response, limit: usize) -> Bounded {
199 let mut stream = response.bytes_stream();
200 let mut bytes = Vec::new();
201 while let Some(chunk) = stream.next().await {
202 let chunk = match chunk {
203 Ok(chunk) => chunk,
204 Err(error) => {
205 return Bounded {
206 bytes,
207 truncated: false,
208 error: Some(TransportError::from(error)),
209 };
210 }
211 };
212 let remaining = limit.saturating_sub(bytes.len());
213 bytes.extend_from_slice(&chunk[..chunk.len().min(remaining)]);
214 if chunk.len() > remaining {
215 return Bounded {
216 bytes,
217 truncated: true,
218 error: None,
219 };
220 }
221 }
222 Bounded {
223 bytes,
224 truncated: false,
225 error: None,
226 }
227}