use hyper::{
body::{self, Body, HttpBody},
Server,
};
use routerify::{Router, RouterService};
use std::net::SocketAddr;
use tokio::sync::oneshot::{self, Sender};
pub struct Serve {
addr: SocketAddr,
tx: Sender<()>,
}
impl Serve {
pub fn addr(&self) -> SocketAddr {
self.addr
}
pub fn new_request(&self, method: &str, route: &str) -> http::request::Builder {
http::request::Request::builder()
.method(method.to_ascii_uppercase().as_str())
.uri(format!("http://{}{}", self.addr(), route))
}
pub fn shutdown(self) {
self.tx.send(()).unwrap();
}
}
pub async fn serve<B, E>(router: Router<B, E>) -> Serve
where
B: HttpBody + Send + Sync + 'static,
E: Into<Box<dyn std::error::Error + Send + Sync>> + 'static,
<B as HttpBody>::Data: Send + Sync + 'static,
<B as HttpBody>::Error: Into<Box<dyn std::error::Error + Send + Sync>> + 'static,
{
let service = RouterService::new(router).unwrap();
let server = Server::bind(&([127, 0, 0, 1], 0).into()).serve(service);
let addr = server.local_addr();
let (tx, rx) = oneshot::channel::<()>();
let graceful_server = server.with_graceful_shutdown(async {
rx.await.unwrap();
});
tokio::spawn(async move {
graceful_server.await.unwrap();
});
Serve { addr, tx }
}
pub async fn into_text(body: Body) -> String {
String::from_utf8_lossy(&body::to_bytes(body).await.unwrap()).to_string()
}