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.
Related
I am using rust rocket rocket = { version = "=0.5.0-rc.2", features = ["json"] } as my web server, this is the code looks like:
/// # search the template list
///
/// return different type of template
#[openapi(tag = "template")]
#[get("/v1/list?<query..>")]
pub fn list(query: TemplateRequest) -> content::RawJson<String> {
let contents = get_template_list(query.template_type, query.name);
return box_rest_response(contents);
}
this code works fine. Now I facing a problem is that the rust rocket return raw json, the client side could not know the return content structure. it only show a json string in swagger:
the client did not know what the response content is. I have read the the rust rocket official document still did not figure out what should I do to return the entity structure. I have tried like this:
pub fn list(query: TemplateRequest) -> Result<Vec<TemplateResponse>, String> {
let contents = get_template_list(query.template_type, query.name);
return Ok(contents);
}
the compiler shows:
error[E0277]: the trait bound `std::vec::Vec<TemplateResponse>: Responder<'_, '_>` is not satisfied
--> src/biz/template/bill_book_template_controller.rs:28:1
|
28 | #[openapi(tag = "账本模版")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Responder<'_, '_>` is not implemented for `std::vec::Vec<TemplateResponse>`
|
= help: the following implementations were found:
<std::vec::Vec<u8> as Responder<'r, 'static>>
= note: required because of the requirements on the impl of `Responder<'_, '_>` for `Result<std::vec::Vec<TemplateResponse>, std::string::String>`
note: required by a bound in `rocket_okapi::response::OpenApiResponder::responses`
--> /Users/xiaoqiangjiang/.cargo/git/checkouts/okapi-727e9b4b9c217cbb/65244f0/rocket-okapi/src/response/mod.rs:10:41
|
10 | pub trait OpenApiResponder<'a, 'r: 'a>: rocket::response::Responder<'a, 'r> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `rocket_okapi::response::OpenApiResponder::responses`
= note: this error originates in the attribute macro `openapi` (in Nightly builds, run with -Z macro-backtrace for more info)
I have tried this way:
pub fn list(query: TemplateRequest) -> Result<content::RawJson<String>, String> {
let contents = get_template_list(query.template_type, query.name);
return Ok(box_rest_response(contents));
}
it could successfully compile but still could not show the response structure in swagger.
I'm a beginner at Rust and I've been following this tutorial on creating a simple blockchain using Rust.
chain.rs
use byteorder::{BigEndian, ReadBytesExt};
use chrono::offset::Utc;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::io::Cursor;
// Represents the entire chain in the network
pub struct Chain {
// Actual chain
pub blocks: Vec<Block>,
}
// Represents a single block in the blockchain
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Block {
pub id: u64,
// Hash representing block
pub hash: String,
// Hash of the previous block
pub previous_hash: String,
// Time of creation in UTC
pub timestamp: i64,
// Data contained in the block
pub data: String,
// Value for hashing the block(PoW)
pub nonce: u64,
}
p2p.rs
use std::collections::HashSet;
use super::chain::{Block, Chain};
use libp2p::{
floodsub::{Floodsub, FloodsubEvent, Topic},
identity::Keypair,
mdns::{Mdns, MdnsEvent},
swarm::{NetworkBehaviourEventProcess, Swarm},
NetworkBehaviour, PeerId,
};
use log::{error, info};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use serde_json;
use tokio::sync::mpsc;
// Topics for pub/sub protocol
// Impairements: Broadcasts on each request thus extremely inefficient
pub static BLOCK_TOPIC: Lazy<Topic> = Lazy::new(|| Topic::new("blocks"));
pub static CHAIN_TOPIC: Lazy<Topic> = Lazy::new(|| Topic::new("chains"));
// Key Pair for peer identification on network
pub static KEYS: Lazy<Keypair> = Lazy::new(Keypair::generate_ed25519);
// Peer id for peer identification on network
pub static PEER_ID: Lazy<PeerId> = Lazy::new(|| PeerId::from(KEYS.public()));
...
// Defines overall NetworkBehaviour for the chain
#[derive(NetworkBehaviour)]
pub struct ChainBehaviour {
// Chain
#[behaviour(ignore)]
pub chain: Chain,
// Handles FloodSub protocol
pub floodsub: Floodsub,
// Sends response to the UnboundedReceiver
#[behaviour(ignore)]
pub init_sender: mpsc::UnboundedSender<bool>,
// Handles automatic discovery of peers on the local network
// and adds them to the topology
pub mdns: Mdns,
// Sends response to the UnboundedReceiver
#[behaviour(ignore)]
pub response_sender: mpsc::UnboundedSender<ChainResponse>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ChainResponse {
pub blocks: Vec<Block>,
pub receiver: String,
}
// Triggers chain communication for requested ID
#[derive(Debug, Deserialize, Serialize)]
pub struct LocalChainRequest {
pub from_peer_id: String,
}
// Keep states for handling incoming messages, lazy init
// and keyboard input by the client's user
pub enum EventType {
LocalChainRequest(ChainResponse),
Input(String),
Init,
}
// Implemnt FloodsubEvent for ChainBehaviour
impl NetworkBehaviourEventProcess<FloodsubEvent> for ChainBehaviour {
fn inject_event(&mut self, event: FloodsubEvent) {
if let FloodsubEvent::Message(msg) = event {
// If message is of type ChainResponse and that the message is ours,
// we execute our consensus.
if let Ok(response) = serde_json::from_slice::<ChainResponse>(&msg.data) {
if response.receiver == PEER_ID.to_string() {
info!("Response from {}:", msg.source);
response.blocks.iter().for_each(|r| info!("{:?}", r));
self.chain.blocks = self
.chain
.choose(self.chain.blocks.clone(), response.blocks);
}
} else if let Ok(response) = serde_json::from_slice::<LocalChainRequest>(&msg.data) {
// If of type LocalChainRequest, we send ChainResponse to
// initiator
info!("sending local chain to {}", msg.source.to_string());
let peer_id = response.from_peer_id;
if PEER_ID.to_string() == peer_id {
if let Err(e) = self.response_sender.send(ChainResponse {
blocks: self.chain.blocks.clone(),
receiver: msg.source.to_string(),
}) {
error!("error sending response via channel, {}", e);
};
}
} else if let Ok(block) = serde_json::from_slice::<Block>(&msg.data) {
// If of type Block, we try adding the block if valid
info!("received new block from {}", msg.source.to_string());
self.chain.try_add_block(block);
}
}
}
}
// Implement MdnsEvents for ChainBehaviour
impl NetworkBehaviourEventProcess<MdnsEvent> for ChainBehaviour {
fn inject_event(&mut self, event: MdnsEvent) {
match event {
// Add node to list of nodes when discovered
MdnsEvent::Discovered(nodes) => {
for (peer, _) in nodes {
self.floodsub.add_node_to_partial_view(peer)
}
}
// Remove node from list of nodes when TTL expires and
// address hasn't been refreshed
MdnsEvent::Expired(nodes) => {
for (peer, _) in nodes {
if !self.mdns.has_node(&peer) {
self.floodsub.remove_node_from_partial_view(&peer);
}
}
}
}
}
}
Here's my cargo.toml:
byteorder = "1"
chrono = "0.4.19"
getrandom = "0.2.3"
hex = "0.4.3"
libp2p = {version = "0.41.0", features = ['tcp-tokio', "mdns"]}
log = "0.4.14"
once_cell = "1.9.0"
oorandom = "11.1.3"
pretty_env_logger = "0.4.0"
serde = {version = "1.0.133", features = ["derive"]}
serde_json = "1.0.74"
sha2 = "0.10.0"
tokio = { version = "1.15.0", features = ["io-util", "io-std", "macros", "rt", "rt-multi-thread", "sync", "time"] }
I keep getting the following error on this struct:
error[E0277]: the trait bound `(): From<MdnsEvent>` is not satisfied
--> src/p2p.rs:30:10
|
30 | #[derive(NetworkBehaviour)]
| ^^^^^^^^^^^^^^^^ the trait `From<MdnsEvent>` is not implemented for `()`
|
= help: see issue #48214
= note: this error originates in the derive macro `NetworkBehaviour` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `(): From<FloodsubEvent>` is not satisfied
--> src/p2p.rs:30:10
|
30 | #[derive(NetworkBehaviour)]
| ^^^^^^^^^^^^^^^^ the trait `From<FloodsubEvent>` is not implemented for `()`
|
= help: see issue #48214
= note: this error originates in the derive macro `NetworkBehaviour` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0277`.
I tried following the suggestions from this forum and got the following error:
error[E0599]: no method named `into_inner` found for struct `OneShotHandler` in the current scope
--> src/p2p.rs:30:10
|
30 | #[derive(NetworkBehaviour)]
| ^^^^^^^^^^^^^^^^ method not found in `OneShotHandler<FloodsubProtocol, FloodsubRpc, floodsub::layer::InnerMessage>`
|
= note: this error originates in the derive macro `NetworkBehaviour` (in Nightly builds, run with -Z macro-backtrace for more info)
I also keep getting an add reference here error on #[derive(NetworkBehaviour)]. What could be causing the error and how can I fix it? I'm using rust-analyzer.
Link to the tutorial's github repo.
Link to my github repo. The entire code is rather large but should only the afforementioned errors.
This question is nearly a year old, but maybe will run into this again in the future.
I stumbled over the same issue, funnily enough while working through the same tutorial. It seems that newer versions of libp2p require you to do two things that the version used for the tutorial did not seem to require:
Specify #[behaviour(out_event="Event")] For the AppBehaviour struct
This is mentioned to be optional in the docs, but if you don't specify this then the macro will use StructName<Event>.
Implement the trait From<> for the enum Event for all events emitted by struct members of AppBehaviour, as the events emitted by the struct members are wrapped in the event enum.
I've added two new enum values for this as shown in the libp2p docs:
use crate::net::{ChainResponse};
use libp2p::{floodsub::FloodsubEvent, mdns::MdnsEvent};
pub enum Event {
ChainResponse(ChainResponse),
Floodsub(FloodsubEvent),
Mdns(MdnsEvent),
Input(String),
Init,
}
impl From<FloodsubEvent> for Event {
fn from(event: FloodsubEvent) -> Self {
Self::Floodsub(event)
}
}
impl From<MdnsEvent> for Event {
fn from(event: MdnsEvent) -> Self {
Self::Mdns(event)
}
}
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)
}
}
This question already has answers here:
Is there another option to share an Arc in multiple closures besides cloning it before each closure?
(2 answers)
Closed 2 years ago.
I am trying to implement a filter that sits in all my routes and extracts a header and matches a possible token to what is stored on my system.
I want to implement something like the warp rejection example but I get the error
expected a closure that implements the Fn trait, but this closure
only implements FnOnce closure is FnOnce because it moves the
variable tmp out of its environment
I kind of get what the compiler saying, but don't know how to solve it.
I thought doing let tmp = store.clone() would.
I have the filter:
pub fn haystack_auth_header(store: Store) -> impl Filter<Extract = (Store,), Error = Rejection> + Clone {
let tmp = store.clone();
warp::header("Authorization").and_then (|auth_header: String| async move {
// Authorization: BEARER authToken=xxxyyyzzz
let result = auth_token(&auth_header); //-> IResult<&'a str, (&'a str, &'a str), (&'a str, ErrorKind)> {
if result.is_err() {
return Err(reject::custom(HayStackAuthToken));
}
let (_, key_value) = result.unwrap();
let auth_token_result = tmp.read().get_authtoken();
if auth_token_result.is_err() {
return Err(reject::custom(HayStackAuthToken));
}
let auth_token_option = auth_token_result.unwrap();
if auth_token_option.is_none() {
return Err(reject::custom(HayStackAuthToken));
}
let auth_token = auth_token_option.unwrap();
if auth_token != key_value.1 {
return Err(reject::custom(HayStackAuthToken));
}
Ok(tmp)
})
}
store is type Store = Arc<RwLock<Box<dyn UserAuthStore>>> and UserAuthStore is trait UserAuthStore: fmt::Debug + Send + Sync.
UserAuthStore is defined as
pub trait UserAuthStore: fmt::Debug + Send + Sync {
// Return handshake token for username. If user has no handshake token generate one
fn get_handshake_token(&self, username: &str) -> HaystackResult<String>;
fn get_username(&self, handshake_token: &str) -> HaystackResult<String>;
fn set_temporary_value(&mut self, k: &str, v: &str) -> HaystackResult<()>;
fn get_temporary_value(&self, k: &str) -> HaystackResult<Option<&String>>;
fn set_authtoken(&mut self, s: String) -> HaystackResult<()>;
/// returns a base64 encoded sha256 salt of password.
fn get_password_salt(&self) -> HaystackResult<String>;
fn get_salted_password(&self) -> HaystackResult<String>;
fn get_authtoken(&self) -> HaystackResult<Option<String>>;
}
Why does clone not work here?
The full error is
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/server/mod.rs:997:45
|
997 | warp::header("Authorization").and_then (|auth_header: String| async move {
| ___________________________________--------__^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^_-
| | | |
| | | this closure implements `FnOnce`, not `Fn`
| | the requirement to implement `Fn` derives from here
998 | |
999 | | // Authorization: BEARER authToken=xxxyyyzzz
1000 | | let result = auth_token(&auth_header); //-> IResult<&'a str, (&'a str, &'a str), (&'a str, ErrorKind)> {
... |
1026 | | Ok(tmp.clone())
1027 | | })
| |_____- closure is `FnOnce` because it moves the variable `tmp` out of its environment
You can see a simplified test case here
After creating a simple test case I managed to get it going with the following function.
pub fn haystack_auth_header(store: Store) -> impl Filter<Extract = (Store,), Error = Rejection> + Clone {
warp::header("Authorization").and_then (
move |auth_header: String|
{
let tmp = store.clone();
async move {
let tmp = tmp.clone();
if tmp.read().get_authtoken().is_none() {
return Err(reject::custom(HayStackAuthToken));
}
Ok(tmp.clone())
}
}
)
}
So in the end just needed clone in the correct place.
This question suggests that you don't have yet a clear distinction about the fn hierarchy:
taken from here.
Warp's filter function need to implement the Fn trait.
This means that you cannot access the outer context(read: environment).
The first time AndThen call your closure, the function will consume the "only" tmp variable available, thus it won't be available for future calls. Since the closure it's taking ownership of its context, it means it's implementing FnOnce.
As suggested by #msrd0 in the comment, probably this is solvable by moving that call in the function, but I would need an MRE to be certain about it.
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.