actix_xml/
config.rs

1use std::sync::Arc;
2
3use actix_web::{web, HttpMessage, HttpRequest};
4
5use crate::error::XMLPayloadError;
6
7/// XML extractor configuration
8///
9/// # Example
10///
11/// ```rust
12/// use actix_web::{error, web, App, FromRequest, HttpResponse};
13/// use actix_xml::{Xml, XmlConfig};
14/// use serde::Deserialize;
15///
16/// #[derive(Deserialize)]
17/// struct Info {
18///     username: String,
19/// }
20///
21/// /// deserialize `Info` from request's body, max payload size is 4kb
22/// async fn index(info: Xml<Info>) -> String {
23///     format!("Welcome {}!", info.username)
24/// }
25///
26/// fn main() {
27///     let app = App::new().service(
28///         web::resource("/index.html")
29///             .app_data(
30///                 // Json extractor configuration for this resource.
31///                 XmlConfig::default()
32///                     .limit(4096) // Limit request payload size
33///                     .content_type(|mime| {  // <- accept text/plain content type
34///                         mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN
35///                     })
36///             )
37///             .route(web::post().to(index))
38///     );
39/// }
40/// ```
41///
42#[derive(Clone)]
43pub struct XmlConfig {
44    pub(crate) limit: usize,
45    content_type: Option<Arc<dyn Fn(mime::Mime) -> bool + Send + Sync>>,
46}
47
48const DEFAULT_CONFIG: XmlConfig = XmlConfig {
49    limit: 262_144,
50    content_type: None,
51};
52
53impl Default for XmlConfig {
54    fn default() -> Self {
55        DEFAULT_CONFIG.clone()
56    }
57}
58
59impl XmlConfig {
60    pub fn new() -> Self {
61        Default::default()
62    }
63
64    /// Change max size of payload. By default max size is 256Kb
65    pub fn limit(mut self, limit: usize) -> Self {
66        self.limit = limit;
67        self
68    }
69
70    /// Set predicate for allowed content types
71    pub fn content_type<F>(mut self, predicate: F) -> Self
72    where
73        F: Fn(mime::Mime) -> bool + Send + Sync + 'static,
74    {
75        self.content_type = Some(Arc::new(predicate));
76        self
77    }
78
79    pub(crate) fn check_content_type(&self, req: &HttpRequest) -> Result<(), XMLPayloadError> {
80        // check content-type
81        if let Ok(Some(mime)) = req.mime_type() {
82            if mime == "text/xml"
83                || mime == "application/xml"
84                || self
85                    .content_type
86                    .as_ref()
87                    .map_or(false, |predicate| predicate(mime))
88            {
89                Ok(())
90            } else {
91                Err(XMLPayloadError::ContentType)
92            }
93        } else {
94            Err(XMLPayloadError::ContentType)
95        }
96    }
97
98    /// Extract payload config from app data. Check both `T` and `Data<T>`, in that order, and fall
99    /// back to the default payload config.
100    pub(crate) fn from_req(req: &HttpRequest) -> &Self {
101        req.app_data::<Self>()
102            .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))
103            .unwrap_or(&DEFAULT_CONFIG)
104    }
105}