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.
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);
}
}