-
Notifications
You must be signed in to change notification settings - Fork 322
/
Copy pathforms.rs
55 lines (47 loc) · 1.95 KB
/
forms.rs
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
use futures::future::FutureObj;
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_qs::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_qs::to_string(&t).unwrap().into_bytes()))
.unwrap()
}