1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-12-18 01:43:58 +01:00

Fix actix-multipart field content_type() to return an Option (#2885)

Co-authored-by: Rob Ede <robjtede@icloud.com>
This commit is contained in:
Jacob Halsey 2022-09-23 18:06:40 +01:00 committed by GitHub
parent ef64d6a27c
commit fd63305859
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 19 deletions

View File

@ -2,6 +2,9 @@
## Unreleased - 2022-xx-xx ## Unreleased - 2022-xx-xx
- Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency. - Minimum supported Rust version (MSRV) is now 1.59 due to transitive `time` dependency.
- `Field::content_type()` now returns `Option<&mime::Mime>` [#2880]
[#2880]: https://github.com/actix/actix-web/pull/2880
## 0.4.0 - 2022-02-25 ## 0.4.0 - 2022-02-25

View File

@ -361,17 +361,18 @@ impl InnerMultipart {
return Poll::Ready(Some(Err(MultipartError::NoContentDisposition))); return Poll::Ready(Some(Err(MultipartError::NoContentDisposition)));
}; };
let ct: mime::Mime = headers let ct: Option<mime::Mime> = headers
.get(&header::CONTENT_TYPE) .get(&header::CONTENT_TYPE)
.and_then(|ct| ct.to_str().ok()) .and_then(|ct| ct.to_str().ok())
.and_then(|ct| ct.parse().ok()) .and_then(|ct| ct.parse().ok());
.unwrap_or(mime::APPLICATION_OCTET_STREAM);
self.state = InnerState::Boundary; self.state = InnerState::Boundary;
// nested multipart stream is not supported // nested multipart stream is not supported
if ct.type_() == mime::MULTIPART { if let Some(mime) = &ct {
return Poll::Ready(Some(Err(MultipartError::Nested))); if mime.type_() == mime::MULTIPART {
return Poll::Ready(Some(Err(MultipartError::Nested)));
}
} }
let field = let field =
@ -399,7 +400,7 @@ impl Drop for InnerMultipart {
/// A single field in a multipart stream /// A single field in a multipart stream
pub struct Field { pub struct Field {
ct: mime::Mime, ct: Option<mime::Mime>,
cd: ContentDisposition, cd: ContentDisposition,
headers: HeaderMap, headers: HeaderMap,
inner: Rc<RefCell<InnerField>>, inner: Rc<RefCell<InnerField>>,
@ -410,7 +411,7 @@ impl Field {
fn new( fn new(
safety: Safety, safety: Safety,
headers: HeaderMap, headers: HeaderMap,
ct: mime::Mime, ct: Option<mime::Mime>,
cd: ContentDisposition, cd: ContentDisposition,
inner: Rc<RefCell<InnerField>>, inner: Rc<RefCell<InnerField>>,
) -> Self { ) -> Self {
@ -428,9 +429,13 @@ impl Field {
&self.headers &self.headers
} }
/// Returns a reference to the field's content (mime) type. /// Returns a reference to the field's content (mime) type, if it is supplied by the client.
pub fn content_type(&self) -> &mime::Mime { ///
&self.ct /// According to [RFC 7578](https://www.rfc-editor.org/rfc/rfc7578#section-4.4), if it is not
/// present, it should default to "text/plain". Note it is the responsibility of the client to
/// provide the appropriate content type, there is no attempt to validate this by the server.
pub fn content_type(&self) -> Option<&mime::Mime> {
self.ct.as_ref()
} }
/// Returns the field's Content-Disposition. /// Returns the field's Content-Disposition.
@ -482,7 +487,11 @@ impl Stream for Field {
impl fmt::Debug for Field { impl fmt::Debug for Field {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "\nField: {}", self.ct)?; if let Some(ct) = &self.ct {
writeln!(f, "\nField: {}", ct)?;
} else {
writeln!(f, "\nField:")?;
}
writeln!(f, " boundary: {}", self.inner.borrow().boundary)?; writeln!(f, " boundary: {}", self.inner.borrow().boundary)?;
writeln!(f, " headers:")?; writeln!(f, " headers:")?;
for (key, val) in self.headers.iter() { for (key, val) in self.headers.iter() {
@ -1024,8 +1033,8 @@ mod tests {
assert_eq!(cd.disposition, DispositionType::FormData); assert_eq!(cd.disposition, DispositionType::FormData);
assert_eq!(cd.parameters[0], DispositionParam::Name("file".into())); assert_eq!(cd.parameters[0], DispositionParam::Name("file".into()));
assert_eq!(field.content_type().type_(), mime::TEXT); assert_eq!(field.content_type().unwrap().type_(), mime::TEXT);
assert_eq!(field.content_type().subtype(), mime::PLAIN); assert_eq!(field.content_type().unwrap().subtype(), mime::PLAIN);
match field.next().await.unwrap() { match field.next().await.unwrap() {
Ok(chunk) => assert_eq!(chunk, "test"), Ok(chunk) => assert_eq!(chunk, "test"),
@ -1041,8 +1050,8 @@ mod tests {
match multipart.next().await.unwrap() { match multipart.next().await.unwrap() {
Ok(mut field) => { Ok(mut field) => {
assert_eq!(field.content_type().type_(), mime::TEXT); assert_eq!(field.content_type().unwrap().type_(), mime::TEXT);
assert_eq!(field.content_type().subtype(), mime::PLAIN); assert_eq!(field.content_type().unwrap().subtype(), mime::PLAIN);
match field.next().await { match field.next().await {
Some(Ok(chunk)) => assert_eq!(chunk, "data"), Some(Ok(chunk)) => assert_eq!(chunk, "data"),
@ -1086,8 +1095,8 @@ mod tests {
assert_eq!(cd.disposition, DispositionType::FormData); assert_eq!(cd.disposition, DispositionType::FormData);
assert_eq!(cd.parameters[0], DispositionParam::Name("file".into())); assert_eq!(cd.parameters[0], DispositionParam::Name("file".into()));
assert_eq!(field.content_type().type_(), mime::TEXT); assert_eq!(field.content_type().unwrap().type_(), mime::TEXT);
assert_eq!(field.content_type().subtype(), mime::PLAIN); assert_eq!(field.content_type().unwrap().subtype(), mime::PLAIN);
assert_eq!(get_whole_field(&mut field).await, "test"); assert_eq!(get_whole_field(&mut field).await, "test");
} }
@ -1096,8 +1105,8 @@ mod tests {
match multipart.next().await { match multipart.next().await {
Some(Ok(mut field)) => { Some(Ok(mut field)) => {
assert_eq!(field.content_type().type_(), mime::TEXT); assert_eq!(field.content_type().unwrap().type_(), mime::TEXT);
assert_eq!(field.content_type().subtype(), mime::PLAIN); assert_eq!(field.content_type().unwrap().subtype(), mime::PLAIN);
assert_eq!(get_whole_field(&mut field).await, "data"); assert_eq!(get_whole_field(&mut field).await, "data");
} }