atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
8599b930cebf9a1fb90b7538c44eab5649929cf9
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/http/ctx.rsRust101 lines2.9 KB
1
use std::{collections::HashMap, fmt::Write as _, time::Instant};
2
3
use axum::{
4
    extract::{FromRequest, FromRequestParts, Query, RawPathParams, Request},
5
    http::{HeaderMap, HeaderName, Method, Uri, Version, header},
6
    response::Response,
7
};
8
use hyper::upgrade::OnUpgrade;
9
10
/// Everything a handler may look at, gathered once per request.
11
pub struct Ctx {
12
    method: Method,
13
    uri: Uri,
14
    version: Version,
15
    headers: HeaderMap,
16
    query: HashMap<String, String>,
17
    params: HashMap<String, String>,
18
    now: Instant,
19
    upgrade: Option<OnUpgrade>,
20
}
21
22
impl Ctx {
23
    pub fn header(&self, name: HeaderName) -> Option<&str> {
24
        self.headers.get(name).and_then(|v| v.to_str().ok())
25
    }
26
27
    pub fn cookie(&self, name: &str) -> Option<&str> {
28
        self.headers
29
            .get_all(header::COOKIE)
30
            .iter()
31
            .filter_map(|v| v.to_str().ok())
32
            .flat_map(|v| v.split(';'))
33
            .filter_map(|pair| pair.trim().split_once('='))
34
            .find(|(key, _)| *key == name)
35
            .map(|(_, value)| value)
36
    }
37
38
    pub fn param(&self, name: &str) -> &str {
39
        self.params.get(name).map_or("", String::as_str)
40
    }
41
42
    pub fn query(&self, name: &str) -> Option<&str> {
43
        self.query.get(name).map(String::as_str)
44
    }
45
46
    pub fn method(&self) -> &Method {
47
        &self.method
48
    }
49
50
    pub fn now(&self) -> Instant {
51
        self.now
52
    }
53
54
    pub fn take_upgrade(&mut self) -> Option<OnUpgrade> {
55
        self.upgrade.take()
56
    }
57
58
    /// The request as it arrived, which is what TRACE sends back.
59
    pub fn echo(&self) -> String {
60
        let mut message = format!("{} {} {:?}\r\n", self.method, self.uri, self.version);
61
        for (name, value) in &self.headers {
62
            let _ = write!(
63
                message,
64
                "{name}: {}\r\n",
65
                value.to_str().unwrap_or("<binary>")
66
            );
67
        }
68
        message.push_str("\r\n");
69
        message
70
    }
71
}
72
73
impl<S: Send + Sync> FromRequest<S> for Ctx {
74
    type Rejection = Response;
75
76
    async fn from_request(mut request: Request, state: &S) -> Result<Self, Self::Rejection> {
77
        let upgrade = request.extensions_mut().remove::<OnUpgrade>();
78
        let (mut parts, _) = request.into_parts();
79
        let params = RawPathParams::from_request_parts(&mut parts, state)
80
            .await
81
            .map(|raw| {
82
                raw.iter()
83
                    .map(|(k, v)| (k.to_owned(), v.to_owned()))
84
                    .collect()
85
            })
86
            .unwrap_or_default();
87
        let query = Query::<HashMap<String, String>>::try_from_uri(&parts.uri)
88
            .map(|q| q.0)
89
            .unwrap_or_default();
90
        Ok(Self {
91
            method: parts.method,
92
            uri: parts.uri,
93
            version: parts.version,
94
            headers: parts.headers,
95
            query,
96
            params,
97
            now: Instant::now(),
98
            upgrade,
99
        })
100
    }
101
}