Trait spirit_hyper::ServerBuilder[][src]

pub trait ServerBuilder<Tr> where
    Tr: Fragment
{ type OutputFut: Future<Output = Result<(), HyperError>> + Send; fn build(
        &self,
        builder: Builder<Acceptor<Tr::Resource>>,
        cfg: &HyperServer<Tr>,
        name: &'static str,
        shutdown: Receiver<()>
    ) -> Self::OutputFut; }

A trait abstracting the creation of servers.

When spawning a server, there are 3 layers.

  • A layer creating the Server from the builder.
  • A layer creating a service for each connection.
  • A layer responding to one request.

Each layer must be able to create new instances of the lower layer (by cloning, creating new instances, etc).

This represents the top-level layer. This shall do:

You don’t have to implement the trait by hand, a closure with the corresponding signature (see build) does the job.

This exists for two reasons:

  • To enable different implementations than just closures.
  • To allow it to live in impl Trait position.

Examples

use std::convert::Infallible;

use hyper::{Body, Request, Response};
use hyper::server::Builder;
use hyper::service::{make_service_fn, service_fn};
use serde::Deserialize;
use spirit::{Empty, Pipeline, Spirit};
use spirit::prelude::*;
use spirit_hyper::{BuildServer, HttpServer};
use spirit_tokio::net::limits::Tracked;
use tokio::net::TcpStream;
use tokio::sync::oneshot::Receiver;

const DEFAULT_CONFIG: &str = r#"
[server]
port = 2235
"#;

#[derive(Default, Deserialize)]
struct Config {
    server: HttpServer,
}

impl Config {
    fn server(&self) -> HttpServer {
        self.server.clone()
    }
}

async fn request() -> Response<Body> {
    Response::new(Body::from("Hello world\n"))
}

type Connection = Tracked<TcpStream>;

fn main() {
    let build_server =
        |builder: Builder<_>, cfg: &HttpServer, name: &str, shutdown: Receiver<()>| {
            eprintln!("Creating server {} for {:?}", name, cfg);
            builder
                .serve(make_service_fn(|conn: &Connection| {
                    let conn_addr = conn.peer_addr().expect("Peer address doesn't fail");
                    eprintln!("New connection {}", conn_addr);
                    async {
                        Ok::<_, Infallible>(service_fn(|_req: Request<Body>| async {
                            Ok::<_, Infallible>(request().await)
                        }))
                    }
                }))
                .with_graceful_shutdown(async {
                    // Shutting down both by receiving a message and the other end being
                    // dropped.
                    let _ = shutdown.await;
                })
        };
    Spirit::<Empty, Config>::new()
        .config_defaults(DEFAULT_CONFIG)
        .with(
            // Let's build a http server as configured by the user
            Pipeline::new("listen")
                .extract_cfg(Config::server)
                // This is where we teach the server what it serves. It is the usual stuff from
                // hyper.
                .transform(BuildServer(build_server))
                .check()
        )
        .run(|spirit| {
            Ok(())
        });
}

Associated Types

type OutputFut: Future<Output = Result<(), HyperError>> + Send[src]

The future returned by the build.

The future shall represent the graceful shut down server.

Loading content...

Required methods

fn build(
    &self,
    builder: Builder<Acceptor<Tr::Resource>>,
    cfg: &HyperServer<Tr>,
    name: &'static str,
    shutdown: Receiver<()>
) -> Self::OutputFut
[src]

Invokes the build with the parameters.

Directly corresponds to calling the closure for the blank implementation.

Loading content...

Implementors

impl<F, Tr, Fut> ServerBuilder<Tr> for F where
    Tr: Fragment,
    F: Fn(Builder<Acceptor<Tr::Resource>>, &HyperServer<Tr>, &'static str, Receiver<()>) -> Fut,
    Fut: Future<Output = Result<(), HyperError>> + Send
[src]

type OutputFut = Fut

Loading content...