Hyper with hyper-rustls, cannot infer an appropriate lifetime for autoref due to conflicting requirements

I'm quite new to rust, I only started a few days ago.

I'm trying to create a gateway with ssl, basically mashing up
this example
and
this example

Clip of the source:

let service = make_service_fn(
    move |s: &tokio_rustls::server::TlsStream<tokio::net::TcpStream>| {
      let client = client.clone();
      let (tls_stream, server_session) = s.get_ref();
      async move {
        Ok::<_, io::Error>(service_fn(move |req: Request<Body>| {
          proxy(req, client.to_owned(), tls_stream.peer_addr().unwrap())
        }))
      }
    },
  );
async fn proxy(
  req: Request<Body>,
  client: hyper::Client<hyper_rustls::HttpsConnector<hyper::client::HttpConnector>>,
  ip: std::net::SocketAddr,
) -> Result<Response<Body>, GenericError> {
  // Prepare the HTTPS connector.
  let out_addr = "https://cloudflare.com";

  let uri_string = format!(
    "{}{}",
    out_addr,
    req
      .uri()
      .path_and_query()
      .map(|x| x.as_str())
      .unwrap_or("/")
  )
  .to_owned();
  let (mut parts, body) = req.into_parts();
  parts.headers.remove("Host");
  // this is the part that I want to implement.
  //   parts.headers.append(
  //  "x-forwarded-for",
  //  HeaderValue::from_str(&format!("{}", remote_addr)).unwrap(),
  //);
  let mut request: Request<Body> = Request::from_parts(parts, body);
  *request.uri_mut() = uri_string.parse().unwrap();
  let forward_res = client.request(request);
  Ok(forward_res.await.unwrap())
}

I'm trying to add the x-forwarded-for header, so I need to get the client's ip address. Hyper's AddrStream

docs.rs/hyper/0.14.9/hyper/server/conn/struct.AddrStream.html#impl( Can't put two links because I'm a new user)

has a remote_addr function to get the remote ip address,

However, hyper-rustls uses a custom acceptor for the incoming tcp stream, which doesn't implement the remote_addr function.

I researched on the topic, and found that I could get the client ip from the underlying tcp stream:

let (tls_stream, server_session) = s.get_ref();
let client_ip = tls_stream.peer_addr().unwrap()

However, I'm getting errors about the compiler not being able to infer an appropropriate lifetime. I couldn't find a solution that worked anywhere.

The error:


error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:75:44
   |
75 |       let (tls_stream, server_session) = s.get_ref();
   |                                            ^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 73:5...
  --> src/main.rs:73:5
   |
73 | /     move |s: &tokio_rustls::server::TlsStream<tokio::net::TcpStream>| {
74 | |       let client = client.clone();
75 | |       let (tls_stream, server_session) = s.get_ref();
76 | |       async move {
...  |
80 | |       }
81 | |     },
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:75:42
   |
75 |       let (tls_stream, server_session) = s.get_ref();
   |                                          ^
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `hyper::server::conn::spawn_all::NewSvcTask<tokio_rustls::server::TlsStream<tokio::net::TcpStream>, impl futures_util::Future, hyper::service::util::ServiceFn<[closure@src/main.rs:77:39: 79:10], Body>, hyper::common::exec::Exec, hyper::server::conn::spawn_all::NoopWatcher>` will meet its required lifetime bounds
  --> src/main.rs:87:4
   |
87 |   .serve(service);
   |    ^^^^^

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0495`.
error: could not compile `hyper-proxy

Thanks in advance!

The ampersand here seems wrong to me. Are you sure that this shouldn't be by-value?

According to this page, I think using the ampersand is correct.. or am I wrong?

Hm you are right. However, it is not possible to access s once you have entered the async block, so you must extract the information you need before entering the async block.

let service = make_service_fn(
    move |s: &tokio_rustls::server::TlsStream<tokio::net::TcpStream>| {
      let client = client.clone();
      let (tls_stream, server_session) = s.get_ref();
      let peer_addr = tls_stream.peer_addr().unwrap();
      async move {
        Ok::<_, io::Error>(service_fn(move |req: Request<Body>| {
          proxy(req, client.to_owned(), peer_addr)
        }))
      }
    },
  );

This does work for getting the client IP, but I think that this is more of a workaround, because s.get_ref()'s first item isn't typed(I think). When I try the same thing for the server session(the second item from get_ref) it doesn't work..

What do you mean that it doesn't work? And I don't understand what you mean by "not typed".

Sorry for being unclear, I'm not very familiar with rust, I'm just winging it from knowledge of other languages.

Here's an example:

let service = make_service_fn(
    move |s: &tokio_rustls::server::TlsStream<tokio::net::TcpStream>| {
      let client = client.clone();
      let (tls_stream, server_session) = s.get_ref();
      let a = tls_stream.peer_addr().unwrap();
      let b = server_session.get_sni_hostname();
      async move {
        let asdf = a.clone();
        let qwerty = b.clone();
        Ok::<_, io::Error>(service_fn(move |req: Request<Body>| {
          let (mut parts, body) = req.into_parts();
          parts.headers.remove("host");
          parts.headers.append(
            "host",
            http::HeaderValue::from_str(&format!("{}", parts.uri.authority().unwrap())).unwrap(),
          );
          parts.headers.remove("x-forwarded-for");
          parts.headers.append(
            "x-forwarded-for",
            http::HeaderValue::from_str(&format!("{}", asdf)).unwrap(),
          );
          let request: Request<Body> = Request::from_parts(parts, body);
          proxy(request, client.to_owned())
        }))
      }
    },
  );

This results in:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
   --> src/main.rs:76:44
    |
76  |       let (tls_stream, server_session) = s.get_ref();
    |                                            ^^^^^^^
    |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 74:5...
   --> src/main.rs:74:5
    |
74  | /     move |s: &tokio_rustls::server::TlsStream<tokio::net::TcpStream>| {
75  | |       let client = client.clone();
76  | |       let (tls_stream, server_session) = s.get_ref();
77  | |       let a = tls_stream.peer_addr().unwrap();
...   |
97  | |       }
98  | |     },
    | |_____^
note: ...so that reference does not outlive borrowed content
   --> src/main.rs:76:42
    |
76  |       let (tls_stream, server_session) = s.get_ref();
    |                                          ^
    = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `hyper::server::conn::spawn_all::NewSvcTask<tokio_rustls::server::TlsStream<tokio::net::TcpStream>, impl futures_util::Future, hyper::service::util::ServiceFn<[closure@src/main.rs:82:39: 96:10], Body>, hyper::common::exec::Exec, hyper::server::conn::spawn_all::NoopWatcher>` will meet its required lifetime bounds
   --> src/main.rs:104:4
    |
104 |   .serve(service);
    |    ^^^^^

error: aborting due to previous error; 1 warning emitted

As you can see, I can't access the ServerSession

The problem is that get_sni_hostname returns an Option<&str>, so holding on to that value still borrows from s. Convert it into an Option<String> like this:

let b = server_session.get_sni_hostname().map(|name| name.to_string());

Thank you so much for the help!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.