Skip to content

Commit 2fecf97

Browse files
use multiparty crate (#1031)
Co-authored-by: Paolo Barbolini <[email protected]>
1 parent 6ae9520 commit 2fecf97

File tree

2 files changed

+59
-39
lines changed

2 files changed

+59
-39
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ hyper = { version = "0.14", features = ["stream", "server", "http1", "http2", "t
2727
log = "0.4"
2828
mime = "0.3"
2929
mime_guess = "2.0.0"
30-
multipart = { version = "0.18", default-features = false, features = ["server"], optional = true }
30+
multiparty = { version = "0.1", features = ["server", "futures03"], optional = true }
3131
scoped-tls = "1.0"
3232
serde = "1.0"
3333
serde_json = "1.0"
@@ -55,6 +55,7 @@ listenfd = "1.0"
5555

5656
[features]
5757
default = ["multipart", "websocket"]
58+
multipart = ["multiparty"]
5859
websocket = ["tokio-tungstenite"]
5960
tls = ["tokio-rustls"]
6061

src/filters/multipart.rs

Lines changed: 57 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
22
//!
33
//! Filters that extract a multipart body for a route.
44
5-
use std::fmt;
65
use std::future::Future;
7-
use std::io::{Cursor, Read};
86
use std::pin::Pin;
97
use std::task::{Context, Poll};
8+
use std::{fmt, io};
109

1110
use bytes::{Buf, Bytes};
1211
use futures_util::{future, Stream};
1312
use headers::ContentType;
13+
use hyper::Body;
1414
use mime::Mime;
15-
use multipart::server::Multipart;
15+
use multiparty::headers::Headers;
16+
use multiparty::server::owned_futures03::{FormData as FormDataInner, Part as PartInner};
1617

1718
use crate::filter::{Filter, FilterBase, Internal};
1819
use crate::reject::{self, Rejection};
@@ -32,17 +33,15 @@ pub struct FormOptions {
3233
///
3334
/// Extracted with a `warp::multipart::form` filter.
3435
pub struct FormData {
35-
inner: Multipart<Cursor<::bytes::Bytes>>,
36+
inner: FormDataInner<BodyIoError>,
3637
}
3738

3839
/// A single "part" of a multipart/form-data body.
3940
///
4041
/// Yielded from the `FormData` stream.
4142
pub struct Part {
42-
name: String,
43-
filename: Option<String>,
44-
content_type: Option<String>,
45-
data: Option<Vec<u8>>,
43+
headers: Headers,
44+
part: PartInner<BodyIoError>,
4645
}
4746

4847
/// Create a `Filter` to extract a `multipart/form-data` body from a request.
@@ -86,9 +85,12 @@ impl FilterBase for FormOptions {
8685

8786
let filt = super::body::content_length_limit(self.max_length)
8887
.and(boundary)
89-
.and(super::body::bytes())
90-
.map(|boundary, body| FormData {
91-
inner: Multipart::with_body(Cursor::new(body), boundary),
88+
.and(super::body::body())
89+
.map(|boundary: String, body| {
90+
let body = BodyIoError(body);
91+
FormData {
92+
inner: FormDataInner::new(body, &boundary),
93+
}
9294
});
9395

9496
let fut = filt.filter(Internal);
@@ -108,23 +110,18 @@ impl fmt::Debug for FormData {
108110
impl Stream for FormData {
109111
type Item = Result<Part, crate::Error>;
110112

111-
fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
112-
match (*self).inner.read_entry() {
113-
Ok(Some(mut field)) => {
114-
let mut data = Vec::new();
115-
field
116-
.data
117-
.read_to_end(&mut data)
118-
.map_err(crate::Error::new)?;
119-
Poll::Ready(Some(Ok(Part {
120-
name: field.headers.name.to_string(),
121-
filename: field.headers.filename,
122-
content_type: field.headers.content_type.map(|m| m.to_string()),
123-
data: Some(data),
124-
})))
113+
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
114+
match Pin::new(&mut self.inner).poll_next(cx) {
115+
Poll::Pending => Poll::Pending,
116+
Poll::Ready(Some(Ok(part))) => {
117+
let headers = match part.raw_headers().parse() {
118+
Ok(headers) => headers,
119+
Err(err) => return Poll::Ready(Some(Err(crate::Error::new(err)))),
120+
};
121+
Poll::Ready(Some(Ok(Part { part, headers })))
125122
}
126-
Ok(None) => Poll::Ready(None),
127-
Err(e) => Poll::Ready(Some(Err(crate::Error::new(e)))),
123+
Poll::Ready(None) => Poll::Ready(None),
124+
Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(crate::Error::new(err)))),
128125
}
129126
}
130127
}
@@ -134,44 +131,49 @@ impl Stream for FormData {
134131
impl Part {
135132
/// Get the name of this part.
136133
pub fn name(&self) -> &str {
137-
&self.name
134+
&self.headers.name
138135
}
139136

140137
/// Get the filename of this part, if present.
141138
pub fn filename(&self) -> Option<&str> {
142-
self.filename.as_deref()
139+
self.headers.filename.as_deref()
143140
}
144141

145142
/// Get the content-type of this part, if present.
146143
pub fn content_type(&self) -> Option<&str> {
147-
self.content_type.as_deref()
144+
self.headers.content_type.as_deref()
148145
}
149146

150147
/// Asynchronously get some of the data for this `Part`.
151148
pub async fn data(&mut self) -> Option<Result<impl Buf, crate::Error>> {
152-
self.take_data()
149+
future::poll_fn(|cx| self.poll_next(cx)).await
153150
}
154151

155152
/// Convert this `Part` into a `Stream` of `Buf`s.
156153
pub fn stream(self) -> impl Stream<Item = Result<impl Buf, crate::Error>> {
157154
PartStream(self)
158155
}
159156

160-
fn take_data(&mut self) -> Option<Result<Bytes, crate::Error>> {
161-
self.data.take().map(|vec| Ok(vec.into()))
157+
fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes, crate::Error>>> {
158+
match Pin::new(&mut self.part).poll_next(cx) {
159+
Poll::Pending => Poll::Pending,
160+
Poll::Ready(Some(Ok(bytes))) => Poll::Ready(Some(Ok(bytes))),
161+
Poll::Ready(None) => Poll::Ready(None),
162+
Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(crate::Error::new(err)))),
163+
}
162164
}
163165
}
164166

165167
impl fmt::Debug for Part {
166168
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167169
let mut builder = f.debug_struct("Part");
168-
builder.field("name", &self.name);
170+
builder.field("name", &self.headers.name);
169171

170-
if let Some(ref filename) = self.filename {
172+
if let Some(ref filename) = self.headers.filename {
171173
builder.field("filename", filename);
172174
}
173175

174-
if let Some(ref mime) = self.content_type {
176+
if let Some(ref mime) = self.headers.content_type {
175177
builder.field("content_type", mime);
176178
}
177179

@@ -184,7 +186,24 @@ struct PartStream(Part);
184186
impl Stream for PartStream {
185187
type Item = Result<Bytes, crate::Error>;
186188

187-
fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
188-
Poll::Ready(self.0.take_data())
189+
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
190+
self.0.poll_next(cx)
191+
}
192+
}
193+
194+
struct BodyIoError(Body);
195+
196+
impl Stream for BodyIoError {
197+
type Item = io::Result<Bytes>;
198+
199+
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
200+
match Pin::new(&mut self.0).poll_next(cx) {
201+
Poll::Pending => Poll::Pending,
202+
Poll::Ready(Some(Ok(bytes))) => Poll::Ready(Some(Ok(bytes))),
203+
Poll::Ready(None) => Poll::Ready(None),
204+
Poll::Ready(Some(Err(err))) => {
205+
Poll::Ready(Some(Err(io::Error::new(io::ErrorKind::Other, err))))
206+
}
207+
}
189208
}
190209
}

0 commit comments

Comments
 (0)