diff options
author | Alex Auvolat <alex@adnab.me> | 2020-04-05 23:33:42 +0200 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2020-04-05 23:33:42 +0200 |
commit | 7102db1d544bec663a8492b24c455168d0b83f08 (patch) | |
tree | cc308cbeefc3f48b55149e85ec737867c24a498e /src/api.rs | |
download | garage-7102db1d544bec663a8492b24c455168d0b83f08.tar.gz garage-7102db1d544bec663a8492b24c455168d0b83f08.zip |
First commit: skeleton for something great
Diffstat (limited to 'src/api.rs')
-rw-r--r-- | src/api.rs | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/src/api.rs b/src/api.rs new file mode 100644 index 00000000..9ee17047 --- /dev/null +++ b/src/api.rs @@ -0,0 +1,65 @@ +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::System; + +/// This is our service handler. It receives a Request, routes on its +/// path, and returns a Future of a Response. +async fn echo(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: &System, api_port: u16, shutdown_signal: impl Future<Output=()>) -> Result<(), hyper::Error> { + let addr = ([0, 0, 0, 0], api_port).into(); + + let service = make_service_fn(|_| async { Ok::<_, Error>(service_fn(echo)) }); + + let server = Server::bind(&addr).serve(service); + + let graceful = server.with_graceful_shutdown(shutdown_signal); + println!("Listening on http://{}", addr); + + graceful.await +} |