Skip to content
This repository was archived by the owner on Feb 14, 2023. It is now read-only.

Commit e389cd7

Browse files
committed
server::field: backport fix for rust-lang/rust#40288 from 0.10
1 parent 5aea9ee commit e389cd7

File tree

2 files changed

+79
-84
lines changed

2 files changed

+79
-84
lines changed

src/server/field.rs

+78-83
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77

88
//! `multipart` field header parsing.
99
10-
use super::httparse::{self, EMPTY_HEADER, Status};
10+
use super::httparse::{self, EMPTY_HEADER, Header, Status};
1111

1212
use super::Multipart;
1313

1414
use super::boundary::BoundaryReader;
1515

16-
use mime::{Attr, Mime, Value};
16+
use mime::Mime;
1717

1818
use std::io::{self, Read, BufRead, Write};
1919
use std::fs::{self, File};
@@ -34,83 +34,94 @@ macro_rules! try_io(
3434
)
3535
);
3636

37-
macro_rules! assert_log_ret_none (
38-
($expr, $else_:expr) => (
39-
if !$expr {
40-
$else_;
41-
return None;
42-
}
43-
)
44-
);
45-
4637
const EMPTY_STR_HEADER: StrHeader<'static> = StrHeader {
4738
name: "",
4839
val: "",
4940
};
5041

42+
/// Not exposed
5143
#[derive(Copy, Clone, Debug)]
52-
struct StrHeader<'a> {
44+
pub struct StrHeader<'a> {
5345
name: &'a str,
5446
val: &'a str,
5547
}
5648

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;
6450

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;
6954

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;
7358

74-
let mut headers = [EMPTY_STR_HEADER; HEADER_LEN];
59+
let mut attempts = 0;
7560

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];
9163

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(&[]));
9670
}
9771

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);
9991

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+
}
101100

102-
r.consume(consume);
101+
Ok(&mut headers[..raw.len()])
102+
}
103103

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)
105116
}
106117

107-
fn read_from(headers: &[StrHeader]) -> Option<FieldHeaders> {
118+
fn parse(headers: &[StrHeader]) -> Option<FieldHeaders> {
108119
let cont_disp = try_opt!(
109-
ContentDisp::read_from(headers),
120+
ContentDisp::parse(headers),
110121
debug!("Failed to read Content-Disposition")
111122
);
112123

113-
let cont_type = ContentType::read_from(headers);
124+
let cont_type = parse_cont_type(headers);
114125

115126
Some(FieldHeaders {
116127
cont_disp: cont_disp,
@@ -122,13 +133,13 @@ impl FieldHeaders {
122133
/// The `Content-Disposition` header.
123134
pub struct ContentDisp {
124135
/// The name of the `multipart/form-data` field.
125-
pub field_name: String,
136+
field_name: String,
126137
/// The optional filename for this field.
127-
pub filename: Option<String>,
138+
filename: Option<String>,
128139
}
129140

130141
impl ContentDisp {
131-
fn read_from(headers: &[StrHeader]) -> Option<ContentDisp> {
142+
fn parse(headers: &[StrHeader]) -> Option<ContentDisp> {
132143
if headers.is_empty() {
133144
return None;
134145
}
@@ -173,34 +184,18 @@ impl ContentDisp {
173184
}
174185
}
175186

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";
183189

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+
);
187194

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)
204199
}
205200

206201
/// 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
224219
MultipartData::File(
225220
MultipartFile::from_stream(
226221
field_headers.cont_disp.filename,
227-
content_type.val,
222+
content_type,
228223
&mut multipart.source,
229224
)
230225
)

src/server/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl<B: Read> Multipart<B> {
110110
}
111111

112112
fn read_field_headers(&mut self) -> io::Result<Option<FieldHeaders>> {
113-
FieldHeaders::parse(&mut self.source)
113+
FieldHeaders::read_from(&mut self.source)
114114
}
115115

116116
/// Call `f` for each entry in the multipart request.

0 commit comments

Comments
 (0)