Skip to content

Commit 676cdef

Browse files
committed
compiling impl From<Multipart> for Body
1 parent 4248120 commit 676cdef

File tree

3 files changed

+170
-30
lines changed

3 files changed

+170
-30
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ http = { version = "0.2.0", optional = true }
3636
anyhow = "1.0.26"
3737
cookie = { version = "0.14.0", features = ["percent-encode"] }
3838
futures-core = "0.3.5"
39+
futures-util = "0.3.7"
3940
infer = "0.2.3"
4041
pin-project-lite = "0.1.0"
4142
url = { version = "2.1.1", features = ["serde"] }
@@ -45,7 +46,7 @@ serde_urlencoded = "0.7.0"
4546
rand = "0.7.3"
4647
serde_qs = "0.7.0"
4748
base64 = "0.13.0"
48-
multipart = { version = "0.16.1", default-features = false, features = ["server"] }
49+
multipart = { version = "0.17.0", default-features = false, features = ["server"] }
4950

5051
[dev-dependencies]
5152
http = "0.2.0"

src/multipart/entry.rs

Lines changed: 78 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
1-
use crate::{Body, Mime};
1+
use crate::{Body, Error, Mime};
22

3+
use std::convert::TryFrom;
34
use std::fmt::{self, Debug};
4-
use std::path::Path;
5-
6-
/// A single multipart entry.
7-
///
8-
/// Structurally Multipart entries are similar to `Body`.
9-
pub struct Entry {
10-
name: String,
11-
body: Body,
5+
// use std::path::Path;
6+
use std::pin::Pin;
7+
use std::task::{Context, Poll};
8+
9+
use futures_lite::{io, prelude::*};
10+
11+
pin_project_lite::pin_project! {
12+
/// A single multipart entry.
13+
///
14+
/// Structurally Multipart entries are similar to `Body`.
15+
pub struct Entry {
16+
name: String,
17+
body: Body,
18+
}
1219
}
1320

1421
impl Entry {
@@ -56,14 +63,14 @@ impl Entry {
5663
self.name = name.as_ref().to_owned();
5764
}
5865

59-
/// Get the content type.
60-
pub fn content_type(&self) -> Option<Mime> {
61-
todo!();
66+
/// Returns the mime type of this Body.
67+
pub fn mime(&self) -> &Mime {
68+
self.body.mime()
6269
}
6370

64-
/// Set the content type.
65-
pub fn set_content_type(&mut self, _mime: Option<Mime>) {
66-
todo!();
71+
/// Sets the mime type of this Body.
72+
pub fn set_mime(&mut self, mime: Mime) {
73+
self.body.set_mime(mime)
6774
}
6875

6976
/// Get the file name of the entry, if it's set.
@@ -89,8 +96,59 @@ impl Debug for Entry {
8996
}
9097
}
9198

92-
// TODO
93-
// impl AsyncRead for Entry {}
94-
// impl AsRef<Body> for Entry {}
95-
// impl AsMut<Body> for Entry {}
96-
// impl Into<Body> for Entry {}
99+
impl AsyncRead for Entry {
100+
#[allow(missing_doc_code_examples)]
101+
fn poll_read(
102+
mut self: Pin<&mut Self>,
103+
cx: &mut Context<'_>,
104+
buf: &mut [u8],
105+
) -> Poll<io::Result<usize>> {
106+
Pin::new(&mut self.body).poll_read(cx, buf)
107+
}
108+
}
109+
110+
impl AsyncBufRead for Entry {
111+
#[allow(missing_doc_code_examples)]
112+
#[allow(unused_mut)]
113+
#[allow(unused_variables)]
114+
fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
115+
// Pin::new(&mut self.body).poll_fill_buf(cx)
116+
todo!("Pin::new(&mut self.body).poll_fill_buf(cx)")
117+
}
118+
119+
fn consume(mut self: Pin<&mut Self>, amt: usize) {
120+
Pin::new(&mut self.body).consume(amt)
121+
}
122+
}
123+
124+
impl AsRef<Body> for Entry {
125+
fn as_ref(&self) -> &Body {
126+
&self.body
127+
}
128+
}
129+
130+
impl AsMut<Body> for Entry {
131+
fn as_mut(&mut self) -> &mut Body {
132+
&mut self.body
133+
}
134+
}
135+
136+
impl Into<Body> for Entry {
137+
fn into(self) -> Body {
138+
self.body
139+
}
140+
}
141+
142+
impl TryFrom<Body> for Entry {
143+
type Error = Error;
144+
145+
fn try_from(body: Body) -> Result<Self, Self::Error> {
146+
match body.file_name.clone() {
147+
Some(name) => Ok(Self { body, name }),
148+
None => Err(Error::from_str(
149+
500,
150+
"Missing file_name on Body to convert to Multipart Entry",
151+
)),
152+
}
153+
}
154+
}

src/multipart/mod.rs

Lines changed: 90 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,18 @@
3131
//! }
3232
//! ```
3333
34+
use std::io::{Cursor, Read};
3435
use std::task::Context;
3536
use std::task::Poll;
3637
use std::{fmt::Debug, pin::Pin, str::FromStr};
3738

3839
use futures_core::stream::Stream;
40+
use futures_lite::{io, prelude::*};
41+
use futures_util::stream::TryStreamExt;
3942
use multipart::server::Multipart as Parser;
40-
use std::io::{Cursor, Read};
4143

42-
use crate::{format_err, Mime, Status};
44+
use crate::mime;
45+
use crate::{format_err, Body, Mime, Status};
4346
pub use entry::Entry;
4447

4548
mod entry;
@@ -67,7 +70,6 @@ impl Multipart {
6770

6871
/// Parse a `Body` stream as a `Multipart` instance.
6972
pub async fn from_req(req: &mut crate::Request) -> crate::Result<Self> {
70-
let body = req.take_body().into_string().await?;
7173
let boundary = req
7274
.content_type()
7375
.map(|ct| ct.param("boundary").cloned())
@@ -83,6 +85,9 @@ impl Multipart {
8385
}
8486
};
8587

88+
// Not ideal, but done for now so we can avoid implementing all of Multipart ourselves for the time being.
89+
let body = req.take_body().into_string().await?;
90+
8691
let multipart = Parser::with_body(Cursor::new(body), boundary);
8792
Ok(Self {
8893
entries: vec![],
@@ -96,6 +101,11 @@ impl Multipart {
96101
E: Into<Entry>,
97102
{
98103
self.entries.push(entry.into());
104+
// if let Some(entries) = self.entries.as_mut() {
105+
// entries.push(entry.into());
106+
// } else {
107+
// self.entries = Some(vec![entry.into()]);
108+
// }
99109
}
100110
}
101111

@@ -120,20 +130,91 @@ impl Stream for Multipart {
120130
.content_type
121131
.map(|ct| Mime::from_str(&ct.to_string()))
122132
.transpose()?;
123-
entry.set_content_type(mime);
133+
if let Some(mime) = mime {
134+
entry.set_mime(mime);
135+
} else {
136+
// https://tools.ietf.org/html/rfc7578#section-4.4
137+
entry.set_mime(mime::PLAIN);
138+
}
124139

125140
Poll::Ready(Some(Ok(entry)))
126141
}
127142
Ok(None) => Poll::Ready(None),
128-
Err(_e) => {
129-
// TODO: forward error?
130-
let mut err = format_err!("Invalid multipart entry");
143+
Err(e) => {
144+
let mut err = format_err!("Invalid multipart entry: {}", e);
131145
err.set_status(400);
132146
Poll::Ready(Some(Err(err)))
133147
}
134148
}
135149
}
136150
}
137151

138-
// TODO
139-
// impl From<Multipart> for Body {}
152+
// struct MultipartReader {
153+
// entry_iter: Box<dyn Iterator<Item = Entry>>,
154+
// }
155+
156+
// impl From<Multipart> for MultipartReader {
157+
// fn from(multipart: Multipart) -> Self {
158+
// Self {
159+
// entry_iter: Box::new(multipart.entries.into_iter())
160+
// }
161+
// }
162+
// }
163+
164+
// impl AsyncRead for MultipartReader {
165+
// #[allow(missing_doc_code_examples)]
166+
// fn poll_read(
167+
// mut self: Pin<&mut Self>,
168+
// cx: &mut Context<'_>,
169+
// buf: &mut [u8],
170+
// ) -> Poll<io::Result<usize>> {
171+
// if let Some(entry) = self.entry_iter.next() {
172+
// Pin::new(&mut entry).poll_read(cx, buf)
173+
// } else {
174+
// Poll::Ready()
175+
// }
176+
// }
177+
// }
178+
179+
// impl AsyncBufRead for MultipartReader {
180+
// #[allow(missing_doc_code_examples)]
181+
// fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&'_ [u8]>> {
182+
// let this = self.project();
183+
// this.reader.poll_fill_buf(cx)
184+
// }
185+
186+
// fn consume(mut self: Pin<&mut Self>, amt: usize) {
187+
// Pin::new(&mut self.reader).consume(amt)
188+
// }
189+
// }
190+
191+
struct BufReader<R: AsyncRead> {
192+
inner: io::BufReader<R>,
193+
}
194+
195+
impl<R: AsyncRead> BufReader<R> {
196+
pub fn new(inner: R) -> Self {
197+
Self {
198+
inner: io::BufReader::new(inner),
199+
}
200+
}
201+
}
202+
203+
impl<R: AsyncRead> AsRef<[u8]> for BufReader<R> {
204+
fn as_ref(&self) -> &[u8] {
205+
self.inner.buffer()
206+
}
207+
}
208+
209+
impl From<Multipart> for Body {
210+
fn from(multipart: Multipart) -> Self {
211+
let stream = multipart.map(|maybe_entry| {
212+
maybe_entry
213+
.map(|entry| BufReader::new(entry))
214+
.map_err(|err| {
215+
std::io::Error::new(std::io::ErrorKind::Other, err.to_string().to_owned())
216+
})
217+
});
218+
Body::from_reader(io::BufReader::new(stream.into_async_read()), None)
219+
}
220+
}

0 commit comments

Comments
 (0)