warp/filters/
multipart.rs1use std::error::Error as StdError;
6use std::fmt::{Display, Formatter};
7use std::future::Future;
8use std::pin::Pin;
9use std::task::{Context, Poll};
10use std::{fmt, io};
11
12use bytes::{Buf, Bytes};
13use futures_util::{future, Stream};
14use headers::ContentType;
15use http_body_util::BodyDataStream;
16use mime::Mime;
17use multer::{Field as PartInner, Multipart as FormDataInner};
18
19use crate::bodyt::Body;
20use crate::filter::{Filter, FilterBase, Internal};
21use crate::reject::{self, Rejection};
22
23const DEFAULT_FORM_DATA_MAX_LENGTH: u64 = 1024 * 1024 * 2;
25
26#[derive(Debug, Clone)]
30pub struct FormOptions {
31 max_length: Option<u64>,
32}
33
34pub struct FormData {
38 inner: FormDataInner<'static>,
39}
40
41pub struct Part {
45 part: PartInner<'static>,
46}
47
48pub fn form() -> FormOptions {
53 FormOptions {
54 max_length: Some(DEFAULT_FORM_DATA_MAX_LENGTH),
55 }
56}
57
58impl FormOptions {
61 pub fn max_length(mut self, max: impl Into<Option<u64>>) -> Self {
66 self.max_length = max.into();
67 self
68 }
69}
70
71type FormFut = Pin<Box<dyn Future<Output = Result<(FormData,), Rejection>> + Send>>;
72
73impl FilterBase for FormOptions {
74 type Extract = (FormData,);
75 type Error = Rejection;
76 type Future = FormFut;
77
78 fn filter(&self, _: Internal) -> Self::Future {
79 let boundary = super::header::header2::<ContentType>().and_then(|ct| {
80 let mime = Mime::from(ct);
81 let mime = mime
82 .get_param("boundary")
83 .map(|v| v.to_string())
84 .ok_or_else(|| reject::invalid_header("content-type"));
85 future::ready(mime)
86 });
87
88 let filt = boundary
89 .and(super::body::body())
90 .map(|boundary: String, body| {
91 let body = BodyIoError(BodyDataStream::new(body));
92 FormData {
93 inner: FormDataInner::new(body, &boundary),
94 }
95 });
96
97 if let Some(max_length) = self.max_length {
98 Box::pin(
99 super::body::content_length_limit(max_length)
100 .and(filt)
101 .filter(Internal),
102 )
103 } else {
104 Box::pin(filt.filter(Internal))
105 }
106 }
107}
108
109impl fmt::Debug for FormData {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 f.debug_struct("FormData").finish()
114 }
115}
116
117impl Stream for FormData {
118 type Item = Result<Part, crate::Error>;
119
120 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
121 match self.inner.poll_next_field(cx) {
122 Poll::Pending => Poll::Pending,
123 Poll::Ready(Ok(Some(part))) => {
124 if part.name().is_some() || part.file_name().is_some() {
125 Poll::Ready(Some(Ok(Part { part })))
126 } else {
127 Poll::Ready(Some(Err(crate::Error::new(MultipartFieldMissingName))))
128 }
129 }
130 Poll::Ready(Ok(None)) => Poll::Ready(None),
131 Poll::Ready(Err(err)) => Poll::Ready(Some(Err(crate::Error::new(err)))),
132 }
133 }
134}
135
136impl Part {
139 pub fn name(&self) -> &str {
141 self.part
142 .name()
143 .unwrap_or_else(|| self.part.file_name().expect("checked for name previously"))
144 }
145
146 pub fn filename(&self) -> Option<&str> {
148 self.part.file_name()
149 }
150
151 pub fn content_type(&self) -> Option<&str> {
153 let content_type = self.part.content_type();
154 content_type.map(|t| t.as_ref())
155 }
156
157 pub async fn data(&mut self) -> Option<Result<impl Buf, crate::Error>> {
159 future::poll_fn(|cx| self.poll_next(cx)).await
160 }
161
162 pub fn stream(self) -> impl Stream<Item = Result<impl Buf, crate::Error>> {
164 PartStream(self)
165 }
166
167 fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes, crate::Error>>> {
168 match Pin::new(&mut self.part).poll_next(cx) {
169 Poll::Pending => Poll::Pending,
170 Poll::Ready(Some(Ok(bytes))) => Poll::Ready(Some(Ok(bytes))),
171 Poll::Ready(None) => Poll::Ready(None),
172 Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(crate::Error::new(err)))),
173 }
174 }
175}
176
177impl fmt::Debug for Part {
178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179 let mut builder = f.debug_struct("Part");
180 builder.field("name", &self.name());
181
182 if let Some(ref filename) = self.part.file_name() {
183 builder.field("filename", filename);
184 }
185
186 if let Some(ref mime) = self.part.content_type() {
187 builder.field("content_type", mime);
188 }
189
190 builder.finish()
191 }
192}
193
194struct PartStream(Part);
195
196impl Stream for PartStream {
197 type Item = Result<Bytes, crate::Error>;
198
199 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
200 self.0.poll_next(cx)
201 }
202}
203
204struct BodyIoError(BodyDataStream<Body>);
205
206impl Stream for BodyIoError {
207 type Item = io::Result<Bytes>;
208
209 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
210 match Pin::new(&mut self.0).poll_next(cx) {
211 Poll::Pending => Poll::Pending,
212 Poll::Ready(Some(Ok(bytes))) => Poll::Ready(Some(Ok(bytes))),
213 Poll::Ready(None) => Poll::Ready(None),
214 Poll::Ready(Some(Err(err))) => {
215 Poll::Ready(Some(Err(io::Error::new(io::ErrorKind::Other, err))))
216 }
217 }
218 }
219}
220
221#[derive(Debug)]
223struct MultipartFieldMissingName;
224
225impl Display for MultipartFieldMissingName {
226 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
227 write!(f, "Multipart field is missing a name")
228 }
229}
230
231impl StdError for MultipartFieldMissingName {}