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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
mod body;
mod cookie;
mod encoded;
mod header;
pub use self::body::{Payload, ReqBody};
pub use self::cookie::Cookies;
pub use self::encoded::{EncodedStr, FromEncodedStr};
pub use self::header::FromHeaderValue;
use futures::Future;
use http;
use http::header::{HeaderMap, HeaderValue};
use http::Request;
use http::{Response, StatusCode};
use mime::Mime;
use self::cookie::{CookieJar, CookieManager};
use error::{bad_request, Error};
type Task = Box<dyn Future<Item = (), Error = ()> + Send + 'static>;
#[derive(Debug)]
pub struct Input {
request: Request<ReqBody>,
#[cfg_attr(feature = "cargo-clippy", allow(option_option))]
media_type: Option<Option<Mime>>,
cookie_manager: CookieManager,
response_headers: Option<HeaderMap>,
}
impl Input {
pub(crate) fn new(request: Request<ReqBody>) -> Input {
Input {
request,
media_type: None,
cookie_manager: Default::default(),
response_headers: None,
}
}
pub fn method(&self) -> &http::Method {
self.request.method()
}
pub fn uri(&self) -> &http::Uri {
self.request.uri()
}
pub fn version(&self) -> http::Version {
self.request.version()
}
pub fn headers(&self) -> &http::HeaderMap {
self.request.headers()
}
pub fn extensions(&self) -> &http::Extensions {
self.request.extensions()
}
pub fn body(&self) -> &ReqBody {
self.request.body()
}
pub fn body_mut(&mut self) -> &mut ReqBody {
self.request.body_mut()
}
pub fn content_type(&mut self) -> Result<Option<&Mime>, Error> {
match self.media_type {
Some(ref m) => Ok(m.as_ref()),
None => {
let mime = match self.request.headers().get(http::header::CONTENT_TYPE) {
Some(raw) => {
let raw_str = raw.to_str().map_err(bad_request)?;
let mime = raw_str.parse().map_err(bad_request)?;
Some(mime)
}
None => None,
};
Ok(self.media_type.get_or_insert(mime).as_ref())
}
}
}
#[doc(hidden)]
#[deprecated(since = "0.13.5", note = "use `Input::cookies2()` instead.")]
pub fn cookies(&mut self) -> Result<&mut CookieJar, Error> {
self.cookies2()?;
Ok(self.cookie_manager.jar().expect("should be available"))
}
pub fn cookies2(&mut self) -> Result<Cookies, Error> {
self.cookie_manager
.ensure_initialized(self.request.headers())
}
pub fn response_headers(&mut self) -> &mut HeaderMap {
self.response_headers.get_or_insert_with(Default::default)
}
#[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
pub(crate) fn finalize<T>(
self,
output: Result<Response<T>, Error>,
) -> (Response<Result<Option<T>, Error>>, Option<Task>) {
let (_parts, body) = self.request.into_parts();
let mut upgraded_opt = None;
let mut response = match output {
Ok(mut response) => match body.into_upgraded() {
Some(upgraded) => {
upgraded_opt = Some(upgraded);
*response.status_mut() = StatusCode::SWITCHING_PROTOCOLS;
response.map(|_bd| Ok(None))
}
None => response.map(|bd| Ok(Some(bd))),
},
Err(err) => err.into_response().map(Err),
};
if let Some(jar) = self.cookie_manager.into_inner() {
for cookie in jar.delta() {
let val = HeaderValue::from_str(&cookie.encoded().to_string()).unwrap();
response.headers_mut().append(http::header::SET_COOKIE, val);
}
}
if let Some(headers) = self.response_headers {
response.headers_mut().extend(headers);
}
(response, upgraded_opt)
}
}