use std::{error::Error as StdError, future::Future, io, net::SocketAddr};
pub use http_body_util;
pub use hyper;
use hyper::{
body::{Body, Incoming},
client::conn::http1::SendRequest,
server::conn::{http1, http2},
Request, Response,
};
use crate::{
addr::ToAddrs,
compat::{Compat, Duplex},
executor::Either,
net::{TcpListener, TcpStream},
select2, spawn,
};
type BoxError = Box<dyn StdError + Send + Sync>;
#[derive(Clone, Copy)]
struct LocalExec;
impl<F> hyper::rt::Executor<F> for LocalExec
where
F: Future + 'static,
{
fn execute(&self, fut: F) {
drop(spawn(fut));
}
}
pub async fn serve_connection<S, Svc, B>(io: S, service: Svc) -> hyper::Result<()>
where
S: Duplex,
Svc: hyper::service::Service<Request<Incoming>, Response = Response<B>>,
Svc::Error: Into<BoxError>,
Svc::Future: 'static,
B: Body + 'static,
B::Error: Into<BoxError>,
{
http1::Builder::new()
.serve_connection(Compat::new(io), service)
.await
}
pub async fn serve_http2_connection<S, Svc, B>(io: S, service: Svc) -> hyper::Result<()>
where
S: Duplex,
Svc: hyper::service::Service<Request<Incoming>, Response = Response<B>>,
Svc::Error: Into<BoxError>,
Svc::Future: 'static,
B: Body + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
{
http2::Builder::new(LocalExec)
.serve_connection(Compat::new(io), service)
.await
}
pub struct Server {
listener: TcpListener,
}
impl Server {
pub async fn bind(addr: impl ToAddrs) -> io::Result<Self> {
let listener = TcpListener::bind(addr).await?;
Ok(Self { listener })
}
pub fn from_listener(listener: TcpListener) -> Self {
Self { listener }
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.listener.local_addr()
}
pub async fn serve<M, Svc, B>(self, mut make_service: M) -> io::Result<()>
where
M: FnMut(&TcpStream) -> Svc,
Svc: hyper::service::Service<Request<Incoming>, Response = Response<B>> + 'static,
Svc::Error: Into<BoxError>,
Svc::Future: 'static,
B: Body + 'static,
B::Error: Into<BoxError>,
{
loop {
let stream = self.listener.accept().await?;
let _ = stream.set_nodelay(true);
let svc = make_service(&stream);
drop(spawn(async move {
let _ = serve_connection(stream, svc).await;
}));
}
}
pub async fn serve_with_shutdown<M, Svc, B, Sh>(
self,
mut make_service: M,
shutdown: Sh,
) -> io::Result<()>
where
M: FnMut(&TcpStream) -> Svc,
Svc: hyper::service::Service<Request<Incoming>, Response = Response<B>> + 'static,
Svc::Error: Into<BoxError>,
Svc::Future: 'static,
B: Body + 'static,
B::Error: Into<BoxError>,
Sh: Future<Output = ()>,
{
let accept_loop = async {
loop {
let stream = self.listener.accept().await?;
let _ = stream.set_nodelay(true);
let svc = make_service(&stream);
drop(spawn(async move {
let _ = serve_connection(stream, svc).await;
}));
}
#[allow(unreachable_code)]
Ok::<(), io::Error>(())
};
match select2(accept_loop, shutdown).await {
Either::Left(res) => res,
Either::Right(()) => Ok(()),
}
}
}
pub struct Client<B> {
sender: SendRequest<B>,
}
pub async fn connect<S, B>(io: S) -> hyper::Result<Client<B>>
where
S: Duplex,
B: Body + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
{
let (sender, conn) = hyper::client::conn::http1::handshake(Compat::new(io)).await?;
drop(spawn(async move {
let _ = conn.await;
}));
Ok(Client { sender })
}
impl<B> Client<B>
where
B: Body + 'static,
{
pub async fn send(&mut self, req: Request<B>) -> hyper::Result<Response<Incoming>> {
self.sender.ready().await?;
self.sender.send_request(req).await
}
pub fn is_closed(&self) -> bool {
self.sender.is_closed()
}
}