atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
9683736a2c5e4af0c861af0df5b33ec31e26cefa
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/http/middleware.rsRust97 lines3.2 KB
1
use std::io::Write as _;
2
3
use axum::{
4
    body::Body,
5
    extract::Request,
6
    http::{HeaderValue, StatusCode, Version, header},
7
    middleware::Next,
8
    response::{IntoResponse, Response},
9
};
10
use flate2::{Compression, write::GzEncoder};
11
12
use crate::{headers::X_POWERED_BY, problem::Problem};
13
14
const MAX_URI: usize = 512;
15
const MAX_HEADER: usize = 1024;
16
17
/// Marks a response for gzip, which the cock ring asks for.
18
#[derive(Clone)]
19
pub struct Compress;
20
21
fn reject(request: &Request) -> Option<Problem> {
22
    if request.version() < Version::HTTP_11 {
23
        return Some(Problem::new(
24
            StatusCode::HTTP_VERSION_NOT_SUPPORTED,
25
            "pdf",
26
            "HTTP/1.0 is the version the original jelqing PDF was written for. The server has read it.",
27
        ));
28
    }
29
    if request.uri().to_string().len() > MAX_URI {
30
        return Some(Problem::new(
31
            StatusCode::URI_TOO_LONG,
32
            "uri-too-long",
33
            "The URI is longer than anything else here.",
34
        ));
35
    }
36
    if request.headers().values().any(|v| v.len() > MAX_HEADER) {
37
        return Some(Problem::new(
38
            StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
39
            "head-too-large",
40
            "Head too large.",
41
        ));
42
    }
43
    let expectation = request.headers().get(header::EXPECT)?;
44
    if expectation.as_bytes().eq_ignore_ascii_case(b"100-continue") {
45
        return None;
46
    }
47
    Some(Problem::new(
48
        StatusCode::EXPECTATION_FAILED,
49
        "expectations",
50
        format!(
51
            "Expect: {} cannot be met.",
52
            expectation.to_str().unwrap_or("that")
53
        ),
54
    ))
55
}
56
57
async fn compress(response: Response) -> Response {
58
    let (mut parts, body) = response.into_parts();
59
    let Ok(bytes) = axum::body::to_bytes(body, usize::MAX).await else {
60
        return Response::from_parts(parts, Body::empty());
61
    };
62
    let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
63
    let gzipped = encoder.write_all(&bytes).and_then(|()| encoder.finish());
64
    let Ok(gzipped) = gzipped else {
65
        return Response::from_parts(parts, Body::from(bytes));
66
    };
67
    parts
68
        .headers
69
        .insert(header::CONTENT_ENCODING, HeaderValue::from_static("gzip"));
70
    parts.headers.remove(header::CONTENT_LENGTH);
71
    parts
72
        .headers
73
        .append(header::VARY, HeaderValue::from_static("Accept-Encoding"));
74
    Response::from_parts(parts, Body::from(gzipped))
75
}
76
77
pub async fn chrome(request: Request, next: Next) -> Response {
78
    let gzip = request
79
        .headers()
80
        .get(header::ACCEPT_ENCODING)
81
        .and_then(|v| v.to_str().ok())
82
        .is_some_and(|e| e.to_ascii_lowercase().contains("gzip"));
83
    let mut response = match reject(&request) {
84
        Some(problem) => problem.into_response(),
85
        None => next.run(request).await,
86
    };
87
    if gzip && response.extensions().get::<Compress>().is_some() {
88
        response = compress(response).await;
89
    }
90
    let headers = response.headers_mut();
91
    headers.insert(header::SERVER, HeaderValue::from_static("jelqd/0.6.9"));
92
    headers.insert(X_POWERED_BY, HeaderValue::from_static("blood"));
93
    headers
94
        .entry(header::CACHE_CONTROL)
95
        .or_insert(HeaderValue::from_static("no-store, private"));
96
    response
97
}