diff --git a/src/error.rs b/src/error.rs index 0949e1cb85..9a1b0e12ab 100644 --- a/src/error.rs +++ b/src/error.rs @@ -71,6 +71,7 @@ pub(super) enum Parse { #[cfg(feature = "http1")] VersionH2, Uri, + UriTooLong, Header(Header), TooLarge, Status, @@ -152,7 +153,7 @@ impl Error { /// Returns true if this was an HTTP parse error caused by a message that was too large. pub fn is_parse_too_large(&self) -> bool { - matches!(self.inner.kind, Kind::Parse(Parse::TooLarge)) + matches!(self.inner.kind, Kind::Parse(Parse::TooLarge) | Kind::Parse(Parse::UriTooLong)) } /// Returns true if this was an HTTP parse error caused by an invalid response status code or @@ -398,6 +399,7 @@ 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::UriTooLong) => "URI too long", Kind::Parse(Parse::Header(Header::Token)) => "invalid HTTP header parsed", #[cfg(feature = "http1")] Kind::Parse(Parse::Header(Header::ContentLengthInvalid)) => { diff --git a/src/proto/h1/role.rs b/src/proto/h1/role.rs index 794ee7945c..1902a774a3 100644 --- a/src/proto/h1/role.rs +++ b/src/proto/h1/role.rs @@ -25,6 +25,7 @@ use crate::proto::{BodyLength, MessageHead, RequestHead, RequestLine}; const MAX_HEADERS: usize = 100; const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific +const MAX_URI_LEN: usize = (u16::MAX - 1) as usize; macro_rules! header_name { ($bytes:expr) => {{ @@ -148,9 +149,13 @@ impl Http1Transaction for Server { Ok(httparse::Status::Complete(parsed_len)) => { trace!("Request.parse Complete({})", parsed_len); len = parsed_len; + let uri = req.path.unwrap(); + if uri.len() > MAX_URI_LEN { + return Err(Parse::UriTooLong); + } subject = RequestLine( Method::from_bytes(req.method.unwrap().as_bytes())?, - req.path.unwrap().parse()?, + uri.parse()?, ); version = if req.version.unwrap() == 1 { keep_alive = true; @@ -408,6 +413,7 @@ impl Http1Transaction for Server { | Kind::Parse(Parse::Uri) | Kind::Parse(Parse::Version) => StatusCode::BAD_REQUEST, Kind::Parse(Parse::TooLarge) => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE, + Kind::Parse(Parse::UriTooLong) => StatusCode::URI_TOO_LONG, _ => return None, }; diff --git a/tests/server.rs b/tests/server.rs index 16e905884c..13530a36c3 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -1025,6 +1025,23 @@ fn http_10_request_receives_http_10_response() { assert_eq!(s(&buf[..expected.len()]), expected); } +#[test] +fn http_11_uri_too_long() { + let server = serve(); + + let long_path = "a".repeat(65534); + let request_line = format!("GET /{} HTTP/1.1\r\n\r\n", long_path); + + let mut req = connect(server.addr()); + req.write_all(request_line.as_bytes()).unwrap(); + + let expected = "HTTP/1.1 414 URI Too Long\r\ncontent-length: 0\r\n"; + let mut buf = [0; 256]; + let n = req.read(&mut buf).unwrap(); + assert!(n >= expected.len(), "read: {:?} >= {:?}", n, expected.len()); + assert_eq!(s(&buf[..expected.len()]), expected); +} + #[tokio::test] async fn disable_keep_alive_mid_request() { let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap()).unwrap();