From 6705d13424ec444814bcfafdbccf51975db47e2e Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Thu, 15 Aug 2019 10:27:25 +0200 Subject: [PATCH] api-test: comments/cleanup Signed-off-by: Wolfgang Bumiller --- api-test/src/bin/api-test.rs | 176 ++++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 76 deletions(-) diff --git a/api-test/src/bin/api-test.rs b/api-test/src/bin/api-test.rs index 522c4d21..8d3a6119 100644 --- a/api-test/src/bin/api-test.rs +++ b/api-test/src/bin/api-test.rs @@ -13,76 +13,9 @@ use tokio::io::AsyncReadExt; use proxmox::api::{api, router}; -async fn run_request(request: Request) -> Result, hyper::Error> { - match route_request(request).await { - Ok(r) => Ok(r), - Err(err) => Ok(Response::builder() - .status(400) - .body(Body::from(format!("ERROR: {}", err))) - .expect("building an error response...")), - } -} - -async fn route_request(request: Request) -> Result, Error> { - let path = request.uri().path(); - - let (target, params) = ROUTER - .lookup(path) - .ok_or_else(|| format_err!("missing path: {}", path))?; - - target - .get - .as_ref() - .ok_or_else(|| format_err!("no GET method for: {}", path))? - .call(params.unwrap_or(Value::Null)) - .await -} - -async fn main_do(www_dir: String) { - // Construct our SocketAddr to listen on... - let addr = ([0, 0, 0, 0], 3000).into(); - - // And a MakeService to handle each connection... - let service = make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(run_request)) }); - - // Then bind and serve... - let server = Server::bind(&addr).serve(service); - - println!("Serving {} under http://localhost:3000/www/", www_dir); - - if let Err(e) = server.await { - eprintln!("server error: {}", e); - } -} - -fn main() { - // We expect a path, where to find our files we expose via the www/ dir: - let mut args = std::env::args(); - - // real code should have better error handling - let _program_name = args.next(); - let www_dir = args.next().expect("expected a www/ subdirectory"); - set_www_dir(www_dir.to_string()); - - // show our api info: - println!( - "{}", - serde_json::to_string_pretty(&ROUTER.api_dump()).unwrap() - ); - - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(main_do(www_dir)); -} - -#[api({ - description: "Hello API call", -})] -async fn hello() -> Result, Error> { - Ok(http::Response::builder() - .status(200) - .header("content-type", "text/html") - .body(Body::from("Hello"))?) -} +// +// Configuration: +// static mut WWW_DIR: Option = None; @@ -103,6 +36,30 @@ pub fn set_www_dir(dir: String) { } } +// +// API methods +// + +router! { + pub static ROUTER: Router = { + GET: hello, + /www/{path}*: { GET: get_www }, + /api/1: { + // fill with more stuff + } + }; +} + +#[api({ + description: "Hello API call", +})] +async fn hello() -> Result, Error> { + Ok(http::Response::builder() + .status(200) + .header("content-type", "text/html") + .body(Body::from("Hello"))?) +} + #[api({ description: "Get a file from the www/ subdirectory.", parameters: { @@ -114,6 +71,7 @@ async fn get_www(path: String) -> Result, Error> { bail!("illegal path"); } + // FIXME: Add support for an ApiError type for 404s etc. to reduce error handling code size: let mut file = match tokio::fs::File::open(format!("{}/{}", www_dir(), path)).await { Ok(file) => file, Err(ref err) if err.kind() == io::ErrorKind::NotFound => { @@ -145,11 +103,77 @@ async fn get_www(path: String) -> Result, Error> { Ok(response.body(Body::from(data))?) } -router! { - pub static ROUTER: Router = { - GET: hello, - /www/{path}*: { GET: get_www }, - /api/1: { - } +// +// Hyper glue +// + +async fn route_request(request: Request) -> Result, Error> { + let path = request.uri().path(); + + let (target, params) = ROUTER + .lookup(path) + .ok_or_else(|| format_err!("missing path: {}", path))?; + + use hyper::Method; + let method = match *request.method() { + Method::GET => target.get.as_ref(), + Method::PUT => target.put.as_ref(), + Method::POST => target.post.as_ref(), + Method::DELETE => target.delete.as_ref(), + _ => bail!("unexpected method type"), }; + + method + .ok_or_else(|| format_err!("no GET method for: {}", path))? + .call(params.unwrap_or(Value::Null)) + .await +} + +async fn service_func(request: Request) -> Result, hyper::Error> { + match route_request(request).await { + Ok(r) => Ok(r), + Err(err) => Ok(Response::builder() + .status(400) + .body(Body::from(format!("ERROR: {}", err))) + .expect("building an error response...")), + } +} + +// +// Main entry point +// +async fn main_do(www_dir: String) { + // Construct our SocketAddr to listen on... + let addr = ([0, 0, 0, 0], 3000).into(); + + // And a MakeService to handle each connection... + let service = make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(service_func)) }); + + // Then bind and serve... + let server = Server::bind(&addr).serve(service); + + println!("Serving {} under http://localhost:3000/www/", www_dir); + + if let Err(e) = server.await { + eprintln!("server error: {}", e); + } +} + +fn main() { + // We expect a path, where to find our files we expose via the www/ dir: + let mut args = std::env::args(); + + // real code should have better error handling + let _program_name = args.next(); + let www_dir = args.next().expect("expected a www/ subdirectory"); + set_www_dir(www_dir.to_string()); + + // show our api info: + println!( + "{}", + serde_json::to_string_pretty(&ROUTER.api_dump()).unwrap() + ); + + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(main_do(www_dir)); }