Repository: seanmonstar/warp (language: rust)

Issue:
Remove "multipart" dependency
**Is your feature request related to a problem? Please describe.**
Warp 0.3.3's dependency on `multipart` v0.18.0 now causes a vulnerability advisory error during `cargo deny check`:
```
error[vulnerability]: Race Condition Enabling Link Following and Time-of-check Time-of-use (TOCTOU)
...
    = Announcement: https://github.com/XAMPPRocky/remove_dir_all/commit/7247a8b6ee59fc99bbb69ca6b3ca4bfd8c809ead
    = Solution: Upgrade to >=0.8.0
    = remove_dir_all v0.5.3
      └── tempfile v3.3.0
          └── multipart v0.18.0
              └── warp v0.3.3
```
I'd like `multipart` to be removed as a dependency for Warp.  It appears to not be maintained anymore either: https://github.com/abonander/multipart

**Describe the solution you'd like**
This PR appears to fix the issue.  I'd like someone to approve it, merge it, and release v0.3.4: https://github.com/seanmonstar/warp/pull/846

**Describe alternatives you've considered**
I can try to patch `remove_dir_all` to v0.8.0 in my TOML, but this will remain an issue for other projects using this version of Warp.


Discussion / hints:
can you please share how you're patching `remove_dir_all` on your toml? I'd like to do the same
In most cases, simply running `cargo update -p tempfile` in your project should work.
I could do it with this, but I wouldn't recommend it since I would have had to use "master" instead of a version.  Unfortunately the version of tempfile that gets rid of `remove_dir_all` isn't tagged (3.4.0) https://github.com/Stebalien/tempfile/tags

```
[patch.crates-io]
tempfile = {git = "https://github.com/Stebalien/tempfile", branch = "master"}
```
Looks like they just forgot to push the tag to GitHub. The version on https://crates.io/crates/tempfile is 3.4.0.
thanks! running `cargo update -p tempfile` as suggested worked for me.
@willclevine You can instead add this: `tempfile = "3.4.0"` to your dependencies block.
`multipart` is incompatible in future。

```
warning: the following packages contain code that will be rejected by a future version of Rust: buf_redux v0.8.4, multipart v0.18.0
note: 
To solve this problem, you can try the following approaches:


- If the issue is not solved by updating the dependencies, a fix has to be
implemented by those dependencies. You can help with that by notifying the
maintainers of this problem (e.g. by creating a bug report) or by proposing a
fix to the maintainers (e.g. by creating a pull request):

  - buf_redux@0.8.4
  - Repository: https://github.com/abonander/buf_redux
  - Detailed warning command: `cargo report future-incompatibilities --id 6 --package buf_redux@0.8.4`

  - multipart@0.18.0
  - Repository: http://github.com/abonander/multipart
  - Detailed warning command: `cargo report future-incompatibilities --id 6 --package multipart@0.18.0`
```
multipart is archived without future maintenance
Please, is there any news here?
Both packages were archived, and they are **only** used by warp:
```
❯ cargo tree -i buf_redux
buf_redux v0.8.4
└── multipart v0.18.0
    └── warp v0.3.3
        ...
        └── warp-embed v0.4.0
            ...
❯ cargo tree -i multipart
multipart v0.18.0
└── warp v0.3.3
    ...
    └── warp-embed v0.4.0
        ...
```

