7
7
8
8
//! `multipart` field header parsing.
9
9
10
- use super :: httparse:: { self , EMPTY_HEADER , Status } ;
10
+ use super :: httparse:: { self , EMPTY_HEADER , Header , Status } ;
11
11
12
12
use super :: Multipart ;
13
13
14
14
use super :: boundary:: BoundaryReader ;
15
15
16
- use mime:: { Attr , Mime , Value } ;
16
+ use mime:: Mime ;
17
17
18
18
use std:: io:: { self , Read , BufRead , Write } ;
19
19
use std:: fs:: { self , File } ;
@@ -34,83 +34,94 @@ macro_rules! try_io(
34
34
)
35
35
) ;
36
36
37
- macro_rules! assert_log_ret_none (
38
- ( $expr, $else_: expr) => (
39
- if !$expr {
40
- $else_;
41
- return None ;
42
- }
43
- )
44
- ) ;
45
-
46
37
const EMPTY_STR_HEADER : StrHeader < ' static > = StrHeader {
47
38
name : "" ,
48
39
val : "" ,
49
40
} ;
50
41
42
+ /// Not exposed
51
43
#[ derive( Copy , Clone , Debug ) ]
52
- struct StrHeader < ' a > {
44
+ pub struct StrHeader < ' a > {
53
45
name : & ' a str ,
54
46
val : & ' a str ,
55
47
}
56
48
57
- /// The headers that (may) appear before a `multipart/form-data` field.
58
- pub struct FieldHeaders {
59
- /// The `Content-Disposition` header, required.
60
- pub cont_disp : ContentDisp ,
61
- /// The `Content-Type` header, optional.
62
- pub cont_type : Option < ContentType > ,
63
- }
49
+ const MAX_ATTEMPTS : usize = 5 ;
64
50
65
- impl FieldHeaders {
66
- /// Parse the field headers from the passed `BufRead`, consuming the relevant bytes.
67
- pub fn parse < R : BufRead > ( r : & mut R ) -> io:: Result < Option < Self > > {
68
- const HEADER_LEN : usize = 4 ;
51
+ fn with_headers < R , F , Ret > ( r : & mut R , closure : F ) -> io:: Result < Ret >
52
+ where R : BufRead , F : FnOnce ( & [ StrHeader ] ) -> Ret {
53
+ const HEADER_LEN : usize = 4 ;
69
54
70
- // These are only written once so they don't need to be `mut` or initialized.
71
- let consume;
72
- let header_len ;
55
+ // These are only written once so they don't need to be `mut` or initialized.
56
+ let consume;
57
+ let ret ;
73
58
74
- let mut headers = [ EMPTY_STR_HEADER ; HEADER_LEN ] ;
59
+ let mut attempts = 0 ;
75
60
76
- {
77
- let mut raw_headers = [ EMPTY_HEADER ; HEADER_LEN ] ;
78
-
79
- loop {
80
- let buf = try!( r. fill_buf ( ) ) ;
81
-
82
- match try_io ! ( httparse:: parse_headers( buf, & mut raw_headers) ) {
83
- Status :: Complete ( ( consume_, raw_headers) ) => {
84
- consume = consume_;
85
- header_len = raw_headers. len ( ) ;
86
- break ;
87
- } ,
88
- Status :: Partial => ( ) ,
89
- }
90
- }
61
+ loop {
62
+ let mut raw_headers = [ EMPTY_HEADER ; HEADER_LEN ] ;
91
63
92
- for ( raw, header) in raw_headers. iter ( ) . take ( header_len) . zip ( & mut headers) {
93
- header. name = raw. name ;
94
- header. val = try!( io_str_utf8 ( raw. value ) ) ;
95
- }
64
+ let buf = try!( r. fill_buf ( ) ) ;
65
+
66
+ if attempts == MAX_ATTEMPTS {
67
+ error ! ( "Could not read field headers." ) ;
68
+ // RFC: return an actual error instead?
69
+ return Ok ( closure ( & [ ] ) ) ;
96
70
}
97
71
98
- let headers = & headers[ ..header_len] ;
72
+ match httparse:: parse_headers ( buf, & mut raw_headers) {
73
+ Ok ( Status :: Complete ( ( consume_, raw_headers) ) ) => {
74
+ consume = consume_;
75
+ let mut headers = [ EMPTY_STR_HEADER ; HEADER_LEN ] ;
76
+ let headers = try!( copy_headers ( raw_headers, & mut headers) ) ;
77
+ debug ! ( "Parsed headers: {:?}" , headers) ;
78
+ ret = closure ( headers) ;
79
+ break ;
80
+ } ,
81
+ Ok ( Status :: Partial ) => { attempts += 1 ; continue } ,
82
+ Err ( err) => {
83
+ error ! ( "Error returned from parse_headers(): {}, Buf: {:?}" ,
84
+ err, String :: from_utf8_lossy( buf) ) ;
85
+ return Err ( io:: Error :: new ( io:: ErrorKind :: InvalidData , err) ) ;
86
+ } ,
87
+ }
88
+ }
89
+
90
+ r. consume ( consume) ;
99
91
100
- debug ! ( "Parsed field headers: {:?}" , headers) ;
92
+ Ok ( ret)
93
+ }
94
+
95
+ fn copy_headers < ' h , ' b : ' h > ( raw : & [ Header < ' b > ] , headers : & ' h mut [ StrHeader < ' b > ] ) -> io:: Result < & ' h [ StrHeader < ' b > ] > {
96
+ for ( raw, header) in raw. iter ( ) . zip ( & mut * headers) {
97
+ header. name = raw. name ;
98
+ header. val = try!( io_str_utf8 ( raw. value ) ) ;
99
+ }
101
100
102
- r. consume ( consume) ;
101
+ Ok ( & mut headers[ ..raw. len ( ) ] )
102
+ }
103
103
104
- Ok ( Self :: read_from ( headers) )
104
+ /// The headers that (may) appear before a `multipart/form-data` field.
105
+ pub struct FieldHeaders {
106
+ /// The `Content-Disposition` header, required.
107
+ cont_disp : ContentDisp ,
108
+ /// The `Content-Type` header, optional.
109
+ cont_type : Option < Mime > ,
110
+ }
111
+
112
+ impl FieldHeaders {
113
+ /// Parse the field headers from the passed `BufRead`, consuming the relevant bytes.
114
+ pub fn read_from < R : BufRead > ( r : & mut R ) -> io:: Result < Option < Self > > {
115
+ with_headers ( r, Self :: parse)
105
116
}
106
117
107
- fn read_from ( headers : & [ StrHeader ] ) -> Option < FieldHeaders > {
118
+ fn parse ( headers : & [ StrHeader ] ) -> Option < FieldHeaders > {
108
119
let cont_disp = try_opt ! (
109
- ContentDisp :: read_from ( headers) ,
120
+ ContentDisp :: parse ( headers) ,
110
121
debug!( "Failed to read Content-Disposition" )
111
122
) ;
112
123
113
- let cont_type = ContentType :: read_from ( headers) ;
124
+ let cont_type = parse_cont_type ( headers) ;
114
125
115
126
Some ( FieldHeaders {
116
127
cont_disp : cont_disp,
@@ -122,13 +133,13 @@ impl FieldHeaders {
122
133
/// The `Content-Disposition` header.
123
134
pub struct ContentDisp {
124
135
/// The name of the `multipart/form-data` field.
125
- pub field_name : String ,
136
+ field_name : String ,
126
137
/// The optional filename for this field.
127
- pub filename : Option < String > ,
138
+ filename : Option < String > ,
128
139
}
129
140
130
141
impl ContentDisp {
131
- fn read_from ( headers : & [ StrHeader ] ) -> Option < ContentDisp > {
142
+ fn parse ( headers : & [ StrHeader ] ) -> Option < ContentDisp > {
132
143
if headers. is_empty ( ) {
133
144
return None ;
134
145
}
@@ -173,34 +184,18 @@ impl ContentDisp {
173
184
}
174
185
}
175
186
176
- /// The `Content-Type` header.
177
- pub struct ContentType {
178
- /// The MIME type of the `multipart` field.
179
- ///
180
- /// May contain a sub-boundary parameter.
181
- pub val : Mime ,
182
- }
187
+ fn parse_cont_type ( headers : & [ StrHeader ] ) -> Option < Mime > {
188
+ const CONTENT_TYPE : & ' static str = "Content-Type" ;
183
189
184
- impl ContentType {
185
- fn read_from ( headers : & [ StrHeader ] ) -> Option < ContentType > {
186
- const CONTENT_TYPE : & ' static str = "Content-Type" ;
190
+ let header = try_opt ! (
191
+ find_header( headers, CONTENT_TYPE ) ,
192
+ debug!( "Content-Type header not found for field." )
193
+ ) ;
187
194
188
- let header = try_opt ! (
189
- find_header( headers, CONTENT_TYPE ) ,
190
- debug!( "Content-Type header not found for field." )
191
- ) ;
192
-
193
- // Boundary parameter will be parsed into the `Mime`
194
- debug ! ( "Found Content-Type: {:?}" , header. val) ;
195
- let content_type = read_content_type ( header. val . trim ( ) ) ;
196
- Some ( ContentType { val : content_type } )
197
- }
198
-
199
- /// Get the optional boundary parameter for this `Content-Type`.
200
- #[ allow( dead_code) ]
201
- pub fn boundary ( & self ) -> Option < & str > {
202
- self . val . get_param ( Attr :: Boundary ) . map ( Value :: as_str)
203
- }
195
+ // Boundary parameter will be parsed into the `Mime`
196
+ debug ! ( "Found Content-Type: {:?}" , header. val) ;
197
+ let content_type = read_content_type ( header. val . trim ( ) ) ;
198
+ Some ( content_type)
204
199
}
205
200
206
201
/// A field in a multipart request. May be either text or a binary stream (file).
@@ -224,7 +219,7 @@ pub fn read_field<B: Read>(multipart: &mut Multipart<B>) -> io::Result<Option<Mu
224
219
MultipartData :: File (
225
220
MultipartFile :: from_stream (
226
221
field_headers. cont_disp . filename ,
227
- content_type. val ,
222
+ content_type,
228
223
& mut multipart. source ,
229
224
)
230
225
)
0 commit comments