Skip to content

Commit

Permalink
feat: add option to ignore content-length with multipart body (#919)
Browse files Browse the repository at this point in the history
The `max_length` field is changed to accept an `impl Into<Option<u64>>`, so people can pass `None` to disable the limit.
  • Loading branch information
outamaa authored Jun 12, 2023
1 parent d73dc54 commit e562afa
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 9 deletions.
24 changes: 15 additions & 9 deletions src/filters/multipart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const DEFAULT_FORM_DATA_MAX_LENGTH: u64 = 1024 * 1024 * 2;
/// Create with the `warp::multipart::form()` function.
#[derive(Debug, Clone)]
pub struct FormOptions {
max_length: u64,
max_length: Option<u64>,
}

/// A `Stream` of multipart/form-data `Part`s.
Expand All @@ -50,7 +50,7 @@ pub struct Part {
/// in turn is a `Stream` of bytes.
pub fn form() -> FormOptions {
FormOptions {
max_length: DEFAULT_FORM_DATA_MAX_LENGTH,
max_length: Some(DEFAULT_FORM_DATA_MAX_LENGTH),
}
}

Expand All @@ -59,9 +59,10 @@ pub fn form() -> FormOptions {
impl FormOptions {
/// Set the maximum byte length allowed for this body.
///
/// `max_length(None)` means that maximum byte length is not checked.
/// Defaults to 2MB.
pub fn max_length(mut self, max: u64) -> Self {
self.max_length = max;
pub fn max_length(mut self, max: impl Into<Option<u64>>) -> Self {
self.max_length = max.into();
self
}
}
Expand All @@ -83,8 +84,7 @@ impl FilterBase for FormOptions {
future::ready(mime)
});

let filt = super::body::content_length_limit(self.max_length)
.and(boundary)
let filt = boundary
.and(super::body::body())
.map(|boundary: String, body| {
let body = BodyIoError(body);
Expand All @@ -93,9 +93,15 @@ impl FilterBase for FormOptions {
}
});

let fut = filt.filter(Internal);

Box::pin(fut)
if let Some(max_length) = self.max_length {
Box::pin(
super::body::content_length_limit(max_length)
.and(filt)
.filter(Internal),
)
} else {
Box::pin(filt.filter(Internal))
}
}
}

Expand Down
48 changes: 48 additions & 0 deletions tests/multipart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,51 @@ async fn form_fields() {
assert_eq!(&vec[0].0, "foo");
assert_eq!(&vec[0].1, b"bar");
}

#[tokio::test]
async fn max_length_is_enforced() {
let _ = pretty_env_logger::try_init();

let route = multipart::form()
.and_then(|_: multipart::FormData| async { Ok::<(), warp::Rejection>(()) });

let boundary = "--abcdef1234--";

let req = warp::test::request()
.method("POST")
// Note no content-length header
.header("transfer-encoding", "chunked")
.header(
"content-type",
format!("multipart/form-data; boundary={}", boundary),
);

// Intentionally don't add body, as it automatically also adds
// content-length header
let resp = req.filter(&route).await;
assert!(resp.is_err());
}

#[tokio::test]
async fn max_length_can_be_disabled() {
let _ = pretty_env_logger::try_init();

let route = multipart::form()
.max_length(None)
.and_then(|_: multipart::FormData| async { Ok::<(), warp::Rejection>(()) });

let boundary = "--abcdef1234--";

let req = warp::test::request()
.method("POST")
.header("transfer-encoding", "chunked")
.header(
"content-type",
format!("multipart/form-data; boundary={}", boundary),
);

// Intentionally don't add body, as it automatically also adds
// content-length header
let resp = req.filter(&route).await;
assert!(resp.is_ok());
}

0 comments on commit e562afa

Please sign in to comment.