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