rustactix-web

lifetime handling for async code in actix extractor


Goal

I want to execute a GET request in an actix-web extractor, in order obtain additional information from a header.

Steps taken

Current implementation

impl FromRequest for AuthorizedHttpRequest {
    type Error = CustomError;
    type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;

    #[inline]
    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
        let path: Path<Url> = Path::new(Url::new(req.path().parse::<Uri>().unwrap().clone()));
        let head: RequestHead = req.head().clone();
        let date_header: Option<&HeaderValue> = req.headers().get(header::DATE).clone();

        Box::pin(async move {
            match date_header {
                Some(date_header_value) => {
                    let date_header_str_res: Result<&str, ToStrError> = date_header_value.to_str();
                    match date_header_str_res {
                        Ok(date_header_str) => {
                            let service_client: CustomServiceClient = CustomServiceClient::new(Env::new().custom_service_url);
                            let additional_data: Result<Data, CustomError> = service_client.get_additonal_data(date_header_str.parse().unwrap()).await.map_err(CustomError::from);
                            Ok(AuthorizedHttpRequest::new(path, head, user))
                        }
                        Err(_) => Ok(AuthorizedHttpRequest::new(
                            path.clone(), head.clone(),
                            Err(CustomError::from(InvalidHeaderError))
                        ))
                    }
                },
                None => Ok(AuthorizedHttpRequest::new(
                    path.clone(), head.clone(),
                    Err(CustomError::from(InvalidHeaderError))
                ))
            }
        })
    }
}
impl CustomServiceClient {
    pub async fn get_additonal_data(&self, date_str: String) -> Result<Data, UnavailableDataError> {
        let client: Client = Client::builder()
            .danger_accept_invalid_certs(true)    // necessary as I have self-signed certificates
            .build().unwrap();
        let response: Response = client
            .get(format!("{}", self.url))
            .header(header::DATE, format!("Date {}", date_str))
            .send().await.unwrap();
        let additional_data: Data = response.json().await.unwrap();

        Ok(additional_data)
    }
}

Lifetime issue

It seems my implementation (which mirrors the one in the answer of the mentioned question) raises a lifetime error, that I am not able to fix. The error is as follows:

error: lifetime may not live long enough   
   --> src/<path>/authorized_http_request.rs:61:9   
    |   
 56 |       fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {   
    |‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎‎- let's call the lifetime of this reference `'1`   
 ...   
 61 | /‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ Box::pin(async move {   
 62 | |‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ match date_header {   
 63 | |‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ Some(date_header_value) => {   
 64 | |‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ let date_header_str_res: Result<&str, ToStrError> = date_header_value.to_str();   
 ...‎ ‎ |   
 96 | |‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ })   
    | |__________^ returning this value requires that `'1` must outlive `'static`

What I need

How can I fix this lifetime issue? Should I have done things another way entirely?


Solution

  • Have you tried .cloned() instead of .clone()?

    let date_header: Option<HeaderValue> = req.headers().get(header::DATE).cloned();
    

    Which gives you an owned Option<HeaderValue> instead of referenced Option<&HeaderValue>