Gotham web framework

A flexible web framework that promotes stability, safety, security and speed.

Quick Tour

//! A Hello World example application for working with Gotham.

extern crate gotham;

use gotham::state::State;

const HELLO_WORLD: &'static str = "Hello World!";

/// Create a `Handler` which is invoked when responding to a `Request`.
///
/// How does a function become a `Handler`?.
/// We've simply implemented the `Handler` trait, for functions that match the signature used here,
/// within Gotham itself.
pub fn say_hello(state: State) -> (State, &'static str) {
    (state, HELLO_WORLD)
}

/// Start a server and call the `Handler` we've defined above for each `Request` we receive.
pub fn main() {
    let addr = "127.0.0.1:7878";
    println!("Listening for requests at http://{}", addr);
    gotham::start(addr, || Ok(say_hello))
}

#[cfg(test)]
mod tests {
    use super::*;
    use gotham::test::TestServer;

    #[test]
    fn receive_hello_world_response() {
        let test_server = TestServer::new(|| Ok(say_hello)).unwrap();
        let response = test_server
            .client()
            .get("http://localhost")
            .perform()
            .unwrap();

        assert_eq!(response.status(), StatusCode::Ok);

        let body = response.read_body().unwrap();
        assert_eq!(&body[..], b"Hello World!");
    }
}
//! An example of the Gotham web framework `Router` that shows how to associate multiple handlers
//! to a single path.

extern crate gotham;
extern crate hyper;
extern crate mime;

use gotham::router::Router;
use gotham::router::builder::*;
use hyper::{Get, Head};

mod handlers;
use self::handlers::*;

/// Create a `Router`
///
/// Results in a tree of routes that that looks like:
///
/// /                     --> GET, HEAD
/// | products            --> GET, HEAD
/// | bag                 --> GET
/// | checkout
///   | start             --> GET
///   | address           --> POST, PUT, PATCH, DELETE
///   | payment_details   --> POST, PUT
///   | complete          --> POST
/// | api
///   | products           --> GET
///
/// If no match for a request is found a 404 will be returned. Both the HTTP verb and the request
/// path are considered when determining if the request matches a defined route.
///
/// The API documentation for `DrawRoutes` describes all the HTTP verbs which Gotham is capable of
/// matching on.
fn router() -> Router {
    build_simple_router(|route| {
        route.request(vec![Get, Head], "/").to(index);
        route.get_or_head("/products").to(products::index);
        route.get("/bag").to(bag::index);

        route.scope("/checkout", |route| {
            route.get("/start").to(checkout::start);

            // Associations allow a single path to be matched for multiple HTTP verbs
            // with each delegating to a unique handler or the same handler, as shown here with
            // put and patch.
            route.associate("/address", |assoc| {
                assoc.post().to(checkout::address::create);
                assoc.put().to(checkout::address::update);
                assoc.patch().to(checkout::address::update);
                assoc.delete().to(checkout::address::delete);
            });

            route
                .post("/payment_details")
                .to(checkout::payment_details::create);

            route
                .put("/payment_details")
                .to(checkout::payment_details::update);

            route.post("/complete").to(checkout::complete);
        });

        route.scope("/api", |route| {
            route.get("/products").to(api::products::index);
        });
    })
}

/// Start a server and use a `Router` to dispatch requests
pub fn main() {
    let addr = "127.0.0.1:7878";
    println!("Listening for requests at http://{}", addr);
    gotham::start(addr, router())
}

#[cfg(test)]
mod tests {
    use super::*;
    use gotham::test::TestServer;
    use hyper::StatusCode;

    // A small subset of possible tests

    #[test]
    fn index_get() {
        let test_server = TestServer::new(router()).unwrap();
        let response = test_server
            .client()
            .get("http://localhost")
            .perform()
            .unwrap();

        assert_eq!(response.status(), StatusCode::Ok);

        let body = response.read_body().unwrap();
        assert_eq!(&body[..], b"index");
    }

    #[test]
    fn checkout_address_patch() {
        let test_server = TestServer::new(router()).unwrap();
        let response = test_server
            .client()
            .patch(
                "http://localhost/checkout/address",
                "data",
                mime::TEXT_PLAIN,
            )
            .perform()
            .unwrap();

        assert_eq!(response.status(), StatusCode::Ok);

        let body = response.read_body().unwrap();
        assert_eq!(&body[..], b"update");
    }
}
//! An introduction to extracting query string name/value pairs, in a type safe way, with the
//! Gotham web framework

extern crate gotham;
#[macro_use]
extern crate gotham_derive;
extern crate hyper;
extern crate mime;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

use hyper::{Body, Response, StatusCode};

use gotham::helpers::http::response::create_response;
use gotham::router::{builder::*, Router};
use gotham::state::{FromState, State};

