Chapter 5: Request context, Standard Middleware and Building Application

Request Context

Request context, (we call this “Rc” for short in starberry) is a struct consisting the Request Meta, Request Body, Request Stream, Response, locals and params.

As discussed in Chapter 4 and 5, we already know how to read data from request and send response. In this chapter we are going to focus more on Stream, locals and params.

Stream is a buffer reader, reading the TcpStream that the user send to the server. Since the RequestBody is lazy load, you may read the body on your own without relying on Starberry’s HttpBody. While it is technically possible to read data the user send after we receive the request, however this does not happen in Http1.1. Starberry will be able to handle Http2.0 response later on

For locals and params, they are 2 sets of data passing through Middleware Chain (which we are going to discuss later on).

Where params stores a value in the type-based params storage. Any previous value of the same type will be replaced.

You may do something like

// Store authentication information
req.set_param(User { id: 123, name: "Alice".to_string() });
 
// Store timing information
req.set_param(RequestTimer::start()); 

// In an authentication process. We do not write this syntax in actual middlewares 
if let Some(user) = req.param::<User>() {
    println!("Request by: {}", user.name);
    // Proceed with authenticated user
} else {
    return HttpResponse::unauthorized();
} 

// Update a request timer
if let Some(timer) = req.param_mut::<RequestTimer>() {
    timer.mark("after_db_query");
} 

// Take ownership of a value
if let Some(token) = req.take_param::<AuthToken>() {
    // Use and consume the token
    validate_token(token);
} 

To store data. No key is provided.

However for locals, it stores a value in the string-based locals storage with the given key. Any previous value with the same key will be replaced

req.set_local("user_id", 123);
req.set_local("is_premium", true);
req.set_local("cart_items", vec!["item1", "item2"]); 

// In a request handler
if let Some(is_premium) = req.local::<bool>("is_premium") {
    if *is_premium {
        // Show premium content
    }
}

// With different types
let user_id = req.local::<i32>("user_id");
let items = req.local::<Vec<String>>("cart_items"); 

// Modify a list of items
if let Some(items) = req.local_mut::<Vec<String>>("cart_items") {
    items.push("new_item".to_string());
} 

// Take ownership of a value
if let Some(token) = req.take_local::<String>("session_token") {
    // Use and consume the token
    validate_and_destroy_token(token);
} 

Standard Middleware

Installing sbmstd to use starberry’s standard middleware library

Building Application

In Chapter 1, we talked about starting a fairly application

pub static APP: SApp = once_cell::sync::Lazy::new(|| {
    App::new().build()
}); 

Now let’s deep dive into setting configs, middlewares and settings in the application

The statement

App::new() 

Initiates an AppBuilder instance. For the AppBuilder instance a set methods passing its owned value in while returning a modified owned value out is provided.

After manipulating and setting the variable into the AppBuilder, we use

AppBuilder::build() 

To build and return a App instance. Once a APP instance is built, you are not allowed to change its config.

For example, in the Starberry example project the following code is provided

    App::new() 
        .binding(String::from("127.0.0.1:1111"))
        .mode(RunMode::Build)
        .max_body_size(1024 * 1024 * 10) 
        .max_header_size(1024 * 10) 
        .append_middleware::<PrintLog>() 
        .append_middleware::<MyMiddleWare2>() 
        .insert_middleware::<MyMiddleWare1>() 
        .set_config("serect_key", "key") 
        .set_statics("static".to_string())
        .build() 

Let’s talk about each function

  • Binding: The way of accessing the application
  • Mode: Production (Production environment), Development (Developing the application), Beta (Testing the application publically), Build (Internal testing for Starberry development)
  • Max body size, max header size
  • Append middleware: Append a middleware in the end of the middleware chain
  • Insert middleware: Insert a middleware in the head of the middleware chain
  • Set config: the same as set_local in Rc
  • Set statics: the same as set_params in Rc

After that the APP is built and run