1

I'm trying to accomplish a very simple task here. I don't understand why it has become so complex. I'm attempting to modify headers before they reach the service and return a failure if a certain condition is not met. Here is my middleware file:

use std::future::{ready, Ready};

use actix_http::header::{HeaderValue, HeaderName};
use actix_web::{
    dev::{self, Service, ServiceRequest, ServiceResponse, Transform},
    Error,http::Method
};
use futures_util::future::LocalBoxFuture;
//use crate::constants;

pub struct Heartbeat;

impl<S, B> Transform<S, ServiceRequest> for Heartbeat
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type InitError = ();
    type Transform = HeartMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(HeartMiddleware { service }))
    }
}

pub struct HeartMiddleware<S> {
    service: S,
}


impl<S, B> Service<ServiceRequest> for HeartMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    dev::forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        

        let fut = self.service.call(req);
        
        

        Box::pin(async move {
            
            let mut res = fut.await?;
            let headers = res.headers_mut();
            headers.insert(
                 HeaderName::from_static("Content-Type"), HeaderValue::from_static("text/plain")
             );
            
            if (Method::POST == req.method() || 
                 Method::GET  == req.method()  ||
                 Method::HEAD == req.method()) && req.path() == "/ping" {
                 Ok(res)
            }
             
            Err(actix_web::error::ErrorImATeapot("TeaPot"))
        })
    }
}

and the main file :

use actix_web::{get,web,http,Result,App,HttpServer,HttpRequest, Responder, HttpResponse};
use serde::{Deserialize,Serialize};
use actix_cors::Cors;

mod heartbeat;

static PORT :u16 = 9091;

#[get("/")]
async fn index()->impl Responder {
    
    HttpResponse::Ok().body("template")
}

 

#[derive(Serialize)]
pub struct Response {
    pub message: String,
}

async fn not_found() ->Result<HttpResponse> {
    let response = Response {
        message: "Resource not found".to_string(),
    };
    Ok(HttpResponse::NotFound().json(response))
}


#[actix_web::main]
async fn main()-> std::io::Result<()> {
    std::env::set_var("RUST_LOG", "debug");
    env_logger::init();
    HttpServer::new(|| App::new()
    .wrap(
        Cors::default()
            .allowed_origin("http://*") // Allow all http origins
            .allowed_origin("https://*") // Allow all https origins
            .allowed_methods(vec!["GET","POST","PUT","DELETE","OPTIONS"])
            .allowed_headers(vec![http::header::AUTHORIZATION,http::header::CONTENT_TYPE,
                http::header::ACCEPT,http::header::LINK])
            .allowed_header("X-CSRF-TOKEN")
            .supports_credentials()
            .max_age(300)
    )
    .wrap(heartbeat::Heartbeat)
    .service(index)
    .service(web::resource("/ping"))
    .default_service(web::route().to(not_found)))
    .bind(("127.0.0.1",PORT))?
    .run()
    .await        
}


compiling it im getting :

cargo build
   Compiling broker-service v0.1.0 (C:\dev\my\rust\workspace\broker-service)
warning: unused import: `HttpRequest`
 --> src\main.rs:1:52
  |
1 | use actix_web::{get,web,http,Result,App,HttpServer,HttpRequest, Responder, HttpResponse};
  |                                                    ^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: unused import: `Deserialize`
 --> src\main.rs:2:13
  |
2 | use serde::{Deserialize,Serialize};
  |             ^^^^^^^^^^^

error[E0308]: mismatched types
  --> src\heartbeat.rs:65:18
   |
62 | /             if (Method::POST == req.method() ||
63 | |                  Method::GET  == req.method()  ||
64 | |                  Method::HEAD == req.method()) && req.path() == "/ping" {
65 | |                  Ok(res)
   | |                  ^^^^^^^ expected `()`, found `Result<ServiceResponse<B>, _>`
66 | |             }
   | |_____________- expected this to be `()`
   |
   = note: expected unit type `()`
                   found enum `Result<ServiceResponse<B>, _>`
note: return type inferred to be `()` here
  --> src\heartbeat.rs:56:27
   |
56 |             let mut res = fut.await?;
   |                           ^^^^^^^^^^

For more information about this error, try `rustc --explain E0308`.
warning: `broker-service` (bin "broker-service") generated 2 warnings
error: could not compile `broker-service` (bin "broker-service") due to previous error; 2 warnings emitted

UPDATE STILL ERROR:

after updateding the solution suggested still getting error :

use std::future::{ready, Ready};

use actix_http::header::{HeaderValue, HeaderName};
use actix_web::{
    dev::{self, Service, ServiceRequest, ServiceResponse, Transform},
    Error,http::Method
};
use futures_util::future::LocalBoxFuture;
//use crate::constants;

