It’s been a little while, but today we’re happy to announce the latest release of the Gotham web framework: Gotham 0.4! This release focuses on some of the more common use cases when writing web services, by introducing several new middlewares and features to improve productivity when working with Gotham.

TLS Support

One of the larger features included in this release is the support for TLS when working with Gotham. This is handled via the popular Rust TLS library rustls, in combination with the Tokio bindings provided by tokio-rustls.

Creation of a TLS server is consistent with the existing Gotham API, with the only difference being the requirement of a rustls::ServerConfig to be provided at server startup:

let addr = "127.0.0.1:7878";
let config = rustls::ServerConfig::new(...);

gotham::tls::start(addr, || Ok(say_hello), config)

This simple tweak to the API makes it easy to handle TLS connections without having to learn an entire new set of APIs.

Middlewares

Gotham 0.4 includes work on a few new middlewares designed to improve some common use cases, and fit in with common patterns when writing web services. Each of the middlewares below is available in either the core of Gotham itself, or distributed as a separate crate on crates.io.

Gotham 0.4 also includes a new middleware named CookieParser, which is designed to make dealing with cookies much simpler and in a much more automatic fashion. All parsing of cookies is done via the de-facto cookie crate.

Unlike some of the other middlewares, the CookieParser middleware ships directly in Gotham’s core library due to it being re-used in the existing SessionMiddleware workflows, and as it’s such a common requirement when building a web service.

To use this middleware, configure it as any other middleware inside your router:

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

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

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

Then you will automatically have a CookieJar available on the State provided to your request, which you can easily access as needed:

CookieJar::borrow_from(&state)

If you have any existing middlewares dealing with cookies, maybe for things like authorization, you can now place them after the CookieParser in the middleware chain and they will then also have access to the new CookieJar entity. This allows you to remove any custom cookie parsing that you may have had to write in the past. One example of this is the existing SessionMiddleware, which will now look for any previously parsed cookies t avoid doing unnecessary work.

An example of this middleware is available in the repository, so please do check it out.

Diesel Middleware

Diesel is a very popular ORM written in Rust, and there has been some work in progress for a compatible middleware for some time. Thanks to the work of @colinbankier this is now available via the gotham_middleware_diesel crate, which offers a convenient API for interacting with Diesel from Gotham.

This middleware introduces a Repo struct, which is used as a layer between Diesel and Gotham to ensure that database interaction can be easily chained alongside other asynchronous operations. This structure is fairly straightfoward and offers an easy way to interact with Diesel from inside Gotham:

// create a new repo, in this case just using a SQLite setup
let repo: Repo<SqliteConnection> = Repo::new("products.db");

// create a middleware pipeline from our middleware
let pipeline = single_middleware(DieselMiddleware::new(repo));

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

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

From there you gain simple access to Repo on the request state, just like when using other middlewares. You can then use the Repo to execute database calls on a separate connection pool:

// borrow the repo from the state
let repo = Repo::borrow_from(&state);

// execute database calls
repo.run(move |conn| {
    diesel::insert_into(products::table)
        .values(&product)
        .execute(&conn)
})

What may not be obvious from the snippet above is that calling repo.run actually returns a Future, allowing you to seamlessly sprinkle your database calls amongst other asynchronous handler code without going through too much pain.

This is the main advantage of the Repo type as it manages the synchronous calls of the underlying connections on a separate thread pool, and maintains the appearance that you’re working with an API that’s fully asynchronous to keep things simple.

Naturally a complete example of database interaction is a little too long to embed into a blog post, so please check out the excellent examples Colin provided in the main repository.

JWT Middleware

JSON Web Tokens (usually referred to as JWT) have become a popular way to authenticate against HTTP APIs due to their ability to pass around data in a secure manner, and in an efficient way. In Gotham 0.4, we’re excited to include a simple middleware to validate these tokens, so that you don’t have to!

As a user of this middleware, you are able to deserialize your web tokens directly into a custom structure automatically. All required validation is taken care of by the middleware itself by checking tokens provided in the HTTP Authorization header.

In the example below we’re defining a new Claims structure to represent our tokens. This structure is automatically populated by the middleware, and is available in your API handlers.

#[derive(Deserialize, Debug)]
struct Claims {
    sub: String,
    exp: usize
}

// create a middleware pipeline from our middleware
let middleware = JWTMiddleware::<Claims>::new("secret".as_ref());
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
gotham::start("127.0.0.1:7878", build_router(chain, pipelines, |route| {
    route.get("/").to(say_hello);
}))

Then, using the typical state access that Gotham provides, you are able to fetch your Claims structure back out from inside your API code:

// borrow the claims from the state
let claims = Claims::borrow_from(&state);

As this is the first implementation of this middleware, please let us know if you have any suggestions or ways we can extend these use cases!

Miscellaneous

Although not technically part of the Gotham core release, we have also added several new examples to the repository. Thank you to all who had any input in the creation of:

In addition to everything mentioned, we have also updated dependencies to their latest versions as well as migrated the codebase to compile under Rust 2018 edition to make contribution a little easier. So please do get involved if there’s something you’d like to see included!