Closed ebkalderon closed 5 years ago
This is a very similar request to #82. My current thought is to punt this until Tower Web is merged with Warp. I believe that warp's filter API would be the best way to add HTTP functionality to an entire resource.
Thoughts?
I've actually managed to mostly get the effect I wanted by writing some middleware which validates the headers of each incoming request, using the existing CorsMiddleware
as a reference. But I'm also curious to hear how Warp's filter API might fare as an alternative?
Also, quick question also in regards to easy proxying with tower-web
: I saw the suggestion in #105 which implements Extract
for http::Request<()>
. Is there an easy way to implement Extract
for Request
such that it can use extract_body()
to also retrieve the body? It would be nice to facilitate easy redirection by receiving a complete request, manipulating its headers and URI, and relaying it directly to its destination. Perhaps it might look something like this:
#[derive(Debug)]
struct SimpleProxy {
target: Uri,
}
impl_web! {
impl SimpleProxy {
#[get("/*path")]
fn proxy_get(&self, mut request: Request<Body>) -> impl Future<Item = Response<Body>> {
let new_uri = format!("{}{}", self.target, request.uri().path());
*request.uri_mut() = new_uri.parse().unwrap();
*request.headers_mut().insert(HOST, self.target.host().unwrap());
hyper::Client::new().request(request)
}
}
}
I don't think that there will be a way to get Request<Body>
, but there will be a way to get the raw body. I need to work on that still though.
I see what you mean. Currently, I'm working around it by creating the following newtype:
#[derive(Debug)]
pub struct Request(pub http::Request<()>);
impl<B: BufStream> Extract<B> for Request {
type Future = Immediate<Self>;
fn extract(ctx: &Context) -> Self::Future {
let request = http::Request::builder()
.method(ctx.request().method())
.version(ctx.request().version())
.uri(ctx.request().uri())
.body(())
.map_err(|e| ExtractError::invalid_argument(&e))
.map(|mut request| {
request
.headers_mut()
.extend(ctx.request().headers().clone());
Request(request)
});
Immediate::result(request)
}
}
Then I use it like so in my application:
#[derive(Debug)]
struct SimpleProxy {
target: Uri,
}
impl_web! {
impl SimpleProxy {
#[get("/*path")]
fn proxy_get(&self, mut request: Request, body: Vec<u8>) -> impl Future<Item = Response<Body>> {
let Request(mut request) = request;
let new_uri = format!("{}{}", self.target, request.uri().path());
*request.uri_mut() = new_uri.parse().unwrap();
*request.headers_mut().insert(HOST, self.target.host().unwrap());
let request = request.map(body);
hyper::Client::new().request(request)
}
}
}
Not sure if there is an easier way to accommodate this in tower-web
, though.
tower-web can provide an implementation of Extract
for Request<()>
.
Awesome! That would be most helpful.
@ebkalderon Do you want to try providing a PR for it? The steps are roughly:
extract/http.rs
impl<B: BufStream> Extract<B> for http::Request<()> { ... }
For Future, use Immediate<http::Request<()>
Then, the implementation is Immediate::ok(ctx.request().clone())
I think.
A test or two would be nice 👍
@carllerche I deeply apologize for the lack of response; this slipped through the cracks somehow. I can prepare a pull request for this now, if you're still interested!
Okay, I've just created #208, and it is ready for review.
By the way, ctx.request().clone()
is unfortunately not possible because http::Request
doesn't implement nor derive Clone
. This was the reason why my Extract
implementation shown above in this thread manually duplicates the http::Request
with a builder (omitting the body and extensions, which cannot be cloned either).
I am exploring
tower-web
by writing a simple HTTP proxy. In this application, I would like to perform some header validation on each request, forward it to a pre-defined endpoint, and then forward the received response back to the original client. I am using ahyper::Client
in each HTTP method handler to connect to the endpoint, but I am finding that I am duplicating the header validation code across each handler, which seems rather brittle to me. Is there a clean way to wrap a given typeT
whereT
implementsIntoResource
such that I can enforce this header validation for all incoming requests on all handlers in one place?