New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relation to tower-web #58

Open
alexreg opened this Issue Aug 14, 2018 · 6 comments

Comments

3 participants
@alexreg

alexreg commented Aug 14, 2018

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?

@seanmonstar

This comment has been minimized.

Owner

seanmonstar commented Aug 14, 2018

We've been talking about how to reduce duplicate efforts, and find pieces that can be combined. It's possible the projects (tower-web and warp) merge at some point, exposing both APIs as "flavors". If you know the Scala world at all, here is how I see the projects:

  • tower is like finagle
  • warp is like finch
  • tower-web is like finatra

So, similar to how in finch, an Endpoint can be converted into a Service, I think a warp::Filter can be converted into a tower::Service (see #56).

does warp support async workflows like Gotham or such?

Yep, the README and the docs both point out that warp is built on top of hyper and thus supports asynchronous request handling.

@alexreg

This comment has been minimized.

alexreg commented Aug 14, 2018

Thanks for the clarification. Unfortunately I don't know Scala, so those analogies don't help much... I'm still left unsure of whether one framework is better for certain use cases.

Yep, the README and the docs both point out that warp is built on top of hyper and thus supports asynchronous request handling.

I just looked at the docs closer, and I think I see how you do it now... I wanted to check, because some web frameworks use hyper internally, but don't expose any async stuff really (even if they use it internally?). But I'm glad!

@seanmonstar

This comment has been minimized.

Owner

seanmonstar commented Aug 14, 2018

one framework is better for certain use cases.

The goal is that functionality is available in both, and the choice comes down to personal taste of how you prefer structure web apps.

@dmexe

This comment has been minimized.

Contributor

dmexe commented Aug 27, 2018

We've been talking about how to reduce duplicate efforts, and find pieces that can be combined. It's possible the projects (tower-web and warp) merge at some point, exposing both APIs as "flavors". If you know the Scala world at all, here is how I see the projects:

The current state of these projects is going to the wrong direction (as I think).

  1. https://github.com/carllerche/tower-web/tree/master/src/middleware
  2. #73 (Cors)
  3. #46 (Content compression)

Both warp and tower-web are starting to develop their web frameworks, which breaks the main finagle rule - "all components just a small functions which implement a simple contract Request -> Future". At this moment, warp has Filter abstraction and tower-web has a Middleware abstraction, which are going to duplicate the same functionality.

So both warp and tower-web should implement only routing solution and content negotiation, all other things such as CorsFilter, ServerStatsFilter, RequestLogFilter, etc have to be in tower-http services.

Here an example which that could be (using LiftService)

use tower_http::{Cors, GzipCompression,  RequestLog, HttpStats, HttpTracing};

let endpoint = warp::any().map(|| "ok");
let new_service = endpoint.lift()
  .and_then(GzipCompression::new)
  .and_then(Cors::new)
  .and_then(RequestLog::new)
  .and_then(HttpStats::new)
  .and_then(HttpTracing::new);

server:run(new_service);
@seanmonstar

This comment has been minimized.

Owner

seanmonstar commented Aug 27, 2018

@dmexe I agree with the feeling, there are some things that we could be duplicating effort, and we do want to reduce that. I think that providing the ability to plug in any Service with warp is a very useful step. It might make sense for there to be "functional" constructors of these things in warp, that basically just use the Service, I'm unsure. It also may be useful to add functionality in warp before such a Service exists.

@dmexe

This comment has been minimized.

Contributor

dmexe commented Sep 19, 2018

A way to integrate with tower-web

At the current time, the "warp" duplicates many things in the tower-web. The most important of them middlewares. Thus the warp's filter abstraction should be revisited.

In the tower-web, the resource uses for matching requests and returning responses. Thus the warp's filter should be another kind of resource, and the warp itself should be "combinators for constructing resources for tower-web" not a yet another web framework.

Required changes at warp:

  1. The filter should implement a trait "IntoResource".
  2. The filter should use response and content negotiation from the tower-web instead current "::Response"
  3. The filter should use an error implementation from tower-web (currently it's not possible).

Required changes at tower-web (later)

  1. The "Error", currently it has only three error kinds and nothing else. It would be better to add "status" and "cause".

At finally it would allow writing code like this:

impl_web! {
    impl HelloWorld {
        #[get("/tower-web")]
        fn hello_world(&self) -> Result<HelloResponse, ()> {
            Ok(HelloResponse)
        }
    }
}

fn warp_routes() -> impl Filter<...> {
   warp::get().and(warp::path("warp")).map(|| "hello world")
}

fn main() {
  ServiceBuilder::new()
        .resource(HelloWorld)
        .resource(warp_routes())
        .run(&addr)
        .unwrap(); 
}

Another good change, which not related to the tower-web, but helps keep the warp small and simple as possible. Switch to use "hlist" implementation from the "frunk" crate. It already has necessary structures and traits for working with heterogeneous lists.

Proposed traits:

/// Filter conversion into tower-web resource.
trait IntoResource {
   type Output: Resource;
   fn into_resource(self) -> Self::Output;
}

/// Rejection conversion into tower-web error.
impl Into<Error> for Rejection {...};

/// The filter which uses frunk's hlists for Input and Output. 
trait Filter<INPUT: Hlist> {
   type Extract: Hlist;
   type Output; // usually would be <INPUT as Add<Self::Extract>>::Output;
   type Future: Future<Item = Self::Output, Error = Rejection>;

   fn call(&self) -> Self::Future;
} 

To: @seanmonstar

This was referenced Sep 28, 2018

@seanmonstar seanmonstar added this to the 0.2 milestone Oct 3, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment