1use std::future::Future;
43use std::pin::Pin;
44use std::task::{Context, Poll};
45use std::{fmt, ops};
46
47use actix_web::dev;
48use actix_web::http::header;
49use actix_web::web::BytesMut;
50use actix_web::Error as ActixError;
51use actix_web::{FromRequest, HttpRequest};
52use futures::future::{err, Either, LocalBoxFuture, Ready};
53use futures::{FutureExt, StreamExt};
54use serde::de::DeserializeOwned;
55
56pub use crate::config::XmlConfig;
57pub use crate::error::XMLPayloadError;
58
59mod config;
60mod error;
61
62#[cfg(test)]
63mod tests;
64
65pub struct Xml<T>(pub T);
97
98impl<T> Xml<T> {
99 pub fn into_inner(self) -> T {
101 self.0
102 }
103}
104
105impl<T> ops::Deref for Xml<T> {
106 type Target = T;
107
108 fn deref(&self) -> &T {
109 &self.0
110 }
111}
112
113impl<T> ops::DerefMut for Xml<T> {
114 fn deref_mut(&mut self) -> &mut T {
115 &mut self.0
116 }
117}
118
119impl<T> fmt::Debug for Xml<T>
120where
121 T: fmt::Debug,
122{
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 write!(f, "XML: {:?}", self.0)
125 }
126}
127
128impl<T> fmt::Display for Xml<T>
129where
130 T: fmt::Display,
131{
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 fmt::Display::fmt(&self.0, f)
134 }
135}
136
137impl<T> FromRequest for Xml<T>
138where
139 T: DeserializeOwned + 'static,
140{
141 type Error = ActixError;
142 #[allow(clippy::type_complexity)]
143 type Future =
144 Either<LocalBoxFuture<'static, Result<Self, ActixError>>, Ready<Result<Self, ActixError>>>;
145
146 fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
147 let path = req.path().to_string();
148 let config = XmlConfig::from_req(req);
149
150 if let Err(e) = config.check_content_type(req) {
151 return Either::Right(err(e.into()));
152 }
153
154 Either::Left(
155 XmlBody::new(req, payload)
156 .limit(config.limit)
157 .map(move |res| match res {
158 Err(e) => {
159 log::debug!(
160 "Failed to deserialize XML from payload. \
161 Request path: {}",
162 path
163 );
164
165 Err(e.into())
166 }
167 Ok(data) => Ok(Xml(data)),
168 })
169 .boxed_local(),
170 )
171 }
172}
173
174pub struct XmlBody<U> {
183 limit: usize,
184 length: Option<usize>,
185 #[cfg(feature = "__compress")]
186 stream: Option<dev::Decompress<dev::Payload>>,
187 #[cfg(not(feature = "__compress"))]
188 stream: Option<dev::Payload>,
189 err: Option<XMLPayloadError>,
190 fut: Option<LocalBoxFuture<'static, Result<U, XMLPayloadError>>>,
191}
192
193impl<U> XmlBody<U>
194where
195 U: DeserializeOwned + 'static,
196{
197 #[allow(clippy::borrow_interior_mutable_const)]
199 pub fn new(req: &HttpRequest, payload: &mut dev::Payload) -> Self {
200 let len = req
201 .headers()
202 .get(&header::CONTENT_LENGTH)
203 .and_then(|l| l.to_str().ok())
204 .and_then(|s| s.parse::<usize>().ok());
205
206 #[cfg(feature = "__compress")]
207 let payload = dev::Decompress::from_headers(payload.take(), req.headers());
208 #[cfg(not(feature = "__compress"))]
209 let payload = payload.take();
210
211 XmlBody {
212 limit: 262_144,
213 length: len,
214 stream: Some(payload),
215 fut: None,
216 err: None,
217 }
218 }
219
220 pub fn limit(mut self, limit: usize) -> Self {
222 self.limit = limit;
223 self
224 }
225}
226
227impl<U> Future for XmlBody<U>
228where
229 U: DeserializeOwned + 'static,
230{
231 type Output = Result<U, XMLPayloadError>;
232
233 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
234 if let Some(ref mut fut) = self.fut {
235 return Pin::new(fut).poll(cx);
236 }
237
238 if let Some(err) = self.err.take() {
239 return Poll::Ready(Err(err));
240 }
241
242 let limit = self.limit;
243 if let Some(len) = self.length.take() {
244 if len > limit {
245 return Poll::Ready(Err(XMLPayloadError::Overflow));
246 }
247 }
248 let mut stream = self.stream.take().unwrap();
249
250 self.fut = Some(
251 async move {
252 let mut body = BytesMut::with_capacity(8192);
253
254 while let Some(item) = stream.next().await {
255 let chunk = item?;
256 if (body.len() + chunk.len()) > limit {
257 return Err(XMLPayloadError::Overflow);
258 } else {
259 body.extend_from_slice(&chunk);
260 }
261 }
262 Ok(quick_xml::de::from_reader(&*body)?)
263 }
264 .boxed_local(),
265 );
266
267 self.poll(cx)
268 }
269}