diff options
author | Martin Fischer <martin@push-f.com> | 2021-01-18 10:03:50 +0100 |
---|---|---|
committer | Martin Fischer <martin@push-f.com> | 2021-01-18 10:17:10 +0100 |
commit | 9b2d39933f3403245f97247166818bef609a0125 (patch) | |
tree | e5819cf6314e357b7b37e23ae277cd2fea165afc /examples/csrf/src/main.rs | |
parent | c27c5b3109e2b4fd8cdbe312f4925edc238a25e9 (diff) |
split Request wrapper into Parts & Bodyv0.2.0
Originally the into_ functions actually consumed the request but I
changed that to make request information like URI and method still
accessible after the request has been read.
Not consuming the Request however allows e.g. into_form() to be called
twice, which results in a panic since the body can only be read once.
This commit splits the Request wrapper into two wrappers Parts & Body,
allowing the borrow checker to guarantee that the body is only consumed
once, while keeping the other request information accessible after the
body has been consumed.
Version bumped to 0.2.0.
Diffstat (limited to 'examples/csrf/src/main.rs')
-rw-r--r-- | examples/csrf/src/main.rs | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/examples/csrf/src/main.rs b/examples/csrf/src/main.rs new file mode 100644 index 0000000..16b31a1 --- /dev/null +++ b/examples/csrf/src/main.rs @@ -0,0 +1,59 @@ +use std::convert::Infallible; +use hyper::service::{service_fn, make_service_fn}; +use hyper::{Method, Server}; +use serde::Deserialize; +use sputnik::security::CsrfToken; +use sputnik::{Error, request::{Parts, Body}, response::Response}; + +async fn route(req: &mut Parts, body: Body) -> Result<Response,Error> { + match (req.method(), req.uri().path()) { + (&Method::GET, "/form") => get_form(req).await, + (&Method::POST, "/form") => post_form(req, body).await, + _ => return Err(Error::not_found("page not found".to_owned())) + } +} + +async fn get_form(req: &mut Parts) -> Result<Response, Error> { + let mut response = Response::new(); + let csrf_token = CsrfToken::from_parts(req, &mut response); + *response.body() = format!("<form method=post> + <input name=text>{}<button>Submit</button></form>", csrf_token.html_input()).into(); + Ok(response) +} + +#[derive(Deserialize)] +struct FormData {text: String} + +async fn post_form(req: &mut Parts, body: Body) -> Result<Response, Error> { + let mut response = Response::new(); + let csrf_token = CsrfToken::from_parts(req, &mut response); + let msg: FormData = body.into_form_csrf(&csrf_token).await?; + *response.body() = format!("hello {}", msg.text).into(); + Ok(response) +} + +/// adapt between Hyper's types and Sputnik's convenience types +async fn service(req: hyper::Request<hyper::Body>) -> Result<hyper::Response<hyper::Body>, Infallible> { + let (mut parts, body) = sputnik::request::adapt(req); + match route(&mut parts, body).await { + Ok(res) => Ok(res.into()), + Err(err) => Ok(err.response_builder().body(err.message.into()).unwrap()) + // you can easily wrap or log errors here + } +} + +#[tokio::main] +async fn main() { + let service = make_service_fn(move |_| { + async move { + Ok::<_, hyper::Error>(service_fn(move |req| { + service(req) + })) + } + }); + + let addr = ([127, 0, 0, 1], 8000).into(); + let server = Server::bind(&addr).serve(service); + println!("Listening on http://{}", addr); + server.await; +}
\ No newline at end of file |