warp

repository·master·Indexed 27 days ago

https://github.com/seanmonstar/warp

A composable web server framework for Rust built on top of hyper. It uses a 'Filter' system to express complex request requirements through composition, supporting HTTP/1, HTTP/2, and asynchronous operations. Key features include path routing, parameter extraction, header requirements, JSON/Form bodies, Websockets, and compression.

Tokens
3.6K
Snippets
17
Records
29
Agent score
94%

What's inside warp

  1. Overview of warp web framework

    master

    warp is a composable web server framework built on top of hyper. Its core building block is the Filter system, which allows you to combine and compose requirements for HTTP requests.

    Key features provided out of the box include:

    • Path routing and parameter extraction
    • Header requirements and extraction
    • Query string deserialization
    • JSON and Form bodies
    • Multipart form data
    • Static Files and Directories
    • Websockets
    • Access logging
    • Compression (Gzip, Deflate, and Brotli)

    Because it uses hyper, it supports HTTP/1, HTTP/2, and is fully asynchronous.

  2. Handle rejections using Filter::recover

    master

    Rejections can be intercepted and converted into valid Reply objects (like HTTP responses) using the .recover() method on a filter. This is the standard way to map errors to specific HTTP status codes and bodies.

    use warp::{reply, Filter, Rejection, http::StatusCode};
    
    #[derive(Debug)]
    struct InvalidParameter;
    impl warp::reject::Reject for InvalidParameter {}
    
    async fn handle_rejection(err: Rejection) -> Result<impl reply::Reply, std::convert::Infallible> {
        if err.is_not_found() {
            Ok(reply::with_status("NOT_FOUND", StatusCode::NOT_FOUND))
        } else if let Some(_e) = err.find::<InvalidParameter>() {
            Ok(reply::with_status("BAD_REQUEST", StatusCode::BAD_REQUEST))
        } else {
            Ok(reply::with_status("INTERNAL_SERVER_ERROR", StatusCode::INTERNAL_SERVER_ERROR))
        }
    }
    
    let route = warp::path::param::<u32>()
        .and_then(|id: u32| async move {
            if id == 0 {
                Err(warp::reject::custom(InvalidParameter))
            } else {
                Ok("id is valid")
            }
        })
        .recover(handle_rejection);
  3. Compose endpoints using the Filter trait

    master

    The core concept of warp is the Filter trait. You build web services by composing different filters together using the .and() method. Filters can handle path routing, parameter extraction, header requirements, query string deserialization, and more. If a request does not meet the requirements of a filter, it will be rejected.

    use warp::Filter;
    
    let hi = warp::path("hello")
        .and(warp::path::param())
        .and(warp::header("user-agent"))
        .map(|param: String, agent: String| {
            format!("Hello {}, whose agent is {}", param, agent)
        });
  4. Create a basic warp server

    master

    You can define routes using the Filter trait. Use warp::path! for path routing and parameter extraction, and warp::serve().run() to start the server on a specific address and port.

    use warp::Filter;
    
    #[tokio::main]
    async fn main() {
        // GET /hello/warp => 200 OK with body "Hello, warp!"
        let hello = warp::path!("hello" / String)
            .map(|name| format!("Hello, {}!", name));
    
        warp::serve(hello)
            .run(([127, 0, 0, 1], 3030))
            .await;
    }
  5. Require an exact HTTP header value

    master

    Use warp::header::exact(name, value) to create a filter that requires a header to match a specific string value exactly. If the header is missing or the value does not match, the request is rejected.

    // Require `dnt: 1` header to be set.
    let must_dnt = warp::header::exact("dnt", "1");
  6. Configure graceful shutdown

    master

    Add graceful shutdown support using the graceful(shutdown_signal) method. You provide a Future that resolves when the server should stop accepting new connections and begin shutting down existing ones.

    # async fn ex(addr: std::net::SocketAddr) {
    # use warp::Filter;
    # let filter = warp::any().map(|| "ok");
    warp::serve(filter)
        .bind(addr).await
        .graceful(async {
            // some signal in here, such as ctrl_c
        })
        .run().await;
    # }
  7. Require an HTTP header value ignoring ASCII case

    master

    Use warp::header::exact_ignore_case(name, value) to create a filter that requires a header to match a specific value, ignoring ASCII case (e.g., Keep-Alive matches keep-alive). If the header is missing or the value does not match, the request is rejected.

    // Require `connection: keep-alive` header to be set.
    let keep_alive = warp::header::exact_ignore_case("connection", "keep-alive");
  8. Extract the HTTP method with `warp::method()`

    master

    Use warp::method() to extract the http::Method from an incoming request. Unlike the specific method filters (like warp::get()), this filter never rejects a request; it simply passes the method type into your filter chain for inspection or logging.

    use warp::Filter;
    
    let route = warp::method()
        .map(|method| {
            format!("You sent a {} request!", method)
        });
  9. Start a server with `serve()`

    master

    Use serve(filter) to create a new Server instance with the provided Filter. The filter defines how requests are processed and what they return.

    let filter = warp::any().map(|| "ok");
    warp::serve(filter)
        .run()
        .await;