0

I am struggling to build a web service using Hyper. The problem I face is best illustrated with with example code ...

use std::convert::Infallible;
use std::sync::Arc;
use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};

struct ContrivedServicesManager {
    some_state: Vec<String>
}

impl ContrivedServicesManager {
    pub /* async */ fn handle_request(&self, _req: Request<Body>) -> Response<Body> {
        hyper::Response::new(Body::from(self.some_state.join(", ")))
    }

    fn new(args: Vec<String>) -> Self {
        Self {
            some_state: args
        }
    }
}

#[tokio::main]
async fn main() {

    // Grab all the command line arges following argv[0]
    let args: Vec<String> = std::env::args()
        .enumerate()
        .filter(|&(i,_)| i > 0)
        .map(|(_, e)| e)
        .collect();

    // The inner layer of request handling machinery
    let services_manager = Arc::new(ContrivedServicesManager::new(args));

    // Build the outer layer of request handling machinery
    let service_maker = make_service_fn(move |_| {
        let services_manager = services_manager.clone();

        return async move {
            Ok::<_, Infallible>(service_fn(move |req| {
                let response = services_manager.handle_request(req);
                async move { Ok::<_, Infallible>(
                    response
                )}
            }))
        };
    });

    // Configure & start the web server
    let addr = ([127, 0, 0, 1], 3000).into();
    let server = Server::bind(&addr).serve(service_maker);
    println!("Listening on http://{}", addr);
    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }

}

This compiles & works. However, if ContrivedServicesManager::handle_request is changed to be async it fails to compile and the compiler outputs 237 lines of complaint, which seem to centre on service_maker being the wrong type.

I would be so glad if someone with stronger Rust-Fu would tell me how to overcome this.

Martin Cowie
  • 2,788
  • 7
  • 38
  • 74

1 Answers1

1

Just some minor adjustments were necessary.

Primarily, the service_manager has to be cloned again in the innermost closure because it might now live forever, referenced in the returned future.

Minor nit: use Arc::clone() instead of .clone() for Arc objects to avoid ambiguities.

use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use std::convert::Infallible;
use std::sync::Arc;

struct ContrivedServicesManager {
    some_state: Vec<String>,
}

impl ContrivedServicesManager {
    pub async fn handle_request(&self, _req: Request<Body>) -> Response<Body> {
        hyper::Response::new(Body::from(self.some_state.join(", ")))
    }

    fn new(args: Vec<String>) -> Self {
        Self { some_state: args }
    }
}

#[tokio::main]
async fn main() {
    // Grab all the command line arges following argv[0]
    let args: Vec<String> = std::env::args()
        .enumerate()
        .filter(|&(i, _)| i > 0)
        .map(|(_, e)| e)
        .collect();

    // The inner layer of request handling machinery
    let services_manager = Arc::new(ContrivedServicesManager::new(args));

    // Build the outer layer of request handling machinery
    let service_maker = make_service_fn(move |_| {
        let services_manager = Arc::clone(&services_manager);

        async move {
            Ok::<_, Infallible>(service_fn(move |req| {
                // Need to clone again, because it will get captured by `handle_request` and live indefinitely
                // until the future gets polled
                let services_manager = Arc::clone(&services_manager);
                async move {
                    // Has to be inside of the `async move` because it's an async function
                    let response = services_manager.handle_request(req).await;
                    Ok::<_, Infallible>(response)
                }
            }))
        }
    });

    // Configure & start the web server
    let addr = ([127, 0, 0, 1], 3000).into();
    let server = Server::bind(&addr).serve(service_maker);
    println!("Listening on http://{}", addr);
    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}
Finomnis
  • 18,094
  • 1
  • 20
  • 27