1use std::any::{Any, TypeId};
5use std::collections::HashMap;
6
7use bytes::Bytes;
8use hyper::{Method, StatusCode};
9
10use crate::error::{Error, Result};
11
12#[derive(Default)]
16pub struct Context {
17 inner: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
18}
19
20impl Context {
21 pub fn insert<T: Any + Send + Sync>(&mut self, value: T) {
23 self.inner.insert(TypeId::of::<T>(), Box::new(value));
24 }
25
26 pub fn get<T: Any + Send + Sync>(&self) -> Option<&T> {
28 self.inner
29 .get(&TypeId::of::<T>())
30 .and_then(|b| b.downcast_ref::<T>())
31 }
32
33 pub fn get_mut<T: Any + Send + Sync>(&mut self) -> Option<&mut T> {
35 self.inner
36 .get_mut(&TypeId::of::<T>())
37 .and_then(|b| b.downcast_mut::<T>())
38 }
39}
40
41pub struct Request {
43 method: Method,
44 path: String,
45 query: String,
46 headers: HashMap<String, String>,
47 params: HashMap<String, String>,
48 body: Bytes,
49 ctx: Context,
50}
51
52impl Request {
53 pub(crate) fn new(
54 method: Method,
55 path: String,
56 query: String,
57 headers: HashMap<String, String>,
58 body: Bytes,
59 ) -> Self {
60 Self {
61 method,
62 path,
63 query,
64 headers,
65 params: HashMap::new(),
66 body,
67 ctx: Context::default(),
68 }
69 }
70
71 pub fn method(&self) -> &Method {
73 &self.method
74 }
75
76 pub fn path(&self) -> &str {
78 &self.path
79 }
80
81 pub fn query_string(&self) -> &str {
83 &self.query
84 }
85
86 pub fn query(&self) -> FormData {
88 FormData::from_urlencoded(&self.query)
89 }
90
91 pub fn header(&self, name: &str) -> Option<&str> {
93 self.headers
94 .get(&name.to_ascii_lowercase())
95 .map(|s| s.as_str())
96 }
97
98 pub fn param(&self, name: &str) -> Option<&str> {
100 self.params.get(name).map(|s| s.as_str())
101 }
102
103 pub fn body(&self) -> &[u8] {
105 &self.body
106 }
107
108 pub fn body_text(&self) -> Result<&str> {
110 std::str::from_utf8(&self.body)
111 .map_err(|_| Error::BadRequest("body is not valid utf-8".into()))
112 }
113
114 pub fn form(&self) -> Result<FormData> {
116 let text = self.body_text()?;
117 Ok(FormData::from_urlencoded(text))
118 }
119
120 pub fn ctx(&self) -> &Context {
122 &self.ctx
123 }
124
125 pub fn ctx_mut(&mut self) -> &mut Context {
127 &mut self.ctx
128 }
129
130 pub(crate) fn set_params(&mut self, params: HashMap<String, String>) {
131 self.params = params;
132 }
133
134 #[doc(hidden)]
141 #[cfg(feature = "integration-test")]
142 pub fn __integration_test_fake(path: String, headers: HashMap<String, String>) -> Self {
143 Self::new(
144 hyper::Method::POST,
145 path,
146 String::new(),
147 headers,
148 bytes::Bytes::new(),
149 )
150 }
151}
152
153#[derive(Debug, Default, Clone)]
157pub struct FormData {
158 fields: HashMap<String, String>,
159}
160
161impl FormData {
162 pub fn from_urlencoded(input: &str) -> Self {
164 let mut fields = HashMap::new();
165 for pair in input.split('&') {
166 if pair.is_empty() {
167 continue;
168 }
169 let (raw_key, raw_val) = match pair.split_once('=') {
170 Some((k, v)) => (k, v),
171 None => (pair, ""),
172 };
173 let key = decode(raw_key);
174 let val = decode(raw_val);
175 fields.insert(key, val);
176 }
177 Self { fields }
178 }
179
180 pub fn get(&self, key: &str) -> Option<&str> {
182 self.fields.get(key).map(|s| s.as_str())
183 }
184
185 pub fn required(&self, key: &str) -> Result<&str> {
187 self.get(key)
188 .ok_or_else(|| Error::BadRequest(format!("field {key} is required")))
189 }
190
191 pub fn bool_flag(&self, key: &str) -> bool {
193 matches!(self.get(key), Some("on" | "true" | "1" | "yes"))
195 }
196
197 pub fn contains(&self, key: &str) -> bool {
199 self.fields.contains_key(key)
200 }
201
202 pub fn as_map(&self) -> &HashMap<String, String> {
204 &self.fields
205 }
206}
207
208fn decode(s: &str) -> String {
209 let spaced = s.replace('+', " ");
210 urlencoding::decode(&spaced)
211 .map(|c| c.into_owned())
212 .unwrap_or(spaced)
213}
214
215pub struct Response {
218 pub status: StatusCode,
219 pub headers: Vec<(String, String)>,
220 pub body: Bytes,
221}
222
223impl Response {
224 pub fn new(status: StatusCode, body: impl Into<Bytes>) -> Self {
226 Self {
227 status,
228 headers: Vec::new(),
229 body: body.into(),
230 }
231 }
232
233 pub fn ok(body: impl Into<Bytes>) -> Self {
235 Self::new(StatusCode::OK, body)
236 }
237
238 pub fn html(body: impl Into<String>) -> Self {
240 let text = body.into();
241 Self {
242 status: StatusCode::OK,
243 headers: vec![("content-type".into(), "text/html; charset=utf-8".into())],
244 body: Bytes::from(text),
245 }
246 }
247
248 pub fn json_raw(body: impl Into<String>) -> Self {
250 let text = body.into();
251 Self {
252 status: StatusCode::OK,
253 headers: vec![("content-type".into(), "application/json".into())],
254 body: Bytes::from(text),
255 }
256 }
257
258 pub fn redirect(to: impl Into<String>) -> Self {
260 let url = to.into();
261 Self {
262 status: StatusCode::SEE_OTHER,
263 headers: vec![("location".into(), url)],
264 body: Bytes::new(),
265 }
266 }
267
268 pub fn text(body: impl Into<String>) -> Self {
270 let text = body.into();
271 Self {
272 status: StatusCode::OK,
273 headers: vec![("content-type".into(), "text/plain; charset=utf-8".into())],
274 body: Bytes::from(text),
275 }
276 }
277
278 pub fn with_header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
280 self.headers.push((name.into(), value.into()));
281 self
282 }
283
284 pub fn with_status(mut self, status: StatusCode) -> Self {
286 self.status = status;
287 self
288 }
289}
290
291pub(crate) fn response_from_error(err: &Error) -> Response {
292 let status = StatusCode::from_u16(err.status()).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
293 let body = err.client_message().to_string();
294 Response {
295 status,
296 headers: vec![("content-type".into(), "text/plain; charset=utf-8".into())],
297 body: Bytes::from(body),
298 }
299}