Trait warp::Filter[][src]

pub trait Filter: FilterBase {
    fn and<F>(self, other: F) -> And<Self, F>
    where
        Self: Sized,
        <Self::Extract as Tuple>::HList: Combine<<F::Extract as Tuple>::HList>,
        F: Filter + Clone,
        F::Error: CombineRejection<Self::Error>
, { ... }
fn or<F>(self, other: F) -> Or<Self, F>
    where
        Self: Filter<Error = Rejection> + Sized,
        F: Filter,
        F::Error: CombineRejection<Self::Error>
, { ... }
fn map<F>(self, fun: F) -> Map<Self, F>
    where
        Self: Sized,
        F: Func<Self::Extract> + Clone
, { ... }
fn and_then<F>(self, fun: F) -> AndThen<Self, F>
    where
        Self: Sized,
        F: Func<Self::Extract> + Clone,
        F::Output: TryFuture + Send,
        <F::Output as TryFuture>::Error: CombineRejection<Self::Error>
, { ... }
fn or_else<F>(self, fun: F) -> OrElse<Self, F>
    where
        Self: Filter<Error = Rejection> + Sized,
        F: Func<Rejection>,
        F::Output: TryFuture<Ok = Self::Extract> + Send,
        <F::Output as TryFuture>::Error: IsReject
, { ... }
fn recover<F>(self, fun: F) -> Recover<Self, F>
    where
        Self: Filter<Error = Rejection> + Sized,
        F: Func<Rejection>,
        F::Output: TryFuture + Send,
        <F::Output as TryFuture>::Error: IsReject
, { ... }
fn unify<T>(self) -> Unify<Self>
    where
        Self: Filter<Extract = (Either<T, T>,)> + Sized,
        T: Tuple
, { ... }
fn untuple_one<T>(self) -> UntupleOne<Self>
    where
        Self: Filter<Extract = (T,)> + Sized,
        T: Tuple
, { ... }
fn with<W>(self, wrapper: W) -> W::Wrapped
    where
        Self: Sized,
        W: Wrap<Self>
, { ... }
fn boxed(self) -> BoxedFilter<Self::Extract>
    where
        Self: Sized + Send + Sync + 'static,
        Self::Extract: Send,
        Self::Error: Into<Rejection>
, { ... } }
Expand description

Composable request filters.

A Filter can optionally extract some data from a request, combine it with others, mutate it, and return back some value as a reply. The power of Filters come from being able to isolate small subsets, and then chain and reuse them in various parts of your app.

Extracting Tuples

You may notice that several of these filters extract some tuple, often times a tuple of just 1 item! Why?

If a filter extracts a (String,), that simply means that it extracts a String. If you were to map the filter, the argument type would be exactly that, just a String.

What is it? It’s just some type magic that allows for automatic combining and flattening of tuples. Without it, combining two filters together with and, where one extracted (), and another String, would mean the map would be given a single argument of ((), String,), which is just no fun.

Provided methods

Composes a new Filter that requires both this and the other to filter a request.

Additionally, this will join together the extracted values of both filters, so that map and and_then receive them as separate arguments.

If a Filter extracts nothing (so, ()), combining with any other filter will simply discard the (). If a Filter extracts one or more items, combining will mean it extracts the values of itself combined with the other.

Example
use warp::Filter;

// Match `/hello/:name`...
warp::path("hello")
    .and(warp::path::param::<String>());
