Coder Social home page Coder Social logo

seanmonstar / warp Goto Github PK

View Code? Open in Web Editor NEW
9.1K 102.0 707.0 904 KB

A super-easy, composable, web server framework for warp speeds.

Home Page: https://seanmonstar.com/post/176530511587/warp

License: MIT License

Rust 100.00%
rust http server framework

warp's Introduction

warp

crates.io Released API docs MIT licensed GHA Build Status Discord chat

A super-easy, composable, web server framework for warp speeds.

The fundamental building block of warp is the Filter: they can be combined and composed to express rich requirements on requests.

Thanks to its Filter system, warp provides these out of the box:

  • 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
  • Gzip, Deflate, and Brotli compression

Since it builds on top of hyper, you automatically get:

  • HTTP/1
  • HTTP/2
  • Asynchronous
  • One of the fastest HTTP implementations
  • Tested and correct

Example

Add warp and Tokio to your dependencies:

tokio = { version = "1", features = ["full"] }
warp = "0.3"

And then get started in your main.rs:

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;
}

For more information you can check the docs or the examples.

warp's People

Contributors

bastidood avatar boxdot avatar cdvv7788 avatar coolreader18 avatar derekdreery avatar dmexe avatar emschwartz avatar floriandr avatar fsmaxb avatar gtsiam avatar hawkw avatar ignatenkobrain avatar jakajancar avatar joseluisq avatar jxs avatar kaj avatar kamalmarhubi avatar luciofranco avatar najamelan avatar nickelc avatar pietroalbini avatar psmit avatar remexre avatar renato-zannon avatar rockwotj avatar seanmonstar avatar simonsapin avatar sunng87 avatar tottoto avatar zenria avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

warp's Issues

Add example showing how to customize rendering errors

If you don't do anything with rejections, the default response rendering will apply, which is just an empty response with an appropriate status code. It's possible to convert rejections into custom responses using or_else, but that may be less obvious. There should be an example!

let simple = warp::get(warp::path("hello").map(warp::reply));

let route = simple.or_else(|rejection| match rejection.status() {
    StatusCode::NOT_FOUND => {
        Ok(my_custom_404_response)
    },
    _ => {
        // convert all?? others into just 400s
        Ok(Response::builder()
            .status(400)
            .body("i swallowed an error!")
            .unwrap())
    }
});

Reject with other status code?

I'd like to be able to reject requests with status codes currently not available, like 401 Unauthorized; is this possible?

Add an Inject type to Filter?

I've run into a couple of situations where it would be convenient to construct a Filter that expects additional input beyond the request. The main use case right now is for having resources that define their own "root route" in a helper function and then rely on a higher level of logic to integrate that Filter into a higher context where it's easier to get to the context objects (like database connection pools). There are some pretty reasonable ways to handle many of those cases right now, like passing connection pools straight into the helper functions that create the lower-level Filters or having the low-level Filter extract a function that maps the additional data to a Future, but those both have their own trade-offs. I don't know how feasible it is, but having an ergonomic way to build a filter that expects more context information (i.e. some kind of "Inject" associated type) might be a neat feature.

url_for

First of all, this is a very cool project, thanks! It feels a bit like Flask for Python, in the sense that it is non-nonsense and lets you get on with writing the code.

In Flask, there is a method called url_for, which can be used both inside and outside templates. An example would be url_for("index"), which resolves to a the URL the index function (view) is mounted at.

Is this possible in warp? I've looked at the code but couldn't see anything obvious. It would be nice to have this functionality as part of the handlebars example.

Idea: helper macros

I was looking over the routing example, and a part of me was wondering if there might be some standardized macros that could help with very common filter patterns. An example:

