1use crate::cookies::Cookies;
4use crate::error::{Error, Result};
5use serde::de::DeserializeOwned;
6use std::cell::OnceCell;
7use std::collections::HashMap;
8
9#[derive(Debug, Clone)]
11pub struct Params {
12 inner: HashMap<String, String>,
13}
14
15impl Params {
16 pub fn new() -> Self {
17 Self {
18 inner: HashMap::new(),
19 }
20 }
21
22 pub fn insert(&mut self, key: String, value: String) {
23 self.inner.insert(key, value);
24 }
25
26 pub fn get(&self, key: &str) -> Result<&str> {
27 self.inner
28 .get(key)
29 .map(|s| s.as_str())
30 .ok_or_else(|| Error::bad_request(format!("Missing param: {}", key)))
31 }
32
33 pub fn get_opt(&self, key: &str) -> Option<&str> {
34 self.inner.get(key).map(|s| s.as_str())
35 }
36}
37
38impl Default for Params {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44#[derive(Debug, Clone)]
46pub struct Query {
47 inner: HashMap<String, String>,
48 raw: String,
49}
50
51impl Query {
52 pub fn new() -> Self {
53 Self {
54 inner: HashMap::new(),
55 raw: String::new(),
56 }
57 }
58
59 pub fn parse_query_string(query: &str) -> Self {
63 let mut q = Query {
64 inner: HashMap::new(),
65 raw: query.to_string(),
66 };
67 for pair in query.split('&').filter(|p| !p.is_empty()) {
68 let (key, value) = pair.split_once('=').unwrap_or((pair, ""));
69 q.inner
70 .insert(decode_component(key), decode_component(value));
71 }
72 q
73 }
74
75 pub fn get(&self, key: &str) -> Option<&str> {
76 self.inner.get(key).map(|s| s.as_str())
77 }
78
79 pub fn contains(&self, key: &str) -> bool {
80 self.inner.contains_key(key)
81 }
82
83 pub fn is_empty(&self) -> bool {
84 self.inner.is_empty()
85 }
86
87 pub fn parse<T: DeserializeOwned>(&self) -> Result<T> {
90 serde_urlencoded::from_str(&self.raw)
91 .map_err(|e| Error::bad_request(format!("Invalid query params: {}", e)))
92 }
93}
94
95fn decode_component(s: &str) -> String {
96 let s = s.replace('+', " ");
97 urlencoding::decode(&s).map(|c| c.into_owned()).unwrap_or(s)
98}
99
100impl Default for Query {
101 fn default() -> Self {
102 Self::new()
103 }
104}
105
106#[derive(Default)]
108pub(crate) struct StateMap {
109 values: HashMap<std::any::TypeId, Box<dyn std::any::Any + Send + Sync>>,
110}
111
112impl StateMap {
113 pub(crate) fn insert<T: Send + Sync + 'static>(&mut self, value: T) {
114 self.values
115 .insert(std::any::TypeId::of::<T>(), Box::new(value));
116 }
117
118 pub(crate) fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
119 self.values
120 .get(&std::any::TypeId::of::<T>())
121 .and_then(|v| v.downcast_ref::<T>())
122 }
123}
124
125pub struct Request {
127 pub method: http::Method,
128 pub uri: http::Uri,
129 pub headers: http::HeaderMap,
130 pub body: bytes::Bytes,
131 pub params: Params,
132 pub query: Query,
133 pub extensions: http::Extensions,
136 pub remote_addr: Option<std::net::SocketAddr>,
138 pub(crate) state: Option<std::sync::Arc<StateMap>>,
139 cookies: OnceCell<Cookies>,
140}
141
142impl Request {
143 pub fn new(
144 method: http::Method,
145 uri: http::Uri,
146 headers: http::HeaderMap,
147 body: bytes::Bytes,
148 ) -> Self {
149 let query = uri
150 .query()
151 .map(Query::parse_query_string)
152 .unwrap_or_default();
153
154 Self {
155 method,
156 uri,
157 headers,
158 body,
159 params: Params::new(),
160 query,
161 extensions: http::Extensions::new(),
162 remote_addr: None,
163 state: None,
164 cookies: OnceCell::new(),
165 }
166 }
167
168 pub fn state<T: Send + Sync + 'static>(&self) -> Result<&T> {
173 self.try_state().ok_or_else(|| {
174 Error::internal(format!(
175 "State of type {} is not registered",
176 std::any::type_name::<T>()
177 ))
178 })
179 }
180
181 pub fn try_state<T: Send + Sync + 'static>(&self) -> Option<&T> {
183 self.state.as_ref()?.get::<T>()
184 }
185
186 pub fn cookies(&self) -> &Cookies {
188 self.cookies.get_or_init(|| {
189 if let Some(cookie_header) = self.headers.get("cookie") {
190 if let Ok(cookie_str) = cookie_header.to_str() {
191 Cookies::parse(cookie_str)
192 } else {
193 Cookies::new()
194 }
195 } else {
196 Cookies::new()
197 }
198 })
199 }
200
201 pub fn form<T: DeserializeOwned>(&self) -> Result<T> {
206 self.require_form_content_type()?;
207 serde_urlencoded::from_bytes(&self.body)
208 .map_err(|e| Error::bad_request(format!("Invalid form data: {}", e)))
209 }
210
211 pub fn form_data(&self) -> Result<Query> {
213 self.require_form_content_type()?;
214 let text = std::str::from_utf8(&self.body)
215 .map_err(|_| Error::bad_request("Form data is not valid UTF-8"))?;
216 Ok(Query::parse_query_string(text))
217 }
218
219 fn require_form_content_type(&self) -> Result<()> {
220 let is_form = self
221 .headers
222 .get(http::header::CONTENT_TYPE)
223 .and_then(|v| v.to_str().ok())
224 .map(|v| {
225 v.split(';')
226 .next()
227 .unwrap_or("")
228 .trim()
229 .eq_ignore_ascii_case("application/x-www-form-urlencoded")
230 })
231 .unwrap_or(false);
232 if is_form {
233 Ok(())
234 } else {
235 Err(Error::custom(
236 415,
237 "Expected content-type application/x-www-form-urlencoded",
238 ))
239 }
240 }
241
242 pub fn json<T: DeserializeOwned>(&self) -> Result<T> {
244 serde_json::from_slice(&self.body)
245 .map_err(|e| Error::bad_request(format!("Invalid JSON: {}", e)))
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 fn form_request(content_type: Option<&str>, body: &'static str) -> Request {
254 let mut headers = http::HeaderMap::new();
255 if let Some(ct) = content_type {
256 headers.insert(http::header::CONTENT_TYPE, ct.parse().unwrap());
257 }
258 Request::new(
259 http::Method::POST,
260 "/".parse().unwrap(),
261 headers,
262 bytes::Bytes::from_static(body.as_bytes()),
263 )
264 }
265
266 #[derive(Debug, serde::Deserialize, PartialEq)]
267 struct Signup {
268 name: String,
269 age: u8,
270 newsletter: Option<bool>,
271 }
272
273 #[test]
274 fn query_decodes_keys_values_and_plus() {
275 let q = Query::parse_query_string("q=rust+web&na%20me=a%26b&flag&&x=1");
276 assert_eq!(q.get("q"), Some("rust web"));
277 assert_eq!(q.get("na me"), Some("a&b"));
278 assert_eq!(q.get("flag"), Some(""));
279 assert!(q.contains("x") && !q.is_empty());
280 }
281
282 #[test]
283 fn query_parse_converts_types() {
284 let q = Query::parse_query_string("name=Ann&age=31&newsletter=true");
285 assert_eq!(
286 q.parse::<Signup>().unwrap(),
287 Signup {
288 name: "Ann".into(),
289 age: 31,
290 newsletter: Some(true)
291 }
292 );
293 let bad = Query::parse_query_string("name=Ann&age=old");
294 assert_eq!(bad.parse::<Signup>().unwrap_err().status_code(), 400);
295 }
296
297 #[test]
298 fn form_typed_and_untyped() {
299 let req = form_request(
300 Some("application/x-www-form-urlencoded; charset=UTF-8"),
301 "name=Ann+Lee&age=31",
302 );
303 let signup: Signup = req.form().unwrap();
304 assert_eq!(
305 signup,
306 Signup {
307 name: "Ann Lee".into(),
308 age: 31,
309 newsletter: None
310 }
311 );
312 assert_eq!(req.form_data().unwrap().get("name"), Some("Ann Lee"));
313 }
314
315 #[test]
316 fn form_rejects_wrong_content_type_and_bad_values() {
317 let json = form_request(Some("application/json"), "name=Ann&age=1");
318 assert_eq!(json.form::<Signup>().unwrap_err().status_code(), 415);
319 let none = form_request(None, "name=Ann&age=1");
320 assert_eq!(none.form_data().unwrap_err().status_code(), 415);
321
322 let bad = form_request(
323 Some("application/x-www-form-urlencoded"),
324 "name=Ann&age=300",
325 );
326 assert_eq!(bad.form::<Signup>().unwrap_err().status_code(), 400);
327 }
328}