use crate::http::response::Body;
use hyper::{Request, Response};
use hyper_util::rt::TokioIo;
use tokio::io::{AsyncRead, AsyncWrite};
pub(super) const NO_PEER_ADDR: std::net::SocketAddr =
std::net::SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED), 0);
pub(super) async fn serve_connection<IO, Svc>(
io: IO,
svc: Svc,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
where
IO: AsyncRead + AsyncWrite + Unpin + Send + 'static,
Svc: hyper::service::Service<
Request<hyper::body::Incoming>,
Response = Response<Body>,
Error = std::io::Error,
> + Send
+ 'static,
Svc::Future: Send,
{
let io = TokioIo::new(io);
#[cfg(all(feature = "http1", feature = "http2"))]
{
let mut builder =
hyper_util::server::conn::auto::Builder::new(hyper_util::rt::TokioExecutor::new());
#[cfg(feature = "ws")]
let _ = builder.http2().enable_connect_protocol();
builder.serve_connection_with_upgrades(io, svc).await?;
}
#[cfg(all(feature = "http1", not(feature = "http2")))]
{
hyper::server::conn::http1::Builder::new()
.serve_connection(io, svc)
.with_upgrades()
.await?;
}
#[cfg(all(feature = "http2", not(feature = "http1")))]
{
let mut builder =
hyper::server::conn::http2::Builder::new(hyper_util::rt::TokioExecutor::new());
#[cfg(feature = "ws")]
let _ = builder.enable_connect_protocol();
builder.serve_connection(io, svc).await?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use bytes::Bytes;
use hyper::service::service_fn;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[test]
fn no_peer_addr_is_the_unspecified_ipv4_wildcard() {
assert_eq!(
NO_PEER_ADDR.ip(),
std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED)
);
assert_eq!(NO_PEER_ADDR.port(), 0);
}
#[tokio::test]
async fn serve_connection_round_trips_a_request_over_a_duplex_pipe() {
let (mut client_io, server_io) = tokio::io::duplex(8 * 1024);
let svc = service_fn(|_req: Request<hyper::body::Incoming>| async {
Ok::<_, std::io::Error>(Response::new(Body::full(Bytes::from_static(
b"hello from conn",
))))
});
let server = tokio::spawn(async move { serve_connection(server_io, svc).await });
client_io
.write_all(b"GET / HTTP/1.1\r\nHost: test\r\nConnection: close\r\n\r\n")
.await
.expect("write request");
let mut buf = Vec::new();
client_io
.read_to_end(&mut buf)
.await
.expect("read response");
let response = String::from_utf8_lossy(&buf);
assert!(response.contains("200"), "unexpected response: {response}");
assert!(
response.contains("hello from conn"),
"unexpected response: {response}"
);
server
.await
.expect("server task join")
.expect("serve_connection ok");
}
}