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`.
This commit is contained in:
Sean McArthur
2021-06-08 09:44:35 -07:00
parent ea8b0cd86e
commit 08b2138e40
2 changed files with 49 additions and 11 deletions

View File

@@ -72,13 +72,24 @@ pub(super) enum Parse {
#[cfg(feature = "http1")]
VersionH2,
Uri,
Header,
Header(Header),
TooLarge,
Status,
#[cfg_attr(debug_assertions, allow(unused))]
Internal,
}
#[derive(Debug)]
pub(super) enum Header {
Token,
#[cfg(feature = "http1")]
ContentLengthInvalid,
#[cfg(feature = "http1")]
TransferEncodingInvalid,
#[cfg(feature = "http1")]
TransferEncodingUnexpected,
}
#[derive(Debug)]
pub(super) enum User {
/// Error calling user's HttpBody::poll_data().
@@ -375,7 +386,19 @@ impl Error {
#[cfg(feature = "http1")]
Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
Kind::Parse(Parse::Uri) => "invalid URI",
Kind::Parse(Parse::Header) => "invalid HTTP header parsed",
Kind::Parse(Parse::Header(Header::Token)) => "invalid HTTP header parsed",
#[cfg(feature = "http1")]
Kind::Parse(Parse::Header(Header::ContentLengthInvalid)) => {
"invalid content-length parsed"
}
#[cfg(feature = "http1")]
Kind::Parse(Parse::Header(Header::TransferEncodingInvalid)) => {
"invalid transfer-encoding parsed"
}
#[cfg(feature = "http1")]
Kind::Parse(Parse::Header(Header::TransferEncodingUnexpected)) => {
"unexpected transfer-encoding parsed"
}
Kind::Parse(Parse::TooLarge) => "message head is too large",
Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
Kind::Parse(Parse::Internal) => {
@@ -475,13 +498,28 @@ impl From<Parse> for Error {
}
}
#[cfg(feature = "http1")]
impl Parse {
pub(crate) fn content_length_invalid() -> Self {
Parse::Header(Header::ContentLengthInvalid)
}
pub(crate) fn transfer_encoding_invalid() -> Self {
Parse::Header(Header::TransferEncodingInvalid)
}
pub(crate) fn transfer_encoding_unexpected() -> Self {
Parse::Header(Header::TransferEncodingUnexpected)
}
}
impl From<httparse::Error> for Parse {
fn from(err: httparse::Error) -> Parse {
match err {
httparse::Error::HeaderName
| httparse::Error::HeaderValue
| httparse::Error::NewLine
| httparse::Error::Token => Parse::Header,
| httparse::Error::Token => Parse::Header(Header::Token),
httparse::Error::Status => Parse::Status,
httparse::Error::TooManyHeaders => Parse::TooLarge,
httparse::Error::Version => Parse::Version,

View File

@@ -205,7 +205,7 @@ impl Http1Transaction for Server {
// malformed. A server should respond with 400 Bad Request.
if !is_http_11 {
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
return Err(Parse::Header);
return Err(Parse::transfer_encoding_unexpected());
}
is_te = true;
if headers::is_chunked_(&value) {
@@ -221,15 +221,15 @@ impl Http1Transaction for Server {
}
let len = value
.to_str()
.map_err(|_| Parse::Header)
.and_then(|s| s.parse().map_err(|_| Parse::Header))?;
.map_err(|_| Parse::content_length_invalid())
.and_then(|s| s.parse().map_err(|_| Parse::content_length_invalid()))?;
if let Some(prev) = con_len {
if prev != len {
debug!(
"multiple Content-Length headers with different values: [{}, {}]",
prev, len,
);
return Err(Parse::Header);
return Err(Parse::content_length_invalid());
}
// we don't need to append this secondary length
continue;
@@ -267,7 +267,7 @@ impl Http1Transaction for Server {
if is_te && !is_te_chunked {
debug!("request with transfer-encoding header, but not chunked, bad request");
return Err(Parse::Header);
return Err(Parse::transfer_encoding_invalid());
}
let mut extensions = http::Extensions::default();
@@ -386,7 +386,7 @@ impl Http1Transaction for Server {
use crate::error::Kind;
let status = match *err.kind() {
Kind::Parse(Parse::Method)
| Kind::Parse(Parse::Header)
| Kind::Parse(Parse::Header(_))
| Kind::Parse(Parse::Uri)
| Kind::Parse(Parse::Version) => StatusCode::BAD_REQUEST,
Kind::Parse(Parse::TooLarge) => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
@@ -1106,7 +1106,7 @@ impl Client {
// malformed. A server should respond with 400 Bad Request.
if inc.version == Version::HTTP_10 {
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
Err(Parse::Header)
Err(Parse::transfer_encoding_unexpected())
} else if headers::transfer_encoding_is_chunked(&inc.headers) {
Ok(Some((DecodedLength::CHUNKED, false)))
} else {
@@ -1117,7 +1117,7 @@ impl Client {
Ok(Some((DecodedLength::checked_new(len)?, false)))
} else if inc.headers.contains_key(header::CONTENT_LENGTH) {
debug!("illegal Content-Length header");
Err(Parse::Header)
Err(Parse::content_length_invalid())
} else {
trace!("neither Transfer-Encoding nor Content-Length");
Ok(Some((DecodedLength::CLOSE_DELIMITED, false)))