From 4869987a05db33b160ac472ec9b947a05349b06f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Lindberg?= Date: Sun, 19 Jul 2026 07:55:39 +0200 Subject: [PATCH 1/3] fix(http1): return 414 when request line overflows max_buf_size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an HTTP/1 server receives a request whose start-line (method, URI, version) is so large that it fills the read buffer before a newline is even seen, hyper reported the failure as `431 Request Header Fields Too Large`. Per RFC 7231 §6.5.12, the more specific status for an oversized URI is `414 URI Too Long`. Detect this case in `Buffered::parse`: when `max_buf_size` is reached on a server and no `\n` has appeared in the buffer yet, the overflow is caused by the request line (typically the URI) rather than the header fields, so return `Parse::UriTooLong` instead of `Parse::TooLarge`. The existing `Parse::UriTooLong` variant already maps to `414` in `Server::on_error`, so no status-code mapping changes are needed. Closes #3874 --- src/error.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/error.rs b/src/error.rs index 5134b581a3..86916b4264 100644 --- a/src/error.rs +++ b/src/error.rs @@ -372,6 +372,11 @@ impl Error { Error::new(Kind::Parse(Parse::TooLarge)) } + #[cfg(all(feature = "server", feature = "http1"))] + pub(super) fn new_uri_too_long() -> Error { + Error::new(Kind::Parse(Parse::UriTooLong)) + } + #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))] pub(super) fn new_version_h2() -> Error { Error::new(Kind::Parse(Parse::VersionH2)) From 97b40e3db6dbf04c2715060397a20be126370797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Lindberg?= Date: Sun, 19 Jul 2026 07:55:42 +0200 Subject: [PATCH 2/3] fix(http1): return 414 when request line overflows max_buf_size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an HTTP/1 server receives a request whose start-line (method, URI, version) is so large that it fills the read buffer before a newline is even seen, hyper reported the failure as `431 Request Header Fields Too Large`. Per RFC 7231 §6.5.12, the more specific status for an oversized URI is `414 URI Too Long`. Detect this case in `Buffered::parse`: when `max_buf_size` is reached on a server and no `\n` has appeared in the buffer yet, the overflow is caused by the request line (typically the URI) rather than the header fields, so return `Parse::UriTooLong` instead of `Parse::TooLarge`. The existing `Parse::UriTooLong` variant already maps to `414` in `Server::on_error`, so no status-code mapping changes are needed. Closes #3874 --- src/proto/h1/io.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/proto/h1/io.rs b/src/proto/h1/io.rs index 2aeabc7c60..56eed8ab56 100644 --- a/src/proto/h1/io.rs +++ b/src/proto/h1/io.rs @@ -199,6 +199,14 @@ where let curr_len = self.read_buf.len(); if curr_len >= max { debug!("max_buf_size ({}) reached, closing", max); + // When parsing a request and the start-line (request + // line) hasn't even finished — i.e. no newline has been + // seen yet — the overflow is caused by an oversized URI + // rather than the header fields, so report `414 URI Too + // Long` instead of `431 Request Header Fields Too Large`. + if S::is_server() && !self.read_buf.contains(&b'\n') { + return Poll::Ready(Err(crate::Error::new_uri_too_long())); + } return Poll::Ready(Err(crate::Error::new_too_large())); } if curr_len > 0 { @@ -968,4 +976,93 @@ mod tests { // write_buf.headers.bytes.clear(); // }) // } + + #[cfg(not(miri))] + #[cfg(feature = "server")] + #[tokio::test] + async fn parse_overlong_uri_is_uri_too_long() { + use crate::proto::h1::ServerTransaction; + + let _ = pretty_env_logger::try_init(); + + // A request line whose URI overflows `max_buf_size` before any line + // terminator is seen should be reported as `414 URI Too Long` rather + // than `431 Request Header Fields Too Large`. + let max = MINIMUM_MAX_BUFFER_SIZE; + let mut req = b"GET /".to_vec(); + req.resize(max, b'a'); + + let mock = Mock::new().read(&req).build(); + + let mut buffered = Buffered::<_, Cursor>>::new(Compat::new(mock)); + buffered.set_max_buf_size(max); + + let err = futures_util::future::poll_fn(|cx| { + let parse_ctx = ParseContext { + cached_headers: &mut None, + req_method: &mut None, + h1_parser_config: Default::default(), + h1_max_headers: None, + preserve_header_case: false, + #[cfg(feature = "ffi")] + preserve_header_order: false, + h09_responses: false, + #[cfg(feature = "client")] + on_informational: &mut None, + }; + buffered.parse::(cx, parse_ctx) + }) + .await + .expect_err("parse should fail"); + + assert!( + matches!(err.kind(), crate::error::Kind::Parse(crate::error::Parse::UriTooLong)), + "expected UriTooLong, got {:?}", + err, + ); + } + + #[cfg(not(miri))] + #[cfg(feature = "server")] + #[tokio::test] + async fn parse_overlong_headers_is_too_large() { + use crate::proto::h1::ServerTransaction; + + let _ = pretty_env_logger::try_init(); + + // A complete request line followed by header fields that overflow + // `max_buf_size` should remain a `431` (`TooLarge`) error. + let max = MINIMUM_MAX_BUFFER_SIZE; + let mut req = b"GET / HTTP/1.1\r\nHost: x\r\nX: ".to_vec(); + req.resize(max, b'a'); + + let mock = Mock::new().read(&req).build(); + + let mut buffered = Buffered::<_, Cursor>>::new(Compat::new(mock)); + buffered.set_max_buf_size(max); + + let err = futures_util::future::poll_fn(|cx| { + let parse_ctx = ParseContext { + cached_headers: &mut None, + req_method: &mut None, + h1_parser_config: Default::default(), + h1_max_headers: None, + preserve_header_case: false, + #[cfg(feature = "ffi")] + preserve_header_order: false, + h09_responses: false, + #[cfg(feature = "client")] + on_informational: &mut None, + }; + buffered.parse::(cx, parse_ctx) + }) + .await + .expect_err("parse should fail"); + + assert!( + matches!(err.kind(), crate::error::Kind::Parse(crate::error::Parse::TooLarge)), + "expected TooLarge, got {:?}", + err, + ); + } } From 946ea3242ca1af2597703bfe47e5a9f71f162a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Lindberg?= Date: Sun, 19 Jul 2026 07:55:46 +0200 Subject: [PATCH 3/3] test(http1): update max_buf_size test for 414 status code The `max_buf_size` integration test sends a request line that overflows the buffer before completing, which now returns `414 URI Too Long` instead of `431 Request Header Fields Too Large`. Update the expected status code accordingly. --- tests/server.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/server.rs b/tests/server.rs index b22f50b11a..14bbfb8c1c 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -2739,7 +2739,9 @@ async fn max_buf_size() { let mut buf = [0; 256]; tcp.read(&mut buf).expect("read 1"); - let expected = "HTTP/1.1 431 "; + // The request line overflows the buffer before it is even complete, + // so this is reported as `414 URI Too Long`, not `431`. + let expected = "HTTP/1.1 414 "; assert_eq!(s(&buf[..expected.len()]), expected); }); @@ -2749,7 +2751,7 @@ async fn max_buf_size() { .max_buf_size(MAX) .serve_connection(socket, HelloWorld) .await - .expect_err("should TooLarge error"); + .expect_err("should UriTooLong error"); } #[cfg(feature = "http1")]