use crate::prelude::*; use std::convert::Infallible; use http_body_util::{Full, BodyExt}; use hyper::{StatusCode, Method}; use hyper::server::conn::http1; use hyper::{body::Bytes, service::service_fn, Request, Response}; use tokio::sync::oneshot::Sender; use tokio::task::JoinHandle; use tokio::net::TcpListener; mod ws; type BoxBody = http_body_util::combinators::BoxBody; async fn hello( _: Request, ) -> std::result::Result>, Infallible> { Ok(Response::new(Full::new(Bytes::from("Hello World!")))) } fn not_found() -> std::result::Result>, Infallible> { let mut response = Response::new(Full::new(Bytes::from("404"))); *response.status_mut() = StatusCode::NOT_FOUND; Ok(response) } async fn route(request: Request) -> std::result::Result, Infallible> { match (request.method(), request.uri().path()) { (&Method::GET, "/hello") => Ok(hello(request).await?.map(BodyExt::boxed)), (&Method::GET, "/socket") => Ok(ws::handle_request(request).await?.map(BodyExt::boxed)), _ => Ok(not_found()?.map(BodyExt::boxed)), } } pub struct HttpServerActor { terminator: Sender<()>, fiber: JoinHandle>, } impl HttpServerActor { pub async fn launch(listener: TcpListener) -> Result { let (terminator, receiver) = tokio::sync::oneshot::channel::<()>(); let fiber = tokio::task::spawn(Self::main_loop(listener, receiver)); Ok(HttpServerActor { terminator, fiber }) } pub async fn terminate(self) -> Result<()> { match self.terminator.send(()) { Ok(_) => {} Err(_) => failure("wat")?, } self.fiber.await??; Ok(()) } async fn main_loop(listener: TcpListener, termination: impl Future) -> Result<()> { log::info!("Starting the http server"); pin!(termination); loop { select! { _ = &mut termination => { log::info!("Terminating the http server"); return Ok(()) }, result = listener.accept() => { let (stream, _) = result?; tokio::task::spawn(async move { if let Err(err) = http1::Builder::new() .serve_connection(stream, service_fn(route)) .with_upgrades() .await { tracing::error!("Error serving connection: {:?}", err); } }); }, } } } }