I'm trying add HTTPS enforcement to my Warp-based web app on GKE.
The GKE platform is mostly irrelevant; the cromulent detail is that the load balancer terminates SSL/TLS connections, so the “real” scheme is provided in the X-Forwarded-Proto header. The literal scheme parsed by Warp will always be HTTP.
The logic goes as follows:
If the scheme is HTTPS, process requests normally.
If the scheme is HTTP, send a 301 redirect to the equivalent HTTPS URL.
If the scheme is anything else, send a 421 (misdirected request) error.
If the X-Forwarded-Proto header is missing (or any other realistically impossible scenario occurs), send a 400 (bad request) error.
The error responses have no body content in this example, and all HTTPS requests should respond with the text Hello, world!.
The problem:
error[E0277]: the trait bound `std::result::Result<(), warp::reject::Rejection>: core::future::future::Future` is not satisfied
--> src/main.rs:23:10
|
23 | .and_then(|scheme_header: Option<String>, host: String, path: FullPath| {
| ^^^^^^^^ the trait `core::future::future::Future` is not implemented for `std::result::Result<(), warp::reject::Rejection>`
|
= note: required because of the requirements on the impl of `futures_core::future::TryFuture` for `std::result::Result<(), warp::reject::Rejection>`
error[E0599]: no method named `and` found for type `warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, impl warp::filter::Filter+std::marker::Copy>, impl warp::filter::Filter+std::marker::Copy>, [closure#src/main.rs:23:19: 43:10]>` in the current scope
--> src/main.rs:44:10
|
44 | .and(filter)
| ^^^ method not found in `warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, impl warp::filter::Filter+std::marker::Copy>, impl warp::filter::Filter+std::marker::Copy>, [closure#src/main.rs:23:19: 43:10]>`
|
= note: the method `and` exists but the following trait bounds were not satisfied:
`&mut warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, impl warp::filter::Filter+std::marker::Copy>, impl warp::filter::Filter+std::marker::Copy>, [closure#src/main.rs:23:19: 43:10]> : warp::filter::Filter`
`&warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, impl warp::filter::Filter+std::marker::Copy>, impl warp::filter::Filter+std::marker::Copy>, [closure#src/main.rs:23:19: 43:10]> : warp::filter::Filter`
`warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, impl warp::filter::Filter+std::marker::Copy>, impl warp::filter::Filter+std::marker::Copy>, [closure#src/main.rs:23:19: 43:10]> : warp::filter::Filter`
Clearly I’m missing something obvious here, so I’m hoping someone can nudge me in the right direction!
use futures::{FutureExt, StreamExt};
use warp::{Filter, Rejection};
use warp::filters::path::{FullPath};
use warp::http::{StatusCode, Uri};
use warp::http::uri::{Parts, Scheme};
use warp::reply::Reply;
enum SchemeError {
InsecureScheme(Uri),
UnknownScheme,
MissingScheme,
}
impl warp::reject::Reject for SchemeError {}
async fn requires_https(filter: impl Filter<Extract = (Scheme,), Error = Rejection> + Copy) -> impl Filter<Extract = (), Error = Rejection> + Copy {
warp::header::optional("X-Forwarded-Proto")
.and(warp::header("Host"))
.and(warp::path::full())
.and_then(|scheme_header: Option<String>, host: String, path: FullPath| {
if let Some(scheme) = scheme_header {
match scheme.to_ascii_lowercase().as_str() {
"https" => Ok(()),
"http" => {
let mut uri_parts = Parts::default();
uri_parts.scheme = Some(Scheme::HTTPS);
uri_parts.authority = Some(host.parse().unwrap());
uri_parts.path_and_query = Some(path.as_str().parse().unwrap());
let uri_parts = uri_parts;
let new_uri = Uri::from_parts(uri_parts).unwrap();
println!("Redirecting to secure URL: {}", new_uri);
Err(warp::reject::custom(SchemeError::InsecureScheme(new_uri)))
},
_ => Err(warp::reject::custom(SchemeError::UnknownScheme)),
}
} else {
Err(warp::reject::custom(SchemeError::MissingScheme))
}
})
.and(filter)
.recover(|err: Rejection| {
if let Some(scheme_error) = err.find::<SchemeError>() {
match scheme_error {
SchemeError::InsecureScheme(new_uri) => Ok(warp::redirect(new_uri)),
SchemeError::UnknownScheme => Ok(StatusCode::MISDIRECTED_REQUEST),
SchemeError::MissingScheme => Ok(StatusCode::BAD_REQUEST),
}
} else {
Err(err)
}
})
}
#[tokio::main]
async fn main() {
let routes = requires_https(warp::any().map(|| "Hello, world!"));
warp::serve(routes)
.run(([0, 0, 0, 0], 8080))
.await;
}
I am rust newbie but ran into similar compiler error
My issue was looking at warp 0.1 docs, whilst using warp 0.2
https://docs.rs/warp/0.2.0/warp/trait.Filter.html#example-3
I needed to put async move after the closure pipes in and_then
If it's not that, could be similar to
understanding error: trait `futures::future::Future` is not implemented for `()`
where std::result::Result<(), warp::reject::Rejection> indicates you are returning a unit type as left-result, which might not have future implemented for it.
Related
I develop an authentication middleware for actix_web. I can send an OK response just fine but I cannot send an error message. Here's what I've done so far using the example given here:
/// ResponseBody is my own custom struct to send an error/content body
#[derive(Debug, Serialize, Deserialize)]
pub struct ResponseBody<T> {
pub message: String,
pub data: T,
}
impl<T> ResponseBody<T> {
pub fn new(message: &str, data: T) -> ResponseBody<T> {
ResponseBody {
message: message.to_string(),
data,
}
}
}
/// ....somewhere in my middleware
/// if user passes then I will send him to the destination
/// this code was copied from https://actix.rs/docs/middleware/ example
if authenticate_pass {
let fut = self.service.call(req);
Box::pin(async move {
let res = fut.await?;
Ok(res)
})
/// but if he fails to signin or for some other reasons
/// I want to send an error response UNAUTHORIZED (401)
} else {
Box::pin(async move {
Ok(req.into_response(
HttpResponse::Unauthorized()
.json(ResponseBody::new("Session ends. Please login again", ""))
.into_body(),
))
})
}
It displays an error message that I should implement a conversion from actix_web::dev::Response into BoxBody. I don't have any idea what BoxBody is and how to implement them.
error[E0277]: the trait bound `actix_web::dev::Response<_>: std::convert::From<BoxBody>` is not satisfied
--> src\auth.rs:103:21
|
102 | Ok(req.into_response(
| ------------- required by a bound introduced by this call
103 | / HttpResponse::Unauthorized()
104 | | .json(ResponseBody::new("Session ends. Please login again", ""))
105 | | .into_body(),
| |____________________________________^ the trait `std::convert::From<BoxBody>` is not implemented for `actix_web::dev::Response<_>`
|
= help: the following other types implement trait `std::convert::From<T>`:
<actix_web::dev::Response<&'static [u8]> as std::convert::From<&'static [u8]>>
<actix_web::dev::Response<&'static str> as std::convert::From<&'static str>>
<actix_web::dev::Response<B> as std::convert::From<HttpResponse<B>>>
<actix_web::dev::Response<B> as std::convert::From<ServiceResponse<B>>>
<actix_web::dev::Response<BoxBody> as std::convert::From<&actix_http::ws::HandshakeError>>
<actix_web::dev::Response<BoxBody> as std::convert::From<HttpResponseBuilder>>
<actix_web::dev::Response<BoxBody> as std::convert::From<Infallible>>
<actix_web::dev::Response<BoxBody> as std::convert::From<Result<I, E>>>
and 13 others
= note: required because of the requirements on the impl of `Into<actix_web::dev::Response<_>>` for `BoxBody`
note: required by a bound in `ServiceRequest::into_response`
--> C:\Users\mdenn\.cargo\registry\src\github.com-1ecc6299db9ec823\actix-web-4.1.0\src\service.rs:144:32
|
144 | pub fn into_response<B, R: Into<Response<B>>>(self, res: R) -> ServiceResponse<B> {
| ^^^^^^^^^^^^^^^^^ required by this bound in `ServiceRequest::into_response`
If anyone have experience with actix_web middleware, please help. Thanks in advance.
FULL CODE: https://pastebin.com/9PtvSyS9
actix-web = "4.1.0"
futures = "0.3.24"
tokio = { version = "1.18.2", features = ["full"] }
serde = { version = "1.0.137", features = ["derive"] }
If you just want to return the UNAUTHORIZED(401) response, you can try the following code.
Ok(req.into_response(
HttpResponse::Unauthorized()
.finish().map_into_boxed_body(),
))
BoxedBody is defined as follows.
/// A boxed message body with boxed errors.
#[derive(Debug)]
pub struct BoxBody(BoxBodyInner);
The reason of using BoxedBody is because of req.into_response needs that type.
I'm getting blocked on what I think it's a simple problem. I'm still learning Rust, and I want to do the following:
I want to create an async trait (using async-trait) that will instantiate a DB connection instance and it will return the struct that is implementing that trait.
mongo.rs
#[async_trait]
pub trait DB {
async fn init<T, E>() -> Result<T, E>;
}
Then: favorites.rs (See the implementation of the DB trait down below)
use async_trait::async_trait;
use mongodb::Collection;
use rocket::form::FromForm;
use rocket::serde::ser::StdError;
use serde::{Deserialize, Serialize};
use std::error::Error;
use uuid::Uuid;
pub struct FavoritesDB {
collection: Collection<Favorite>,
}
#[derive(Debug)]
pub enum FavoritesError {
UnknownError(Box<dyn Error>),
}
// Conflicts with the one down below
// impl From<Box<dyn Error>> for FavoritesError {
// fn from(err: Box<dyn Error>) -> FavoritesError {
// FavoritesError::UnknownError(err)
// }
// }
impl From<Box<dyn StdError>> for FavoritesError {
fn from(err: Box<dyn StdError>) -> FavoritesError {
FavoritesError::UnknownError(err)
}
}
#[async_trait]
impl mongo::DB for FavoritesDB {
async fn init<FavoritesDB, FavoritesError>() -> Result<FavoritesDB, FavoritesError> {
let main_db = mongo::init::<Favorite>("Favorites").await?;
let db = FavoritesDB {
collection: main_db.collection,
};
Ok(db)
}
}
There are a list of problems with this:
1)
error[E0574]: expected struct, variant or union type, found type parameter `FavoritesDB`
--> src\db\favorites.rs:41:18
|
41 | let db = FavoritesDB {
| ^^^^^^^^^^^ not a struct, variant or union type
|
help: consider importing this struct instead
I've tried implementing From<Box<dyn tdError>> manually but it conflicts with what I have.
error[E0277]: `?` couldn't convert the error to `FavoritesError`
--> src\db\favorites.rs:40:65
|
40 | let main_db = mongo::init::<Favorite>("Favorites").await?;
| ^ the trait `From<Box<dyn StdError>>` is not implemented for `FavoritesError`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, Box<dyn StdError>>>` for `Result<FavoritesDB, FavoritesError>`
note: required by `from_residual`
--> C:\Users\asili\.rustup\toolchains\nightly-2021-11-15-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\ops\try_trait.rs:339:5
|
339 | fn from_residual(residual: R) -> Self;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider further restricting this bound
|
39 | async fn init<FavoritesDB, FavoritesError + std::convert::From<std::boxed::Box<dyn std::error::Error>>>() -> Result<FavoritesDB, FavoritesError> {
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Some errors have detailed explanations: E0277, E0282, E0574.
For more information about an error, try `rustc --explain E0277`.
Just for more context, here's the DB struct and impl (Currently connecting to a local MongoDB) included in mongo.rs
pub struct Database<T> {
client: mongodb::Database,
pub collection: Collection<T>,
}
impl<T> Database<T> {
pub async fn init() -> Result<mongodb::Database, Box<dyn Error>> {
let mut client_options = ClientOptions::parse("mongodb://localhost:27017").await?;
client_options.app_name = Some("My App".to_string());
// Get a handle to the deployment.
let client = Client::with_options(client_options)?;
let db = client.database("rust-svelte");
return Ok(db);
}
}
pub async fn init<T>(collection: &str) -> Result<Database<T>, Box<dyn Error>> {
let client = Database::<T>::init().await?;
let collection = client.collection::<T>(collection);
let db = Database { client, collection };
Ok(db)
}
I've been searching for a few days over SO and the Rust community and my Google-Rust-Fu isn't good enough to spot what's the problem. Any ideas?
You've declared init to take 2 generic parameters: T and E.
This means that the code that calls init has to provide the concrete types to fill in those parameters. For example, if someone was using your library, it would be totally feasible for them to write init::<i64, ()>(), and your code should deal with that.
Because of that, when you define your impl DB for FavouritesDB, you write this:
async fn init<FavoritesDB, FavoritesError>() -> Result<FavoritesDB, FavoritesError>
This is no different to writing:
async fn init<T, E>() -> Result<T, E>
you've just given the type parameters different names that happen to match a struct that you're probably trying to use.
A better pattern might be an associated type. Instead of the caller deciding what the concrete types are (as is the case with generics), with associated types, the implementation of the trait on the type sets the type.
This is common with things like Iterator. Iterator has no generic parameters, but a single associated type Item. This is because it wouldn't make sense to be able to impl Iterator<String> for MyStruct and impl Iterator<i64> for MyStruct at the same time. Instead, we want to implement Iterator for a type once, and that implementation carries with it the definition of the types it expects.
So something like this (I've omitted the async-ness for brevity since it doesn't seem to be a factor here):
trait DB {
type InitOk;
type InitErr;
fn init() -> Result<Self::InitOk, Self::InitErr>;
}
impl Db for FavouritesDB {
type InitOk = FavouritesDB;
type InitErr = FavouritesError;
fn init() -> Result<Self::InitOk, Self::InitErr> {
// now you can reference FavouritesDB the struct, rather than the generic parameter
}
}
I'd also add you may want to not have the InitOk type, and just return Self, but that's up to you if you think you might want a struct to be able to create a different type.
For part 2, Rust assumes nothing (other than Sized) about generic parameters. If you want Rust to force a generic to have some property, you have to add a bound.
The compiler is telling you here that it can't use the ? operator to convert automatically, because it doesn't know that your error type has a From<Box<dyn Error>> implementation.
If you know that every error type is going to implement that, you can add it as a bound on the associated type, like this:
trait DB {
type InitOk;
type InitErr: From<Box<dyn Error>>;
// ...
}
I have a HTTP api that uses pagination, and I want to wrap it into a general Rust stream so that the same interface can be used for all endpoints, and so that I can use the trait functions that come with the Stream trait.
I'm getting this error that I do not quite understand:
error[E0599]: no method named `try_collect` found for opaque type `impl futures::Future` in the current scope
--> src/lib.rs:316:69
|
316 | let result: Result<Vec<Vec<NotificationEvent>>, _> = stream.try_collect().await;
| ^^^^^^^^^^^ method not found in `impl futures::Future`
|
= note: the method `try_collect` exists but the following trait bounds were not satisfied:
`impl futures::Future: futures::TryStream`
which is required by `impl futures::Future: futures::TryStreamExt`
`&impl futures::Future: futures::TryStream`
which is required by `&impl futures::Future: futures::TryStreamExt`
`&mut impl futures::Future: futures::TryStream`
which is required by `&mut impl futures::Future: futures::TryStreamExt`
This is my implementation:
pub async fn v1_events_get_st(
&self,
params: &Option<GetEvents>,
) -> impl TryStream<Item = Result<Vec<NotificationEvent>, UniErr>> + '_ {
let x = stream::try_unfold((true, (*params).clone()), move |state: (bool, Option<GetEvents>)| async move {
let (remaining, mut thisParams) = state;
if !remaining {
return Ok(None);
}
return match self.v1_events_get(&thisParams).await {
Ok(res) => {
if let Some(ref mut p) = thisParams {
if let Some((_, last)) = res.get_from_to() {
p.set_after(last);
}
}
Ok(Some((res.data, (res.remaining, thisParams))))
},
Err(err) => Err(err),
}
});
return x;
}
let stream = c.v1_events_get_st(&p);
let result: Result<Vec<Vec<NotificationEvent>>, _> = stream.try_collect().await;
These are the bounds on try_collect:
fn try_collect<C: Default + Extend<Self::Ok>>(self) -> TryCollect<Self, C>
where
Self: Sized,
{
assert_future::<Result<C, Self::Error>, _>(TryCollect::new(self))
}
Why would I need to implement Default and Extend?
I cannot easily implement Default as it cannot be derived with structs with enums, I have a lot of enums.
And as for extend, wouldn't the Vec already have this?
Question: Is there a way to get a more detailed error on the exact trait bounds that are not satisfied?
Question: Is there a better way to wrap this into a Stream or other common interface? I'm starting to think it may be better just to use a for loop and not implement the Stream trait.
Thanks.
When you use async fn, it automatically wraps the return value in a future, hence the error about impl futures::Future. Just remove the async keyword and it should work.
I'm working on some code whose structure is similar to that of "middlewares" in web frameworks, so I'll use that terminology in this minimal example. The idea of a middleware function is that it wraps the actual response handler, so it can modify the request before it goes to the handler, and modify the response after it comes back:
struct Request;
struct Response;
trait Handler {
fn handle(&self, request: Request) -> Response;
}
trait Middleware {
fn handle(&self, request: Request, next: Box<dyn FnOnce(Request) -> Response>) -> Response;
}
I considered having two separate functions, one to preprocess the request and one to postprocess the response, but then I wouldn't be able to store any additional state during the request without resorting to hacks.
The server contains dynamically configured handlers and middlewares, so it has to use some boxed trait objects:
struct Server {
handler: Box<dyn Handler>,
middlewares: Vec<Box<dyn Middleware>>,
}
Now, how to implement the response handling? Here are my two attempts:
impl Server {
// First attempt: using Iterator::fold()
fn handle_request_fold<'a>(&'a self, request: Request) -> Response {
let handler_with_middlewares = self.middlewares.iter()
.rev()
.fold::<Box<dyn FnOnce(Request) -> Response + 'a>, _>(
Box::new(|request| self.handler.handle(request)),
|next, middleware| {
Box::new(|request| middleware.handle(request, next))
}
);
handler_with_middlewares(request)
}
// Second attempt: using recursion
fn handle_request_recurse(&self, request: Request) -> Response {
self.handle_request_from(request, 0)
}
fn handle_request_from<'a>(&'a self, request: Request, index: usize) -> Response {
if index >= self.middlewares.len() {
self.handler.handle(request)
} else {
let next = Box::new(
|r: Request| self.handle_request_from(r, index + 1));
self.middlewares[index].handle(request, next)
}
}
}
Both attempts give the same error, which suggests that I'm doing something fundamentally wrong here:
error[E0759]: `self` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
--> src/lib.rs:25:67
|
19 | fn handle_request_fold<'a>(&'a self, request: Request) -> Response {
| -------- this data with lifetime `'a`...
...
25 | Box::new(|request| middleware.handle(request, next))
| ^^^^ ...is captured and required to live as long as `'static` here
I know that trait objects have an implicit 'static lifetime, so I tried adding explicit lifetimes as you can see, but it didn't help. I don't understand why the compiler would demand a 'static lifetime anywhere here; nothing (including the closures) can escape the handle_request_* functions, right?
Playground link
And just after posting, I figured it out: the boxed trait object passed to the Middleware needs a non-'static lifetime as well.
trait Middleware {
fn handle<'a>(&self, request: Request,
next: Box<dyn FnOnce(Request) -> Response + 'a>)
-> Response;
}
fn handle_request_from(&self, request: Request, index: usize) -> Response {
if index >= self.middlewares.len() {
self.handler.handle(request)
} else {
let next = Box::new(
move |request| self.handle_request_from(request, index + 1));
self.middlewares[index].handle(request, next)
}
}
I have code that used to work on nightly-2016-11-15. When I upgraded to 1.15.1 stable, I started getting a bunch of errors about type implementations not being found. Here's an example:
error[E0277]: the trait bound `errors::Error: core::convert::From<r2d2_postgres::<unnamed>::error::Error>` is not satisfied
--> src/pg/datastore.rs:79:23
|
79 | let results = conn.query("DELETE FROM accounts WHERE id=$1 RETURNING 1", &[&account_id])?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `core::convert::From<r2d2_postgres::<unnamed>::error::Error>` is not implemented for `errors::Error`
|
= help: the following implementations were found:
= help: <errors::Error as core::convert::From<r2d2_postgres::Error>>
= help: <errors::Error as core::convert::From<postgres::error::Error>>
= help: <errors::Error as core::convert::From<r2d2::GetTimeout>>
= help: <errors::Error as core::convert::From<rocksdb::Error>>
= help: and 3 others
= note: required by `core::convert::From::from`
... even though there is a relevant From implementation. Here's a shortened version:
use std::error::Error as StdError;
use r2d2::GetTimeout;
use postgres::error::Error as PostgresError;
use r2d2_postgres::Error as R2D2PostgresError;
use super::fmt;
#[derive(Eq, PartialEq, Clone, Debug)]
pub enum Error {
Unexpected(String),
...
}
impl StdError for Error {
fn description(&self) -> &str {
...
}
fn cause(&self) -> Option<&StdError> {
None
}
}
impl From<R2D2PostgresError> for Error {
fn from(err: R2D2PostgresError) -> Error {
Error::Unexpected(format!("{}", err))
}
}
impl From<PostgresError> for Error {
fn from(err: PostgresError) -> Error {
Error::Unexpected(pg_error_to_description(err))
}
}
impl From<GetTimeout> for Error {
fn from(err: GetTimeout) -> Error {
Error::Unexpected(format!("Could not fetch connection: {}", err))
}
}
I think this has something to do with the use of associated types, as it doesn't appear to happen in other contexts. Additionally, the namespace r2d2_postgres::<unnamed>::error::Error doesn't make sense - what is <unnamed>? Here is the relevant type association.
This turned out to be due to version conflicts. I had switched to postgres's master branch to fix a separate version conflict, but r2d2_postgres was referencing a different version of postgres.
Luckily, as explained in this issue, Cargo.toml has a [replace] section that allows you to handle it like this:
[replace]
"postgres:0.13.6" = { git = "https://github.com/sfackler/rust-postgres" }