fn main() {
    let routes = routes!{
        route!{
            warp::index(),
            ||{
                "Welcome!"
            }
        },
        route!{
            path!("hello"/String),
            |name|{
                format!("Hello {}",name)
            }
        }
        route!{
            warp::any(),
            ||{
                "How'd you get here?"
            }
        }
    };

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

routes! would .or(...) a chain of filters

route! would .and(...) a chain of filters and .map(...) to the last param function

Please keep in mind i'm a Rust noob so I might be missing some capabilities of macros right now :)

HTML support built-in

Needing to output HTML using a templating system is required by many applications. warp should provide an easy default way to do so (clearly, the filter system allows someone to plug in any output they want). It seems like handlebars is the most popular.

Routing for many endpoints compiles very slowly

When writing a router for an API with a lot of endpoints, compilation crawls to a halt.

The code in question can be found here: http://gist.github.com/kiljacken/866f6617b2998d672ac8f67cb6037186

When compiling with cargo build and only one route enabled the compilation takes around ~5s. With all routes enabled this increases to 1m 12s. This obviously makes iteration time quite high, and thus decreases productivity quite a bit.

It would be nice if there was more effective way to do this.

Example on crates.io does not compile

On https://crates.io/crates/warp, I see the following example:

extern crate warp;

use warp::Filter;

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));
}

This does not compile for me using rust 1.28.0. It looks like it is a cross between using the path! macro and using the warp::path function.

Add `headers` filter to access the HeaderMap

A user may wish to access multiple headers at once, or dynamically determine which headers to look at. There isn't yet a way, but there should be! Some options to implement this:

  • warp::headers() that just clones the HeaderMap. It's the easiest thing to do, but it's also the costliest.
  • warp::headers(|headers| do_something(headers)) would allow accessing &HeaderMap, so you don't need to clone, but filters of this design feel less natural. It's also harder to determine what the return type can be (the Filter being Copy depends on the closure being Copy).
  • If there were some way to be able to extract non-static references, that'd be the best. It's probably not going to be possible until async syntax, however.

Return error or go to `or` path

Hello,

Sorry if this is kind of a basic question, but I'm trying to figure out how to return an error instead of going the or path.

For example, if a querystring fails to parse, the way I have it set up now, the router will fallback to another or branch. But what if I want to return an error immediately?

I'm probably overlooking something obvious...

(I'll update with a code snippet when I have time in a bit, sorry if the question is too vague)

Add `address` filter that gives the client / peer's address

Near as I can tell there's no way to get the address of the client. Something like

let ip = warp::ip()
  .map(|addr| format!("your address is {}", addr.ip()));

where addr is a std::net::SocketAddr. Alternatively, do some trait fanciness to allow specifying IpAddr or SocketAddr in the closure.

Debug routes

Is there a way to debug routes? I'm 99% sure my route is correct, yet it gets rejected with 204 No Content.

I'm missing the following to effectively debug the routes:

  • A way to log all requests with
    • status:
      • "/route/to/data" - 200 Ok
      • "/route/does/not/exist" - 204 No Content
    • and optionally debug output:
      • "/route/does"... rejected, no matching path element "does" found
  • A debug view, perhaps tree style, which shows all possible paths

Is there something along those lines available?

FYI: warp example in ructe

Hi!

I have added a warp example in the ructe repository: https://github.com/kaj/ructe/tree/master/examples/warp (ructe is Rust Compiled Templates , a web page template system where as much work as possible is done compile-time).

I welcome any suggestions for things that could be done more "warp-like". In particular, I'm a bit shady about error handling, and I have not implemented a proper far-expires header yet. I really like the routing / parameter passing to handlers, but at the same time, I think it would be nice to be able to also get the full url and other details of the current request, especially in the custom error handling routine.

Feel free to mention this in any documentation as you may see fit, or you can just close this PR.

Filter and Service conversions

It's a goal to allow turning a warp::Filter into a tower::Service, and to allow for warp::Server to be given a Service (or NewService) as its argument. This would facilitate making use of the wider tower middleware directly.

Redirecting the index

Hi there,

I'm fiddling with warp and I'm having trouble redirecting the root route. I basically have 3 routes defined.

  • Root
  • Login
  • Assets

Here is what I'm attempting:

...
    let asset_routes = generate_asset_routes();
    let index = warp::get2().and(warp::index()).and_then(index_page);
    let login = warp::get2()
        .and(warp::path("login"))
        .map(|| "login")
        .map(render);

    let routes = asset_routes.or(login).or(index);
...

fn index_page() -> Result<impl warp::Reply, warp::Rejection> {
    Ok(Response::builder()
        .status(StatusCode::FOUND)
        .header("Location", "/login/")
        .body("")
        .expect("response is valid"))
}

fn generate_asset_routes() -> BoxedFilter<(impl Reply,)> {
    let css = warp::path("css").and(warp::fs::dir("./content/css/"));
    let fonts = warp::path("fonts").and(warp::fs::dir("./content/fonts/"));
    let js = warp::path("js").and(warp::fs::dir("./content/js/"));
    let images = warp::path("images").and(warp::fs::dir("./content/images/"));

    css.or(fonts).or(js).or(images).boxed()
}

So this happily redirects the root route to /login. However, the assets are also being redirected to /login. If I remove the index route, this works. I can browse to /login and the page and its assets render as expected.

What am I doing wrong here?

body::form rejects if content-type includes charset

I'm seeing seemingly-good requests be rejected. Looks like this might happen when the client specifies a charset in the content-type. Relevant log output:

 TRACE warp::filters::body        > is_content_type "application/x-www-form-urlencoded"? "application/x-www-form-urlencoded; charset=utf-8"
 DEBUG warp::filters::body        > content-type doesn't match "application/x-www-form-urlencoded"

I'm not an http expert and am unaware whether it's technically correct for charset to be included here, but random googling indicates it's not rare.

Maybe is_content_type should just check what the header starts with? or split on semicolons?

Returning unit from map or and_then is weird

While warp usually handles unit () from built-in Filters quite well, if a user tries to map (or and_then) a Filter and return nothing, the unit is no longer dropped.

Example

warp::any()
    // unit was dropped, no arg needed
    .map(|| {
        println!("no returning anything");
    })
    // return of `map` was auto-wrapped in `(T,)`, becoming `((),)`
    .map(|((),))| {
        println!("gross");
    })

