diff options
Diffstat (limited to 'src/api_server.rs')
-rw-r--r-- | src/api_server.rs | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/src/api_server.rs b/src/api_server.rs new file mode 100644 index 00000000..cf70dbdf --- /dev/null +++ b/src/api_server.rs @@ -0,0 +1,76 @@ +use std::sync::Arc; + +use futures_util::TryStreamExt; +use hyper::service::{make_service_fn, service_fn}; +use hyper::{Body, Method, Request, Response, Server, StatusCode}; +use futures::future::Future; + +use crate::error::Error; +use crate::membership::System; + +/// This is our service handler. It receives a Request, routes on its +/// path, and returns a Future of a Response. +async fn handler(sys: Arc<System>, req: Request<Body>) -> Result<Response<Body>, Error> { + match (req.method(), req.uri().path()) { + // Serve some instructions at / + (&Method::GET, "/") => Ok(Response::new(Body::from( + "Try POSTing data to /echo such as: `curl localhost:3000/echo -XPOST -d 'hello world'`", + ))), + + // Simply echo the body back to the client. + (&Method::POST, "/echo") => Ok(Response::new(req.into_body())), + + // Convert to uppercase before sending back to client using a stream. + (&Method::POST, "/echo/uppercase") => { + let chunk_stream = req.into_body().map_ok(|chunk| { + chunk + .iter() + .map(|byte| byte.to_ascii_uppercase()) + .collect::<Vec<u8>>() + }); + Ok(Response::new(Body::wrap_stream(chunk_stream))) + } + + // Reverse the entire body before sending back to the client. + // + // Since we don't know the end yet, we can't simply stream + // the chunks as they arrive as we did with the above uppercase endpoint. + // So here we do `.await` on the future, waiting on concatenating the full body, + // then afterwards the content can be reversed. Only then can we return a `Response`. + (&Method::POST, "/echo/reversed") => { + let whole_body = hyper::body::to_bytes(req.into_body()).await?; + + let reversed_body = whole_body.iter().rev().cloned().collect::<Vec<u8>>(); + Ok(Response::new(Body::from(reversed_body))) + } + + // Return the 404 Not Found for other routes. + _ => { + let mut not_found = Response::default(); + *not_found.status_mut() = StatusCode::NOT_FOUND; + Ok(not_found) + } + } +} + +pub async fn run_api_server(sys: Arc<System>, shutdown_signal: impl Future<Output=()>) -> Result<(), hyper::Error> { + let addr = ([0, 0, 0, 0], sys.config.api_port).into(); + + let service = make_service_fn(|_| { + let sys = sys.clone(); + async move { + let sys = sys.clone(); + Ok::<_, Error>(service_fn(move |req: Request<Body>| { + let sys = sys.clone(); + handler(sys, req) + })) + } + }); + + let server = Server::bind(&addr).serve(service); + + let graceful = server.with_graceful_shutdown(shutdown_signal); + println!("API server listening on http://{}", addr); + + graceful.await +} |