Services

To work correctly, a website usually needs access to some sort of "global service". This includes for example:

  • A templates engine.
  • A pages cache.
  • A database.

One important part of rouille is that it doesn't integrate any service like a database or a templating engine. The code of rouille is entirely dedicated to HTTP only. However its design makes it possible to easily use any third-party library without using plugins or glue libraries.

Accessing values from the outside

Accessing local variables created outside of the request-handling closure is easy as pie:

let some_text = "hello world";

rouille::start_server("locahost:8000", move |request| {
    Response::text(some_text)
})

Objects that are "global" to the server should be created on the stack before calling start_server, and objects that are "per-request" should be created inside the function.

If you get lifetime errors, make sure that you didn't forget the move keyword before the closure.

Thread safety

However one important thing to note is that you must handle synchronization. The requests-handling function that you pass to start_server can be called multiple times simulatenously, therefore you can't naively modify values from the outside.

For example, let's try to implement a requests counter:

let mut counter = 0;

rouille::start_server("locahost:8000", move |request| {
    counter += 1;       // compilation error!
    Response::text(format!("Request n#{}", counter))
})

If this code compiled, there is a possibility that counter is modified twice simultaneously, which could lead to a bad value being written in counter!

Instead the Rust language forces you to use a Mutex:

use std::sync::Mutex;

let counter = Mutex::new(0);

rouille::start_server("locahost:8000", move |request| {
    let mut counter = counter.lock().unwrap();
    // we now have an exclusive access to `counter`

    *counter += 1;
    Response::text(format!("Request n#{}", counter))
})

Note that in this example we could also have used a AtomicUsize.

Example: a database connection

Let's take a concrete example by looking at how we could connect to a database. This example assumes that you are familiar with a library that allows you to connect to a database, and is only here to show you how to use this library in conjuction with rouille.

use std::sync::Mutex;

// this variable contains a cache of all the database connections
let connections = Mutex::new(Vec::new());

rouille::start_server("locahost:8000", move |request| {
    // obtaining a connection from the connections list, or creating a new one if necessary
    let connection = {
        let mut connections = connections.lock().unwrap();

        if connections.len() >= 1 {
            connections.remove(0)
        } else {
            let new_connection = Connection::new("database_url").unwrap();
            new_connection
        }
    };

    // handle the request
    let response = handle_request(request, &connection);

    // store the database connection in the cache
    connections.lock().unwrap().push(connection);

    // returning the response
    response
})