1use crate::cookies::Cookies;
4use crate::error::{Error, Result};
5use serde::de::DeserializeOwned;
6use std::collections::HashMap;
7use std::sync::{Arc, OnceLock};
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 pub fn parse<T: std::str::FromStr>(&self, key: &str) -> Result<T> {
41 self.get(key)?
42 .parse()
43 .map_err(|_| Error::bad_request(format!("Invalid path parameter '{}'", key)))
44 }
45}
46
47impl Default for Params {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53#[derive(Debug, Clone)]
55pub struct Query {
56 inner: HashMap<String, String>,
57 raw: String,
58}
59
60impl Query {
61 pub fn new() -> Self {
62 Self {
63 inner: HashMap::new(),
64 raw: String::new(),
65 }
66 }
67
68 pub fn parse_query_string(query: &str) -> Self {
72 let mut q = Query {
73 inner: HashMap::new(),
74 raw: query.to_string(),
75 };
76 for pair in query.split('&').filter(|p| !p.is_empty()) {
77 let (key, value) = pair.split_once('=').unwrap_or((pair, ""));
78 q.inner
79 .insert(decode_component(key), decode_component(value));
80 }
81 q
82 }
83
84 pub fn get(&self, key: &str) -> Option<&str> {
85 self.inner.get(key).map(|s| s.as_str())
86 }
87
88 pub fn contains(&self, key: &str) -> bool {
89 self.inner.contains_key(key)
90 }
91
92 pub fn is_empty(&self) -> bool {
93 self.inner.is_empty()
94 }
95
96 pub fn parse<T: DeserializeOwned>(&self) -> Result<T> {
99 serde_urlencoded::from_str(&self.raw)
100 .map_err(|e| Error::bad_request(format!("Invalid query params: {}", e)))
101 }
102}
103
104fn decode_component(s: &str) -> String {
105 let s = s.replace('+', " ");
106 urlencoding::decode(&s).map(|c| c.into_owned()).unwrap_or(s)
107}
108
109impl Default for Query {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115#[derive(Debug, Clone)]
117pub struct ClientCertificate(pub Vec<u8>);
118
119#[derive(Default)]
121pub(crate) struct StateMap {
122 values: HashMap<std::any::TypeId, Arc<dyn std::any::Any + Send + Sync>>,
123}
124
125impl StateMap {
126 pub(crate) fn insert<T: Send + Sync + 'static>(&mut self, value: T) {
127 self.values
128 .insert(std::any::TypeId::of::<T>(), Arc::new(value));
129 }
130
131 pub(crate) fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
132 self.values
133 .get(&std::any::TypeId::of::<T>())
134 .and_then(|v| v.downcast_ref::<T>())
135 }
136
137 pub(crate) fn get_arc<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
138 self.values
139 .get(&std::any::TypeId::of::<T>())
140 .and_then(|v| v.clone().downcast::<T>().ok())
141 }
142}
143
144pub struct Request {
146 pub method: http::Method,
147 pub uri: http::Uri,
148 pub headers: http::HeaderMap,
149 pub body: bytes::Bytes,
150 pub params: Params,
151 pub query: Query,
152 pub extensions: http::Extensions,
155 pub remote_addr: Option<std::net::SocketAddr>,
157 pub matched_path: Option<std::sync::Arc<str>>,
161 pub secure: bool,
164 pub(crate) state: Option<std::sync::Arc<StateMap>>,
165 pub(crate) shutdown: Option<crate::shutdown::Shutdown>,
166 pub(crate) stream: Option<crate::upload::BodyStream>,
167 cookies: OnceLock<Cookies>,
168}
169
170impl Request {
171 pub fn new(
172 method: http::Method,
173 uri: http::Uri,
174 headers: http::HeaderMap,
175 body: bytes::Bytes,
176 ) -> Self {
177 let query = uri
178 .query()
179 .map(Query::parse_query_string)
180 .unwrap_or_default();
181
182 Self {
183 method,
184 uri,
185 headers,
186 body,
187 params: Params::new(),
188 query,
189 extensions: http::Extensions::new(),
190 remote_addr: None,
191 matched_path: None,
192 secure: false,
193 state: None,
194 shutdown: None,
195 stream: None,
196 cookies: OnceLock::new(),
197 }
198 }
199
200 pub fn accepts<'a>(&self, offered: &[&'a str]) -> Option<&'a str> {
203 let header = self
204 .headers
205 .get(http::header::ACCEPT)
206 .and_then(|v| v.to_str().ok())
207 .unwrap_or("");
208 crate::negotiate::best(header, offered)
209 }
210
211 pub fn accepts_language<'a>(&self, offered: &[&'a str]) -> Option<&'a str> {
213 let header = self
214 .headers
215 .get(http::header::ACCEPT_LANGUAGE)
216 .and_then(|v| v.to_str().ok())
217 .unwrap_or("");
218 crate::negotiate::best_language(header, offered)
219 }
220
221 pub fn body_stream(&mut self) -> Result<crate::upload::BodyStream> {
226 Ok(self.stream.take().unwrap_or_else(|| {
227 crate::upload::BodyStream::from_bytes(std::mem::take(&mut self.body), None)
228 }))
229 }
230
231 pub(crate) fn duplicate(&self) -> Request {
233 let mut copy = Request::new(
234 self.method.clone(),
235 self.uri.clone(),
236 self.headers.clone(),
237 self.body.clone(),
238 );
239 copy.params = self.params.clone();
240 copy.extensions = self.extensions.clone();
241 copy.remote_addr = self.remote_addr;
242 copy.matched_path = self.matched_path.clone();
243 copy.secure = self.secure;
244 copy.state = self.state.clone();
245 copy.shutdown = self.shutdown.clone();
246 copy
247 }
248
249 pub fn is_secure(&self) -> bool {
251 self.secure
252 }
253
254 pub fn csp_nonce(&self) -> Option<&str> {
258 self.extensions
259 .get::<crate::security_headers::CspNonce>()
260 .map(|n| n.0.as_str())
261 }
262
263 pub fn client_certificate(&self) -> Option<&[u8]> {
269 self.extensions
270 .get::<ClientCertificate>()
271 .map(|c| c.0.as_slice())
272 }
273
274 pub fn state<T: Send + Sync + 'static>(&self) -> Result<&T> {
279 self.try_state().ok_or_else(|| {
280 Error::internal(format!(
281 "State of type {} is not registered",
282 std::any::type_name::<T>()
283 ))
284 })
285 }
286
287 pub fn shutdown_signal(&self) -> crate::shutdown::Shutdown {
292 self.shutdown
293 .clone()
294 .unwrap_or_else(crate::shutdown::Shutdown::never)
295 }
296
297 pub fn state_arc<T: Send + Sync + 'static>(&self) -> Result<Arc<T>> {
301 self.state
302 .as_ref()
303 .and_then(|s| s.get_arc::<T>())
304 .ok_or_else(|| {
305 Error::internal(format!(
306 "State of type {} is not registered",
307 std::any::type_name::<T>()
308 ))
309 })
310 }
311
312 pub fn try_state<T: Send + Sync + 'static>(&self) -> Option<&T> {
314 self.state.as_ref()?.get::<T>()
315 }
316
317 pub fn cookies(&self) -> &Cookies {
319 self.cookies.get_or_init(|| {
320 if let Some(cookie_header) = self.headers.get("cookie") {
321 if let Ok(cookie_str) = cookie_header.to_str() {
322 Cookies::parse(cookie_str)
323 } else {
324 Cookies::new()
325 }
326 } else {
327 Cookies::new()
328 }
329 })
330 }
331
332 pub fn form<T: DeserializeOwned>(&self) -> Result<T> {
337 self.require_form_content_type()?;
338 serde_urlencoded::from_bytes(&self.body)
339 .map_err(|e| Error::bad_request(format!("Invalid form data: {}", e)))
340 }
341
342 pub fn form_data(&self) -> Result<Query> {
344 self.require_form_content_type()?;
345 let text = std::str::from_utf8(&self.body)
346 .map_err(|_| Error::bad_request("Form data is not valid UTF-8"))?;
347 Ok(Query::parse_query_string(text))
348 }
349
350 fn require_form_content_type(&self) -> Result<()> {
351 let is_form = self
352 .headers
353 .get(http::header::CONTENT_TYPE)
354 .and_then(|v| v.to_str().ok())
355 .map(|v| {
356 v.split(';')
357 .next()
358 .unwrap_or("")
359 .trim()
360 .eq_ignore_ascii_case("application/x-www-form-urlencoded")
361 })
362 .unwrap_or(false);
363 if is_form {
364 Ok(())
365 } else {
366 Err(Error::custom(
367 415,
368 "Expected content-type application/x-www-form-urlencoded",
369 ))
370 }
371 }
372
373 pub fn json<T: DeserializeOwned>(&self) -> Result<T> {
375 serde_json::from_slice(&self.body)
376 .map_err(|e| Error::bad_request(format!("Invalid JSON: {}", e)))
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383
384 fn form_request(content_type: Option<&str>, body: &'static str) -> Request {
385 let mut headers = http::HeaderMap::new();
386 if let Some(ct) = content_type {
387 headers.insert(http::header::CONTENT_TYPE, ct.parse().unwrap());
388 }
389 Request::new(
390 http::Method::POST,
391 "/".parse().unwrap(),
392 headers,
393 bytes::Bytes::from_static(body.as_bytes()),
394 )
395 }
396
397 #[derive(Debug, serde::Deserialize, PartialEq)]
398 struct Signup {
399 name: String,
400 age: u8,
401 newsletter: Option<bool>,
402 }
403
404 #[test]
405 fn params_parse_typed_values() {
406 let mut p = Params::new();
407 p.insert("id".into(), "42".into());
408 p.insert("name".into(), "x".into());
409 assert_eq!(p.parse::<u64>("id").unwrap(), 42);
410 assert_eq!(p.parse::<String>("name").unwrap(), "x");
411 let bad = p.parse::<u64>("name").unwrap_err();
412 assert_eq!(bad.status_code(), 400);
413 assert_eq!(bad.message(), "Invalid path parameter 'name'");
414 assert_eq!(p.parse::<u64>("missing").unwrap_err().status_code(), 400);
415 assert!(p.parse::<u8>("id").is_ok());
416 p.insert("big".into(), "300".into());
417 assert!(p.parse::<u8>("big").is_err());
418 }
419
420 #[test]
421 fn query_decodes_keys_values_and_plus() {
422 let q = Query::parse_query_string("q=rust+web&na%20me=a%26b&flag&&x=1");
423 assert_eq!(q.get("q"), Some("rust web"));
424 assert_eq!(q.get("na me"), Some("a&b"));
425 assert_eq!(q.get("flag"), Some(""));
426 assert!(q.contains("x") && !q.is_empty());
427 }
428
429 #[test]
430 fn query_parse_converts_types() {
431 let q = Query::parse_query_string("name=Ann&age=31&newsletter=true");
432 assert_eq!(
433 q.parse::<Signup>().unwrap(),
434 Signup {
435 name: "Ann".into(),
436 age: 31,
437 newsletter: Some(true)
438 }
439 );
440 let bad = Query::parse_query_string("name=Ann&age=old");
441 assert_eq!(bad.parse::<Signup>().unwrap_err().status_code(), 400);
442 }
443
444 #[test]
445 fn form_typed_and_untyped() {
446 let req = form_request(
447 Some("application/x-www-form-urlencoded; charset=UTF-8"),
448 "name=Ann+Lee&age=31",
449 );
450 let signup: Signup = req.form().unwrap();
451 assert_eq!(
452 signup,
453 Signup {
454 name: "Ann Lee".into(),
455 age: 31,
456 newsletter: None
457 }
458 );
459 assert_eq!(req.form_data().unwrap().get("name"), Some("Ann Lee"));
460 }
461
462 #[test]
463 fn form_rejects_wrong_content_type_and_bad_values() {
464 let json = form_request(Some("application/json"), "name=Ann&age=1");
465 assert_eq!(json.form::<Signup>().unwrap_err().status_code(), 415);
466 let none = form_request(None, "name=Ann&age=1");
467 assert_eq!(none.form_data().unwrap_err().status_code(), 415);
468
469 let bad = form_request(
470 Some("application/x-www-form-urlencoded"),
471 "name=Ann&age=300",
472 );
473 assert_eq!(bad.form::<Signup>().unwrap_err().status_code(), 400);
474 }
475}