pub struct Heartbeat;

impl<S, B> Transform<S, ServiceRequest> for Heartbeat
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type InitError = ();
    type Transform = HeartMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(HeartMiddleware { service }))
    }
}

pub struct HeartMiddleware<S> {
    service: S,
}


impl<S, B> Service<ServiceRequest> for HeartMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    dev::forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        

        let fut = self.service.call(req);
        
        

        Box::pin(async move {
            
            let mut res = fut.await?;
            let headers = res.headers_mut();
            headers.insert(
                 HeaderName::from_static("Content-Type"), HeaderValue::from_static("text/plain")
             );
            
            if (Method::POST == req.method() || 
                 Method::GET  == req.method()  ||
                 Method::HEAD == req.method()) && req.path() == "/ping" {
                 return Ok(res)
            }
             
            Err(actix_web::error::ErrorImATeapot("TeaPot"))
        })
    }
}
cargo build
   Compiling broker-service v0.1.0 (C:\dev\my\rust\workspace\broker-service)
warning: unused import: `HttpRequest`
 --> src\main.rs:1:52
  |
1 | use actix_web::{get,web,http,Result,App,HttpServer,HttpRequest, Responder, HttpResponse};
  |                                                    ^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: unused import: `Deserialize`
 --> src\main.rs:2:13
  |
2 | use serde::{Deserialize,Serialize};
  |             ^^^^^^^^^^^

error[E0382]: use of moved value: `req`
  --> src\heartbeat.rs:54:18
   |
47 |       fn call(&self, req: ServiceRequest) -> Self::Future {
   |                      --- move occurs because `req` has type `ServiceRequest`, which does not implement the `Copy` trait
...
50 |           let fut = self.service.call(req);
   |                                       --- value moved here
...
54 |           Box::pin(async move {
   |  __________________^
55 | |
56 | |             let mut res = fut.await?;
57 | |             let headers = res.headers_mut();
...  |
62 | |             if (Method::POST == req.method() ||
   | |                                 --- use occurs due to use in generator
...  |
68 | |             Err(actix_web::error::ErrorImATeapot("TeaPot"))
69 | |         })
   | |_________^ value used here after move

For more information about this error, try `rustc --explain E0382`.
warning: `broker-service` (bin "broker-service") generated 2 warnings
error: could not compile `broker-service` (bin "broker-service") due to previous error; 2 warnings emitted


user63898
  • 29,839
  • 85
  • 272
  • 514

1 Answers1

0

Your if is a statement, not an expression. You're basically telling the compiler "execute this if and throw away the result, then return Err". The result must be of type ().

You want an explicit return:

Box::pin(async move {
    let mut res = fut.await?;
    let headers = res.headers_mut();
    headers.insert(
        HeaderName::from_static("Content-Type"),
        HeaderValue::from_static("text/plain"),
    );

    if (Method::POST == req.method()
        || Method::GET == req.method()
        || Method::HEAD == req.method())
        && req.path() == "/ping"
    {
        return Ok(res);
    }

    Err(actix_web::error::ErrorImATeapot("TeaPot"))
})

Or add an else, to make the whole thing an expression:

Box::pin(async move {
    let mut res = fut.await?;
    let headers = res.headers_mut();
    headers.insert(
        HeaderName::from_static("Content-Type"),
        HeaderValue::from_static("text/plain"),
    );

    if (Method::POST == req.method()
        || Method::GET == req.method()
        || Method::HEAD == req.method())
        && req.path() == "/ping"
    {
        Ok(res)
    } else {
        Err(actix_web::error::ErrorImATeapot("TeaPot"))
    }
})
Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • did you try it ? it dosnt work gives me both gives me : Box::pin(async move { | __________________^ 55 | | 56 | | let mut res = fut.await?; 57 | | let headers = res.headers_mut(); ... | 62 | | if (Method::POST == req.method() || | | --- use occurs due to use in generator ... | 68 | | Err(actix_web::error::ErrorImATeapot("TeaPot")) 69 | | }) | |_________^ value used here after move – user63898 Aug 31 '23 at 11:02
  • @user63898 I didn't, but this looks like a different, unrelated error. – Chayim Friedman Aug 31 '23 at 11:02
  • this is error im getting after what you suggest ( i did it before ) – user63898 Aug 31 '23 at 11:06
  • @user63898 I understand, but this is not related. What I said is still correct, and `return` (or `else`) is the correct way to write this code. Now we have another, different error that we need to solve. – Chayim Friedman Aug 31 '23 at 11:07
  • ok any idea what is the error ? – user63898 Aug 31 '23 at 11:11
  • @user63898 It is hard to read in a comment, so I don't know. – Chayim Friedman Aug 31 '23 at 11:12
  • i updated the main questions – user63898 Aug 31 '23 at 11:18