This commit is contained in:
Mateusz Gruszczyński committed 2026-10-05 17:05:06 +02:00
1 parent d168ee2afa
commit ddc5c6593f
70 files changed
+2293 -594

No files matched your search

+117
View File
@@ -0,0 +1,117 @@
fn invalid_headers(headers: &HeaderMap) -> bool {
[
"connection",
"keep-alive",
"proxy-connection",
"transfer-encoding",
"upgrade",
]
.iter()
.any(|name| headers.contains_key(*name))
|| headers
.get_all("te")
.iter()
.any(|value| value.as_bytes() != b"trailers")
|| headers.get_all("content-length").iter().count() > 1
|| headers.get("content-length").is_some_and(|value| {
value
.to_str()
.ok()
.and_then(|s| s.parse::<u64>().ok())
.is_none()
})
}
struct ReceiveBody {
stream: h3::server::RequestStream<h3::quinn::RecvStream, Bytes>,
expected: Option<u64>,
received: u64,
data_done: bool,
done: bool,
}
impl HttpBody for ReceiveBody {
type Data = Bytes;
type Error = io::Error;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<std::result::Result<Frame<Bytes>, io::Error>>> {
let this = self.get_mut();
if this.done {
return Poll::Ready(None);
}
if !this.data_done {
match this.stream.poll_recv_data(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(error)) => {
this.done = true;
return Poll::Ready(Some(Err(io::Error::other(error.to_string()))));
}
Poll::Ready(Ok(Some(mut data))) => {
let length = data.remaining();
this.received = this.received.saturating_add(length as u64);
if this
.expected
.is_some_and(|expected| this.received > expected)
{
drop(data);
this.done = true;
this.stream.stop_sending(Code::H3_MESSAGE_ERROR);
return Poll::Ready(Some(Err(io::Error::other(
"HTTP/3 Content-Length exceeded",
))));
}
return Poll::Ready(Some(Ok(Frame::data(data.copy_to_bytes(length)))));
}
Poll::Ready(Ok(None)) => this.data_done = true,
}
}
match this.stream.poll_recv_trailers(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(Err(error)) => {
this.done = true;
Poll::Ready(Some(Err(io::Error::other(error.to_string()))))
}
Poll::Ready(Ok(trailers)) => {
this.done = true;
if this
.expected
.is_some_and(|expected| expected != this.received)
{
this.stream.stop_sending(Code::H3_MESSAGE_ERROR);
return Poll::Ready(Some(Err(io::Error::other(
"HTTP/3 Content-Length mismatch",
))));
}
Poll::Ready(trailers.map(|mut headers| {
crate::proxy::headers::strip_hop_by_hop(&mut headers);
Ok(Frame::trailers(headers))
}))
}
}
}
fn is_end_stream(&self) -> bool {
self.done
}
fn size_hint(&self) -> SizeHint {
// A declared zero is not proof that the peer ended its stream. Unknown
// lengths are verified by the cache's bounded empty-body probe.
let mut hint = SizeHint::new();
if self.done {
hint.set_exact(0);
} else if let Some(remaining) = self
.expected
.map(|n| n.saturating_sub(self.received))
.filter(|n| *n > 0)
{
hint.set_upper(remaining);
}
hint
}
}
impl Drop for ReceiveBody {
fn drop(&mut self) {
// Dropping the request stream cancels the receive side at QUIC level.
// Explicit STOP_SENDING is used only from poll_frame paths where the
// stream handle is available and a protocol error was detected.
}
}