use std::net::SocketAddr;
use std::sync::Arc;
use bytes::Bytes;
use http_body_util::BodyExt;
use hyper::{Response, StatusCode};
use super::body::H3ReqBody;
use super::http3_err;
use crate::ServerState;
use crate::error::ServerError;
use crate::proxy::proxy_core;
use crate::respond::{fault, synth};
pub(super) async fn handle_h3_request(
state: Arc<ServerState>,
peer: SocketAddr,
resolver: h3::server::RequestResolver<h3_quinn::Connection, Bytes>,
) -> Result<(), ServerError> {
let (req, stream) = resolver.resolve_request().await.map_err(http3_err)?;
let (mut send, recv) = stream.split();
let (parts, ()) = req.into_parts();
let resp = match declared_content_length(&parts.headers) {
Ok(content_length) => {
let body = H3ReqBody::new(recv, content_length).boxed();
match proxy_core(state, "https", peer, parts, body).await {
Ok(r) => r,
Err(e) => {
tracing::warn!(error = %e, "h3 fast-path error");
synth(StatusCode::BAD_GATEWAY, &fault::UPSTREAM, b"upstream error")
}
}
}
Err(()) => synth(
StatusCode::BAD_REQUEST,
&fault::BAD_CONTENT_LENGTH,
b"bad content-length",
),
};
let (rparts, mut rbody) = resp.into_parts();
send.send_response(Response::from_parts(rparts, ()))
.await
.map_err(http3_err)?;
while let Some(frame) = rbody.frame().await {
match frame {
Ok(f) => match f.into_data() {
Ok(data) => send.send_data(data).await.map_err(http3_err)?,
Err(f) => {
if let Ok(trailers) = f.into_trailers() {
send.send_trailers(trailers).await.map_err(http3_err)?;
send.finish().await.map_err(http3_err)?;
return Ok(());
}
tracing::debug!("h3 response stream produced a non-trailer unknown frame");
return Ok(());
}
},
Err(e) => {
tracing::debug!(error = %e, "h3 response body error; resetting the response stream");
return Ok(());
}
}
}
send.finish().await.map_err(http3_err)?;
Ok(())
}
fn declared_content_length(headers: &hyper::HeaderMap) -> Result<Option<u64>, ()> {
let mut declared: Option<u64> = None;
for value in headers.get_all(hyper::header::CONTENT_LENGTH) {
let parsed = value
.to_str()
.ok()
.and_then(|s| s.trim().parse::<u64>().ok())
.ok_or(())?;
match declared {
None => declared = Some(parsed),
Some(existing) if existing == parsed => {}
Some(_) => return Err(()),
}
}
Ok(declared)
}