Examples found in repository
examples/returning.rs (line 8)
7
8
9
pub fn assets_filter() -> BoxedFilter<(impl Reply,)> {
    warp::path("assets").and(warp::fs::dir("./assets")).boxed()
}
More examples
examples/todos.rs (line 53)
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
pub fn todos_list(
        db: Db,
    ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
        warp::path!("todos")
            .and(warp::get())
            .and(warp::query::<ListOptions>())
            .and(with_db(db))
            .and_then(handlers::list_todos)
    }

    /// POST /todos with JSON body
    pub fn todos_create(
        db: Db,
    ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
        warp::path!("todos")
            .and(warp::post())
            .and(json_body())
            .and(with_db(db))
            .and_then(handlers::create_todo)
    }

    /// PUT /todos/:id with JSON body
    pub fn todos_update(
        db: Db,
    ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
        warp::path!("todos" / u64)
            .and(warp::put())
            .and(json_body())
            .and(with_db(db))
            .and_then(handlers::update_todo)
    }

    /// DELETE /todos/:id
    pub fn todos_delete(
        db: Db,
    ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
        // We'll make one of our endpoints admin-only to show how authentication filters are used
        let admin_only = warp::header::exact("authorization", "Bearer admin");

        warp::path!("todos" / u64)
            // It is important to put the auth check _after_ the path filters.
            // If we put the auth check before, the request `PUT /todos/invalid-string`
            // would try this filter and reject because the authorization header doesn't match,
            // rather because the param is wrong for that other path.
            .and(admin_only)
            .and(warp::delete())
            .and(with_db(db))
            .and_then(handlers::delete_todo)
    }

    fn with_db(db: Db) -> impl Filter<Extract = (Db,), Error = std::convert::Infallible> + Clone {
        warp::any().map(move || db.clone())
    }

    fn json_body() -> impl Filter<Extract = (Todo,), Error = warp::Rejection> + Clone {
        // When accepting a body, we want a JSON body
        // (and to reject huge payloads)...
        warp::body::content_length_limit(1024 * 16).and(warp::body::json())
    }
