1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
use http_service::Body;
use multipart::server::Multipart;
use std::io::Cursor;

use crate::{
    error::{BoxTryFuture, ResultExt},
    Context, Response,
};

/// An extension trait for `Context`, providing form extraction.
pub trait ExtractForms {
    /// Asynchronously extract the entire body as a single form.
    fn body_form<T: serde::de::DeserializeOwned>(&mut self) -> BoxTryFuture<T>;

    /// Asynchronously extract the entire body as a multipart form.
    fn body_multipart(&mut self) -> BoxTryFuture<Multipart<Cursor<Vec<u8>>>>;
}

impl<AppData: Send + Sync + 'static> ExtractForms for Context<AppData> {
    fn body_form<T: serde::de::DeserializeOwned>(&mut self) -> BoxTryFuture<T> {
        let body = self.take_body();
        box_async! {
            let body = await!(body.into_vec()).client_err()?;
            Ok(serde_urlencoded::from_bytes(&body).map_err(|e| err_fmt!("could not decode form: {}", e)).client_err()?)
        }
    }

    fn body_multipart(&mut self) -> BoxTryFuture<Multipart<Cursor<Vec<u8>>>> {
        const BOUNDARY: &str = "boundary=";
        let boundary = self.headers().get("content-type").and_then(|ct| {
            let ct = ct.to_str().ok()?;
            let idx = ct.find(BOUNDARY)?;
            Some(ct[idx + BOUNDARY.len()..].to_string())
        });

        let body = self.take_body();

        box_async! {
            let body = await!(body.into_vec()).client_err()?;
            let boundary = boundary.ok_or_else(|| err_fmt!("no boundary found")).client_err()?;
            Ok(Multipart::with_body(Cursor::new(body), boundary))
        }
    }
}

/// Encode `t` as a form response.
pub fn form<T: serde::Serialize>(t: T) -> Response {
    // TODO: think about how to handle errors
    http::Response::builder()
        .status(http::status::StatusCode::OK)
        .header("Content-Type", "application/x-www-form-urlencoded")
        .body(Body::from(
            serde_urlencoded::to_string(&t).unwrap().into_bytes(),
        ))
        .unwrap()
}