/// Holds data extracted from the Request query string.
///
/// When a query string extraction struct is configured for a route as part of `Router` creation
/// the `Router` will attempt to extract data from each matching request's query string and store
/// it in `state` ready for your application to use, ensuring that all type safety requirements have
/// been met by the request before handing back control.
///
/// The key requirements for struct to act as a query string extractor are:
///
///     1. That the struct implements the `serde::de::Deserialize` trait which we're doing here by
///        simply deriving it. The Gotham router uses this property during Request query string
///        evaluation to create and instance of your struct, populate it and store it into state
///        ready for access by application code.
///     2. That the struct implements `gotham::state::data::StateData` trait so that it can be
///        stored, retrieved and removed from state. You generally get this for free by deriving
///        `StateData` as shown here.
///     3. That the struct implements the
///        `gotham::router::response::extender::StaticResponseExtender` trait so that bad request
///        query string data can be appropriately refused by the Router. You generally get this
///        for free by deriving `StaticResponseExtender` as shown here which results in bad
///        requests being refuted with a HTTP 400 (BadRequest) response status code.
///
/// Naming of fields in extraction structs is important, the same names must appear in the
/// query string.
#[derive(Deserialize, StateData, StaticResponseExtender)]
struct QueryStringExtractor {
    name: String,
}

/// A Product
#[derive(Serialize)]
struct Product {
    name: String,
}

/// Handler function for `GET` requests directed to `/products`
///
/// This handler uses the Serde project when generating responses. You don't need to
/// know about Serde in order to understand the response that is being created here but if you're
/// interested you can learn more at `http://serde.rs`.
fn get_product_handler(mut state: State) -> (State, Response<Body>) {
    let res = {
        // Access the `QueryStringExtractor` instance from `state` which was put there for us by the
        // `Router` during request evaluation.
        //
        // As well as permitting storage in `State` by deriving `StateData` our query string
        // extractor struct automatically gains the `take_from` method and a number of other
        // methods via the `gotham::state::FromState` trait.
        //
        // n.b. Once taken out of `state` values can no longer be accessed by other application
        // code or middlewares.
        let query_param = QueryStringExtractor::take_from(&mut state);

        let product = Product {
            name: query_param.name,
        };
        create_response(
            &state,
            StatusCode::OK,
            mime::APPLICATION_JSON,
            serde_json::to_vec(&product).expect("serialized product"),
        )
    };
    (state, res)
}

/// Create a `Router`
///
/// /products?name=...             --> GET
fn router() -> Router {
    build_simple_router(|route| {
        route
            .get("/products")
            // This tells the Router that for requests which match this route that query string
            // extraction should be invoked storing the result in a `QueryStringExtractor` instance.
            .with_query_string_extractor::<QueryStringExtractor>()
            .to(get_product_handler);
    })
}

/// Start a server and use a `Router` to dispatch requests
pub fn main() {
    let addr = "127.0.0.1:7878";
    println!("Listening for requests at http://{}", addr);
    gotham::start(addr, router())
}

#[cfg(test)]
mod tests {
    use super::*;
    use gotham::test::TestServer;

    #[test]
    fn product_name_is_extracted() {
        let test_server = TestServer::new(router()).unwrap();
        let response = test_server
            .client()
            .get("http://localhost/products?name=t-shirt")
            .perform()
            .unwrap();

        assert_eq!(response.status(), StatusCode::Ok);

        let body = response.read_body().unwrap();
        let expected_product = Product {
            name: "t-shirt".to_string(),
        };
        let expected_body = serde_json::to_string(&expected_product).expect("serialized product");
        assert_eq!(&body[..], expected_body.as_bytes());
    }
}
//! Introduces the Middleware and Pipeline concepts provided by the Gotham web framework.

extern crate futures;
extern crate gotham;
#[macro_use]
extern crate gotham_derive;
extern crate hyper;
extern crate mime;

use futures::{future, Future};
use gotham::handler::HandlerFuture;
use gotham::helpers::http::response::create_empty_response;
use gotham::middleware::Middleware;
use gotham::pipeline::new_pipeline;
use gotham::pipeline::single::single_pipeline;
use gotham::router::builder::*;
use gotham::router::Router;
use gotham::state::{FromState, State};
use hyper::{Body, HeaderMap};
use hyper::{Response, StatusCode};

/// A simple struct which holds an identifier for the user agent which made the request.
///
/// It is created by our Middleware and then accessed via `state` by both our Middleware and Handler.
#[derive(StateData)]
pub struct ExampleMiddlewareData {
    pub user_agent: String,
    pub supported: bool,
}

