1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
use std::sync::Arc;

use actix_web::{web, HttpMessage, HttpRequest};

use crate::error::XMLPayloadError;

/// XML extractor configuration
///
/// # Example
///
/// ```rust
/// use actix_web::{error, web, App, FromRequest, HttpResponse};
/// use actix_xml::{Xml, XmlConfig};
/// use serde::Deserialize;
///
/// #[derive(Deserialize)]
/// struct Info {
///     username: String,
/// }
///
/// /// deserialize `Info` from request's body, max payload size is 4kb
/// async fn index(info: Xml<Info>) -> String {
///     format!("Welcome {}!", info.username)
/// }
///
/// fn main() {
///     let app = App::new().service(
///         web::resource("/index.html")
///             .app_data(
///                 // Json extractor configuration for this resource.
///                 XmlConfig::default()
///                     .limit(4096) // Limit request payload size
///                     .content_type(|mime| {  // <- accept text/plain content type
///                         mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN
///                     })
///             )
///             .route(web::post().to(index))
///     );
/// }
/// ```
///
#[derive(Clone)]
pub struct XmlConfig {
    pub(crate) limit: usize,
    content_type: Option<Arc<dyn Fn(mime::Mime) -> bool + Send + Sync>>,
}

const DEFAULT_CONFIG: XmlConfig = XmlConfig {
    limit: 262_144,
    content_type: None,
};

impl Default for XmlConfig {
    fn default() -> Self {
        DEFAULT_CONFIG.clone()
    }
}

impl XmlConfig {
    pub fn new() -> Self {
        Default::default()
    }

    /// Change max size of payload. By default max size is 256Kb
    pub fn limit(mut self, limit: usize) -> Self {
        self.limit = limit;
        self
    }

    /// Set predicate for allowed content types
    pub fn content_type<F>(mut self, predicate: F) -> Self
    where
        F: Fn(mime::Mime) -> bool + Send + Sync + 'static,
    {
        self.content_type = Some(Arc::new(predicate));
        self
    }

    pub(crate) fn check_content_type(&self, req: &HttpRequest) -> Result<(), XMLPayloadError> {
        // check content-type
        if let Ok(Some(mime)) = req.mime_type() {
            if mime == "text/xml"
                || mime == "application/xml"
                || self
                    .content_type
                    .as_ref()
                    .map_or(false, |predicate| predicate(mime))
            {
                Ok(())
            } else {
                Err(XMLPayloadError::ContentType)
            }
        } else {
            Err(XMLPayloadError::ContentType)
        }
    }

    /// Extract payload config from app data. Check both `T` and `Data<T>`, in that order, and fall
    /// back to the default payload config.
    pub(crate) fn from_req(req: &HttpRequest) -> &Self {
        req.app_data::<Self>()
            .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))
            .unwrap_or(&DEFAULT_CONFIG)
    }
}