Skip to content

Commit 08b2138

Browse files
committed
refactor(error): add header parse error details in hyper::Error
When a header parse error is because of content-length or transfer-encoding semantics, include a better error message in the `hyper::Error`.
1 parent ea8b0cd commit 08b2138

File tree

2 files changed

+49
-11
lines changed

2 files changed

+49
-11
lines changed

src/error.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,24 @@ pub(super) enum Parse {
7272
#[cfg(feature = "http1")]
7373
VersionH2,
7474
Uri,
75-
Header,
75+
Header(Header),
7676
TooLarge,
7777
Status,
7878
#[cfg_attr(debug_assertions, allow(unused))]
7979
Internal,
8080
}
8181

82+
#[derive(Debug)]
83+
pub(super) enum Header {
84+
Token,
85+
#[cfg(feature = "http1")]
86+
ContentLengthInvalid,
87+
#[cfg(feature = "http1")]
88+
TransferEncodingInvalid,
89+
#[cfg(feature = "http1")]
90+
TransferEncodingUnexpected,
91+
}
92+
8293
#[derive(Debug)]
8394
pub(super) enum User {
8495
/// Error calling user's HttpBody::poll_data().
@@ -375,7 +386,19 @@ impl Error {
375386
#[cfg(feature = "http1")]
376387
Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
377388
Kind::Parse(Parse::Uri) => "invalid URI",
378-
Kind::Parse(Parse::Header) => "invalid HTTP header parsed",
389+
Kind::Parse(Parse::Header(Header::Token)) => "invalid HTTP header parsed",
390+
#[cfg(feature = "http1")]
391+
Kind::Parse(Parse::Header(Header::ContentLengthInvalid)) => {
392+
"invalid content-length parsed"
393+
}
394+
#[cfg(feature = "http1")]
395+
Kind::Parse(Parse::Header(Header::TransferEncodingInvalid)) => {
396+
"invalid transfer-encoding parsed"
397+
}
398+
#[cfg(feature = "http1")]
399+
Kind::Parse(Parse::Header(Header::TransferEncodingUnexpected)) => {
400+
"unexpected transfer-encoding parsed"
401+
}
379402
Kind::Parse(Parse::TooLarge) => "message head is too large",
380403
Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
381404
Kind::Parse(Parse::Internal) => {
@@ -475,13 +498,28 @@ impl From<Parse> for Error {
475498
}
476499
}
477500

501+
#[cfg(feature = "http1")]
502+
impl Parse {
503+
pub(crate) fn content_length_invalid() -> Self {
504+
Parse::Header(Header::ContentLengthInvalid)
505+
}
506+
507+
pub(crate) fn transfer_encoding_invalid() -> Self {
508+
Parse::Header(Header::TransferEncodingInvalid)
509+
}
510+
511+
pub(crate) fn transfer_encoding_unexpected() -> Self {
512+
Parse::Header(Header::TransferEncodingUnexpected)
513+
}
514+
}
515+
478516
impl From<httparse::Error> for Parse {
479517
fn from(err: httparse::Error) -> Parse {
480518
match err {
481519
httparse::Error::HeaderName
482520
| httparse::Error::HeaderValue
483521
| httparse::Error::NewLine
484-
| httparse::Error::Token => Parse::Header,
522+
| httparse::Error::Token => Parse::Header(Header::Token),
485523
httparse::Error::Status => Parse::Status,
486524
httparse::Error::TooManyHeaders => Parse::TooLarge,
487525
httparse::Error::Version => Parse::Version,

src/proto/h1/role.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ impl Http1Transaction for Server {
205205
// malformed. A server should respond with 400 Bad Request.
206206
if !is_http_11 {
207207
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
208-
return Err(Parse::Header);
208+
return Err(Parse::transfer_encoding_unexpected());
209209
}
210210
is_te = true;
211211
if headers::is_chunked_(&value) {
@@ -221,15 +221,15 @@ impl Http1Transaction for Server {
221221
}
222222
let len = value
223223
.to_str()
224-
.map_err(|_| Parse::Header)
225-
.and_then(|s| s.parse().map_err(|_| Parse::Header))?;
224+
.map_err(|_| Parse::content_length_invalid())
225+
.and_then(|s| s.parse().map_err(|_| Parse::content_length_invalid()))?;
226226
if let Some(prev) = con_len {
227227
if prev != len {
228228
debug!(
229229
"multiple Content-Length headers with different values: [{}, {}]",
230230
prev, len,
231231
);
232-
return Err(Parse::Header);
232+
return Err(Parse::content_length_invalid());
233233
}
234234
// we don't need to append this secondary length
235235
continue;
@@ -267,7 +267,7 @@ impl Http1Transaction for Server {
267267

268268
if is_te && !is_te_chunked {
269269
debug!("request with transfer-encoding header, but not chunked, bad request");
270-
return Err(Parse::Header);
270+
return Err(Parse::transfer_encoding_invalid());
271271
}
272272

273273
let mut extensions = http::Extensions::default();
@@ -386,7 +386,7 @@ impl Http1Transaction for Server {
386386
use crate::error::Kind;
387387
let status = match *err.kind() {
388388
Kind::Parse(Parse::Method)
389-
| Kind::Parse(Parse::Header)
389+
| Kind::Parse(Parse::Header(_))
390390
| Kind::Parse(Parse::Uri)
391391
| Kind::Parse(Parse::Version) => StatusCode::BAD_REQUEST,
392392
Kind::Parse(Parse::TooLarge) => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
@@ -1106,7 +1106,7 @@ impl Client {
11061106
// malformed. A server should respond with 400 Bad Request.
11071107
if inc.version == Version::HTTP_10 {
11081108
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
1109-
Err(Parse::Header)
1109+
Err(Parse::transfer_encoding_unexpected())
11101110
} else if headers::transfer_encoding_is_chunked(&inc.headers) {
11111111
Ok(Some((DecodedLength::CHUNKED, false)))
11121112
} else {
@@ -1117,7 +1117,7 @@ impl Client {
11171117
Ok(Some((DecodedLength::checked_new(len)?, false)))
11181118
} else if inc.headers.contains_key(header::CONTENT_LENGTH) {
11191119
debug!("illegal Content-Length header");
1120-
Err(Parse::Header)
1120+
Err(Parse::content_length_invalid())
11211121
} else {
11221122
trace!("neither Transfer-Encoding nor Content-Length");
11231123
Ok(Some((DecodedLength::CLOSE_DELIMITED, false)))

0 commit comments

Comments
 (0)