an http based roguelike rpg about jelqing
Blame: src/http/ctx.rs
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 1 | use std::{collections::HashMap, fmt::Write as _, time::Instant}; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 2 | |
782069860236 | atagen | 2026-10-10 12:44 UTC | 3 | use axum::{ |
782069860236 | atagen | 2026-10-10 12:44 UTC | 4 | body::Bytes, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 5 | extract::{FromRequest, FromRequestParts, Query, RawPathParams, Request}, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 6 | http::{HeaderMap, HeaderName, Method, StatusCode, Uri, Version, header}, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 7 | response::{IntoResponse, Response}, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 8 | }; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 9 | use hyper::upgrade::OnUpgrade; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 10 | |
782069860236 | atagen | 2026-10-10 12:44 UTC | 11 | use crate::problem::Problem; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 12 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 13 | pub const BODY_LIMIT: usize = 64 * 1024; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 14 | |
782069860236 | atagen | 2026-10-10 12:44 UTC | 15 | /// Everything a handler may look at, gathered once per request. |
782069860236 | atagen | 2026-10-10 12:44 UTC | 16 | pub struct Ctx { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 17 | method: Method, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 18 | uri: Uri, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 19 | version: Version, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 20 | headers: HeaderMap, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 21 | query: HashMap<String, String>, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 22 | params: HashMap<String, String>, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 23 | body: Bytes, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 24 | now: Instant, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 25 | upgrade: Option<OnUpgrade>, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 26 | } |
782069860236 | atagen | 2026-10-10 12:44 UTC | 27 | |
782069860236 | atagen | 2026-10-10 12:44 UTC | 28 | impl Ctx { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 29 | pub fn header(&self, name: HeaderName) -> Option<&str> { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 30 | self.headers.get(name).and_then(|v| v.to_str().ok()) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 31 | } |
782069860236 | atagen | 2026-10-10 12:44 UTC | 32 | |
782069860236 | atagen | 2026-10-10 12:44 UTC | 33 | pub fn cookie(&self, name: &str) -> Option<&str> { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 34 | self.headers |
782069860236 | atagen | 2026-10-10 12:44 UTC | 35 | .get_all(header::COOKIE) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 36 | .iter() |
782069860236 | atagen | 2026-10-10 12:44 UTC | 37 | .filter_map(|v| v.to_str().ok()) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 38 | .flat_map(|v| v.split(';')) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 39 | .filter_map(|pair| pair.trim().split_once('=')) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 40 | .find(|(key, _)| *key == name) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 41 | .map(|(_, value)| value) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 42 | } |
782069860236 | atagen | 2026-10-10 12:44 UTC | 43 | |
782069860236 | atagen | 2026-10-10 12:44 UTC | 44 | pub fn param(&self, name: &str) -> &str { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 45 | self.params.get(name).map_or("", String::as_str) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 46 | } |
782069860236 | atagen | 2026-10-10 12:44 UTC | 47 | |
782069860236 | atagen | 2026-10-10 12:44 UTC | 48 | pub fn query(&self, name: &str) -> Option<&str> { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 49 | self.query.get(name).map(String::as_str) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 50 | } |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 51 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 52 | pub fn method(&self) -> &Method { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 53 | &self.method |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 54 | } |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 55 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 56 | pub fn body(&self) -> &[u8] { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 57 | &self.body |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 58 | } |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 59 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 60 | pub fn now(&self) -> Instant { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 61 | self.now |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 62 | } |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 63 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 64 | pub fn take_upgrade(&mut self) -> Option<OnUpgrade> { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 65 | self.upgrade.take() |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 66 | } |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 67 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 68 | /// The request as it arrived, which is what TRACE sends back. |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 69 | pub fn echo(&self) -> String { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 70 | let mut message = format!("{} {} {:?}\r\n", self.method, self.uri, self.version); |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 71 | for (name, value) in &self.headers { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 72 | let _ = write!( |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 73 | message, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 74 | "{name}: {}\r\n", |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 75 | value.to_str().unwrap_or("<binary>") |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 76 | ); |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 77 | } |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 78 | message.push_str("\r\n"); |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 79 | message |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 80 | } |
782069860236 | atagen | 2026-10-10 12:44 UTC | 81 | } |
782069860236 | atagen | 2026-10-10 12:44 UTC | 82 | |
782069860236 | atagen | 2026-10-10 12:44 UTC | 83 | impl<S: Send + Sync> FromRequest<S> for Ctx { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 84 | type Rejection = Response; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 85 | |
782069860236 | atagen | 2026-10-10 12:44 UTC | 86 | async fn from_request(mut request: Request, state: &S) -> Result<Self, Self::Rejection> { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 87 | let upgrade = request.extensions_mut().remove::<OnUpgrade>(); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 88 | let (mut parts, body) = request.into_parts(); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 89 | let params = RawPathParams::from_request_parts(&mut parts, state) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 90 | .await |
782069860236 | atagen | 2026-10-10 12:44 UTC | 91 | .map(|raw| { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 92 | raw.iter() |
782069860236 | atagen | 2026-10-10 12:44 UTC | 93 | .map(|(k, v)| (k.to_owned(), v.to_owned())) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 94 | .collect() |
782069860236 | atagen | 2026-10-10 12:44 UTC | 95 | }) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 96 | .unwrap_or_default(); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 97 | let query = Query::<HashMap<String, String>>::try_from_uri(&parts.uri) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 98 | .map(|q| q.0) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 99 | .unwrap_or_default(); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 100 | let body = axum::body::to_bytes(body, BODY_LIMIT).await.map_err(|_| { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 101 | Problem::new( |
782069860236 | atagen | 2026-10-10 12:44 UTC | 102 | StatusCode::PAYLOAD_TOO_LARGE, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 103 | "body-too-large", |
782069860236 | atagen | 2026-10-10 12:44 UTC | 104 | "The request body is larger than anything the server is prepared to receive.", |
782069860236 | atagen | 2026-10-10 12:44 UTC | 105 | ) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 106 | .into_response() |
782069860236 | atagen | 2026-10-10 12:44 UTC | 107 | })?; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 108 | Ok(Self { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 109 | method: parts.method, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 110 | uri: parts.uri, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 111 | version: parts.version, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 112 | headers: parts.headers, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 113 | query, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 114 | params, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 115 | body, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 116 | now: Instant::now(), |
782069860236 | atagen | 2026-10-10 12:44 UTC | 117 | upgrade, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 118 | }) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 119 | } |
782069860236 | atagen | 2026-10-10 12:44 UTC | 120 | } |