1use std::{
4 borrow::Cow,
5 future::Future,
6 pin::Pin,
7 str,
8 task::{Context, Poll},
9};
10
11use actix_http::error::PayloadError;
12#[expect(deprecated, reason = "preserve the public ready future type")]
13use actix_utils::future::{ready, Either, Ready};
14use bytes::{Bytes, BytesMut};
15use encoding_rs::{Encoding, UTF_8};
16use futures_core::{ready, stream::Stream};
17use mime::Mime;
18
19use crate::{
20 body, dev, error::ErrorBadRequest, http::header, web, Error, FromRequest, HttpMessage,
21 HttpRequest,
22};
23
24pub struct Payload(dev::Payload);
48
49impl Payload {
50 #[inline]
52 pub fn into_inner(self) -> dev::Payload {
53 self.0
54 }
55
56 pub async fn to_bytes_limited(
84 self,
85 limit: usize,
86 ) -> Result<crate::Result<Bytes>, body::BodyLimitExceeded> {
87 let stream = body::BodyStream::new(self.0);
88
89 match body::to_bytes_limited(stream, limit).await {
90 Ok(Ok(body)) => Ok(Ok(body)),
91 Ok(Err(err)) => Ok(Err(err.into())),
92 Err(err) => Err(err),
93 }
94 }
95
96 pub async fn to_bytes(self) -> crate::Result<Bytes> {
118 let stream = body::BodyStream::new(self.0);
119 Ok(body::to_bytes(stream).await?)
120 }
121}
122
123impl Stream for Payload {
124 type Item = Result<Bytes, PayloadError>;
125
126 #[inline]
127 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
128 Pin::new(&mut self.0).poll_next(cx)
129 }
130}
131
132#[expect(deprecated, reason = "preserve the public ready future type")]
134impl FromRequest for Payload {
135 type Error = Error;
136 type Future = Ready<Result<Self, Self::Error>>;
137
138 #[inline]
139 fn from_request(_: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
140 ready(Ok(Payload(payload.take())))
141 }
142}
143
144#[expect(deprecated, reason = "preserve the public ready future type")]
161impl FromRequest for Bytes {
162 type Error = Error;
163 type Future = Either<BytesExtractFut, Ready<Result<Bytes, Error>>>;
164
165 #[inline]
166 fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
167 let cfg = PayloadConfig::from_req(req);
169
170 if let Err(err) = cfg.check_mimetype(req) {
171 return Either::right(ready(Err(err)));
172 }
173
174 Either::left(BytesExtractFut {
175 body_fut: HttpMessageBody::new(req, payload).limit(cfg.limit),
176 })
177 }
178}
179
180pub struct BytesExtractFut {
182 body_fut: HttpMessageBody,
183}
184
185impl Future for BytesExtractFut {
186 type Output = Result<Bytes, Error>;
187
188 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
189 Pin::new(&mut self.body_fut).poll(cx).map_err(Into::into)
190 }
191}
192
193#[expect(deprecated, reason = "preserve the public ready future type")]
209impl FromRequest for String {
210 type Error = Error;
211 type Future = Either<StringExtractFut, Ready<Result<String, Error>>>;
212
213 #[inline]
214 fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
215 let cfg = PayloadConfig::from_req(req);
216
217 if let Err(err) = cfg.check_mimetype(req) {
219 return Either::right(ready(Err(err)));
220 }
221
222 let encoding = match req.encoding() {
224 Ok(enc) => enc,
225 Err(err) => return Either::right(ready(Err(err.into()))),
226 };
227 let limit = cfg.limit;
228 let body_fut = HttpMessageBody::new(req, payload).limit(limit);
229
230 Either::left(StringExtractFut { body_fut, encoding })
231 }
232}
233
234pub struct StringExtractFut {
236 body_fut: HttpMessageBody,
237 encoding: &'static Encoding,
238}
239
240impl Future for StringExtractFut {
241 type Output = Result<String, Error>;
242
243 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
244 let encoding = self.encoding;
245
246 Pin::new(&mut self.body_fut).poll(cx).map(|out| {
247 let body = out?;
248 bytes_to_string(body, encoding)
249 })
250 }
251}
252
253fn bytes_to_string(body: Bytes, encoding: &'static Encoding) -> Result<String, Error> {
254 if encoding == UTF_8 {
255 Ok(str::from_utf8(body.as_ref())
256 .map_err(|_| ErrorBadRequest("Can not decode body"))?
257 .to_owned())
258 } else {
259 Ok(encoding
260 .decode_without_bom_handling_and_without_replacement(&body)
261 .map(Cow::into_owned)
262 .ok_or_else(|| ErrorBadRequest("Can not decode body"))?)
263 }
264}
265
266#[derive(Clone)]
278pub struct PayloadConfig {
279 limit: usize,
280 mimetype: Option<Mime>,
281}
282
283impl PayloadConfig {
284 pub fn new(limit: usize) -> Self {
286 Self {
287 limit,
288 ..Default::default()
289 }
290 }
291
292 pub fn limit(mut self, limit: usize) -> Self {
294 self.limit = limit;
295 self
296 }
297
298 pub fn mimetype(mut self, mt: Mime) -> Self {
300 self.mimetype = Some(mt);
301 self
302 }
303
304 fn check_mimetype(&self, req: &HttpRequest) -> Result<(), Error> {
305 if let Some(ref mt) = self.mimetype {
307 match req.mime_type() {
308 Ok(Some(ref req_mt)) => {
309 if mt != req_mt {
310 return Err(ErrorBadRequest("Unexpected Content-Type"));
311 }
312 }
313 Ok(None) => {
314 return Err(ErrorBadRequest("Content-Type is expected"));
315 }
316 Err(err) => {
317 return Err(err.into());
318 }
319 }
320 }
321
322 Ok(())
323 }
324
325 fn from_req(req: &HttpRequest) -> &Self {
328 req.app_data::<Self>()
329 .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))
330 .unwrap_or(&DEFAULT_CONFIG)
331 }
332}
333
334const DEFAULT_CONFIG_LIMIT: usize = 262_144; const DEFAULT_CONFIG: PayloadConfig = PayloadConfig {
338 limit: DEFAULT_CONFIG_LIMIT,
339 mimetype: None,
340};
341
342impl Default for PayloadConfig {
343 fn default() -> Self {
344 DEFAULT_CONFIG
345 }
346}
347
348pub struct HttpMessageBody {
353 limit: usize,
354 length: Option<usize>,
355 #[cfg(feature = "__compress")]
356 stream: dev::Decompress<dev::Payload>,
357 #[cfg(not(feature = "__compress"))]
358 stream: dev::Payload,
359 buf: BytesMut,
360 err: Option<PayloadError>,
361}
362
363impl HttpMessageBody {
364 #[allow(clippy::borrow_interior_mutable_const)]
366 pub fn new(req: &HttpRequest, payload: &mut dev::Payload) -> HttpMessageBody {
367 let mut length = None;
368 let mut err = None;
369
370 if let Some(l) = req.headers().get(&header::CONTENT_LENGTH) {
371 match l.to_str() {
372 Ok(s) => match s.parse::<usize>() {
373 Ok(l) => {
374 if l > DEFAULT_CONFIG_LIMIT {
375 err = Some(PayloadError::Overflow);
376 }
377 length = Some(l)
378 }
379 Err(_) => err = Some(PayloadError::UnknownLength),
380 },
381 Err(_) => err = Some(PayloadError::UnknownLength),
382 }
383 }
384
385 let stream = {
386 cfg_if::cfg_if! {
387 if #[cfg(feature = "__compress")] {
388 dev::Decompress::from_headers(payload.take(), req.headers())
389 .limit(DEFAULT_CONFIG_LIMIT)
390 } else {
391 payload.take()
392 }
393 }
394 };
395
396 HttpMessageBody {
397 stream,
398 limit: DEFAULT_CONFIG_LIMIT,
399 length,
400 buf: BytesMut::with_capacity(8192),
401 err,
402 }
403 }
404
405 pub fn limit(mut self, limit: usize) -> Self {
407 if let Some(l) = self.length {
408 self.err = if l > limit {
409 Some(PayloadError::Overflow)
410 } else {
411 None
412 };
413 }
414 self.limit = limit;
415
416 #[cfg(feature = "__compress")]
417 {
418 self.stream = self.stream.limit(limit);
419 }
420
421 self
422 }
423}
424
425impl Future for HttpMessageBody {
426 type Output = Result<Bytes, PayloadError>;
427
428 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
429 let this = self.get_mut();
430
431 if let Some(err) = this.err.take() {
432 return Poll::Ready(Err(err));
433 }
434
435 loop {
436 let res = ready!(Pin::new(&mut this.stream).poll_next(cx));
437 match res {
438 Some(chunk) => {
439 let chunk = chunk?;
440 if this.buf.len() + chunk.len() > this.limit {
441 return Poll::Ready(Err(PayloadError::Overflow));
442 } else {
443 this.buf.extend_from_slice(&chunk);
444 }
445 }
446 None => return Poll::Ready(Ok(this.buf.split().freeze())),
447 }
448 }
449 }
450}
451
452#[cfg(test)]
453mod tests {
454 use super::*;
455 use crate::{
456 http::StatusCode,
457 test::{call_service, init_service, read_body, TestRequest},
458 App, Responder,
459 };
460
461 #[actix_rt::test]
462 async fn payload_to_bytes() {
463 async fn payload_handler(pl: Payload) -> crate::Result<impl Responder> {
464 pl.to_bytes().await
465 }
466
467 async fn limited_payload_handler(pl: Payload) -> crate::Result<impl Responder> {
468 match pl.to_bytes_limited(5).await {
469 Ok(res) => res,
470 Err(_limited) => Err(ErrorBadRequest("too big")),
471 }
472 }
473
474 let srv = init_service(
475 App::new()
476 .route("/all", web::to(payload_handler))
477 .route("limited", web::to(limited_payload_handler)),
478 )
479 .await;
480
481 let req = TestRequest::with_uri("/all")
482 .set_payload("1234567890")
483 .to_request();
484 let res = call_service(&srv, req).await;
485 assert_eq!(res.status(), StatusCode::OK);
486 let body = read_body(res).await;
487 assert_eq!(body, "1234567890");
488
489 let req = TestRequest::with_uri("/limited")
490 .set_payload("1234567890")
491 .to_request();
492 let res = call_service(&srv, req).await;
493 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
494
495 let req = TestRequest::with_uri("/limited")
496 .set_payload("12345")
497 .to_request();
498 let res = call_service(&srv, req).await;
499 assert_eq!(res.status(), StatusCode::OK);
500 let body = read_body(res).await;
501 assert_eq!(body, "12345");
502 }
503
504 #[actix_rt::test]
505 async fn test_payload_config() {
506 let req = TestRequest::default().to_http_request();
507 let cfg = PayloadConfig::default().mimetype(mime::APPLICATION_JSON);
508 assert!(cfg.check_mimetype(&req).is_err());
509
510 let req = TestRequest::default()
511 .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
512 .to_http_request();
513 assert!(cfg.check_mimetype(&req).is_err());
514
515 let req = TestRequest::default()
516 .insert_header((header::CONTENT_TYPE, "application/json"))
517 .to_http_request();
518 assert!(cfg.check_mimetype(&req).is_ok());
519 }
520
521 #[allow(deprecated)]
523 #[actix_rt::test]
524 async fn test_config_recall_locations() {
525 async fn bytes_handler(_: Bytes) -> impl Responder {
526 "payload is probably json bytes"
527 }
528
529 async fn string_handler(_: String) -> impl Responder {
530 "payload is probably json string"
531 }
532
533 let srv = init_service(
534 App::new()
535 .service(
536 web::resource("/bytes-app-data")
537 .app_data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))
538 .route(web::get().to(bytes_handler)),
539 )
540 .service(
541 web::resource("/bytes-data")
542 .data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))
543 .route(web::get().to(bytes_handler)),
544 )
545 .service(
546 web::resource("/string-app-data")
547 .app_data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))
548 .route(web::get().to(string_handler)),
549 )
550 .service(
551 web::resource("/string-data")
552 .data(PayloadConfig::default().mimetype(mime::APPLICATION_JSON))
553 .route(web::get().to(string_handler)),
554 ),
555 )
556 .await;
557
558 let req = TestRequest::with_uri("/bytes-app-data").to_request();
559 let resp = call_service(&srv, req).await;
560 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
561
562 let req = TestRequest::with_uri("/bytes-data").to_request();
563 let resp = call_service(&srv, req).await;
564 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
565
566 let req = TestRequest::with_uri("/string-app-data").to_request();
567 let resp = call_service(&srv, req).await;
568 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
569
570 let req = TestRequest::with_uri("/string-data").to_request();
571 let resp = call_service(&srv, req).await;
572 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
573
574 let req = TestRequest::with_uri("/bytes-app-data")
575 .insert_header(header::ContentType(mime::APPLICATION_JSON))
576 .to_request();
577 let resp = call_service(&srv, req).await;
578 assert_eq!(resp.status(), StatusCode::OK);
579
580 let req = TestRequest::with_uri("/bytes-data")
581 .insert_header(header::ContentType(mime::APPLICATION_JSON))
582 .to_request();
583 let resp = call_service(&srv, req).await;
584 assert_eq!(resp.status(), StatusCode::OK);
585
586 let req = TestRequest::with_uri("/string-app-data")
587 .insert_header(header::ContentType(mime::APPLICATION_JSON))
588 .to_request();
589 let resp = call_service(&srv, req).await;
590 assert_eq!(resp.status(), StatusCode::OK);
591
592 let req = TestRequest::with_uri("/string-data")
593 .insert_header(header::ContentType(mime::APPLICATION_JSON))
594 .to_request();
595 let resp = call_service(&srv, req).await;
596 assert_eq!(resp.status(), StatusCode::OK);
597 }
598
599 #[actix_rt::test]
600 async fn test_bytes() {
601 let (req, mut pl) = TestRequest::default()
602 .insert_header((header::CONTENT_LENGTH, "11"))
603 .set_payload(Bytes::from_static(b"hello=world"))
604 .to_http_parts();
605
606 let s = Bytes::from_request(&req, &mut pl).await.unwrap();
607 assert_eq!(s, Bytes::from_static(b"hello=world"));
608 }
609
610 #[actix_rt::test]
611 async fn test_string() {
612 let (req, mut pl) = TestRequest::default()
613 .insert_header((header::CONTENT_LENGTH, "11"))
614 .set_payload(Bytes::from_static(b"hello=world"))
615 .to_http_parts();
616
617 let s = String::from_request(&req, &mut pl).await.unwrap();
618 assert_eq!(s, "hello=world");
619 }
620
621 #[actix_rt::test]
622 async fn test_message_body() {
623 let (req, mut pl) = TestRequest::default()
624 .insert_header((header::CONTENT_LENGTH, "xxxx"))
625 .to_srv_request()
626 .into_parts();
627 let res = HttpMessageBody::new(&req, &mut pl).await;
628 match res.err().unwrap() {
629 PayloadError::UnknownLength => {}
630 _ => unreachable!("error"),
631 }
632
633 let (req, mut pl) = TestRequest::default()
634 .insert_header((header::CONTENT_LENGTH, "1000000"))
635 .to_srv_request()
636 .into_parts();
637 let res = HttpMessageBody::new(&req, &mut pl).await;
638 match res.err().unwrap() {
639 PayloadError::Overflow => {}
640 _ => unreachable!("error"),
641 }
642
643 let (req, mut pl) = TestRequest::default()
644 .set_payload(Bytes::from_static(b"test"))
645 .to_http_parts();
646 let res = HttpMessageBody::new(&req, &mut pl).await;
647 assert_eq!(res.ok().unwrap(), Bytes::from_static(b"test"));
648
649 let (req, mut pl) = TestRequest::default()
650 .set_payload(Bytes::from_static(b"11111111111111"))
651 .to_http_parts();
652 let res = HttpMessageBody::new(&req, &mut pl).limit(5).await;
653 match res.err().unwrap() {
654 PayloadError::Overflow => {}
655 _ => unreachable!("error"),
656 }
657 }
658}