/// A struct that can act as a Gotham web framework middleware.
///
/// The key requirements for struct to act as a Middleware are:
///
///     1. That the struct implements the `gotham::middleware::NewMiddleware` trait which allows
///        the Gotham web framework to create a new instance of your middleware to service every
///        request. In many cases, as we're doing here, this can simply be derived.
///     2. That the struct implements the `gotham::middleware::Middleware` trait as we're doing
///        next.
#[derive(Clone, NewMiddleware)]
pub struct ExampleMiddleware;

/// Implementing `gotham::middleware::Middleware` allows the logic that you want your Middleware to
/// provided to be correctly executed by the Gotham web framework Router.
///
/// As shown here Middleware can make changes to the environment both before and after the handler^
/// for the route is executed.
///
/// ^Later examples will show how Middlewares in a pipeline can work with each other in a similar
/// manner.
impl Middleware for ExampleMiddleware {
    fn call<Chain>(self, mut state: State, chain: Chain) -> Box<HandlerFuture>
    where
        Chain: FnOnce(State) -> Box<HandlerFuture>,
    {
        let user_agent = {
            let headers = HeaderMap::borrow_from(&state);
            match headers.get("user-agent") {
                Some(ua) => ua.to_str().unwrap().into(),
                None => String::from("None"),
            }
        };

        // Prior to letting Request handling proceed our middleware creates some new data and adds
        // it to `state`.
        state.put(ExampleMiddlewareData {
            user_agent,
            supported: false,
        });

        // We're finished working on the Request, so allow other components to continue processing
        // the Request.
        //
        // Alternatively we could elect to not call chain and return a Response we've created if we
        // want to prevent any further processing from occuring on the Request.
        let result = chain(state);

        // Once a Response is generated by another part of the application, in this example's case
        // the middleware_reliant_handler function, we want to do some more work.
        //
        // The syntax used here is part of the async environment in which the Gotham web framework
        // operates, you may not have encountered this before. For more details you can read about
        // the Tokio project at https://tokio.rs/docs/getting-started/hello-world/
        let f = result.and_then(move |(state, mut response)| {
            {
                let headers = response.headers_mut();
                let data = ExampleMiddlewareData::borrow_from(&state);

                // All our middleware does is add a header to the Response generated by our handler.
                headers.insert(
                    "X-User-Agent",
                    format!(
                        "Supplied: {}, Supported: {}",
                        data.user_agent, data.supported
                    ).parse()
                    .unwrap(),
                );
            };
            future::ok((state, response))
        });

        Box::new(f)
    }
}

/// The handler which is invoked for all requests to "/".
///
/// This handler expects that `ExampleMiddleware` has already been executed by Gotham before
/// it is invoked. As a result of that middleware being run our handler trusts that it must
/// have placed data into state that we can perform operations on.
pub fn middleware_reliant_handler(mut state: State) -> (State, Response<Body>) {
    {
        let data = ExampleMiddlewareData::borrow_mut_from(&mut state);

        // Mark any kind of web client as supported. A trival example but it highlights the
        // interaction that is possible between Middleware and Handlers via state.
        data.supported = true;
    };

    // Finally we create a basic Response to complete our handling of the Request.
    let res = create_empty_response(&state, StatusCode::OK);
    (state, res)
}

/// Create a `Router`
fn router() -> Router {
    // Within the Gotham web framework Middleware is added to and referenced from a Pipeline.
    //
    // A pipeline can consist of multiple Middleware types and guarantees to call them all in the
    // ordering which is established by successive calls to the `add` method.
    //
    // A pipeline is considered complete once the build method is called and can no longer
    // be modified.
    //
    // The Gotham web framework supports multiple Pipelines and even Pipelines containing Pipelines.
    // However, as shown here, many applications will get sufficent power and flexibility
    // from a `single_pipeline` which we've provided specific API assitance for.
    let (chain, pipelines) = single_pipeline(new_pipeline().add(ExampleMiddleware).build());

    // Notice we've switched from build_simple_router which has been present in all our examples up
    // until this point. Under the hood build_simple_router has simply been creating an empty
    // set of Pipelines on your behalf.
    //
    // Now that we're creating and populating our own Pipelines we'll switch to using
    // build_router directly.
    //
    // Tip: Use build_simple_router for as long as you can. Switching to build_router is simple once
    // do need to introduce Pipelines and Middleware.
    build_router(chain, pipelines, |route| {
        route.get("/").to(middleware_reliant_handler);
    })
}

/// Start a server and use a `Router` to dispatch requests
pub fn main() {
    let addr = "127.0.0.1:7878";
    println!("Listening for requests at http://{}", addr);
    gotham::start(addr, router())
}

#[cfg(test)]
mod tests {
    use super::*;
    use gotham::test::TestServer;

