#![forbid(unsafe_code)]
#![warn(missing_docs)]
use std::error::Error;
use bytes::{Buf, Bytes};
use http::{Request, Response};
use http_body_util;
pub type BoxError = Box<dyn Error + Send + Sync + 'static>;
pub fn is_graceful_h3_close(err: &h3::error::ConnectionError) -> bool {
let err_debug = format!("{:?}", err);
if err_debug.contains("NO_ERROR")
|| err_debug.contains("ApplicationClose: 0x0")
|| err_debug.contains("ApplicationClose(0x0)")
|| err_debug.contains("ConnectionClosed")
{
return true;
}
let mut cur: &(dyn std::error::Error + 'static) = err;
while let Some(src) = cur.source() {
let src_debug = format!("{:?}", src);
if src_debug.contains("NO_ERROR") || src_debug.contains("ApplicationClose") {
return true;
}
cur = src;
}
false
}
pub async fn serve_h3_with_axum<Q>(
app: axum::Router,
resolver: h3::server::RequestResolver<Q, Bytes>,
) -> Result<(), BoxError>
where
Q: h3::quic::Connection<Bytes>,
{
let (request_head, mut stream) = resolver.resolve_request().await?;
let mut body_bytes = bytes::BytesMut::new();
loop {
match stream.recv_data().await {
Ok(Some(mut chunk)) => {
body_bytes.extend_from_slice(&chunk.copy_to_bytes(chunk.remaining()));
}
Ok(None) => break,
Err(e) => {
let mut error_response: Response<()> = Response::new(());
*error_response.status_mut() = http::StatusCode::BAD_REQUEST;
let _ = stream.send_response(error_response).await;
let _ = stream.finish().await;
return Err(Box::new(e));
}
}
}
let (parts, _) = request_head.into_parts();
let axum_req = Request::from_parts(parts, axum::body::Body::from(body_bytes.freeze()));
let axum_resp = tower::ServiceExt::oneshot(app, axum_req).await?;
let (parts, axum_body) = axum_resp.into_parts();
let head_only: Response<()> = Response::from_parts(parts, ());
stream.send_response(head_only).await?;
let mut body_stream = std::pin::pin!(axum_body);
while let Some(frame_result) = http_body_util::BodyExt::frame(&mut body_stream).await {
let frame = frame_result?;
if let Some(chunk) = frame.data_ref() {
if !chunk.is_empty() {
stream.send_data(chunk.clone().into()).await?;
}
}
}
stream.finish().await?;
Ok(())
}