examples/file.rs (line 10)
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
    pretty_env_logger::init();

    let readme = warp::get()
        .and(warp::path::end())
        .and(warp::fs::file("./README.md"));

    // dir already requires GET...
    let examples = warp::path("ex").and(warp::fs::dir("./examples/"));

    // GET / => README.md
    // GET /ex/... => ./examples/..
    let routes = readme.or(examples);

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
examples/wrapping.rs (line 16)
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
fn hello_wrapper<F, T>(
    filter: F,
) -> impl Filter<Extract = (&'static str,)> + Clone + Send + Sync + 'static
where
    F: Filter<Extract = (T,), Error = std::convert::Infallible> + Clone + Send + Sync + 'static,
    F::Extract: warp::Reply,
{
    warp::any()
        .map(|| {
            println!("before filter");
        })
        .untuple_one()
        .and(filter)
        .map(|_arg| "wrapped hello world")
}
examples/headers.rs (line 23)
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
fn main() {
    pretty_env_logger::init();

    // For this example, we assume no DNS was used,
    // so the Host header should be an address.
    let host = warp::header::<SocketAddr>("host");

    // Match when we get `accept: */*` exactly.
    let accept_stars = warp::header::exact("accept", "*/*");

    let routes = host
        .and(accept_stars)
        .map(|addr| format!("accepting stars on {}", addr));

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
examples/body.rs (line 19)
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
fn main() {
    pretty_env_logger::init();

    // POST /employees/:rate  {"name":"Sean","rate":2}
    let promote = warp::post()
        .and(warp::path("employees"))
        .and(warp::path::param::<u32>())
        // Only accept bodies smaller than 16kb...
        .and(warp::body::content_length_limit(1024 * 16))
        .and(warp::body::json())
        .map(|rate, mut employee: Employee| {
            employee.rate = rate;
            warp::reply::json(&employee)
        });

    warp::serve(promote).run(([127, 0, 0, 1], 3030)).await
}

Composes a new Filter of either this or the other filter.

Example
use std::net::SocketAddr;
use warp::Filter;

// Match either `/:u32` or `/:socketaddr`
warp::path::param::<u32>()
    .or(warp::path::param::<SocketAddr>());
Examples found in repository
examples/returning.rs (line 18)
17
18
19
20
fn main() {
    let routes = index_filter().or(assets_filter());
    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
More examples
examples/todos.rs (line 43)
39
40
41
42
43
44
45
46
pub fn todos(
        db: Db,
    ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
        todos_list(db.clone())
            .or(todos_create(db.clone()))
            .or(todos_update(db.clone()))
            .or(todos_delete(db))
    }
examples/file.rs (line 18)
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
    pretty_env_logger::init();

    let readme = warp::get()
        .and(warp::path::end())
        .and(warp::fs::file("./README.md"));

    // dir already requires GET...
    let examples = warp::path("ex").and(warp::fs::dir("./examples/"));

    // GET / => README.md
    // GET /ex/... => ./examples/..
    let routes = readme.or(examples);

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
examples/rejections.rs (line 36)
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
fn main() {
    let math = warp::path!("math" / u16);
    let div_with_header = math
        .and(warp::get())
        .and(div_by())
        .map(|num: u16, denom: NonZeroU16| {
            warp::reply::json(&Math {
                op: format!("{} / {}", num, denom),
                output: num / denom.get(),
            })
        });

    let div_with_body =
        math.and(warp::post())
            .and(warp::body::json())
            .map(|num: u16, body: DenomRequest| {
                warp::reply::json(&Math {
                    op: format!("{} / {}", num, body.denom),
                    output: num / body.denom.get(),
                })
            });

    let routes = div_with_header.or(div_with_body).recover(handle_rejection);

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
examples/websockets_chat.rs (line 46)
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
fn main() {
    pretty_env_logger::init();

    // Keep track of all connected users, key is usize, value
    // is a websocket sender.
    let users = Users::default();
    // Turn our "state" into a new Filter...
    let users = warp::any().map(move || users.clone());

    // GET /chat -> websocket upgrade
    let chat = warp::path("chat")
        // The `ws()` filter will prepare Websocket handshake...
        .and(warp::ws())
        .and(users)
        .map(|ws: warp::ws::Ws, users| {
            // This will call our function if the handshake succeeds.
            ws.on_upgrade(move |socket| user_connected(socket, users))
        });

    // GET / -> index html
    let index = warp::path::end().map(|| warp::reply::html(INDEX_HTML));

    let routes = index.or(chat);

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
examples/sse_chat.rs (line 53)
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
fn main() {
    pretty_env_logger::init();

    // Keep track of all connected users, key is usize, value
    // is an event stream sender.
    let users = Arc::new(Mutex::new(HashMap::new()));
    // Turn our "state" into a new Filter...
    let users = warp::any().map(move || users.clone());

    // POST /chat -> send message
    let chat_send = warp::path("chat")
        .and(warp::post())
        .and(warp::path::param::<usize>())
        .and(warp::body::content_length_limit(500))
        .and(
            warp::body::bytes().and_then(|body: bytes::Bytes| async move {
                std::str::from_utf8(&body)
                    .map(String::from)
                    .map_err(|_e| warp::reject::custom(NotUtf8))
            }),
        )
        .and(users.clone())
        .map(|my_id, msg, users| {
            user_message(my_id, msg, &users);
            warp::reply()
        });

    // GET /chat -> messages stream
    let chat_recv = warp::path("chat").and(warp::get()).and(users).map(|users| {
        // reply using server-sent events
        let stream = user_connected(users);
        warp::sse::reply(warp::sse::keep_alive().stream(stream))
    });

    // GET / -> index html
    let index = warp::path::end().map(|| {
        warp::http::Response::builder()
            .header("content-type", "text/html; charset=utf-8")
            .body(INDEX_HTML)
    });

    let routes = index.or(chat_recv).or(chat_send);

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}

Composes this Filter with a function receiving the extracted value.

Example
use warp::Filter;

// Map `/:id`
warp::path::param().map(|id: u64| {
  format!("Hello #{}", id)
});
Func

The generic Func trait is implemented for any function that receives the same arguments as this Filter extracts. In practice, this shouldn’t ever bother you, and simply makes things feel more natural.

For example, if three Filters were combined together, suppose one extracts nothing (so ()), and the other two extract two integers, a function that accepts exactly two integer arguments is allowed. Specifically, any Fn(u32, u32).

Without Product and Func, this would be a lot messier. First of all, the ()s couldn’t be discarded, and the tuples would be nested. So, instead, you’d need to pass an Fn(((), (u32, u32))). That’s just a single argument. Bleck!

Even worse, the tuples would shuffle the types around depending on the exact invocation of ands. So, unit.and(int).and(int) would result in a different extracted type from unit.and(int.and(int)), or from int.and(unit).and(int). If you changed around the order of filters, while still having them be semantically equivalent, you’d need to update all your maps as well.

Product, HList, and Func do all the heavy work so that none of this is a bother to you. What’s more, the types are enforced at compile-time, and tuple flattening is optimized away to nothing by LLVM.

Examples found in repository
examples/returning.rs (line 13)
12
13
14
pub fn index_filter() -> impl Filter<Extract = (&'static str,), Error = Rejection> + Clone {
    warp::path::end().map(|| "Index page")
}
More examples
examples/todos.rs (line 100)
99
100
101
fn with_db(db: Db) -> impl Filter<Extract = (Db,), Error = std::convert::Infallible> + Clone {
        warp::any().map(move || db.clone())
    }
examples/hello.rs (line 7)
5
6
7
8
9
10
fn main() {
    // Match any request and return hello world!
    let routes = warp::any().map(|| "Hello, World!");

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
examples/wrapping.rs (lines 12-14)
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
fn hello_wrapper<F, T>(
    filter: F,
) -> impl Filter<Extract = (&'static str,)> + Clone + Send + Sync + 'static
where
    F: Filter<Extract = (T,), Error = std::convert::Infallible> + Clone + Send + Sync + 'static,
    F::Extract: warp::Reply,
{
    warp::any()
        .map(|| {
            println!("before filter");
        })
        .untuple_one()
        .and(filter)
        .map(|_arg| "wrapped hello world")
}

#[tokio::main]
async fn main() {
    // Match any request and return hello world!
    let routes = warp::any()
        .map(|| "hello world")
        .boxed()
        .recover(|_err| async { Ok("recovered") })
        // wrap the filter with hello_wrapper
        .with(warp::wrap_fn(hello_wrapper));

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
examples/headers.rs (line 24)
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
fn main() {
    pretty_env_logger::init();

    // For this example, we assume no DNS was used,
    // so the Host header should be an address.
    let host = warp::header::<SocketAddr>("host");

    // Match when we get `accept: */*` exactly.
    let accept_stars = warp::header::exact("accept", "*/*");

    let routes = host
        .and(accept_stars)
        .map(|addr| format!("accepting stars on {}", addr));

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
examples/body.rs (lines 24-27)
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
fn main() {
    pretty_env_logger::init();

    // POST /employees/:rate  {"name":"Sean","rate":2}
    let promote = warp::post()
        .and(warp::path("employees"))
        .and(warp::path::param::<u32>())
        // Only accept bodies smaller than 16kb...
        .and(warp::body::content_length_limit(1024 * 16))
        .and(warp::body::json())
        .map(|rate, mut employee: Employee| {
            employee.rate = rate;
            warp::reply::json(&employee)
        });

    warp::serve(promote).run(([127, 0, 0, 1], 3030)).await
}

Composes this Filter with a function receiving the extracted value.

The function should return some TryFuture type.

The Error type of the return Future needs be a Rejection, which means most futures will need to have their error mapped into one.

Example
use warp::Filter;

// Validate after `/:id`
warp::path::param().and_then(|id: u64| async move {
    if id != 0 {
        Ok(format!("Hello #{}", id))
    } else {
        Err(warp::reject::not_found())
    }
});
Examples found in repository
examples/dyn_reply.rs (line 14)
13
14
15
16
17
fn main() {
    let routes = warp::path::param().and_then(dyn_reply);

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
More examples
examples/futures.rs (line 13)
9
10
11
12
13
14
15
16
fn main() {
    // Match `/:Seconds`...
    let routes = warp::path::param()
        // and_then create a `Future` that will simply wait N seconds...
        .and_then(sleepy);

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
examples/rejections.rs (lines 43-49)
42
43
44
45
46
47
48
49
50
fn div_by() -> impl Filter<Extract = (NonZeroU16,), Error = Rejection> + Copy {
    warp::header::<u16>("div-by").and_then(|n: u16| async move {
        if let Some(denom) = NonZeroU16::new(n) {
            Ok(denom)
        } else {
            Err(reject::custom(DivideByZero))
        }
    })
}
examples/todos.rs (line 56)
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
pub fn todos_list(
        db: Db,
    ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
        warp::path!("todos")
            .and(warp::get())
            .and(warp::query::<ListOptions>())
            .and(with_db(db))
            .and_then(handlers::list_todos)
    }

    /// POST /todos with JSON body
    pub fn todos_create(
        db: Db,
    ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
        warp::path!("todos")
            .and(warp::post())
            .and(json_body())
            .and(with_db(db))
            .and_then(handlers::create_todo)
    }

    /// PUT /todos/:id with JSON body
    pub fn todos_update(
        db: Db,
    ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
        warp::path!("todos" / u64)
            .and(warp::put())
            .and(json_body())
            .and(with_db(db))
            .and_then(handlers::update_todo)
    }

    /// DELETE /todos/:id
    pub fn todos_delete(
        db: Db,
    ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
        // We'll make one of our endpoints admin-only to show how authentication filters are used
        let admin_only = warp::header::exact("authorization", "Bearer admin");

        warp::path!("todos" / u64)
            // It is important to put the auth check _after_ the path filters.
            // If we put the auth check before, the request `PUT /todos/invalid-string`
            // would try this filter and reject because the authorization header doesn't match,
            // rather because the param is wrong for that other path.
            .and(admin_only)
            .and(warp::delete())
            .and(with_db(db))
            .and_then(handlers::delete_todo)
    }
examples/sse_chat.rs (lines 27-31)
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
fn main() {
    pretty_env_logger::init();

    // Keep track of all connected users, key is usize, value
    // is an event stream sender.
    let users = Arc::new(Mutex::new(HashMap::new()));
    // Turn our "state" into a new Filter...
    let users = warp::any().map(move || users.clone());

    // POST /chat -> send message
    let chat_send = warp::path("chat")
        .and(warp::post())
        .and(warp::path::param::<usize>())
        .and(warp::body::content_length_limit(500))
        .and(
            warp::body::bytes().and_then(|body: bytes::Bytes| async move {
                std::str::from_utf8(&body)
                    .map(String::from)
                    .map_err(|_e| warp::reject::custom(NotUtf8))
            }),
        )
        .and(users.clone())
        .map(|my_id, msg, users| {
            user_message(my_id, msg, &users);
            warp::reply()
        });

    // GET /chat -> messages stream
    let chat_recv = warp::path("chat").and(warp::get()).and(users).map(|users| {
        // reply using server-sent events
        let stream = user_connected(users);
        warp::sse::reply(warp::sse::keep_alive().stream(stream))
    });

    // GET / -> index html
    let index = warp::path::end().map(|| {
        warp::http::Response::builder()
            .header("content-type", "text/html; charset=utf-8")
            .body(INDEX_HTML)
    });

    let routes = index.or(chat_recv).or(chat_send);

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}

Compose this Filter with a function receiving an error.

The function should return some TryFuture type yielding the same item and error types.

Examples found in repository
examples/query_string.rs (line 39)
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
fn main() {
    pretty_env_logger::init();

    // get /example1?key=value
    // demonstrates an optional parameter.
    let example1 = warp::get()
        .and(warp::path("example1"))
        .and(warp::query::<HashMap<String, String>>())
        .map(|p: HashMap<String, String>| match p.get("key") {
            Some(key) => Response::builder().body(format!("key = {}", key)),
            None => Response::builder().body(String::from("No \"key\" param in query.")),
        });

    // get /example2?key1=value&key2=42
    // uses the query string to populate a custom object
    let example2 = warp::get()
        .and(warp::path("example2"))
        .and(warp::query::<MyObject>())
        .map(|p: MyObject| {
            Response::builder().body(format!("key1 = {}, key2 = {}", p.key1, p.key2))
        });

    let opt_query = warp::query::<MyObject>()
        .map(Some)
        .or_else(|_| async { Ok::<(Option<MyObject>,), std::convert::Infallible>((None,)) });

    // get /example3?key1=value&key2=42
    // builds on example2 but adds custom error handling
    let example3 =
        warp::get()
            .and(warp::path("example3"))
            .and(opt_query)
            .map(|p: Option<MyObject>| match p {
                Some(obj) => {
                    Response::builder().body(format!("key1 = {}, key2 = {}", obj.key1, obj.key2))
                }
                None => Response::builder()
                    .status(StatusCode::BAD_REQUEST)
                    .body(String::from("Failed to decode query param.")),
            });

    warp::serve(example1.or(example2).or(example3))
        .run(([127, 0, 0, 1], 3030))
        .await
}

Compose this Filter with a function receiving an error and returning a new type, instead of the same type.

This is useful for “customizing” rejections into new response types. See also the rejections example.

Examples found in repository
examples/wrapping.rs (line 26)
21
22
23
24
25
26
27
28
29
30
31
fn main() {
    // Match any request and return hello world!
    let routes = warp::any()
        .map(|| "hello world")
        .boxed()
        .recover(|_err| async { Ok("recovered") })
        // wrap the filter with hello_wrapper
        .with(warp::wrap_fn(hello_wrapper));

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
More examples
examples/rejections.rs (line 36)
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
fn main() {
    let math = warp::path!("math" / u16);
    let div_with_header = math
        .and(warp::get())
        .and(div_by())
        .map(|num: u16, denom: NonZeroU16| {
            warp::reply::json(&Math {
                op: format!("{} / {}", num, denom),
                output: num / denom.get(),
            })
        });

    let div_with_body =
        math.and(warp::post())
            .and(warp::body::json())
            .map(|num: u16, body: DenomRequest| {
                warp::reply::json(&Math {
                    op: format!("{} / {}", num, body.denom),
                    output: num / body.denom.get(),
                })
            });

    let routes = div_with_header.or(div_with_body).recover(handle_rejection);

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}

Unifies the extracted value of Filters composed with or.

When a Filter extracts some Either<T, T>, where both sides are the same type, this combinator can be used to grab the inner value, regardless of which side of Either it was. This is useful for values that could be extracted from multiple parts of a request, and the exact place isn’t important.

Example
use std::net::SocketAddr;
use warp::Filter;

let client_ip = warp::header("x-real-ip")
    .or(warp::header("x-forwarded-for"))
    .unify()
    .map(|ip: SocketAddr| {
        // Get the IP from either header,
        // and unify into the inner type.
    });

Convenience method to remove one layer of tupling.

This is useful for when things like map don’t return a new value, but just (), since warp will wrap it up into a ((),).

Example
use warp::Filter;

let route = warp::path::param()
    .map(|num: u64| {
        println!("just logging: {}", num);
        // returning "nothing"
    })
    .untuple_one()
    .map(|| {
        println!("the ((),) was removed");
        warp::reply()
    });
use warp::Filter;

let route = warp::any()
    .map(|| {
        // wanting to return a tuple
        (true, 33)
    })
    .untuple_one()
    .map(|is_enabled: bool, count: i32| {
        println!("untupled: ({}, {})", is_enabled, count);
    });
Examples found in repository
examples/wrapping.rs (line 15)
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
fn hello_wrapper<F, T>(
    filter: F,
) -> impl Filter<Extract = (&'static str,)> + Clone + Send + Sync + 'static
where
    F: Filter<Extract = (T,), Error = std::convert::Infallible> + Clone + Send + Sync + 'static,
    F::Extract: warp::Reply,
{
    warp::any()
        .map(|| {
            println!("before filter");
        })
        .untuple_one()
        .and(filter)
        .map(|_arg| "wrapped hello world")
}

Wraps the current filter with some wrapper.

The wrapper may do some preparation work before starting this filter, and may do post-processing after the filter completes.

Example
use warp::Filter;

let route = warp::any()
    .map(warp::reply);

// Wrap the route with a log wrapper.
let route = route.with(warp::log("example"));
Examples found in repository
examples/wrapping.rs (line 28)
21
22
23
24
25
26
27
28
29
30
31
fn main() {
    // Match any request and return hello world!
    let routes = warp::any()
        .map(|| "hello world")
        .boxed()
        .recover(|_err| async { Ok("recovered") })
        // wrap the filter with hello_wrapper
        .with(warp::wrap_fn(hello_wrapper));

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
More examples
examples/todos.rs (line 28)
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
fn main() {
    if env::var_os("RUST_LOG").is_none() {
        // Set `RUST_LOG=todos=debug` to see debug logs,
        // this only shows access logs.
        env::set_var("RUST_LOG", "todos=info");
    }
    pretty_env_logger::init();

    let db = models::blank_db();

    let api = filters::todos(db);

    // View access logs by setting `RUST_LOG=todos`.
    let routes = api.with(warp::log("todos"));
    // Start up the server...
    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
examples/tracing.rs (line 38)
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
fn main() {
    // Filter traces based on the RUST_LOG env var, or, if it's not set,
    // default to show the output of the example.
    let filter = std::env::var("RUST_LOG").unwrap_or_else(|_| "tracing=info,warp=debug".to_owned());

    // Configure the default `tracing` subscriber.
    // The `fmt` subscriber from the `tracing-subscriber` crate logs `tracing`
    // events to stdout. Other subscribers are available for integrating with
    // distributed tracing systems such as OpenTelemetry.
    tracing_subscriber::fmt()
        // Use the filter we built above to determine which traces to record.
        .with_env_filter(filter)
        // Record an event when each span closes. This can be used to time our
        // routes' durations!
        .with_span_events(FmtSpan::CLOSE)
        .init();

    let hello = warp::path("hello")
        .and(warp::get())
        // When the `hello` route is called, emit a `tracing` event.
        .map(|| {
            tracing::info!("saying hello...");
            "Hello, World!"
        })
        // Wrap the route in a `tracing` span to add the route's name as context
        // to any events that occur inside it.
        .with(warp::trace::named("hello"));

    let goodbye = warp::path("goodbye")
        .and(warp::get())
        .map(|| {
            tracing::info!("saying goodbye...");
            "So long and thanks for all the fish!"
        })
        // We can also provide our own custom `tracing` spans to wrap a route.
        .with(warp::trace(|info| {
            // Construct our own custom span for this route.
            tracing::info_span!("goodbye", req.path = ?info.path())
        }));

    let routes = hello
        .or(goodbye)
        // Wrap all the routes with a filter that creates a `tracing` span for
        // each request we receive, including data about the request.
        .with(warp::trace::request());

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}

Boxes this filter into a trait object, making it easier to name the type.

Example
use warp::Filter;

fn impl_reply() -> warp::filters::BoxedFilter<(impl warp::Reply,)> {
    warp::any()
        .map(warp::reply)
        .boxed()
}

fn named_i32() -> warp::filters::BoxedFilter<(i32,)> {
    warp::path::param::<i32>()
        .boxed()
}

fn named_and() -> warp::filters::BoxedFilter<(i32, String)> {
    warp::path::param::<i32>()
        .and(warp::header::<String>("host"))
        .boxed()
}
Examples found in repository
examples/returning.rs (line 8)
7
8
9
pub fn assets_filter() -> BoxedFilter<(impl Reply,)> {
    warp::path("assets").and(warp::fs::dir("./assets")).boxed()
}
More examples
examples/wrapping.rs (line 25)
21
22
23
24
25
26
27
28
29
30
31
fn main() {
    // Match any request and return hello world!
    let routes = warp::any()
        .map(|| "hello world")
        .boxed()
        .recover(|_err| async { Ok("recovered") })
        // wrap the filter with hello_wrapper
        .with(warp::wrap_fn(hello_wrapper));

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}

Implementors