// #![deny(warnings)] // FIXME: https://github.com/rust-lang/rust/issues/62411 #![warn(rust_2018_idioms)] use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Method, Request, Response, Server, StatusCode}; use std::collections::HashMap; use url::form_urlencoded; static INDEX: &[u8] = b"
Name:
Number:
"; static MISSING: &[u8] = b"Missing field"; static NOTNUMERIC: &[u8] = b"Number field is not numeric"; // Using service_fn, we can turn this function into a `Service`. async fn param_example(req: Request) -> Result, hyper::Error> { match (req.method(), req.uri().path()) { (&Method::GET, "/") | (&Method::GET, "/post") => Ok(Response::new(INDEX.into())), (&Method::POST, "/post") => { // Concatenate the body... let b = hyper::body::to_bytes(req).await?; // Parse the request body. form_urlencoded::parse // always succeeds, but in general parsing may // fail (for example, an invalid post of json), so // returning early with BadRequest may be // necessary. // // Warning: this is a simplified use case. In // principle names can appear multiple times in a // form, and the values should be rolled up into a // HashMap>. However in this // example the simpler approach is sufficient. let params = form_urlencoded::parse(b.as_ref()) .into_owned() .collect::>(); // Validate the request parameters, returning // early if an invalid input is detected. let name = if let Some(n) = params.get("name") { n } else { return Ok(Response::builder() .status(StatusCode::UNPROCESSABLE_ENTITY) .body(MISSING.into()) .unwrap()); }; let number = if let Some(n) = params.get("number") { if let Ok(v) = n.parse::() { v } else { return Ok(Response::builder() .status(StatusCode::UNPROCESSABLE_ENTITY) .body(NOTNUMERIC.into()) .unwrap()); } } else { return Ok(Response::builder() .status(StatusCode::UNPROCESSABLE_ENTITY) .body(MISSING.into()) .unwrap()); }; // Render the response. This will often involve // calls to a database or web service, which will // require creating a new stream for the response // body. Since those may fail, other error // responses such as InternalServiceError may be // needed here, too. let body = format!("Hello {}, your number is {}", name, number); Ok(Response::new(body.into())) } (&Method::GET, "/get") => { let query = if let Some(q) = req.uri().query() { q } else { return Ok(Response::builder() .status(StatusCode::UNPROCESSABLE_ENTITY) .body(MISSING.into()) .unwrap()); }; let params = form_urlencoded::parse(query.as_bytes()) .into_owned() .collect::>(); let page = if let Some(p) = params.get("page") { p } else { return Ok(Response::builder() .status(StatusCode::UNPROCESSABLE_ENTITY) .body(MISSING.into()) .unwrap()); }; let body = format!("You requested {}", page); Ok(Response::new(body.into())) } _ => Ok(Response::builder() .status(StatusCode::NOT_FOUND) .body(Body::empty()) .unwrap()), } } #[tokio::main] async fn main() -> Result<(), Box> { pretty_env_logger::init(); let addr = ([127, 0, 0, 1], 1337).into(); let server = Server::bind(&addr).serve(make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(param_example)) })); println!("Listening on http://{}", addr); server.await?; Ok(()) }