Skip to content

Commit 9915783

Browse files
committed
feat(error): preserve InvalidUri details in error source chain
When an invalid URI is parsed by the http crate, the InvalidUri error contains useful details about what part of the URI was invalid. Previously this information was discarded when converting to hyper::Error. Now the original InvalidUri/InvalidUriParts error is stored as the source of the hyper::Error, making it available through the standard Error::source() chain and downcastable to the concrete type. Closes #3043
1 parent 8ba9008 commit 9915783

File tree

2 files changed

+35
-9
lines changed

2 files changed

+35
-9
lines changed

src/error.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ pub(super) enum Parse {
9292
Version,
9393
#[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
9494
VersionH2,
95-
Uri,
95+
Uri(Option<Cause>),
9696
#[cfg(all(feature = "http1", feature = "server"))]
9797
UriTooLong,
9898
#[cfg(feature = "http1")]
@@ -459,7 +459,7 @@ impl Error {
459459
Kind::Parse(Parse::Version) => "invalid HTTP version parsed",
460460
#[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
461461
Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
462-
Kind::Parse(Parse::Uri) => "invalid URI",
462+
Kind::Parse(Parse::Uri(_)) => "invalid URI",
463463
#[cfg(all(feature = "http1", feature = "server"))]
464464
Kind::Parse(Parse::UriTooLong) => "URI too long",
465465
#[cfg(feature = "http1")]
@@ -582,7 +582,10 @@ impl StdError for Error {
582582
#[doc(hidden)]
583583
impl From<Parse> for Error {
584584
fn from(err: Parse) -> Error {
585-
Error::new(Kind::Parse(err))
585+
match err {
586+
Parse::Uri(Some(cause)) => Error::new(Kind::Parse(Parse::Uri(None))).with(cause),
587+
other => Error::new(Kind::Parse(other)),
588+
}
586589
}
587590
}
588591

@@ -632,14 +635,14 @@ impl From<http::status::InvalidStatusCode> for Parse {
632635
}
633636

634637
impl From<http::uri::InvalidUri> for Parse {
635-
fn from(_: http::uri::InvalidUri) -> Parse {
636-
Parse::Uri
638+
fn from(err: http::uri::InvalidUri) -> Parse {
639+
Parse::Uri(Some(Box::new(err)))
637640
}
638641
}
639642

640643
impl From<http::uri::InvalidUriParts> for Parse {
641-
fn from(_: http::uri::InvalidUriParts) -> Parse {
642-
Parse::Uri
644+
fn from(err: http::uri::InvalidUriParts) -> Parse {
645+
Parse::Uri(Some(Box::new(err)))
643646
}
644647
}
645648

@@ -692,4 +695,27 @@ mod tests {
692695
let svc_err = Error::new_user_service(recvd);
693696
assert_eq!(svc_err.h2_reason(), h2::Reason::HTTP_1_1_REQUIRED);
694697
}
698+
699+
#[test]
700+
fn uri_error_preserves_source() {
701+
use std::error::Error as _;
702+
703+
// Parse an invalid URI through the http crate
704+
let invalid: std::result::Result<http::Uri, _> = "dangling whitespace ".parse();
705+
let uri_err: http::uri::InvalidUri = invalid.unwrap_err();
706+
707+
// Convert through the same path hyper uses: InvalidUri -> Parse -> Error
708+
let parse: Parse = Parse::from(uri_err);
709+
let error: Error = Error::from(parse);
710+
711+
// The error should have a source
712+
assert!(error.source().is_some(), "URI error should preserve source");
713+
714+
// The source should be the original InvalidUri
715+
let source = error.source().unwrap();
716+
assert!(
717+
source.downcast_ref::<http::uri::InvalidUri>().is_some(),
718+
"source should be http::uri::InvalidUri"
719+
);
720+
}
695721
}

src/proto/h1/role.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ impl Http1Transaction for Server {
199199
Parse::Method
200200
} else {
201201
debug_assert!(req.path.is_none());
202-
Parse::Uri
202+
Parse::Uri(None)
203203
}
204204
})
205205
}
@@ -467,7 +467,7 @@ impl Http1Transaction for Server {
467467
let status = match *err.kind() {
468468
Kind::Parse(Parse::Method)
469469
| Kind::Parse(Parse::Header(_))
470-
| Kind::Parse(Parse::Uri)
470+
| Kind::Parse(Parse::Uri(_))
471471
| Kind::Parse(Parse::Version) => StatusCode::BAD_REQUEST,
472472
Kind::Parse(Parse::TooLarge) => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
473473
Kind::Parse(Parse::UriTooLong) => StatusCode::URI_TOO_LONG,

0 commit comments

Comments
 (0)