Content-Disposition header is required in multipart

This commit is contained in:
Craig Pastro 2021-03-19 11:01:48 +09:00
parent b75b5114c3
commit 51b705a629
3 changed files with 29 additions and 9 deletions

View File

@ -72,7 +72,7 @@ impl<'a> From<&'a str> for DispositionType {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
pub enum DispositionParam { pub enum DispositionParam {
/// For [`DispositionType::FormData`] (i.e. *multipart/form-data*), the name of an field from /// For [`DispositionType::FormData`] (i.e. *multipart/form-data*), the name of a field from
/// the form. /// the form.
Name(String), Name(String),
/// A plain file name. /// A plain file name.

View File

@ -10,6 +10,9 @@ pub enum MultipartError {
/// Content-Type header is not found /// Content-Type header is not found
#[display(fmt = "No Content-type header found")] #[display(fmt = "No Content-type header found")]
NoContentType, NoContentType,
/// Content-Disposition header satisfying RFC7578 is not found (see https://tools.ietf.org/html/rfc7578#section-4.2)
#[display(fmt = "No content-disposition header satisfying RFC7578 found")]
NoContentDisposition,
/// Can not parse Content-Type header /// Can not parse Content-Type header
#[display(fmt = "Can not parse Content-Type header")] #[display(fmt = "Can not parse Content-Type header")]
ParseContentType, ParseContentType,

View File

@ -13,7 +13,10 @@ use futures_util::stream::{LocalBoxStream, Stream, StreamExt};
use actix_utils::task::LocalWaker; use actix_utils::task::LocalWaker;
use actix_web::error::{ParseError, PayloadError}; use actix_web::error::{ParseError, PayloadError};
use actix_web::http::header::{self, ContentDisposition, HeaderMap, HeaderName, HeaderValue}; use actix_web::http::header::{
self, ContentDisposition, DispositionParam, DispositionType, HeaderMap, HeaderName,
HeaderValue,
};
use crate::error::MultipartError; use crate::error::MultipartError;
@ -402,14 +405,28 @@ impl Field {
} }
/// Get the content disposition of the field, if it exists /// Get the content disposition of the field, if it exists
pub fn content_disposition(&self) -> Option<ContentDisposition> { pub fn content_disposition(&self) -> Result<ContentDisposition, MultipartError> {
// RFC 7578: 'Each part MUST contain a Content-Disposition header field // RFC 7578: 'Each part MUST contain a Content-Disposition header field
// where the disposition type is "form-data".' // where the disposition type is "form-data". The Content-Disposition
if let Some(content_disposition) = self.headers.get(&header::CONTENT_DISPOSITION) { // header field MUST also contain an additional parameter of "name"; the
ContentDisposition::from_raw(content_disposition).ok() // value of the "name" parameter is the original field name from the
} else { // form.
None self.headers
} .get(&header::CONTENT_DISPOSITION)
.and_then(|content_disposition| {
ContentDisposition::from_raw(content_disposition).ok()
})
.filter(|content_disposition| {
content_disposition.disposition == DispositionType::FormData
&& content_disposition
.parameters
.iter()
.any(|param| match param {
DispositionParam::Name(_) => true,
_ => false,
})
})
.ok_or(MultipartError::NoContentDisposition)
} }
} }