webrune 0.1.2

A composable web server.
Documentation
use std::{convert::Infallible, sync::Arc};

use hyper::{body::Incoming, service::Service};
use hyper_util::{
    rt::{TokioExecutor, TokioIo},
    server::conn::auto::Builder,
};
use tokio::{net::TcpListener, spawn};

use crate::{
    Flow::{Continue, Exit},
    Handler, IntoResponse, Request, Response,
};

/// An HTTP server backed by a [`Handler`] and shared application state.
///
/// `Server` is a small wrapper around Hyper that:
/// - accepts TCP connections,
/// - converts incoming HTTP requests into [`Request`],
/// - forwards them to a user-provided [`Handler`],
/// - and returns a [`Response`].
///
/// # Type Parameters
///
/// - `H`: The request handler
/// - `S`: Shared application state, cloned per request
pub struct Server<H, S> {
    handler: H,
    state: S,
}

impl<S> Server<(), S> {
    /// Creates a new server with the given shared state and no handler.
    ///
    /// This is the entry point for building a server. A handler must be
    /// attached later using [`Server::handler`] before the server can
    /// be started.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// let server = Server::new(app_state)
    ///     .handler(my_handler);
    /// ```
    pub fn new(state: S) -> Server<(), S> {
        Self { handler: (), state }
    }

    /// Attaches a request handler to the server.
    ///
    /// The handler is responsible for processing a [`Request`] together with
    /// shared state `S` and producing a [`Response`].
    ///
    /// Both the normal output and exception paths must resolve to a
    /// [`Response`], ensuring the server can always reply to the client.
    ///
    /// This method consumes the server and returns a new one with the
    /// handler installed.
    pub fn handler<H, I>(self, handler: H) -> Server<H, S>
    where
        H: Handler<I, S, Output = Response, Exception = Response>,
    {
        Server {
            handler,
            state: self.state,
        }
    }
}

impl<H, S> Server<H, S> {
    /// Starts listening for incoming connections on the given TCP listener.
    ///
    /// This method runs indefinitely, accepting new connections and spawning
    /// a Tokio task per connection to drive the HTTP state machine.
    ///
    /// Each request:
    /// - clones the shared state `S`,
    /// - invokes the configured [`Handler`],
    /// - and converts the handler’s [`crate::Flow`] into a [`Response`].
    ///
    /// # Concurrency
    ///
    /// - Each connection is handled concurrently.
    /// - The handler is shared across connections via an [`Arc`].
    ///
    /// # Panics
    ///
    /// This function does not intentionally panic, but failures to accept
    /// connections are silently ignored.
    pub async fn listen(self, listener: TcpListener)
    where
        H: Handler<Request, S, Output = Response> + Send + Sync + 'static,
        H::Future: Send + 'static,
        S: Clone + Send + 'static,
    {
        let http = Builder::new(TokioExecutor::new());
        let handler = Arc::new(self.handler);

        loop {
            if let Ok((stream, _client)) = listener.accept().await {
                let io = TokioIo::new(stream);
                let connection = http
                    .serve_connection_with_upgrades(
                        io,
                        ServerService(handler.clone(), self.state.clone()),
                    )
                    .into_owned();

                spawn(async move {
                    if let Err(err) = connection.await {
                        println!("Connection error: {:?}", err);
                    }
                });
            }
        }
    }
}

/// Hyper [`Service`] implementation that bridges HTTP requests to a [`Handler`].
///
/// This type is created per connection and holds:
/// - a shared reference to the handler, and
/// - a clone of the application state.
///
/// It is responsible for translating Hyper’s request/response types
/// into the server’s internal abstractions.
struct ServerService<H, S>(Arc<H>, S);

impl<H, S> Service<http::Request<Incoming>> for ServerService<H, S>
where
    H: Handler<Request, S, Output = Response> + Send + Sync + 'static,
    H::Future: Send + 'static,
    S: Clone,
{
    /// The HTTP response type produced by the handler.
    type Response = Response;

    /// Errors are not surfaced to Hyper; all failures are converted
    /// into responses instead.
    type Error = Infallible;

    /// The asynchronous response future returned by the handler.
    type Future = impl Future<Output = Result<Self::Response, Self::Error>> + Send + 'static;

    /// Dispatches an incoming HTTP request to the underlying handler.
    ///
    /// The handler’s [`Flow`] is resolved as follows:
    /// - [`Continue`] returns the response directly
    /// - [`Exit`] is converted into a response via [`IntoResponse`]
    fn call(&self, req: http::Request<Incoming>) -> Self::Future {
        let future = self.0.handle(Request::from(req), self.1.clone());

        async move {
            match future.await {
                Continue(value) => Ok(value),
                Exit(value) => Ok(value.into_response()),
            }
        }
    }
}