    #[test]
    fn ensure_middleware_and_handler_collaborate() {
        let test_server = TestServer::new(router()).unwrap();
        let response = test_server
            .client()
            .get("http://localhost")
            .with_header(UserAgent::new("TestServer/0.0.0"))
            .perform()
            .unwrap();

        assert_eq!(response.status(), StatusCode::Ok);

        // Ensure Middleware has set a header after our handler generated the Response.
        assert_eq!(
            response.headers().get_raw("X-User-Agent").unwrap(),
            "Supplied: TestServer/0.0.0, Supported: true"
        );
    }
}
//! An introduction to sharing state across handlers in a safe way.

extern crate gotham;
#[macro_use]
extern crate gotham_derive;
extern crate hyper;
extern crate mime;

use gotham::middleware::state::StateMiddleware;
use gotham::pipeline::single::single_pipeline;
use gotham::pipeline::single_middleware;
use gotham::router::builder::*;
use gotham::router::Router;
use gotham::state::{FromState, State};

use std::sync::{Arc, Mutex};

/// Request counting struct, used to track the number of requests made.
///
/// Due to being shared across many worker threads, the internal counter
/// is bound inside an `Arc` (to enable sharing) and a `Mutex` (to enable
/// modification from multiple threads safely).
///
/// This struct must implement `Clone` and `StateData` to be applicable
/// for use with the `StateMiddleware`, and be shared via `Middleware`.
#[derive(Clone, StateData)]
struct RequestCounter {
    inner: Arc<Mutex<usize>>,
}

/// Counter implementation.
impl RequestCounter {
    /// Creates a new request counter, setting the base state to `0`.
    fn new() -> Self {
        Self {
            inner: Arc::new(Mutex::new(0)),
        }
    }

    /// Increments the internal counter state by `1`, and returns the
    /// new request counter as an atomic operation.
    fn incr(&self) -> usize {
        let mut w = self.inner.lock().unwrap();
        *w += 1;
        *w
    }
}

/// Basic `Handler` to say hello and return the current request count.
///
/// The request counter is shared via the state, so we can safely
/// borrow one from the provided state. As the counter uses locks
/// internally, we don't have to borrow a mutable reference either!
fn say_hello(state: State) -> (State, String) {
    let message = {
        // borrow a reference of the counter from the state
        let counter = RequestCounter::borrow_from(&state);

        // create our message, incrementing our request counter
        format!("Hello from request #{}!\n", counter.incr())
    };

    // return message
    (state, message)
}

/// Constructs a simple router on `/` to say hello, along with
/// the current request count.
fn router() -> Router {
    // create the counter to share across handlers
    let counter = RequestCounter::new();

    // create our state middleware to share the counter
    let middleware = StateMiddleware::new(counter);

    // create a middleware pipeline from our middleware
    let pipeline = single_middleware(middleware);

    // construct a basic chain from our pipeline
    let (chain, pipelines) = single_pipeline(pipeline);

    // build a router with the chain & pipeline
    build_router(chain, pipelines, |route| {
        route.get("/").to(say_hello);
    })
}

/// Start a server and call the `Handler` we've defined above
/// for each `Request` we receive.
pub fn main() {
    let addr = "127.0.0.1:7878";
    println!("Listening for requests at http://{}", addr);
    gotham::start(addr, router())
}

#[cfg(test)]
mod tests {
    use super::*;
    use gotham::test::TestServer;
    use hyper::StatusCode;

    #[test]
    fn receive_incrementing_hello_response() {
        let test_server = TestServer::new(router()).unwrap();

        for i in 1..6 {
            let response = test_server
                .client()
                .get("http://localhost")
                .perform()
                .unwrap();

            assert_eq!(response.status(), StatusCode::OK);

            let body = response.read_body().unwrap();
            let expc = format!("Hello from request #{}!\n", i);

            assert_eq!(&body[..], expc.as_bytes());
        }
    }
}

Features

Built with Rust

Rust is a programming language that’s focused on safety, speed, and concurrency. It offers all the performance and control of a low-level language, but with the powerful abstractions of a high-level language.

Stability Focused

Gotham targets stable Rust. This will never change.

Gotham is also automatically tested against Rust beta and nightly.

No Garbage Collection

One of Rust’s key innovations is guaranteeing memory safety without requiring garbage collection. Gotham based applications automatically benefit from the predictability and performance of a system without garbage collection.

Statically Typed

Gotham is statically typed ensuring your application is correctly expressed at compile time.

Async Everything

By leveraging Tokio, Gotham types are async out of the box.

Our async story is further enhanced by Hyper, a fast server that provides an elegant layer over stringly typed HTTP.

Blazingly Fast

Measure completed requests, including the 99th percentile, in µs.

Getting involved