1use std::{
4 fmt,
5 future::Future,
6 marker::PhantomData,
7 ops,
8 pin::Pin,
9 sync::Arc,
10 task::{Context, Poll},
11};
12
13use actix_http::{error::PayloadError, Payload};
14use bytes::BytesMut;
15use futures_core::{ready, Stream as _};
16use serde::{de::DeserializeOwned, Serialize};
17
18#[cfg(feature = "__compress")]
19use crate::dev::Decompress;
20use crate::{
21 body::EitherBody,
22 error::{Error, JsonPayloadError},
23 extract::FromRequest,
24 http::header::{ContentLength, Header as _},
25 request::HttpRequest,
26 web, HttpMessage, HttpResponse, Responder,
27};
28
29#[derive(Debug)]
77pub struct Json<T>(pub T);
78
79impl<T> Json<T> {
80 pub fn into_inner(self) -> T {
82 self.0
83 }
84}
85
86impl<T> ops::Deref for Json<T> {
87 type Target = T;
88
89 fn deref(&self) -> &T {
90 &self.0
91 }
92}
93
94impl<T> ops::DerefMut for Json<T> {
95 fn deref_mut(&mut self) -> &mut T {
96 &mut self.0
97 }
98}
99
100impl<T: fmt::Display> fmt::Display for Json<T> {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 fmt::Display::fmt(&self.0, f)
103 }
104}
105
106impl<T: Serialize> Serialize for Json<T> {
107 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
108 where
109 S: serde::Serializer,
110 {
111 self.0.serialize(serializer)
112 }
113}
114
115impl<T: Serialize> Responder for Json<T> {
119 type Body = EitherBody<String>;
120
121 fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
122 match serde_json::to_string(&self.0) {
123 Ok(body) => match HttpResponse::Ok()
124 .content_type(mime::APPLICATION_JSON)
125 .message_body(body)
126 {
127 Ok(res) => res.map_into_left_body(),
128 Err(err) => HttpResponse::from_error(err).map_into_right_body(),
129 },
130
131 Err(err) => {
132 HttpResponse::from_error(JsonPayloadError::Serialize(err)).map_into_right_body()
133 }
134 }
135 }
136}
137
138impl<T: DeserializeOwned> FromRequest for Json<T> {
140 type Error = Error;
141 type Future = JsonExtractFut<T>;
142
143 #[inline]
144 fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
145 let config = JsonConfig::from_req(req);
146
147 let limit = config.limit;
148 let ctype_required = config.content_type_required;
149 let ctype_fn = config.content_type.as_deref();
150 let err_handler = config.err_handler.clone();
151
152 JsonExtractFut {
153 req: Some(req.clone()),
154 fut: JsonBody::new(req, payload, ctype_fn, ctype_required).limit(limit),
155 err_handler,
156 }
157 }
158}
159
160type JsonErrorHandler = Option<Arc<dyn Fn(JsonPayloadError, &HttpRequest) -> Error + Send + Sync>>;
161
162pub struct JsonExtractFut<T> {
163 req: Option<HttpRequest>,
164 fut: JsonBody<T>,
165 err_handler: JsonErrorHandler,
166}
167
168impl<T: DeserializeOwned> Future for JsonExtractFut<T> {
169 type Output = Result<Json<T>, Error>;
170
171 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
172 let this = self.get_mut();
173
174 let res = ready!(Pin::new(&mut this.fut).poll(cx));
175
176 let res = match res {
177 Err(err) => {
178 let req = this.req.take().unwrap();
179 log::debug!(
180 "Failed to deserialize Json from payload. \
181 Request path: {}",
182 req.path()
183 );
184
185 if let Some(err_handler) = this.err_handler.as_ref() {
186 Err((*err_handler)(err, &req))
187 } else {
188 Err(err.into())
189 }
190 }
191 Ok(data) => Ok(Json(data)),
192 };
193
194 Poll::Ready(res)
195 }
196}
197
198#[derive(Clone)]
232pub struct JsonConfig {
233 limit: usize,
234 err_handler: JsonErrorHandler,
235 content_type: Option<Arc<dyn Fn(mime::Mime) -> bool + Send + Sync>>,
236 content_type_required: bool,
237}
238
239impl JsonConfig {
240 pub fn limit(mut self, limit: usize) -> Self {
242 self.limit = limit;
243 self
244 }
245
246 pub fn error_handler<F>(mut self, f: F) -> Self
248 where
249 F: Fn(JsonPayloadError, &HttpRequest) -> Error + Send + Sync + 'static,
250 {
251 self.err_handler = Some(Arc::new(f));
252 self
253 }
254
255 pub fn content_type<F>(mut self, predicate: F) -> Self
257 where
258 F: Fn(mime::Mime) -> bool + Send + Sync + 'static,
259 {
260 self.content_type = Some(Arc::new(predicate));
261 self
262 }
263
264 pub fn content_type_required(mut self, content_type_required: bool) -> Self {
266 self.content_type_required = content_type_required;
267 self
268 }
269
270 fn from_req(req: &HttpRequest) -> &Self {
273 req.app_data::<Self>()
274 .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))
275 .unwrap_or(&DEFAULT_CONFIG)
276 }
277}
278
279const DEFAULT_LIMIT: usize = 2_097_152; const DEFAULT_CONFIG: JsonConfig = JsonConfig {
283 limit: DEFAULT_LIMIT,
284 err_handler: None,
285 content_type: None,
286 content_type_required: true,
287};
288
289impl Default for JsonConfig {
290 fn default() -> Self {
291 DEFAULT_CONFIG
292 }
293}
294
295pub enum JsonBody<T> {
305 Error(Option<JsonPayloadError>),
306 Body {
307 limit: usize,
308 length: Option<usize>,
310 #[cfg(feature = "__compress")]
311 payload: Decompress<Payload>,
312 #[cfg(not(feature = "__compress"))]
313 payload: Payload,
314 buf: BytesMut,
315 _res: PhantomData<T>,
316 },
317}
318
319impl<T> Unpin for JsonBody<T> {}
320
321impl<T: DeserializeOwned> JsonBody<T> {
322 #[allow(clippy::borrow_interior_mutable_const)]
324 pub fn new(
325 req: &HttpRequest,
326 payload: &mut Payload,
327 ctype_fn: Option<&(dyn Fn(mime::Mime) -> bool + Send + Sync)>,
328 ctype_required: bool,
329 ) -> Self {
330 let can_parse_json = match (ctype_required, req.mime_type()) {
332 (true, Ok(Some(mime))) => {
333 mime.subtype() == mime::JSON
334 || mime.suffix() == Some(mime::JSON)
335 || ctype_fn.is_some_and(|predicate| predicate(mime))
336 }
337
338 (true, _) => false,
340
341 (false, _) => true,
344 };
345
346 if !can_parse_json {
347 return JsonBody::Error(Some(JsonPayloadError::ContentType));
348 }
349
350 let length = ContentLength::parse(req).ok().map(|x| x.0);
351
352 let payload = {
357 cfg_if::cfg_if! {
358 if #[cfg(feature = "__compress")] {
359 Decompress::from_headers(payload.take(), req.headers())
360 .limit(DEFAULT_LIMIT)
361 } else {
362 payload.take()
363 }
364 }
365 };
366
367 JsonBody::Body {
368 limit: DEFAULT_LIMIT,
369 length,
370 payload,
371 buf: BytesMut::with_capacity(8192),
372 _res: PhantomData,
373 }
374 }
375
376 pub fn limit(self, limit: usize) -> Self {
378 match self {
379 JsonBody::Body {
380 length,
381 payload,
382 buf,
383 ..
384 } => {
385 if let Some(len) = length {
386 if len > limit {
387 return JsonBody::Error(Some(JsonPayloadError::OverflowKnownLength {
388 length: len,
389 limit,
390 }));
391 }
392 }
393
394 #[cfg(feature = "__compress")]
395 let payload = payload.limit(limit);
396
397 JsonBody::Body {
398 limit,
399 length,
400 payload,
401 buf,
402 _res: PhantomData,
403 }
404 }
405 JsonBody::Error(err) => JsonBody::Error(err),
406 }
407 }
408}
409
410impl<T: DeserializeOwned> Future for JsonBody<T> {
411 type Output = Result<T, JsonPayloadError>;
412
413 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
414 let this = self.get_mut();
415
416 match this {
417 JsonBody::Body {
418 limit,
419 buf,
420 payload,
421 ..
422 } => loop {
423 let res = ready!(Pin::new(&mut *payload).poll_next(cx));
424 match res {
425 Some(chunk) => {
426 let chunk = chunk.map_err(|err| match err {
427 PayloadError::Overflow => JsonPayloadError::Overflow { limit: *limit },
428 err => JsonPayloadError::Payload(err),
429 })?;
430 let buf_len = buf.len() + chunk.len();
431 if buf_len > *limit {
432 return Poll::Ready(Err(JsonPayloadError::Overflow { limit: *limit }));
433 } else {
434 buf.extend_from_slice(&chunk);
435 }
436 }
437 None => {
438 let json = serde_json::from_slice::<T>(buf)
439 .map_err(JsonPayloadError::Deserialize)?;
440 return Poll::Ready(Ok(json));
441 }
442 }
443 },
444 JsonBody::Error(err) => Poll::Ready(Err(err.take().unwrap())),
445 }
446 }
447}
448
449#[cfg(test)]
450mod tests {
451 use bytes::Bytes;
452 use serde::{Deserialize, Serialize};
453
454 use super::*;
455 use crate::{
456 body,
457 error::InternalError,
458 http::{
459 header::{self, CONTENT_LENGTH, CONTENT_TYPE},
460 StatusCode,
461 },
462 test::{assert_body_eq, TestRequest},
463 };
464
465 #[derive(Serialize, Deserialize, PartialEq, Debug)]
466 struct MyObject {
467 name: String,
468 }
469
470 fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool {
471 match err {
472 JsonPayloadError::Overflow { .. } => {
473 matches!(other, JsonPayloadError::Overflow { .. })
474 }
475 JsonPayloadError::OverflowKnownLength { .. } => {
476 matches!(other, JsonPayloadError::OverflowKnownLength { .. })
477 }
478 JsonPayloadError::ContentType => matches!(other, JsonPayloadError::ContentType),
479 _ => false,
480 }
481 }
482
483 #[actix_rt::test]
484 async fn test_responder() {
485 let req = TestRequest::default().to_http_request();
486
487 let j = Json(MyObject {
488 name: "test".to_string(),
489 });
490 let res = j.respond_to(&req);
491 assert_eq!(res.status(), StatusCode::OK);
492 assert_eq!(
493 res.headers().get(header::CONTENT_TYPE).unwrap(),
494 header::HeaderValue::from_static("application/json")
495 );
496 assert_body_eq!(res, b"{\"name\":\"test\"}");
497 }
498
499 #[actix_rt::test]
500 async fn test_custom_error_responder() {
501 let (req, mut pl) = TestRequest::default()
502 .insert_header((
503 header::CONTENT_TYPE,
504 header::HeaderValue::from_static("application/json"),
505 ))
506 .insert_header((
507 header::CONTENT_LENGTH,
508 header::HeaderValue::from_static("16"),
509 ))
510 .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
511 .app_data(JsonConfig::default().limit(10).error_handler(|err, _| {
512 let msg = MyObject {
513 name: "invalid request".to_string(),
514 };
515 let resp = HttpResponse::BadRequest().body(serde_json::to_string(&msg).unwrap());
516 InternalError::from_response(err, resp).into()
517 }))
518 .to_http_parts();
519
520 let s = Json::<MyObject>::from_request(&req, &mut pl).await;
521 let resp = HttpResponse::from_error(s.unwrap_err());
522 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
523
524 let body = body::to_bytes(resp.into_body()).await.unwrap();
525 let msg: MyObject = serde_json::from_slice(&body).unwrap();
526 assert_eq!(msg.name, "invalid request");
527 }
528
529 #[actix_rt::test]
530 async fn test_extract() {
531 let (req, mut pl) = TestRequest::default()
532 .insert_header((
533 header::CONTENT_TYPE,
534 header::HeaderValue::from_static("application/json"),
535 ))
536 .insert_header((
537 header::CONTENT_LENGTH,
538 header::HeaderValue::from_static("16"),
539 ))
540 .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
541 .to_http_parts();
542
543 let s = Json::<MyObject>::from_request(&req, &mut pl).await.unwrap();
544 assert_eq!(s.name, "test");
545 assert_eq!(
546 s.into_inner(),
547 MyObject {
548 name: "test".to_string()
549 }
550 );
551
552 let (req, mut pl) = TestRequest::default()
553 .insert_header((
554 header::CONTENT_TYPE,
555 header::HeaderValue::from_static("application/json"),
556 ))
557 .insert_header((
558 header::CONTENT_LENGTH,
559 header::HeaderValue::from_static("16"),
560 ))
561 .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
562 .app_data(JsonConfig::default().limit(10))
563 .to_http_parts();
564
565 let s = Json::<MyObject>::from_request(&req, &mut pl).await;
566 assert!(format!("{}", s.err().unwrap())
567 .contains("JSON payload (16 bytes) is larger than allowed (limit: 10 bytes)."));
568
569 let (req, mut pl) = TestRequest::default()
570 .insert_header((
571 header::CONTENT_TYPE,
572 header::HeaderValue::from_static("application/json"),
573 ))
574 .insert_header((
575 header::CONTENT_LENGTH,
576 header::HeaderValue::from_static("16"),
577 ))
578 .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
579 .app_data(
580 JsonConfig::default()
581 .limit(10)
582 .error_handler(|_, _| JsonPayloadError::ContentType.into()),
583 )
584 .to_http_parts();
585 let s = Json::<MyObject>::from_request(&req, &mut pl).await;
586 assert!(format!("{}", s.err().unwrap()).contains("Content type error"));
587 }
588
589 #[actix_rt::test]
590 async fn test_json_body() {
591 let (req, mut pl) = TestRequest::default().to_http_parts();
592 let json = JsonBody::<MyObject>::new(&req, &mut pl, None, true).await;
593 assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
594
595 let (req, mut pl) = TestRequest::default()
596 .insert_header((
597 header::CONTENT_TYPE,
598 header::HeaderValue::from_static("application/text"),
599 ))
600 .to_http_parts();
601 let json = JsonBody::<MyObject>::new(&req, &mut pl, None, true).await;
602 assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
603
604 let (req, mut pl) = TestRequest::default()
605 .insert_header((
606 header::CONTENT_TYPE,
607 header::HeaderValue::from_static("application/json"),
608 ))
609 .insert_header((
610 header::CONTENT_LENGTH,
611 header::HeaderValue::from_static("10000"),
612 ))
613 .to_http_parts();
614
615 let json = JsonBody::<MyObject>::new(&req, &mut pl, None, true)
616 .limit(100)
617 .await;
618 assert!(json_eq(
619 json.err().unwrap(),
620 JsonPayloadError::OverflowKnownLength {
621 length: 10000,
622 limit: 100
623 }
624 ));
625
626 let (mut req, mut pl) = TestRequest::default()
627 .insert_header((
628 header::CONTENT_TYPE,
629 header::HeaderValue::from_static("application/json"),
630 ))
631 .set_payload(Bytes::from_static(&[0u8; 1000]))
632 .to_http_parts();
633
634 req.head_mut().headers_mut().remove(header::CONTENT_LENGTH);
635 let json = JsonBody::<MyObject>::new(&req, &mut pl, None, true)
636 .limit(100)
637 .await;
638
639 assert!(json_eq(
640 json.err().unwrap(),
641 JsonPayloadError::Overflow { limit: 100 }
642 ));
643
644 let (req, mut pl) = TestRequest::default()
645 .insert_header((
646 header::CONTENT_TYPE,
647 header::HeaderValue::from_static("application/json"),
648 ))
649 .insert_header((
650 header::CONTENT_LENGTH,
651 header::HeaderValue::from_static("16"),
652 ))
653 .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
654 .to_http_parts();
655
656 let json = JsonBody::<MyObject>::new(&req, &mut pl, None, true).await;
657 assert_eq!(
658 json.ok().unwrap(),
659 MyObject {
660 name: "test".to_owned()
661 }
662 );
663 }
664
665 #[actix_rt::test]
666 async fn test_with_json_and_bad_content_type() {
667 let (req, mut pl) = TestRequest::default()
668 .insert_header((
669 header::CONTENT_TYPE,
670 header::HeaderValue::from_static("text/plain"),
671 ))
672 .insert_header((
673 header::CONTENT_LENGTH,
674 header::HeaderValue::from_static("16"),
675 ))
676 .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
677 .app_data(JsonConfig::default().limit(4096))
678 .to_http_parts();
679
680 let s = Json::<MyObject>::from_request(&req, &mut pl).await;
681 assert!(s.is_err())
682 }
683
684 #[actix_rt::test]
685 async fn test_with_json_and_good_custom_content_type() {
686 let (req, mut pl) = TestRequest::default()
687 .insert_header((
688 header::CONTENT_TYPE,
689 header::HeaderValue::from_static("text/plain"),
690 ))
691 .insert_header((
692 header::CONTENT_LENGTH,
693 header::HeaderValue::from_static("16"),
694 ))
695 .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
696 .app_data(JsonConfig::default().content_type(|mime: mime::Mime| {
697 mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN
698 }))
699 .to_http_parts();
700
701 let s = Json::<MyObject>::from_request(&req, &mut pl).await;
702 assert!(s.is_ok())
703 }
704
705 #[actix_rt::test]
706 async fn test_with_json_and_bad_custom_content_type() {
707 let (req, mut pl) = TestRequest::default()
708 .insert_header((
709 header::CONTENT_TYPE,
710 header::HeaderValue::from_static("text/html"),
711 ))
712 .insert_header((
713 header::CONTENT_LENGTH,
714 header::HeaderValue::from_static("16"),
715 ))
716 .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
717 .app_data(JsonConfig::default().content_type(|mime: mime::Mime| {
718 mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN
719 }))
720 .to_http_parts();
721
722 let s = Json::<MyObject>::from_request(&req, &mut pl).await;
723 assert!(s.is_err())
724 }
725
726 #[actix_rt::test]
727 async fn test_json_with_no_content_type() {
728 let (req, mut pl) = TestRequest::default()
729 .insert_header((
730 header::CONTENT_LENGTH,
731 header::HeaderValue::from_static("16"),
732 ))
733 .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
734 .app_data(JsonConfig::default().content_type_required(false))
735 .to_http_parts();
736
737 let s = Json::<MyObject>::from_request(&req, &mut pl).await;
738 assert!(s.is_ok())
739 }
740
741 #[actix_rt::test]
742 async fn test_json_ignoring_content_type() {
743 let (req, mut pl) = TestRequest::default()
744 .insert_header((
745 header::CONTENT_LENGTH,
746 header::HeaderValue::from_static("16"),
747 ))
748 .insert_header((
749 header::CONTENT_TYPE,
750 header::HeaderValue::from_static("invalid/value"),
751 ))
752 .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
753 .app_data(JsonConfig::default().content_type_required(false))
754 .to_http_parts();
755
756 let s = Json::<MyObject>::from_request(&req, &mut pl).await;
757 assert!(s.is_ok());
758 }
759
760 #[actix_rt::test]
761 async fn test_with_config_in_data_wrapper() {
762 let (req, mut pl) = TestRequest::default()
763 .insert_header((CONTENT_TYPE, mime::APPLICATION_JSON))
764 .insert_header((CONTENT_LENGTH, 16))
765 .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
766 .app_data(web::Data::new(JsonConfig::default().limit(10)))
767 .to_http_parts();
768
769 let s = Json::<MyObject>::from_request(&req, &mut pl).await;
770 assert!(s.is_err());
771
772 let err_str = s.err().unwrap().to_string();
773 assert!(
774 err_str.contains("JSON payload (16 bytes) is larger than allowed (limit: 10 bytes).")
775 );
776 }
777}