1use std::{borrow::Cow, fmt, future::Future, ops, pin::Pin, task::Context, task::Poll};
3
4use encoding_rs::{Encoding, UTF_8};
5use serde::{Serialize, de::DeserializeOwned};
6
7#[cfg(feature = "compress")]
8use crate::http::encoding::Decoder;
9use crate::http::header::{CONTENT_LENGTH, CONTENT_TYPE};
10use crate::http::{HttpMessage, Payload, Response, StatusCode};
11use crate::util::BoxFuture;
12use crate::web::error::{UrlencodedError, WebResponseError};
13use crate::web::{FromRequest, HttpRequest, Responder, State};
14
15#[derive(PartialEq, Eq, PartialOrd, Ord)]
76pub struct Form<T>(pub T);
77
78impl<T> Form<T> {
79 pub fn into_inner(self) -> T {
81 self.0
82 }
83}
84
85impl<T> ops::Deref for Form<T> {
86 type Target = T;
87
88 fn deref(&self) -> &T {
89 &self.0
90 }
91}
92
93impl<T> ops::DerefMut for Form<T> {
94 fn deref_mut(&mut self) -> &mut T {
95 &mut self.0
96 }
97}
98
99impl<St, T> FromRequest<St> for Form<T>
100where
101 St: State,
102 T: DeserializeOwned + 'static,
103{
104 type Error = UrlencodedError;
105
106 async fn from_request(
107 _: &St,
108 req: &HttpRequest,
109 payload: &mut Payload,
110 ) -> Result<Self, Self::Error> {
111 let limit = req.app_state::<FormConfig>().map_or(16384, |c| c.limit);
112
113 match UrlEncoded::new(req, payload).limit(limit).await {
114 Err(e) => Err(e),
115 Ok(item) => Ok(Form(item)),
116 }
117 }
118}
119
120impl<T: fmt::Debug> fmt::Debug for Form<T> {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.debug_tuple("Form").field(&self.0).finish()
123 }
124}
125
126impl<T: fmt::Display> fmt::Display for Form<T> {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 self.0.fmt(f)
129 }
130}
131
132impl<St, T: Serialize> Responder<St> for Form<T>
133where
134 St: State,
135 serde_urlencoded::ser::Error: WebResponseError<St, St::Error>,
136{
137 async fn respond_to(self, st: &St, _: &HttpRequest) -> Response {
138 let body = match serde_urlencoded::to_string(&self.0) {
139 Ok(body) => body,
140 Err(e) => return e.error_response(st),
141 };
142
143 Response::builder(StatusCode::OK)
144 .header(CONTENT_TYPE, "application/x-www-form-urlencoded")
145 .body(body)
146 }
147}
148
149#[derive(Clone, Debug)]
177pub struct FormConfig {
178 limit: usize,
179}
180
181impl FormConfig {
182 #[must_use]
183 pub fn limit(mut self, limit: usize) -> Self {
187 self.limit = limit;
188 self
189 }
190}
191
192impl Default for FormConfig {
193 fn default() -> Self {
194 FormConfig { limit: 16384 }
195 }
196}
197
198struct UrlEncoded<U> {
211 #[cfg(feature = "compress")]
212 stream: Option<Decoder<Payload>>,
213 #[cfg(not(feature = "compress"))]
214 stream: Option<Payload>,
215 limit: usize,
216 length: Option<usize>,
217 encoding: &'static Encoding,
218 err: Option<UrlencodedError>,
219 fut: Option<BoxFuture<'static, Result<U, UrlencodedError>>>,
220}
221
222impl<U> UrlEncoded<U> {
223 fn new(req: &HttpRequest, payload: &mut Payload) -> UrlEncoded<U> {
225 if !req
227 .content_type()
228 .eq_ignore_ascii_case("application/x-www-form-urlencoded")
229 {
230 return Self::err(UrlencodedError::ContentType);
231 }
232 let Ok(encoding) = req.encoding() else {
233 return Self::err(UrlencodedError::ContentType);
234 };
235
236 let mut len = None;
237 if let Some(l) = req.headers().get(&CONTENT_LENGTH) {
238 if let Ok(s) = l.to_str() {
239 if let Ok(l) = s.parse::<usize>() {
240 len = Some(l);
241 } else {
242 return Self::err(UrlencodedError::UnknownLength);
243 }
244 } else {
245 return Self::err(UrlencodedError::UnknownLength);
246 }
247 }
248
249 #[cfg(feature = "compress")]
250 let payload = Decoder::from_headers(payload.take(), req.headers());
251 #[cfg(not(feature = "compress"))]
252 let payload = payload.take();
253
254 UrlEncoded {
255 encoding,
256 stream: Some(payload),
257 limit: 32_768,
258 length: len,
259 fut: None,
260 err: None,
261 }
262 }
263
264 fn err(e: UrlencodedError) -> Self {
265 UrlEncoded {
266 stream: None,
267 limit: 32_768,
268 fut: None,
269 err: Some(e),
270 length: None,
271 encoding: UTF_8,
272 }
273 }
274
275 fn limit(mut self, limit: usize) -> Self {
277 self.limit = limit;
278 self
279 }
280}
281
282impl<U> Future for UrlEncoded<U>
283where
284 U: DeserializeOwned + 'static,
285{
286 type Output = Result<U, UrlencodedError>;
287
288 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
289 if let Some(ref mut fut) = self.fut {
290 return Pin::new(fut).poll(cx);
291 }
292
293 if let Some(err) = self.err.take() {
294 return Poll::Ready(Err(err));
295 }
296
297 let (limit, length) = (self.limit, self.length);
299 if let Some(len) = length
300 && len > limit
301 {
302 return Poll::Ready(Err(UrlencodedError::Overflow { size: len, limit }));
303 }
304
305 let encoding = self.encoding;
307 let mut stream = self.stream.take().unwrap();
308
309 self.fut = Some(Box::pin(async move {
310 let body = super::read_body(&mut stream, limit, length, |size| {
311 UrlencodedError::Overflow { size, limit }
312 })
313 .await?;
314
315 if encoding == UTF_8 {
316 serde_urlencoded::from_bytes::<U>(&body).map_err(|_| UrlencodedError::Parse)
317 } else {
318 let body = encoding
319 .decode_without_bom_handling_and_without_replacement(&body)
320 .map(Cow::into_owned)
321 .ok_or(UrlencodedError::Parse)?;
322 serde_urlencoded::from_str::<U>(&body).map_err(|_| UrlencodedError::Parse)
323 }
324 }));
325 self.poll(cx)
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use serde::{Deserialize, Serialize};
332
333 use super::*;
334 use crate::http::header::HeaderValue;
335 use crate::util::Bytes;
336 use crate::web::test::{TestRequest, from_request, respond_to};
337
338 #[derive(Deserialize, Serialize, Debug, PartialEq, thiserror::Error)]
339 #[error("Info({hello})")]
340 struct Info {
341 hello: String,
342 counter: i64,
343 }
344
345 fn eq(err: &UrlencodedError, other: &UrlencodedError) -> bool {
346 if let UrlencodedError::Overflow { .. } = err
347 && let UrlencodedError::Overflow { .. } = other
348 {
349 return true;
350 } else if let UrlencodedError::UnknownLength = err
351 && let UrlencodedError::UnknownLength = other
352 {
353 return true;
354 } else if let UrlencodedError::ContentType = err
355 && let UrlencodedError::ContentType = other
356 {
357 return true;
358 }
359 false
360 }
361
362 #[test]
363 fn test_basic() {
364 let mut f = Form(Info {
365 hello: "world".into(),
366 counter: 123,
367 });
368 assert_eq!(f.hello, "world");
369 f.hello = "test".to_string();
370 assert_eq!(f.hello, "test");
371 assert!(format!("{f:?}").contains("Form"));
372 assert!(format!("{f}").contains("test"));
373 }
374
375 #[crate::rt_test]
376 async fn test_form() {
377 let (req, mut pl, ()) =
378 TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
379 .header(CONTENT_LENGTH, "11")
380 .payload(Bytes::from_static(b"hello=world&counter=123"))
381 .to_http_parts();
382
383 let Form(s) = from_request::<_, Form<Info>>(&(), &req, &mut pl)
384 .await
385 .unwrap();
386 assert_eq!(
387 s,
388 Info {
389 hello: "world".into(),
390 counter: 123
391 }
392 );
393
394 let (req, mut pl, ()) =
395 TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
396 .header(CONTENT_LENGTH, "xx")
397 .payload(Bytes::from_static(b"hello=world&counter=123"))
398 .to_http_parts();
399 let res = from_request::<_, Form<Info>>(&(), &req, &mut pl).await;
400 assert!(eq(&res.err().unwrap(), &UrlencodedError::UnknownLength));
401 }
402
403 #[crate::rt_test]
404 async fn test_urlencoded_error() {
405 let (req, mut pl, ()) =
406 TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
407 .header(CONTENT_LENGTH, "xxxx")
408 .to_http_parts();
409 let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
410 assert!(eq(&info.err().unwrap(), &UrlencodedError::UnknownLength));
411
412 let (req, mut pl, ()) =
413 TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
414 .header(CONTENT_LENGTH, "1000000")
415 .to_http_parts();
416 let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
417 assert!(eq(
418 &info.err().unwrap(),
419 &UrlencodedError::Overflow { size: 0, limit: 0 }
420 ));
421
422 let (req, mut pl, ()) = TestRequest::with_header(CONTENT_TYPE, "text/plain")
423 .header(CONTENT_LENGTH, "10")
424 .to_http_parts();
425 let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
426 assert!(eq(&info.err().unwrap(), &UrlencodedError::ContentType));
427 }
428
429 #[crate::rt_test]
430 async fn test_urlencoded() {
431 let (req, mut pl, ()) =
432 TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
433 .header(CONTENT_LENGTH, "11")
434 .payload(Bytes::from_static(b"hello=world&counter=123"))
435 .to_http_parts();
436
437 let info = UrlEncoded::<Info>::new(&req, &mut pl).await.unwrap();
438 assert_eq!(
439 info,
440 Info {
441 hello: "world".to_owned(),
442 counter: 123
443 }
444 );
445
446 let (req, mut pl, ()) =
447 TestRequest::with_header(CONTENT_TYPE, "Application/X-WWW-Form-URLEncoded")
448 .header(CONTENT_LENGTH, "11")
449 .payload(Bytes::from_static(b"hello=world&counter=123"))
450 .to_http_parts();
451 assert!(UrlEncoded::<Info>::new(&req, &mut pl).await.is_ok());
452
453 let (req, mut pl, ()) = TestRequest::with_header(
454 CONTENT_TYPE,
455 "application/x-www-form-urlencoded; charset=utf-8",
456 )
457 .header(CONTENT_LENGTH, "11")
458 .payload(Bytes::from_static(b"hello=world&counter=123"))
459 .to_http_parts();
460
461 let info = UrlEncoded::<Info>::new(&req, &mut pl).await.unwrap();
462 assert_eq!(
463 info,
464 Info {
465 hello: "world".to_owned(),
466 counter: 123
467 }
468 );
469
470 let (req, mut pl, ()) = TestRequest::with_header(
471 CONTENT_TYPE,
472 "application/x-www-form-urlencoded; charset=cp1251",
473 )
474 .header(CONTENT_LENGTH, "11")
475 .payload(Bytes::from_static(b"hello=world&counter=123"))
476 .to_http_parts();
477
478 let info = UrlEncoded::<Info>::new(&req, &mut pl).await.unwrap();
479 assert_eq!(
480 info,
481 Info {
482 hello: "world".to_owned(),
483 counter: 123
484 }
485 );
486 }
487
488 #[crate::rt_test]
489 async fn test_responder() {
490 let req = TestRequest::default().to_http_request();
491
492 let form = Form(Info {
493 hello: "world".to_string(),
494 counter: 123,
495 });
496 let resp = respond_to(form, &req).await;
497 assert_eq!(resp.status(), StatusCode::OK);
498 assert_eq!(
499 resp.headers().get(CONTENT_TYPE).unwrap(),
500 HeaderValue::from_static("application/x-www-form-urlencoded")
501 );
502
503 assert_eq!(resp.get_body_ref(), b"hello=world&counter=123");
504 }
505
506 #[crate::rt_test]
507 async fn test_urlencoded_errors2() {
508 let (req, mut pl, ()) = TestRequest::with_header(
509 CONTENT_TYPE,
510 "application/x-www-form-urlencoded; charset=unknown",
511 )
512 .to_http_parts();
513 let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
514 assert!(eq(&info.err().unwrap(), &UrlencodedError::ContentType));
515
516 let (req, mut pl, ()) =
517 TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
518 .header(CONTENT_LENGTH, HeaderValue::from_bytes(b"1\xff").unwrap())
519 .to_http_parts();
520 let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
521 assert!(eq(&info.err().unwrap(), &UrlencodedError::UnknownLength));
522
523 let (req, mut pl, ()) =
525 TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded")
526 .payload(Bytes::from_static(b"hello=world&counter=123"))
527 .to_http_parts();
528 let info = UrlEncoded::<Info>::new(&req, &mut pl).limit(5).await;
529 assert!(eq(
530 &info.err().unwrap(),
531 &UrlencodedError::Overflow { size: 0, limit: 0 }
532 ));
533 }
534
535 #[crate::rt_test]
536 async fn test_responder_error() {
537 let req = TestRequest::default().to_http_request();
538 let resp = respond_to(Form(1), &req).await;
539 assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
540 }
541}