starberry_core/context.rs
1use std::io::BufReader;
2use std::pin::Pin;
3use std::{collections::HashMap, sync::Arc};
4use std::net::TcpStream;
5use std::future::{ready, Ready};
6use std::any::{Any, TypeId};
7
8use akari::Object;
9use once_cell::sync::Lazy;
10
11use crate::app::{application::App, urls::Url};
12use crate::http::response::HttpResponse;
13use crate::http::{http_value::{HttpMethod, MultiForm, UrlEncodedForm}, request::{HttpMeta, HttpRequestBody}};
14
15pub trait SendResponse {
16 fn send(&self, stream: &mut TcpStream);
17}
18
19/// The `RequestContext` struct is used to hold the context of a request.
20pub struct Rc {
21 pub meta: HttpMeta,
22 pub body: HttpRequestBody,
23 pub reader: BufReader<TcpStream>,
24 pub app: Arc<App>,
25 pub endpoint: Arc<Url>,
26 pub response: HttpResponse,
27
28 /// Type-based extension storage, typically used by middleware
29 /// Each type can have exactly one value
30 params: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
31
32 /// String-based extension storage, typically used by application code
33 /// Multiple values of the same type can be stored with different keys
34 locals: HashMap<String, Box<dyn Any + Send + Sync>>,
35}
36
37impl Rc {
38 pub fn new(
39 meta: HttpMeta,
40 body: HttpRequestBody,
41 reader: BufReader<TcpStream>,
42 app: Arc<App>,
43 endpoint: Arc<Url>,
44 ) -> Self {
45 Self {
46 meta,
47 body,
48 reader,
49 app,
50 endpoint,
51 response: HttpResponse::default(),
52 params: HashMap::new(),
53 locals: HashMap::new(),
54 }
55 }
56
57 pub async fn handle(app: Arc<App>, stream: TcpStream) -> Self {
58 // Create one BufReader up-front, pass this throughout.
59 let mut reader = BufReader::new(stream);
60 let meta = HttpMeta::from_request_stream(&mut reader, &app.connection_config)
61 .await
62 .unwrap_or_default();
63
64 let body = HttpRequestBody::Unparsed;
65 let endpoint = app
66 .root_url
67 .clone()
68 .walk_str(meta.path())
69 .await;
70
71 Rc::new(meta, body, reader, app.clone(), endpoint.clone())
72 }
73
74 pub async fn run(mut self) {
75 let endpoint = self.endpoint.clone();
76 if !endpoint.clone().request_check(&mut self).await {
77 self.response.send(self.reader.get_mut());
78 return;
79 }
80 let parsed = endpoint.run(self);
81 parsed.await.send_response();
82 }
83
84 pub fn send_response(mut self) {
85 self.response.send(self.reader.get_mut());
86 }
87
88 pub fn meta(&self) -> &HttpMeta {
89 &self.meta
90 }
91
92 pub fn app(&self) -> Arc<App> {
93 self.app.clone()
94 }
95
96 pub fn endpoint(&self) -> Arc<Url> {
97 self.endpoint.clone()
98 }
99
100 pub fn parse_body(&mut self) {
101 if let HttpRequestBody::Unparsed = self.body {
102 self.body = HttpRequestBody::parse(
103 &mut self.reader,
104 self.endpoint.get_max_body_size().unwrap_or(self.app.get_max_body_size()),
105 &mut self.meta.header,
106 );
107 }
108 }
109
110 pub fn get_path(&mut self, part: usize) -> String {
111 self.meta.get_path(part)
112 }
113
114 pub fn path(&self) -> &str {
115 self.meta.path()
116 }
117
118 /// Returns the method of the request.
119 pub fn method(&mut self) -> &HttpMethod {
120 self.meta.method()
121 }
122
123 pub fn get_cookies(&mut self) -> &HashMap<String, String> {
124 self.meta.get_cookies()
125 }
126
127 pub fn get_cookie(&mut self, key: &str) -> Option<String> {
128 self.meta.get_cookie(key)
129 }
130
131 pub fn get_cookie_or_default(&mut self, key: &str) -> String {
132 self.meta.get_cookie_or_default(key)
133 }
134
135 pub fn form(&mut self) -> Option<&UrlEncodedForm> {
136 self.parse_body();
137 if let HttpRequestBody::Form(ref data) = self.body {
138 Some(data)
139 } else {
140 None
141 }
142 }
143
144 pub fn form_or_default(&mut self) -> &UrlEncodedForm {
145 self.form().unwrap_or_else(|| {
146 static EMPTY: Lazy<UrlEncodedForm> = Lazy::new(|| HashMap::new().into());
147 &EMPTY
148 })
149 }
150
151 pub fn files(&mut self) -> Option<&MultiForm> {
152 self.parse_body();
153 if let HttpRequestBody::Files(ref data) = self.body {
154 Some(data)
155 } else {
156 None
157 }
158 }
159
160 pub fn files_or_default(&mut self) -> &MultiForm {
161 self.files().unwrap_or_else(|| {
162 static EMPTY: Lazy<MultiForm> = Lazy::new(|| HashMap::new().into());
163 &EMPTY
164 })
165 }
166
167 pub fn json(&mut self) -> Option<&Object> {
168 self.parse_body();
169 if let HttpRequestBody::Json(ref data) = self.body {
170 Some(data)
171 } else {
172 None
173 }
174 }
175
176 pub fn json_or_default(&mut self) -> &Object {
177 self.json().unwrap_or_else(|| {
178 static EMPTY: Lazy<Object> = Lazy::new(|| Object::new(""));
179 &EMPTY
180 })
181 }
182
183 //
184 // Type-based params methods (for middleware)
185 //
186
187 /// Stores a value in the type-based params storage.
188 /// Any previous value of the same type will be replaced.
189 ///
190 /// # Examples
191 ///
192 /// ```rust
193 /// let mut req = HttpRequest::default();
194 ///
195 /// // Store authentication information
196 /// req.set_param(User { id: 123, name: "Alice".to_string() });
197 ///
198 /// // Store timing information
199 /// req.set_param(RequestTimer::start());
200 /// ```
201 pub fn set_param<T: 'static + Send + Sync>(&mut self, value: T) {
202 self.params.insert(TypeId::of::<T>(), Box::new(value));
203 }
204
205 /// Retrieves a reference to a value from the type-based params storage.
206 /// Returns `None` if no value of this type has been stored.
207 ///
208 /// # Examples
209 ///
210 /// ```rust
211 /// // In an authentication middleware
212 /// if let Some(user) = req.param::<User>() {
213 /// println!("Request by: {}", user.name);
214 /// // Proceed with authenticated user
215 /// } else {
216 /// return HttpResponse::unauthorized();
217 /// }
218 /// ```
219 pub fn param<T: 'static + Send + Sync>(&self) -> Option<&T> {
220 self.params
221 .get(&TypeId::of::<T>())
222 .and_then(|boxed| boxed.downcast_ref::<T>())
223 }
224
225 /// Retrieves a mutable reference to a value from the type-based params storage.
226 /// Returns `None` if no value of this type has been stored.
227 ///
228 /// # Examples
229 ///
230 /// ```rust
231 /// // Update a request timer
232 /// if let Some(timer) = req.param_mut::<RequestTimer>() {
233 /// timer.mark("after_db_query");
234 /// }
235 /// ```
236 pub fn param_mut<T: 'static + Send + Sync>(&mut self) -> Option<&mut T> {
237 self.params
238 .get_mut(&TypeId::of::<T>())
239 .and_then(|boxed| boxed.downcast_mut::<T>())
240 }
241
242 /// Removes a value from the type-based params storage and returns it.
243 /// Returns `None` if no value of this type has been stored.
244 ///
245 /// # Examples
246 ///
247 /// ```rust
248 /// // Take ownership of a value
249 /// if let Some(token) = req.take_param::<AuthToken>() {
250 /// // Use and consume the token
251 /// validate_token(token);
252 /// }
253 /// ```
254 pub fn take_param<T: 'static + Send + Sync>(&mut self) -> Option<T> {
255 self.params
256 .remove(&TypeId::of::<T>())
257 .and_then(|boxed| boxed.downcast::<T>().ok())
258 .map(|boxed| *boxed)
259 }
260
261 //
262 // String-based locals methods (for application code)
263 //
264
265 /// Stores a value in the string-based locals storage with the given key.
266 /// Any previous value with the same key will be replaced.
267 ///
268 /// # Examples
269 ///
270 /// ```rust
271 /// let mut req = HttpRequest::default();
272 ///
273 /// // Store various data with descriptive keys
274 /// req.set_local("user_id", 123);
275 /// req.set_local("is_premium", true);
276 /// req.set_local("cart_items", vec!["item1", "item2"]);
277 /// ```
278 pub fn set_local<T: 'static + Send + Sync>(&mut self, key: impl Into<String>, value: T) {
279 self.locals.insert(key.into(), Box::new(value));
280 }
281
282 /// Retrieves a reference to a value from the string-based locals storage by key.
283 /// Returns `None` if no value with this key exists or if the type doesn't match.
284 ///
285 /// # Examples
286 ///
287 /// ```rust
288 /// // In a request handler
289 /// if let Some(is_premium) = req.local::<bool>("is_premium") {
290 /// if *is_premium {
291 /// // Show premium content
292 /// }
293 /// }
294 ///
295 /// // With different types
296 /// let user_id = req.local::<i32>("user_id");
297 /// let items = req.local::<Vec<String>>("cart_items");
298 /// ```
299 pub fn local<T: 'static + Send + Sync>(&self, key: &str) -> Option<&T> {
300 self.locals
301 .get(key)
302 .and_then(|boxed| boxed.downcast_ref::<T>())
303 }
304
305 /// Retrieves a mutable reference to a value from the string-based locals storage by key.
306 /// Returns `None` if no value with this key exists or if the type doesn't match.
307 ///
308 /// # Examples
309 ///
310 /// ```rust
311 /// // Modify a list of items
312 /// if let Some(items) = req.local_mut::<Vec<String>>("cart_items") {
313 /// items.push("new_item".to_string());
314 /// }
315 /// ```
316 pub fn local_mut<T: 'static + Send + Sync>(&mut self, key: &str) -> Option<&mut T> {
317 self.locals
318 .get_mut(key)
319 .and_then(|boxed| boxed.downcast_mut::<T>())
320 }
321
322 /// Removes a value from the string-based locals storage and returns it.
323 /// Returns `None` if no value with this key exists or if the type doesn't match.
324 ///
325 /// # Examples
326 ///
327 /// ```rust
328 /// // Take ownership of a value
329 /// if let Some(token) = req.take_local::<String>("session_token") {
330 /// // Use and consume the token
331 /// validate_and_destroy_token(token);
332 /// }
333 /// ```
334 pub fn take_local<T: 'static + Send + Sync>(&mut self, key: &str) -> Option<T> {
335 self.locals
336 .remove(key)
337 .and_then(|boxed| boxed.downcast::<T>().ok())
338 .map(|boxed| *boxed)
339 }
340
341 /// Returns all keys currently stored in the locals map
342 ///
343 /// # Examples
344 ///
345 /// ```rust
346 /// // Inspect what data is attached to the request
347 /// for key in req.local_keys() {
348 /// println!("Request has data with key: {}", key);
349 /// }
350 /// ```
351 pub fn local_keys(&self) -> Vec<&str> {
352 self.locals.keys().map(|s| s.as_str()).collect()
353 }
354
355 //
356 // Utility bridging methods
357 //
358
359 /// Exports a param value to the locals storage with the given key.
360 /// The value must implement Clone. Does nothing if the param doesn't exist.
361 ///
362 /// # Examples
363 ///
364 /// ```rust
365 /// // Make the authenticated user available in locals for convenience
366 /// req.export_param_to_local::<User>("current_user");
367 /// ```
368 pub fn export_param_to_local<T: 'static + Clone + Send + Sync>(&mut self, key: impl Into<String>) {
369 if let Some(value) = self.param::<T>() {
370 let cloned = value.clone();
371 self.set_local(key, cloned);
372 }
373 }
374
375 /// Imports a local value into the params storage.
376 /// The value must implement Clone. Does nothing if the local doesn't exist.
377 ///
378 /// # Examples
379 ///
380 /// ```rust
381 /// // Make a manually set user available to middleware expecting it in params
382 /// req.import_local_to_param::<User>("manual_user");
383 /// ```
384 pub fn import_local_to_param<T: 'static + Clone + Send + Sync>(&mut self, key: &str) {
385 if let Some(value) = self.local::<T>(key) {
386 let cloned = value.clone();
387 self.set_param(cloned);
388 }
389 }
390
391 /// Converts this response into a Future that resolves to itself.
392 /// Useful for middleware functions that need to return a Future<Output = HttpResponse>.
393 pub fn future(self) -> impl Future<Output = Rc> + Send {
394 ready(self)
395 }
396
397 /// Creates a boxed future from this response (useful for trait objects).
398 pub fn boxed_future(self) -> Pin<Box<dyn Future<Output = Rc> + Send>> {
399 Box::pin(self.future())
400 }
401}