Also, the OP even reported there is an open PR that might fix this (#846, since 2021 actually)...
Is there any problem there? Can I do something to help you, even if it is a donation?
Thank you.
@rsalmei Sorry for not posting sooner, but both @LegendL3n and @horacimacias solutions worked for me.  Either worked, you don't need to do both.
Well @willclevine, updating `tempfile` might resolve the `error[vulnerability]` problem you put on description, but it definitely does not solve the problem you put on the **title** of this thread...

I'm talking about the problem @letbt has brought up.
I'm talking about how ALL projects of mine that use warp now give an ugly warning every single time I compile them:
```
warning: the following packages contain code that will be rejected by a future version of Rust: buf_redux v0.8.4, multipart v0.18.0
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 92`
```

BOTH buf_redux and multipart were **archived**, so there's nothing we can do to fix this, only @seanmonstar can fix this on warp itself. And removing `multipart` will fix both problems, because it is multipart itself that uses buf_redux.

So, please, do not close this issue. We still do need to 'Remove "multipart" dependency' as you've put on the title. This is **not** fixed.
@rsalmei you are right! I was overzealous haha.  Reopened
Thanks @willclevine. We can only hope this will soon be fixed.

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index afcde0f76..7fbeb74c3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,7 +27,7 @@ hyper = { version = "0.14", features = ["stream", "server", "http1", "http2", "t
 log = "0.4"
 mime = "0.3"
 mime_guess = "2.0.0"
-multipart = { version = "0.18", default-features = false, features = ["server"], optional = true }
+multiparty = { version = "0.1", features = ["server", "futures03"], optional = true }
 scoped-tls = "1.0"
 serde = "1.0"
 serde_json = "1.0"
@@ -55,6 +55,7 @@ listenfd = "1.0"
 
 [features]
 default = ["multipart", "websocket"]
+multipart = ["multiparty"]
 websocket = ["tokio-tungstenite"]
 tls = ["tokio-rustls"]
 
diff --git a/src/filters/multipart.rs b/src/filters/multipart.rs
index ef2ec9268..307f8f47a 100644
--- a/src/filters/multipart.rs
+++ b/src/filters/multipart.rs
@@ -2,17 +2,18 @@
 //!
 //! Filters that extract a multipart body for a route.
 
-use std::fmt;
 use std::future::Future;
-use std::io::{Cursor, Read};
 use std::pin::Pin;
 use std::task::{Context, Poll};
+use std::{fmt, io};
 
 use bytes::{Buf, Bytes};
 use futures_util::{future, Stream};
 use headers::ContentType;
+use hyper::Body;
 use mime::Mime;
-use multipart::server::Multipart;
+use multiparty::headers::Headers;
+use multiparty::server::owned_futures03::{FormData as FormDataInner, Part as PartInner};
 
 use crate::filter::{Filter, FilterBase, Internal};
 use crate::reject::{self, Rejection};
@@ -32,17 +33,15 @@ pub struct FormOptions {
 ///
 /// Extracted with a `warp::multipart::form` filter.
 pub struct FormData {
-    inner: Multipart<Cursor<::bytes::Bytes>>,
+    inner: FormDataInner<BodyIoError>,
 }
 
 /// A single "part" of a multipart/form-data body.
 ///
 /// Yielded from the `FormData` stream.
 pub struct Part {
-    name: String,
-    filename: Option<String>,
-    content_type: Option<String>,
-    data: Option<Vec<u8>>,
+    headers: Headers,
+    part: PartInner<BodyIoError>,
 }
 
 /// Create a `Filter` to extract a `multipart/form-data` body from a request.
@@ -86,9 +85,12 @@ impl FilterBase for FormOptions {
 
         let filt = super::body::content_length_limit(self.max_length)
             .and(boundary)
-            .and(super::body::bytes())
-            .map(|boundary, body| FormData {
-                inner: Multipart::with_body(Cursor::new(body), boundary),
+            .and(super::body::body())
+            .map(|boundary: String, body| {
+                let body = BodyIoError(body);
+                FormData {
+                    inner: FormDataInner::new(body, &boundary),
+                }
             });
 
         let fut = filt.filter(Internal);
@@ -108,23 +110,18 @@ impl fmt::Debug for FormData {
 impl Stream for FormData {
     type Item = Result<Part, crate::Error>;
 
-    fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        match (*self).inner.read_entry() {
-            Ok(Some(mut field)) => {
-                let mut data = Vec::new();
-                field
-                    .data
-                    .read_to_end(&mut data)
-                    .map_err(crate::Error::new)?;
-                Poll::Ready(Some(Ok(Part {
-                    name: field.headers.name.to_string(),
-                    filename: field.headers.filename,
-                    content_type: field.headers.content_type.map(|m| m.to_string()),
-                    data: Some(data),
-                })))
+    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        match Pin::new(&mut self.inner).poll_next(cx) {
+            Poll::Pending => Poll::Pending,
+            Poll::Ready(Some(Ok(part))) => {
+                let headers = match part.raw_headers().parse() {
+                    Ok(headers) => headers,
+                    Err(err) => return Poll::Ready(Some(Err(crate::Error::new(err)))),
+                };
+                Poll::Ready(Some(Ok(Part { part, headers })))
             }
-            Ok(None) => Poll::Ready(None),
-            Err(e) => Poll::Ready(Some(Err(crate::Error::new(e)))),
+            Poll::Ready(None) => Poll::Ready(None),
+            Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(crate::Error::new(err)))),
         }
     }
 }
@@ -134,22 +131,22 @@ impl Stream for FormData {
 impl Part {
     /// Get the name of this part.
     pub fn name(&self) -> &str {
-        &self.name
+        &self.headers.name
     }
 
     /// Get the filename of this part, if present.
     pub fn filename(&self) -> Option<&str> {
-        self.filename.as_deref()
+        self.headers.filename.as_deref()
     }
 
     /// Get the content-type of this part, if present.
     pub fn content_type(&self) -> Option<&str> {
-        self.content_type.as_deref()
+        self.headers.content_type.as_deref()
     }
 
     /// Asynchronously get some of the data for this `Part`.
     pub async fn data(&mut self) -> Option<Result<impl Buf, crate::Error>> {
-        self.take_data()
+        future::poll_fn(|cx| self.poll_next(cx)).await
     }
 
     /// Convert this `Part` into a `Stream` of `Buf`s.
@@ -157,21 +154,26 @@ impl Part {
         PartStream(self)
     }
 
-    fn take_data(&mut self) -> Option<Result<Bytes, crate::Error>> {
-        self.data.take().map(|vec| Ok(vec.into()))
+    fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes, crate::Error>>> {
+        match Pin::new(&mut self.part).poll_next(cx) {
+            Poll::Pending => Poll::Pending,
+            Poll::Ready(Some(Ok(bytes))) => Poll::Ready(Some(Ok(bytes))),
+            Poll::Ready(None) => Poll::Ready(None),
+            Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(crate::Error::new(err)))),
+        }
     }
 }
 
 impl fmt::Debug for Part {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let mut builder = f.debug_struct("Part");
-        builder.field("name", &self.name);
+        builder.field("name", &self.headers.name);
 
-        if let Some(ref filename) = self.filename {
+        if let Some(ref filename) = self.headers.filename {
             builder.field("filename", filename);
         }
 
-        if let Some(ref mime) = self.content_type {
+        if let Some(ref mime) = self.headers.content_type {
             builder.field("content_type", mime);
         }
 
@@ -184,7 +186,24 @@ struct PartStream(Part);
 impl Stream for PartStream {
     type Item = Result<Bytes, crate::Error>;
 
-    fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        Poll::Ready(self.0.take_data())
+    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        self.0.poll_next(cx)
+    }
+}
+
+struct BodyIoError(Body);
+
+impl Stream for BodyIoError {
+    type Item = io::Result<Bytes>;
+
+    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        match Pin::new(&mut self.0).poll_next(cx) {
+            Poll::Pending => Poll::Pending,
+            Poll::Ready(Some(Ok(bytes))) => Poll::Ready(Some(Ok(bytes))),
+            Poll::Ready(None) => Poll::Ready(None),
+            Poll::Ready(Some(Err(err))) => {
+                Poll::Ready(Some(Err(io::Error::new(io::ErrorKind::Other, err))))
+            }
+        }
     }
 }


Test patch: