Rocket Web Framework

repository·master·Indexed 12 days ago

https://github.com/rwf2/Rocket

An async web framework for Rust focused on usability, security, extensibility, and speed. It provides built-in support for routing, state management, and responders, with official extensions for database pooling (rocket_db_pools, rocket_sync_db_pools), dynamic templating (rocket_dyn_templates), and WebSockets (rocket_ws).

Tokens
86.4K
Snippets
292
Records
355
Agent score
96%

What's inside Rocket

  1. What is Rocket?

    master
    Rocket is a web framework for Rust designed to be fast, easy, and flexible. It aims to provide guaranteed safety and security while minimizing the amount of boilerplate code a developer needs to write. It is positioned as a modern, flexible alternative to frameworks like Rails, Flask, and Yesod, optimized for the Rust ecosystem.
  2. Explore Rocket application examples

    master

    The examples/ directory contains several full-scale applications that demonstrate how to combine Rocket's features into functional software:

    • pastebin: An API-only application that stores pastes on the file system. It demonstrates implementing custom parameter guards (e.g., PasteId) for parsing and validating identifiers.
    • todo: A web application with a UI for managing tasks. It demonstrates using a SQLite database with diesel, running automatic migrations at startup, and rendering templates with tera.
    • chat: A real-time multi-room chat app. It demonstrates using Server-Sent Events (SSE) with JavaScript's EventSource, including handling automatic reconnection with exponential backoff.
  3. Explore Rocket feature examples

    master

    Rocket provides specific examples for individual features to help you understand their implementation details:

    • config: Extracting values from Figment, using AdHoc::config() to store application-specific configuration in managed state, and setting values in Rocket.toml.
    • cookies: Implementing client-side message boxes and using private cookies for session-based authentication.
    • databases: Implementing CRUD APIs backed by SQLite using sqlx, diesel, or rusqlite. Demonstrates using rocket_db_pools (for sqlx) and rocket_sync_db_pools (for diesel/rusqlite), including automatic migrations.
    • error-handling: Using scoped catchers and customizing error displays.
    • fairings: Creating custom fairings (e.g., a Counter) and using AdHoc fairings.
    • forms: Handling multipart file uploads, ad-hoc validations, field renaming, and staged forms using form context.
    • hello: Core features including route declaration (path and query parameters), mounting, launching, testing, and returning simple responses.
    • manual-routing: Implementing routing without Rocket's codegen (use with caution as it bypasses some automatic security guarantees).
    • responders: Using built-in responders like Stream, Redirect, File, NamedFile, content (for manual Content-Types), and Either. Also shows custom derived Responder implementations and using TempFile for uploads.
    • serialization: JSON and MessagePack (de)serialization, including UUID parsing support.
    • state: Using request-local state for caching per-request operations and managed state for global data like hit counters or concurrent queues.
    • static-files: Serving static files using FileServer or manual safe implementations.
    • templating: Using contrib templates support with handlebars or tera.
    • testing: Using Rocket's local libraries and the async Client to test applications.
    • tls: Configuring TLS with various key pair types.
    • upgrade: Implementing WebSocket support using the connection upgrade API and tungstenite.
  4. Parse nested structures and collections

    master

    Rocket supports complex, nested form data using dot notation (.) or bracket notation ([]).

    Nesting

    To parse nested structs, use parent.child or parent[child] syntax.

    Vectors

    To parse into a Vec<T>, use name[$k] where $k is a key. If the key is the same as the previous one, the value is pushed to the current element; otherwise, a new element is created. name[] (empty key) always creates a new element.

    Maps

    To parse into a HashMap<K, V>, use name[$key]. Unlike vectors, the key $key is semantically meaningful and is stored in the map. If the key is a complex type (a struct), use name[k:$key].field to define the key's components.

    // Nesting example
    #[derive(FromForm)]
    struct MyForm<'r> {
        owner: Person<'r>,
        pet: Pet<'r>,
    }
    // Form fields: owner.name=Bob&pet.name=Sally
    
    // Vector example
    #[derive(FromForm)]
    struct MyForm {
        numbers: Vec<usize>,
    }
    // Form fields: numbers[]=1&numbers[]=2
    
    // Map example
    #[derive(FromForm)]
    struct MyForm {
        ids: HashMap<String, usize>,
    }
    // Form fields: ids[a]=1&ids[b]=2
  5. Use Managed State instead of global state

    master
    Avoid using global state (e.g., lazy_static!). Instead, use Rocket's [managed state] mechanism. Managed state allows for better testability, the ability to run the application on different threads with different state, and makes a route's state dependencies explicit in its signature.
  6. Scope Catchers using Path Prefixes

    master

    When registering catchers with .register(base, ...), the base determines which requests the catcher handles. A catcher's base must be a prefix of the erroring request. If multiple catchers match, the one with the longest base takes precedence.

    #[catch(404)]
    fn general_not_found() -> &'static str {
        "General 404"
    }
    
    #[catch(404)]
    fn foo_not_found() -> &'static str {
        "Foo 404"
    }
    
    #[launch]
    fn rocket() -> _ {
        rocket::build()
            .register("/", catchers![general_not_found])
            .register("/foo", catchers![foo_not_found])
    }
    // Requests to /foo/bar will trigger 'foo_not_found'.
    // Requests to /bar will trigger 'general_not_found'.
  7. Reinterpret request methods via HTML forms

    master
    To support non-POST methods (like PUT or DELETE) from standard HTML forms, Rocket supports method reinterpretation. If a POST request has a Content-Type of application/x-www-form-urlencoded and the first field in the body is named _method with a valid HTTP method name as its value, Rocket will treat the request as that method instead of POST.
  8. Implement load balancing and DDoS mitigation

    master

    Rocket does not include built-in DDoS mitigation. For production, you should place your Rocket application behind a load balancer or reverse proxy.

    • Managed Environments (Kubernetes, Heroku, Google Cloud Run): Load balancing and DDoS protection are typically handled automatically by the platform.
    • Self-Managed Environments (VPS, Bare Metal): It is recommended to use a mature reverse proxy such as HAProxy or NGINX in front of your Rocket instance.
  9. Understand Rocket's core design philosophies

    master

    Rocket's architecture is built upon three primary pillars:

    1. Security, correctness, and developer experience: The framework is designed so that the easiest way to write code (the path of least resistance) is also the most secure and correct way. It aims to provide security without increasing cognitive overhead.
    2. Typed and self-contained request handling: To bridge the gap between untyped HTTP/web protocols and Rust's type system, Rocket automatically converts request data into native types. Handlers are implemented as regular functions with regular arguments, maintaining zero global state.
    3. Pluggable components (No forced decisions): Rocket follows a modular approach. While it provides official support for templates, serialization, and sessions, these are all optional and swappable components. You are not forced to use a specific implementation if you prefer another.
  10. How request forwarding works in Rocket

    master

    When a parameter type mismatch occurs (e.g., a route expects a u8 but receives a string), Rocket forwards the request to the next matching route. This process continues in increasing rank order until a route succeeds or no more matching routes exist. If no routes match, the error catcher associated with the last forwarding guard's status is called.

    To prevent a route from forwarding on a type mismatch, you can use Result or Option types in the handler signature. Using Result<T, E> or Option<T> catches the failure within that specific route instead of triggering a forward to the next rank.

    #[get("/hello/<name>/<age>/<cool>")]
    fn hello(name: &str, age: u8, cool: bool) { /* ... */ }
    
    // If 'age' is not a u8, Rocket forwards to the next route with a matching path.
    // To catch the error instead of forwarding, use:
    #[get("/hello/<name>/<age>/<cool>")]
    fn hello(name: &str, age: Result<u8, &str>, cool: bool) { /* ... */ }
  11. Use Managed State to maintain application-wide data

    master

    Managed state allows you to maintain data that is accessible throughout your entire application. Rocket manages state on a per-type basis: you can only manage at most one value of any given type.

    Requirements:

    • All managed state must be thread-safe (must implement Send + Sync) because Rocket parallelizes request handling.

    Workflow:

    1. Add State: Call .manage(value) on your Rocket instance during initialization. Each call must use a different type.
    2. Retrieve State: Add &State<T> as an argument to any request handler, where T is the type of the managed value.
    use std::sync::atomic::AtomicUsize;
    
    struct HitCount {
        count: AtomicUsize
    }
    
    // 1. Adding State
    rocket::build().manage(HitCount { count: AtomicUsize::new(0) });
    
    // 2. Retrieving State
    use rocket::State;
    
    #[get("/count")]
    fn count(hit_count: &State<HitCount>) -> String {
        let current_count = hit_count.count.load(std::sync::atomic::Ordering::Relaxed);
        format!("Number of visits: {}", current_count)
    }
  12. Handle service management and graceful shutdown

    master

    To deploy updates without dropping requests, your application should implement graceful shutdown. Rocket listens for termination signals (like SIGTERM in Kubernetes) by default.

    To ensure a clean exit, your application should:

    1. Use Rocket's Shutdown future or shutdown fairings to clean up resources (database connections, file handles, etc.) before the process terminates.
    2. Ensure your environment's termination signal is recognized by Rocket. If your environment uses non-standard signals, you may need to configure triggers in your shutdown configuration.
    3. In self-managed environments, use a service manager like systemd alongside a reverse proxy to manage the application lifecycle.