1use std::{
4 fmt,
5 marker::PhantomData,
6 pin::Pin,
7 task::{Context, Poll, ready},
8};
9
10use actix_web::{
11 Error, FromRequest, HttpMessage, HttpRequest, ResponseError,
12 dev::Payload,
13 error::PayloadError,
14 http::{StatusCode, header},
15 web,
16};
17use derive_more::{Display, Error};
18use futures_core::Stream as _;
19use serde::de::DeserializeOwned;
20use tracing::debug;
21
22pub const DEFAULT_URL_ENCODED_FORM_LIMIT: usize = 2_097_152;
24
25#[doc(alias = "html_form", alias = "html form", alias = "form")]
61#[derive(Debug)]
62pub struct UrlEncodedForm<T, const LIMIT: usize = DEFAULT_URL_ENCODED_FORM_LIMIT>(pub T);
64
65mod waiting_on_derive_more_to_start_using_syn_2_due_to_proc_macro_panic {
66 use super::*;
67
68 impl<T, const LIMIT: usize> std::ops::Deref for UrlEncodedForm<T, LIMIT> {
69 type Target = T;
70
71 fn deref(&self) -> &Self::Target {
72 &self.0
73 }
74 }
75
76 impl<T, const LIMIT: usize> std::ops::DerefMut for UrlEncodedForm<T, LIMIT> {
77 fn deref_mut(&mut self) -> &mut Self::Target {
78 &mut self.0
79 }
80 }
81
82 impl<T: std::fmt::Display, const LIMIT: usize> std::fmt::Display for UrlEncodedForm<T, LIMIT> {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 std::fmt::Display::fmt(&self.0, f)
85 }
86 }
87}
88
89impl<T, const LIMIT: usize> UrlEncodedForm<T, LIMIT> {
90 pub fn into_inner(self) -> T {
92 self.0
93 }
94}
95
96impl<T: DeserializeOwned, const LIMIT: usize> FromRequest for UrlEncodedForm<T, LIMIT> {
98 type Error = Error;
99 type Future = UrlEncodedFormExtractFut<T, LIMIT>;
100
101 #[inline]
102 fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
103 UrlEncodedFormExtractFut {
104 req: Some(req.clone()),
105 fut: UrlEncodedFormBody::new(req, payload),
106 }
107 }
108}
109
110#[allow(missing_debug_implementations)]
111pub struct UrlEncodedFormExtractFut<T, const LIMIT: usize> {
112 req: Option<HttpRequest>,
113 fut: UrlEncodedFormBody<T, LIMIT>,
114}
115
116impl<T: DeserializeOwned, const LIMIT: usize> Future for UrlEncodedFormExtractFut<T, LIMIT> {
117 type Output = Result<UrlEncodedForm<T, LIMIT>, Error>;
118
119 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
120 let this = self.get_mut();
121
122 let res = ready!(Pin::new(&mut this.fut).poll(cx));
123
124 let res = match res {
125 Err(err) => {
126 let req = this.req.take().unwrap();
127 debug!(
128 "Failed to deserialize UrlEncodedForm<{}> from payload in handler: {}",
129 core::any::type_name::<T>(),
130 req.match_name().unwrap_or_else(|| req.path())
131 );
132
133 Err(err.into())
134 }
135 Ok(data) => Ok(UrlEncodedForm(data)),
136 };
137
138 Poll::Ready(res)
139 }
140}
141
142pub enum UrlEncodedFormBody<T, const LIMIT: usize> {
151 Error(Option<UrlEncodedFormError>),
152 Body {
153 #[allow(dead_code)]
155 length: Option<usize>,
156 payload: Payload,
157 buf: web::BytesMut,
158 _res: PhantomData<T>,
159 },
160}
161
162impl<T, const LIMIT: usize> Unpin for UrlEncodedFormBody<T, LIMIT> {}
163
164impl<T: DeserializeOwned, const LIMIT: usize> UrlEncodedFormBody<T, LIMIT> {
165 pub fn new(req: &HttpRequest, payload: &mut Payload) -> Self {
167 let can_parse_form = if let Ok(Some(mime)) = req.mime_type() {
169 mime == mime::APPLICATION_WWW_FORM_URLENCODED
170 } else {
171 false
172 };
173
174 if !can_parse_form {
175 return UrlEncodedFormBody::Error(Some(UrlEncodedFormError::ContentType));
176 }
177
178 let length = req
179 .headers()
180 .get(&header::CONTENT_LENGTH)
181 .and_then(|l| l.to_str().ok())
182 .and_then(|s| s.parse::<usize>().ok());
183
184 let payload = payload.take();
189
190 if let Some(len) = length {
191 if len > LIMIT {
192 return UrlEncodedFormBody::Error(Some(UrlEncodedFormError::Overflow {
193 size: len,
194 limit: LIMIT,
195 }));
196 }
197 }
198
199 UrlEncodedFormBody::Body {
200 length,
201 payload,
202 buf: web::BytesMut::with_capacity(8192),
203 _res: PhantomData,
204 }
205 }
206}
207
208impl<T: DeserializeOwned, const LIMIT: usize> Future for UrlEncodedFormBody<T, LIMIT> {
209 type Output = Result<T, UrlEncodedFormError>;
210
211 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
212 let this = self.get_mut();
213
214 match this {
215 UrlEncodedFormBody::Body { buf, payload, .. } => loop {
216 let res = ready!(Pin::new(&mut *payload).poll_next(cx));
217
218 match res {
219 Some(chunk) => {
220 let chunk =
221 chunk.map_err(|err| UrlEncodedFormError::Payload { source: err })?;
222
223 let buf_len = buf.len() + chunk.len();
224 if buf_len > LIMIT {
225 return Poll::Ready(Err(UrlEncodedFormError::Overflow {
226 size: buf_len,
227 limit: LIMIT,
228 }));
229 } else {
230 buf.extend_from_slice(&chunk);
231 }
232 }
233
234 None => {
235 let de = serde_html_form::Deserializer::from_bytes(buf);
236
237 let form = serde_path_to_error::deserialize(de).map_err(|err| {
238 UrlEncodedFormError::Deserialize {
239 source: UrlEncodedFormDeserializeError {
240 path: err.path().clone(),
241 source: err.into_inner(),
242 },
243 }
244 })?;
245
246 return Poll::Ready(Ok(form));
247 }
248 }
249 },
250
251 UrlEncodedFormBody::Error(err) => Poll::Ready(Err(err.take().unwrap())),
252 }
253 }
254}
255
256#[derive(Debug, Display, Error)]
258#[non_exhaustive]
259pub enum UrlEncodedFormError {
260 #[display(
262 "URL encoded payload is larger ({} bytes) than allowed (limit: {} bytes).",
263 size,
264 limit
265 )]
266 Overflow { size: usize, limit: usize },
267
268 #[display("Content type error.")]
270 ContentType,
271
272 #[display("Deserialization error")]
274 Deserialize {
275 source: UrlEncodedFormDeserializeError,
277 },
278
279 #[display("Error that occur during reading payload")]
281 Payload { source: PayloadError },
282}
283
284impl ResponseError for UrlEncodedFormError {
285 fn status_code(&self) -> StatusCode {
286 match self {
287 Self::Overflow { .. } => StatusCode::PAYLOAD_TOO_LARGE,
288 Self::ContentType => StatusCode::UNSUPPORTED_MEDIA_TYPE,
289 Self::Payload { source: err } => err.status_code(),
290 Self::Deserialize { .. } => StatusCode::UNPROCESSABLE_ENTITY,
291 }
292 }
293}
294
295#[derive(Debug, Error)]
297pub struct UrlEncodedFormDeserializeError {
298 path: serde_path_to_error::Path,
300
301 source: serde_html_form::de::Error,
303}
304
305impl UrlEncodedFormDeserializeError {
306 pub fn path(&self) -> impl fmt::Display + '_ {
308 &self.path
309 }
310
311 pub fn source(&self) -> &serde_html_form::de::Error {
313 &self.source
314 }
315}
316
317impl fmt::Display for UrlEncodedFormDeserializeError {
318 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319 f.write_str("URL-encoded form deserialization failed")?;
320
321 if self.path.iter().len() > 0 {
322 write!(f, " at path: {}", &self.path)?;
323 }
324
325 Ok(())
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use actix_web::{http::header, test::TestRequest, web::Bytes};
332 use serde::{Deserialize, Serialize};
333
334 use super::*;
335
336 #[derive(Serialize, Deserialize, PartialEq, Debug)]
337 struct MyObject {
338 name: String,
339 }
340
341 fn err_eq(err: UrlEncodedFormError, other: UrlEncodedFormError) -> bool {
342 match err {
343 UrlEncodedFormError::Overflow { .. } => {
344 matches!(other, UrlEncodedFormError::Overflow { .. })
345 }
346
347 UrlEncodedFormError::ContentType => matches!(other, UrlEncodedFormError::ContentType),
348
349 _ => false,
350 }
351 }
352
353 #[actix_web::test]
354 async fn test_extract() {
355 let (req, mut pl) = TestRequest::default()
356 .insert_header(header::ContentType::form_url_encoded())
357 .insert_header((
358 header::CONTENT_LENGTH,
359 header::HeaderValue::from_static("9"),
360 ))
361 .set_payload(Bytes::from_static(b"name=test"))
362 .to_http_parts();
363
364 let s =
365 UrlEncodedForm::<MyObject, DEFAULT_URL_ENCODED_FORM_LIMIT>::from_request(&req, &mut pl)
366 .await
367 .unwrap();
368 assert_eq!(s.name, "test");
369 assert_eq!(
370 s.into_inner(),
371 MyObject {
372 name: "test".to_string()
373 }
374 );
375
376 let (req, mut pl) = TestRequest::default()
377 .insert_header(header::ContentType::form_url_encoded())
378 .insert_header((
379 header::CONTENT_LENGTH,
380 header::HeaderValue::from_static("9"),
381 ))
382 .set_payload(Bytes::from_static(b"name=test"))
383 .to_http_parts();
384
385 let s = UrlEncodedForm::<MyObject, 8>::from_request(&req, &mut pl).await;
386 let err = format!("{}", s.unwrap_err());
387 assert_eq!(
388 err,
389 "URL encoded payload is larger (9 bytes) than allowed (limit: 8 bytes).",
390 );
391
392 let (req, mut pl) = TestRequest::default()
393 .insert_header(header::ContentType::form_url_encoded())
394 .insert_header((
395 header::CONTENT_LENGTH,
396 header::HeaderValue::from_static("9"),
397 ))
398 .set_payload(Bytes::from_static(b"name=test"))
399 .to_http_parts();
400 let s = UrlEncodedForm::<MyObject, 8>::from_request(&req, &mut pl).await;
401 let err = format!("{}", s.unwrap_err());
402 assert!(
403 err.contains("payload is larger") && err.contains("than allowed"),
404 "unexpected error string: {err:?}"
405 );
406 }
407
408 #[actix_web::test]
409 async fn test_form_body() {
410 let (req, mut pl) = TestRequest::default().to_http_parts();
411 let form =
412 UrlEncodedFormBody::<MyObject, DEFAULT_URL_ENCODED_FORM_LIMIT>::new(&req, &mut pl)
413 .await;
414 assert!(err_eq(form.unwrap_err(), UrlEncodedFormError::ContentType));
415
416 let (req, mut pl) = TestRequest::default()
417 .insert_header((
418 header::CONTENT_TYPE,
419 header::HeaderValue::from_static("application/text"),
420 ))
421 .to_http_parts();
422 let form =
423 UrlEncodedFormBody::<MyObject, DEFAULT_URL_ENCODED_FORM_LIMIT>::new(&req, &mut pl)
424 .await;
425 assert!(err_eq(form.unwrap_err(), UrlEncodedFormError::ContentType));
426
427 let (req, mut pl) = TestRequest::default()
428 .insert_header(header::ContentType::form_url_encoded())
429 .insert_header((
430 header::CONTENT_LENGTH,
431 header::HeaderValue::from_static("10000"),
432 ))
433 .to_http_parts();
434
435 let form = UrlEncodedFormBody::<MyObject, 100>::new(&req, &mut pl).await;
436 assert!(err_eq(
437 form.unwrap_err(),
438 UrlEncodedFormError::Overflow {
439 size: 10000,
440 limit: 100
441 }
442 ));
443
444 let (req, mut pl) = TestRequest::default()
445 .insert_header(header::ContentType::form_url_encoded())
446 .set_payload(Bytes::from_static(&[0u8; 1000]))
447 .to_http_parts();
448
449 let form = UrlEncodedFormBody::<MyObject, 100>::new(&req, &mut pl).await;
450
451 assert!(err_eq(
452 form.unwrap_err(),
453 UrlEncodedFormError::Overflow {
454 size: 1000,
455 limit: 100
456 }
457 ));
458
459 let (req, mut pl) = TestRequest::default()
460 .insert_header(header::ContentType::form_url_encoded())
461 .insert_header((
462 header::CONTENT_LENGTH,
463 header::HeaderValue::from_static("9"),
464 ))
465 .set_payload(Bytes::from_static(b"name=test"))
466 .to_http_parts();
467
468 let form =
469 UrlEncodedFormBody::<MyObject, DEFAULT_URL_ENCODED_FORM_LIMIT>::new(&req, &mut pl)
470 .await;
471 assert_eq!(
472 form.ok().unwrap(),
473 MyObject {
474 name: "test".to_owned()
475 }
476 );
477 }
478
479 #[actix_web::test]
480 async fn test_with_form_and_bad_content_type() {
481 let (req, mut pl) = TestRequest::default()
482 .insert_header((
483 header::CONTENT_TYPE,
484 header::HeaderValue::from_static("text/plain"),
485 ))
486 .insert_header((
487 header::CONTENT_LENGTH,
488 header::HeaderValue::from_static("9"),
489 ))
490 .set_payload(Bytes::from_static(b"name=test"))
491 .to_http_parts();
492
493 let s = UrlEncodedForm::<MyObject, 4096>::from_request(&req, &mut pl).await;
494 assert!(s.is_err())
495 }
496
497 #[actix_web::test]
498 async fn test_with_config_in_data_wrapper() {
499 let (req, mut pl) = TestRequest::default()
500 .insert_header(header::ContentType::form_url_encoded())
501 .insert_header((header::CONTENT_LENGTH, 9))
502 .set_payload(Bytes::from_static(b"name=test"))
503 .to_http_parts();
504
505 let s = UrlEncodedForm::<MyObject, 8>::from_request(&req, &mut pl).await;
506 assert!(s.is_err());
507
508 let err_str = s.unwrap_err().to_string();
509 assert_eq!(
510 err_str,
511 "URL encoded payload is larger (9 bytes) than allowed (limit: 8 bytes).",
512 );
513 }
514}