31
31
//! }
32
32
//! ```
33
33
34
- use crate :: Body ;
34
+ use std:: task:: Context ;
35
+ use std:: task:: Poll ;
36
+ use std:: { fmt:: Debug , pin:: Pin , str:: FromStr } ;
37
+
38
+ use futures_core:: stream:: Stream ;
39
+ use multipart:: server:: Multipart as Parser ;
40
+ use std:: io:: { Cursor , Read } ;
41
+
42
+ use crate :: { format_err, Mime , Status } ;
35
43
pub use entry:: Entry ;
36
44
37
45
mod entry;
38
46
39
47
/// A multipart response body.
40
- #[ derive( Debug ) ]
41
48
pub struct Multipart {
42
49
entries : Vec < Entry > ,
43
- body : Option < Body > ,
50
+ body : Option < Parser < Cursor < String > > > ,
51
+ }
52
+
53
+ impl Debug for Multipart {
54
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
55
+ f. debug_struct ( "Multipart" ) . finish ( )
56
+ }
44
57
}
45
58
46
59
impl Multipart {
@@ -53,11 +66,28 @@ impl Multipart {
53
66
}
54
67
55
68
/// Parse a `Body` stream as a `Multipart` instance.
56
- pub fn from_body ( body : Body ) -> Self {
57
- Self {
69
+ pub async fn from_req ( req : & mut crate :: Request ) -> crate :: Result < Self > {
70
+ let body = req. take_body ( ) . into_string ( ) . await ?;
71
+ let boundary = req
72
+ . content_type ( )
73
+ . map ( |ct| ct. param ( "boundary" ) . cloned ( ) )
74
+ . flatten ( ) ;
75
+
76
+ let boundary = match boundary {
77
+ Some ( boundary) => boundary. as_str ( ) . to_owned ( ) ,
78
+ None => {
79
+ let mut err =
80
+ format_err ! ( "Invalid `Content-Type` header. Expected a `boundary` param" ) ;
81
+ err. set_status ( 400 ) ;
82
+ return Err ( err) ;
83
+ }
84
+ } ;
85
+
86
+ let multipart = Parser :: with_body ( Cursor :: new ( body) , boundary) ;
87
+ Ok ( Self {
58
88
entries : vec ! [ ] ,
59
- body : Some ( body ) ,
60
- }
89
+ body : Some ( multipart ) ,
90
+ } )
61
91
}
62
92
63
93
/// Add a new entry to the `Multipart` instance.
@@ -69,8 +99,41 @@ impl Multipart {
69
99
}
70
100
}
71
101
72
- // TODO
73
- // impl Stream for Multipart {}
102
+ impl Stream for Multipart {
103
+ type Item = crate :: Result < Entry > ;
104
+
105
+ fn poll_next ( mut self : Pin < & mut Self > , _cx : & mut Context ) -> Poll < Option < Self :: Item > > {
106
+ let body = match self . body . as_mut ( ) {
107
+ None => return Poll :: Ready ( None ) ,
108
+ Some ( body) => body,
109
+ } ;
110
+
111
+ match body. read_entry ( ) {
112
+ Ok ( Some ( mut field) ) => {
113
+ let mut body = vec ! [ ] ;
114
+ field. data . read_to_end ( & mut body) . status ( 400 ) ?;
115
+
116
+ let mut entry = Entry :: new ( field. headers . name , body) ;
117
+ entry. set_file_name ( field. headers . filename ) ;
118
+ let mime = field
119
+ . headers
120
+ . content_type
121
+ . map ( |ct| Mime :: from_str ( & ct. to_string ( ) ) )
122
+ . transpose ( ) ?;
123
+ entry. set_content_type ( mime) ;
124
+
125
+ Poll :: Ready ( Some ( Ok ( entry) ) )
126
+ }
127
+ Ok ( None ) => Poll :: Ready ( None ) ,
128
+ Err ( _e) => {
129
+ // TODO: forward error?
130
+ let mut err = format_err ! ( "Invalid multipart entry" ) ;
131
+ err. set_status ( 400 ) ;
132
+ Poll :: Ready ( Some ( Err ( err) ) )
133
+ }
134
+ }
135
+ }
136
+ }
74
137
75
138
// TODO
76
139
// impl From<Multipart> for Body {}
0 commit comments