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:
44
src/error.rs
44
src/error.rs
@@ -72,13 +72,24 @@ pub(super) enum Parse {
|
|||||||
#[cfg(feature = "http1")]
|
#[cfg(feature = "http1")]
|
||||||
VersionH2,
|
VersionH2,
|
||||||
Uri,
|
Uri,
|
||||||
Header,
|
Header(Header),
|
||||||
TooLarge,
|
TooLarge,
|
||||||
Status,
|
Status,
|
||||||
#[cfg_attr(debug_assertions, allow(unused))]
|
#[cfg_attr(debug_assertions, allow(unused))]
|
||||||
Internal,
|
Internal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) enum Header {
|
||||||
|
Token,
|
||||||
|
#[cfg(feature = "http1")]
|
||||||
|
ContentLengthInvalid,
|
||||||
|
#[cfg(feature = "http1")]
|
||||||
|
TransferEncodingInvalid,
|
||||||
|
#[cfg(feature = "http1")]
|
||||||
|
TransferEncodingUnexpected,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) enum User {
|
pub(super) enum User {
|
||||||
/// Error calling user's HttpBody::poll_data().
|
/// Error calling user's HttpBody::poll_data().
|
||||||
@@ -375,7 +386,19 @@ impl Error {
|
|||||||
#[cfg(feature = "http1")]
|
#[cfg(feature = "http1")]
|
||||||
Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
|
Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
|
||||||
Kind::Parse(Parse::Uri) => "invalid URI",
|
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::TooLarge) => "message head is too large",
|
||||||
Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
|
Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
|
||||||
Kind::Parse(Parse::Internal) => {
|
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 {
|
impl From<httparse::Error> for Parse {
|
||||||
fn from(err: httparse::Error) -> Parse {
|
fn from(err: httparse::Error) -> Parse {
|
||||||
match err {
|
match err {
|
||||||
httparse::Error::HeaderName
|
httparse::Error::HeaderName
|
||||||
| httparse::Error::HeaderValue
|
| httparse::Error::HeaderValue
|
||||||
| httparse::Error::NewLine
|
| httparse::Error::NewLine
|
||||||
| httparse::Error::Token => Parse::Header,
|
| httparse::Error::Token => Parse::Header(Header::Token),
|
||||||
httparse::Error::Status => Parse::Status,
|
httparse::Error::Status => Parse::Status,
|
||||||
httparse::Error::TooManyHeaders => Parse::TooLarge,
|
httparse::Error::TooManyHeaders => Parse::TooLarge,
|
||||||
httparse::Error::Version => Parse::Version,
|
httparse::Error::Version => Parse::Version,
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ impl Http1Transaction for Server {
|
|||||||
// malformed. A server should respond with 400 Bad Request.
|
// malformed. A server should respond with 400 Bad Request.
|
||||||
if !is_http_11 {
|
if !is_http_11 {
|
||||||
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
|
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
|
||||||
return Err(Parse::Header);
|
return Err(Parse::transfer_encoding_unexpected());
|
||||||
}
|
}
|
||||||
is_te = true;
|
is_te = true;
|
||||||
if headers::is_chunked_(&value) {
|
if headers::is_chunked_(&value) {
|
||||||
@@ -221,15 +221,15 @@ impl Http1Transaction for Server {
|
|||||||
}
|
}
|
||||||
let len = value
|
let len = value
|
||||||
.to_str()
|
.to_str()
|
||||||
.map_err(|_| Parse::Header)
|
.map_err(|_| Parse::content_length_invalid())
|
||||||
.and_then(|s| s.parse().map_err(|_| Parse::Header))?;
|
.and_then(|s| s.parse().map_err(|_| Parse::content_length_invalid()))?;
|
||||||
if let Some(prev) = con_len {
|
if let Some(prev) = con_len {
|
||||||
if prev != len {
|
if prev != len {
|
||||||
debug!(
|
debug!(
|
||||||
"multiple Content-Length headers with different values: [{}, {}]",
|
"multiple Content-Length headers with different values: [{}, {}]",
|
||||||
prev, len,
|
prev, len,
|
||||||
);
|
);
|
||||||
return Err(Parse::Header);
|
return Err(Parse::content_length_invalid());
|
||||||
}
|
}
|
||||||
// we don't need to append this secondary length
|
// we don't need to append this secondary length
|
||||||
continue;
|
continue;
|
||||||
@@ -267,7 +267,7 @@ impl Http1Transaction for Server {
|
|||||||
|
|
||||||
if is_te && !is_te_chunked {
|
if is_te && !is_te_chunked {
|
||||||
debug!("request with transfer-encoding header, but not chunked, bad request");
|
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();
|
let mut extensions = http::Extensions::default();
|
||||||
@@ -386,7 +386,7 @@ impl Http1Transaction for Server {
|
|||||||
use crate::error::Kind;
|
use crate::error::Kind;
|
||||||
let status = match *err.kind() {
|
let status = match *err.kind() {
|
||||||
Kind::Parse(Parse::Method)
|
Kind::Parse(Parse::Method)
|
||||||
| Kind::Parse(Parse::Header)
|
| Kind::Parse(Parse::Header(_))
|
||||||
| Kind::Parse(Parse::Uri)
|
| Kind::Parse(Parse::Uri)
|
||||||
| Kind::Parse(Parse::Version) => StatusCode::BAD_REQUEST,
|
| Kind::Parse(Parse::Version) => StatusCode::BAD_REQUEST,
|
||||||
Kind::Parse(Parse::TooLarge) => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
|
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.
|
// malformed. A server should respond with 400 Bad Request.
|
||||||
if inc.version == Version::HTTP_10 {
|
if inc.version == Version::HTTP_10 {
|
||||||
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
|
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) {
|
} else if headers::transfer_encoding_is_chunked(&inc.headers) {
|
||||||
Ok(Some((DecodedLength::CHUNKED, false)))
|
Ok(Some((DecodedLength::CHUNKED, false)))
|
||||||
} else {
|
} else {
|
||||||
@@ -1117,7 +1117,7 @@ impl Client {
|
|||||||
Ok(Some((DecodedLength::checked_new(len)?, false)))
|
Ok(Some((DecodedLength::checked_new(len)?, false)))
|
||||||
} else if inc.headers.contains_key(header::CONTENT_LENGTH) {
|
} else if inc.headers.contains_key(header::CONTENT_LENGTH) {
|
||||||
debug!("illegal Content-Length header");
|
debug!("illegal Content-Length header");
|
||||||
Err(Parse::Header)
|
Err(Parse::content_length_invalid())
|
||||||
} else {
|
} else {
|
||||||
trace!("neither Transfer-Encoding nor Content-Length");
|
trace!("neither Transfer-Encoding nor Content-Length");
|
||||||
Ok(Some((DecodedLength::CLOSE_DELIMITED, false)))
|
Ok(Some((DecodedLength::CLOSE_DELIMITED, false)))
|
||||||
|
|||||||
Reference in New Issue
Block a user