What is the type of a filter?

I'm looking at writing functions that return filters, so that I can more easily write integration tests and split my routes across multiple modules. I want to start by doing something like:

main.rs

use myapp::routes;

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

lib.rs

pub fn routes() -> // ??? {
    path!("ping").map(|| "pong")
}

What type should my routes() function return?

Content Negotiation

Provide a content-negotiation filter with these objectives:

  • User can chain together serializers, choosing what formats can be output.
  • Serializers include content-type headers
  • The filter checks for "best" serializer using the request's accept header.
  • Wrapped filters can return either T: Serialize, or Negotiate(Response<T: Serialize>), which will automatically be serialized by the "best" format.

Example:

let negotiation = warp::reply::with::negotiate()
    .format(json)
    .format(xml);

let list = warp::path("todos")
    .and_then(|| DB::load_todos());
let one = path!("todos" / u64)
    .and_then(|id| Db::get_todo(id));

let api = list.or(one)
    .with(negotiate);

Relation to tower-web

I just read your blog post that introduces the warp library, so I'm quite intrigued now as to how things will develop now with tower and tower-web quickly entering the stage. I am certainly attracted by the design philosophy of warp, but I do want to know how you see it in relation to tower-web, which you mentioned in passing. From an issue you have open here, it would seem the Filter and Service traits are somehow analogous? I'm presuming neither tower-web nor warp will ever include the other as a dependency, but what will the relation between them be, going forwards? Direct competitors?

Side question: does warp support async workflows like Gotham or such?

Add multipart filters

It would be nice to include some built-in filters in warp::multipart (or warp::body?). I'm not sure what the API should look like. Some things that come to mind that a user could want to do:

  • Decode a multipart body via serde::Deserialize.
  • Receive a file upload.
  • Receive multiple file uploads.
  • Allow streaming of the data, since buffering an entire file upload in memory could be bad if the files are huge.

Error message with rejection

It would be nice if the rejections allowed passing an error message, so the end user of an API has a chance of knowing what they did wrong (for example on bad request).

