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(router: Router) -> Serve where B: HttpBody + Send + Sync + 'static, E: Into> + 'static, ::Data: Send + Sync + 'static, ::Error: Into> + '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() }