Returning a HTTP handler from a higher order function

I am writing a small http app and need to return hard coded response in a http handler written using Hyper.

The issue is that the response varies with the requst path, say /test returns First response, /no-test returns Second response, and so on.

A simple HTTP handler in hyper looks like this:

async fn raw_body(_: Request<Body>) -> Result<Response<Body>, anyhow::Error> {
    Ok(Response::new(Body::from("Home page")))
}

which will desugar to (in my case)

fn raw_body(_: Request<Body>) -> impl Future<Output = Result<Response<Body>, anyhow::Error>> {
    async { Ok(Response::new(Body::from("Home page"))) }
}

Which I understand and is working fine for me.

Now I want to be able to do something like this:

fn handler_factory(response_body: String, headers: HashMap<String, String>) -> Fn(Request<Body>) -> impl Future<Output=Result<Request<Body>, anyhow::Error>> {
    return |_req: Request<Body>| {
        async move {
            Ok(Response::new(Body::from(response_body.as_ref()))
        }
    };
}

which does work, because impl is not allowed inside the return type of a function.

Then I tried something like this:

fn handler_factory<H, R>(response_body: String, headers: HashMap<String, String>) -> H 
where
    H: Fn(Request<Body>) -> R + 'static,
    R: Future<Output=Result<Response<Body>, anyhow::Error>>
{
    return |_req: Request<Body>| {
        async move {
            Ok(Response::new(Body::from(response_body.as_ref()))
        }
    };
}

Now I am out of ideas what could be a better way to achieve this. The library "routerify" does something similar while accepting the handler as an argument, but I can't seem to make it work while returning the function back to the caller. Can I please get some help on this?

You definitely can't use generics for this. You have to use impl Trait as the return type.

Thanks for the reply, but I don't think impl Trait would work inside the function that I wish to return. Iirc impl Trait is not allowed inside the types that are returned from the functions - it can be returned by the function but not the function returned by the function.

Looked at it from a fresh perspective today. Figured out within minutes! :stuck_out_tongue:

Key was to use Pin<Box<dyn Future<...>>> as return type of closure.

fn get_raw_body_handler(
    raw_body: String,
    headers: HashMap<String, String>,
    status_code: Option<u16>
) -> impl Fn(
    Request<Body>,
) -> Pin<Box<dyn Future<Output = Result<Response<Body>, anyhow::Error>> + Send + Sync>> {
    let response_headers: hyper::header::HeaderMap = headers
        .iter()
        .map(|(k, v)| {
            (
                hyper::header::HeaderName::from_str(k).ok(),
                hyper::header::HeaderValue::from_str(v).ok(),
            )
        })
        .filter(|(k, v)| (k.is_some() && v.is_some()))
        .map(|(k, v)| (k.unwrap(), v.unwrap()))
        .collect();

    let status_code = hyper::StatusCode::from_u16(status_code.unwrap_or(200)).unwrap_or_default();

    move |_: Request<Body>| -> Pin<Box<dyn Future<Output = Result<Response<Body>, anyhow::Error>> + Send + Sync>> {
      let response_headers = response_headers.clone();
      let raw_body = raw_body.clone();
  Box::pin(async move {
    let mut response = Response::new(Body::from(raw_body));
    *response.status_mut() = status_code;
    *response.headers_mut() = response_headers;
    Ok(response)
  })
  }
}

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.