Expose more HTTP methods as convenience filters, eg PATCH, HEAD

I'm not sure what the right line is for which ones merit convenience filters, but at least PATCH I'd think. Alternatively, given the small number, all could be exposed.

List of methods:

  • ✔️ GET
  • HEAD this could be promoted, I think
  • ✔️ POST
  • ✔️ PUT
  • ✔️ DELETE
  • CONNECT(pretty niche for use cases of warp?)
  • OPTIONS (again, niche for use cases of warp)
  • TRACE don't think this is worth it, unless exposing all methods
  • PATCH I think this one should be promoted

(I'm ignoring webdav and any other such protocols on top of HTTP here.)

Add compression Filters

These would likely need to be with filters.

Some hypothetical example:

let route = warp::fs::dir("static")
    .with(warp::compress::gzip());

Non-ascii urls results in 400s

curl localhost:3030/bye/tö -v
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3030 (#0)
> GET /bye/tö HTTP/1.1
> Host: localhost:3030
> User-Agent: curl/7.61.0
> Accept: */*
> 
< HTTP/1.1 400 Bad Request
< content-length: 0
< date: Sun, 19 Aug 2018 15:38:41 GMT
< 
* Connection #0 to host localhost left intact

Output from warp:

RUST_LOG=debug cargo run --example routing
...
   Finished dev [unoptimized + debuginfo] target(s) in 0.11s                                 
     Running `target/debug/examples/routing`
 INFO  warp::server > warp drive engaged: listening on 127.0.0.1:3030                         
 DEBUG tokio_reactor::background > starting background reactor                                
 DEBUG tokio_reactor::background > starting background reactor                                
 DEBUG tokio_reactor             > loop process - 1 events, 0.000s
 DEBUG tokio_reactor             > loop process - 1 events, 0.000s
 DEBUG hyper::proto::h1::io      > read 85 bytes
 DEBUG hyper::proto::h1::conn    > parse error (invalid Header provided) with 85 bytes
 DEBUG hyper::proto::h1::role    > sending automatic response (400 Bad Request) for parse error
 DEBUG hyper::proto::h1::io      > flushed 84 bytes
 DEBUG hyper::server::conn::upgrades > error polling connection protocol: invalid Header provided
 DEBUG hyper::server::conn           > conn error: invalid Header provided
 DEBUG tokio_reactor                 > dropping I/O source: 0

nested querystrings

Hi!

Thanks for your hard work on this! I'm enjoying the filters api.

I have a use case for nested querystrings (or some way to create a list out of repeated querystring keys). I don't believe that serde_urlencoded supports this, while I think serde_qs does.

Would you want to support this in warp, or is there a way for me to write a custom filter?

Regex pattern matching route parameters

It might be that there's a way to do this by composing filters, but it seems to escape me for the moment.

If I have a route of /:something, is there a way to provide a pattern to filter? For example, say I wanted to accept /:something, but only where :something is value1 or value2. It seems I could maybe join some handlers up together to make this happen, but not in a way that would provide access to the value of :something?

Rejecting with warp::reply::not_found results in a 405 Method Not Allowed error

Hello,
with the following code, I would get a 405 error while I expect a 404:

fn main() {
  let routes = warp::post2().map(warp::reply).or(warp::get2().and(warp::path("test").and_then(send_test)));
  warp::serve(routes).run(([127, 0, 0, 1], 3000));
}

fn send_test() -> Result<String, warp::Rejection> {
  Err(warp::reject::not_found())
}

Is there something I am doing wrong?

Providing parameters to websocket closure

I'd like to use a path parameter from the update request inside the websocket handling code, but the current API doesn't provide an easy way to do that:

warp::path("example")
    .and(warp::path::param())
    .and(warp::ws(|websocket| {
        // cannot access path parameter easily here
    }))

in example routing.rs sum and times are *moved* under math

Me a noob: /sum/1/2 doesn't work, let's get off rust nightly and see if that's the issue, let's rebuild the app in debug and non debug

You the author: "math routes should be mounted to a different path"

Me: that implies a copy not a move otherwise why would the example imply the original two routes work...

Turns out they don't which is fine but I was about 1 click away to just abandoning everything until I tried it. I would suggest showing 'can be moved to a new route' separate from 'here are the 2 examples folks will try to see the neat pattern matching'

Show GitHub: An experimental router DSL for warp

I'm posting this as an issue because some people here may be interested in exploring an idea like this one. If you feel that this is not an appropriate place, please feel free to close the issue.

My intention is not change the code in the warp crate, but to discuss the idea of using a DSL to write the routes.

Context

As someone who used spray-routing (which was integrated in Akka HTTP a few years ago) for 5 years, it was a pleasant surprise to discover this project in the Reddit post.

I miss the routing DSL of Spray/Akka-HTTP, and when I saw the warp documentation I thought that it could be a perfect foundation to write something like that.

The warp_dsl crate

Repository

During the last couple of days I made a crate that implements a DSL to write routes. The code is a bit junky, there are a lot of missing features, and I'm pretty sure that there are some edge cases that will generate broken code, but at least it works well enough to experiment with it. It also works on Rust stable.

As an example, it converts the following code:

let routes = router!(
    path("blogs") {
        get & index {
            complete { "Get all blogs" }
        }

        path(u32) |blog_id| {
            get {
                complete { format!("Details for blog {}", blog_id) }
            }

            delete {
                complete { format!("Delete blog {}", blog_id) }
            }
        }
    }

    path("blog" / u32 / "posts") |blog_id| {
        post & index & (body::json()) |post: Post| {
            complete {
                format!("Publish post '{}' with body '{}' in blog {}", post.title, post.body, blog_id)
            }
        }

        get & path(u32) & index |post_id| {
            complete {
                format!("Read post {} in blog {}", post_id, blog_id)
            }
        }
    }
);

To this:

let routes = (((::warp::get((path!("blogs")).and(::warp::index()).map(
    || "Get all blogs",
)))).or(
    ((::warp::get((path!("blogs")).and(path!(u32)).map(|blog_id| {
        format!("Details for blog {}", blog_id)
    })))).or(
        (::warp::delete((path!("blogs")).and(path!(u32)).map(|blog_id| {
            format!("Delete blog {}", blog_id)
        }))),
    ),
)).or(
        ((::warp::post(
            (path!("blog" / u32 / "posts"))
                .and(::warp::index())
                .and(body::json())
                .map(|blog_id, post: Post| {
                    format!(
                        "Publish post '{}' with body '{}' in blog {}",
                        post.title,
                        post.body,
                        blog_id
                    )
                }),
        ))).or(
            (::warp::get(
                (path!("blog" / u32 / "posts"))
                    .and(path!(u32))
                    .and(::warp::index())
                    .map(|blog_id, post_id| {
                        format!("Read post {} in blog {}", post_id, blog_id)
                    }),
            )),
        ),
    );

This example is available in the repository of the crate, and can be tested with:

$ cargo run --example blog_api

Some implementation details

  • It relies on proc-macro-hack to use procedural macros in Rust stable.

  • The DSL is heavily inspired by Spray/Akka.

  • The rules to combine filters are simple:

    • Nested filters are combined with and.
    • Filters in the same block are combined with or.

    So, a declaration like:

    path("p") {
        foo { ... }
        bar { ... }
    }

    Emits the code:

    (
        path!("p").and(foo()).and(...)
    ).or(
        path!("p").and(bar()).and(...)
    )
  • The operator & can be used to combine filters when there is only one child (from Spray/Akka).

    A declaration like:

    a {
        b {
            c {
                complete { ... }
            }
        }
    }

    Can be written as:

    a & b & c {
        complete { ... }
    }
  • The handler of the request is defined with the complete directive (also from Spray/Akka).

  • Closure arguments are accumulated until the complete block is found.

    For example, a route like:

    path("x" / usize) |x| {
        (warp::header::<SocketAddr>("host")) |host| {
            complete {
                do_stuff(x, host)
            }
        }
    }   

    Will emit something like:

    path!("x" / usize)
        .and(warp::header::<SocketAddr>("host"))
        .map(|x, host| {
            do_stuff(x, host)
        });
  • HTTP methods are moved to the top of the filter, since the current functions in warp::filters::method expect a filter as an argument.

    The DSL is able to detect when a route contain multiple HTTP methods. A declaration like

    post {
        get {
            ...
        }
    }

    Produces an error during compilation.

Custom `with` filters

Under the current api, Filter::with requires that the type be W: Wrap however the Wrap trait is sealed making it difficult to write custom wrapping filters. For instance, I'm trying to write a filter that will forward a header from a request to the response. So far I've come up with the filter to generate the either grab the request id or generate a new one:

let req_id = warp::header::<String>("x-request-id").or_else(|_| Ok(("generated".to_owned(),)))

But I can't figure out using the combinators provided how to store the request-id in the filter and use it on the response.

It also seems that you cannot call map after returning a warp::reply in the combinator chain so I can't even make the above work with boiler plate something like

let my_route = req_id.map(warp::reply).map(|id: String, reply: warp::Reply| {
    reply.set_header("x-request-id", id)
})

Reply with a stream

Looking over the list of implementations for Reply, there doesn't seem to be a way to reply with a Stream.

I am basically trying to port the following code from using hyper to warp:

fn main () {
    let addr = ([127, 0, 0, 1], 8000).into();
    let server = Server::bind(&addr)
        .serve(|| hyper::service::service_fn(handler))
        .map(|r| {
            println!("Response: {:?}", r);
        }).map_err(|e| {
            println!("Error: {}", e);
        });

    hyper::rt::run(server);
}

type BoxedFuture = Box<Future<Item = Response<Body>, Error = hyper::Error> + Send>;

fn handler(_request: Request<Body>) -> BoxedFuture {
    let mut response = Response::new(Body::empty());

    *response.body_mut() =
        Body::wrap_stream(<something that implements Stream>);

    Box::new(future::ok(response))
}

`.map` reply with a Future.

If we were implementing something like a proxy (which might be a good example to add), you would want to issue a request that would return a Future. I'm not exactly sure which combinator to use with it. I thought it would be .and_then, but the example is geared towards validation and returning a CombineReject, and I believe that map doesn't accept a future.

Document returning filters from functions

I'm not really sure how much of this is an artifact of warp's design and how much is just my lack of experience, but in a small test project, I found that attempting to abstract parts of filter/server construction in separate functions seems to produce some complex issues. For instance, consider the following reduced example:

extern crate warp;

pub fn get_routes() -> impl warp::Filter {
    warp::path::index()
}

fn main() {
    warp::serve(routes);
}

This produces the following error:

error[E0277]: the trait bound `<impl warp::Filter as warp::filter::FilterBase>::Extract: warp::reply::sealed::ReplySealed` is not satisfied
 --> src/main.rs:8:5
  |
8 |     warp::serve(get_routes());
  |     ^^^^^^^^^^^ the trait `warp::reply::sealed::ReplySealed` is not implemented for `<impl warp::Filter as warp::filter::FilterBase>::Extract`
  |
  = note: required because of the requirements on the impl of `warp::Reply` for `<impl warp::Filter as warp::filter::FilterBase>::Extract`
  = note: required because of the requirements on the impl of `warp::server::IntoWarpService` for `impl warp::Filter`
  = note: required by `warp::serve`

I haven't found a good workaround for the error yet; just extending the signature of get_routes to return impl warp::Filter + Sync + Send + 'static isn't enough to satisfy the bounds on the impl of IntoWarpService for Filters in src/filter/service.rs.

Basically, it seems like there are so many trait bounds that trying to modularize a large application's route structure across multiple functions could get interesting fast. Most of the filter combinators (or, etc.) seem to include trait bounds that would cause similar issues to this one.

I don't really have a proposed solution (if this is even really a practical issue), but I guess it's something to think about…

Add Authorization header filters

I took a shot at adding support for Authorization header filters. I would like to know whether you like the approach I took: https://github.com/algermissen/warp/blob/add-auth-headers/src/filters/authorization.rs

If so, I'll work on making the code better and submit a PR after that - given I am relatively new to Rust, there is a lot of stuff that needs improvement (eg reduce String usage), so please do not judge that yet.

Some questions:

  • I think an addition to Rejections is needed to support the 401 challenge response, yes?
  • Since the 401 response includes parameters that must be settable by the code using warp, Rejections must somehow support additional information that can be set (eg the Basic auth filter would need to return a rejection with realm information):
    let text3 = warp::path("protected").
        and(warp::authorization::basic("myRealm"))
        .map(|b: warp::authorization::BasicCredentials| {
            format!("User: {} Passwd: {}", b.user,b.password)
        });
  • Or would you rather return an error response instead of a rejection since from the point of view of the HTTP authentication protocol, the thing to do is to return 401 + challenge. Allowing for further rejection handling does not really make sense.

warp::path requires &'static str

Hi,

I'm attempting to use this crate in a project where some of the paths to route aren't known until runtime, as they're acquired from the CLI via clap. Unfortunately, this appears to mean that I cannot use warp at all as warp::path requires p to be &'static str.

I'm wondering if warp will ever support the use case where it's simply given a &str to route.

Thanks.

Curl having difficulty with `body.rs` example

The Content-type seems suspicious, since we are returning warp::reply::json(&employee):

examples$ curl -v -X POST -d '{"name":"frodo","rate":33}' http://localhost:3030/employees/44
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 3030 (#0)
> POST /employees/44 HTTP/1.1
> Host: localhost:3030
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Length: 26
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 26 out of 26 bytes
< HTTP/1.1 415 Unsupported Media Type
< content-length: 0
< date: Sat, 04 Aug 2018 07:22:08 GMT

Edit: Never mind, being stoopid - forget to say -H "Content-Type: application/json" :)

Allow access to the Either type

The Filter::or combinator returns an Either<A, B> type, but it currently isn't exported publicly, making it impossible to match on the variants. It does implement Reply, making the full framework function, but it's currently impossible to use or to retrieve either A or B.

Some reasons why it hasn't been exposed yet:

  • It may be beneficial to change from Either<A, B> to instead more like Coprod<A, Coprod<B, Never>>. The benefit of doing this is that nesting Either<A, B> will result in the enums getting fatter and fatter, where as the Coprod approach makes unlimited nested ors still only need space for 1 variant tag (turns out I was wrong about Coprod, it also grows the size).

Custom Failure / error handling

Hey,
Really like what you did !

Say I want to define very rich, specific errors, I usually do this with failure.
I'd like to be able to return my own custom failures in the and_then combinator, and then either simply log them or present a detailed error page.

Do you currently have a way to do this ? Is it a goal at all ?
Maybe I just have the wrong approach, what would you recommend to deal with this ?

Clarify use of futures in filter chains

I am working on case, where one can look up an item from a database. The get_item(id) function returns a future because the lookup is async, of course.

I had some substantial trouble figuring out how to work with the future inside the and_then() combinator, mostly due to the lack of impls of Rejection for common error (and other types).

This is where I ended up:

let get_item = path!("item" / i32 ).and_then(|id| {
        repository::get_item(id).map(|item:String| {
            http::Response::builder().status(http::StatusCode::OK)
                .body(format!("This is item {}", item)).unwrap()
        }).map_err(|_| warp::reject())
    });

Note the use of map_err(|_| warp::reject()) to have an error-type that the filter chain can work with.

So, even if I return HTTP responses for success and error cases (and explicitly not a rejection), I need to convert to Rejection so that the types match.

Two solutions would have helped me (I think):

  • Make warp::never::Never public, so that I can err_map to that
  • provide CombineRejection impls for () as is done for Never so that I can err_map to ()

Am I making sense? Did I miss something? Or is it just due to missing examples?

I am also curious whether you plan to add another combinator like and_then but which allows me to supply an explicit reactor to run the supplied future on. (eg see https://docs.rs/futures/0.2.1/futures/trait.FutureExt.html#method.with_executor )

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.