atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
782069860236703e2dd3c52bb589b9e2d8d48176
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git

Blame: src/http/middleware.rs

782069860236atagen2026-10-10 12:44 UTC1
use std::io::Write as _;
782069860236atagen2026-10-10 12:44 UTC2
782069860236atagen2026-10-10 12:44 UTC3
use axum::{
782069860236atagen2026-10-10 12:44 UTC4
    body::Body,
782069860236atagen2026-10-10 12:44 UTC5
    extract::Request,
782069860236atagen2026-10-10 12:44 UTC6
    http::{HeaderValue, StatusCode, Version, header},
782069860236atagen2026-10-10 12:44 UTC7
    middleware::Next,
782069860236atagen2026-10-10 12:44 UTC8
    response::{IntoResponse, Response},
782069860236atagen2026-10-10 12:44 UTC9
};
782069860236atagen2026-10-10 12:44 UTC10
use flate2::{Compression, write::GzEncoder};
782069860236atagen2026-10-10 12:44 UTC11
782069860236atagen2026-10-10 12:44 UTC12
use crate::{headers::X_POWERED_BY, problem::Problem};
782069860236atagen2026-10-10 12:44 UTC13
782069860236atagen2026-10-10 12:44 UTC14
const MAX_URI: usize = 512;
782069860236atagen2026-10-10 12:44 UTC15
const MAX_HEADER: usize = 1024;
782069860236atagen2026-10-10 12:44 UTC16
782069860236atagen2026-10-10 12:44 UTC17
/// Marks a response for gzip, which the cock ring asks for.
782069860236atagen2026-10-10 12:44 UTC18
#[derive(Clone)]
782069860236atagen2026-10-10 12:44 UTC19
pub struct Compress;
782069860236atagen2026-10-10 12:44 UTC20
782069860236atagen2026-10-10 12:44 UTC21
fn reject(request: &Request) -> Option<Problem> {
782069860236atagen2026-10-10 12:44 UTC22
    if request.version() < Version::HTTP_11 {
782069860236atagen2026-10-10 12:44 UTC23
        return Some(Problem::new(
782069860236atagen2026-10-10 12:44 UTC24
            StatusCode::HTTP_VERSION_NOT_SUPPORTED,
782069860236atagen2026-10-10 12:44 UTC25
            "pdf",
782069860236atagen2026-10-10 12:44 UTC26
            "HTTP/1.0 is the version the original jelqing PDF was written for. The server has read it.",
782069860236atagen2026-10-10 12:44 UTC27
        ));
782069860236atagen2026-10-10 12:44 UTC28
    }
782069860236atagen2026-10-10 12:44 UTC29
    if request.uri().to_string().len() > MAX_URI {
782069860236atagen2026-10-10 12:44 UTC30
        return Some(Problem::new(
782069860236atagen2026-10-10 12:44 UTC31
            StatusCode::URI_TOO_LONG,
782069860236atagen2026-10-10 12:44 UTC32
            "uri-too-long",
782069860236atagen2026-10-10 12:44 UTC33
            "The URI is longer than anything else here.",
782069860236atagen2026-10-10 12:44 UTC34
        ));
782069860236atagen2026-10-10 12:44 UTC35
    }
782069860236atagen2026-10-10 12:44 UTC36
    if request.headers().values().any(|v| v.len() > MAX_HEADER) {
782069860236atagen2026-10-10 12:44 UTC37
        return Some(Problem::new(
782069860236atagen2026-10-10 12:44 UTC38
            StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
782069860236atagen2026-10-10 12:44 UTC39
            "head-too-large",
782069860236atagen2026-10-10 12:44 UTC40
            "Head too large.",
782069860236atagen2026-10-10 12:44 UTC41
        ));
782069860236atagen2026-10-10 12:44 UTC42
    }
782069860236atagen2026-10-10 12:44 UTC43
    let expectation = request.headers().get(header::EXPECT)?;
782069860236atagen2026-10-10 12:44 UTC44
    if expectation.as_bytes().eq_ignore_ascii_case(b"100-continue") {
782069860236atagen2026-10-10 12:44 UTC45
        return None;
782069860236atagen2026-10-10 12:44 UTC46
    }
782069860236atagen2026-10-10 12:44 UTC47
    Some(Problem::new(
782069860236atagen2026-10-10 12:44 UTC48
        StatusCode::EXPECTATION_FAILED,
782069860236atagen2026-10-10 12:44 UTC49
        "expectations",
782069860236atagen2026-10-10 12:44 UTC50
        format!(
782069860236atagen2026-10-10 12:44 UTC51
            "Expect: {} cannot be met.",
782069860236atagen2026-10-10 12:44 UTC52
            expectation.to_str().unwrap_or("that")
782069860236atagen2026-10-10 12:44 UTC53
        ),
782069860236atagen2026-10-10 12:44 UTC54
    ))
782069860236atagen2026-10-10 12:44 UTC55
}
782069860236atagen2026-10-10 12:44 UTC56
782069860236atagen2026-10-10 12:44 UTC57
async fn compress(response: Response) -> Response {
782069860236atagen2026-10-10 12:44 UTC58
    let (mut parts, body) = response.into_parts();
782069860236atagen2026-10-10 12:44 UTC59
    let Ok(bytes) = axum::body::to_bytes(body, usize::MAX).await else {
782069860236atagen2026-10-10 12:44 UTC60
        return Response::from_parts(parts, Body::empty());
782069860236atagen2026-10-10 12:44 UTC61
    };
782069860236atagen2026-10-10 12:44 UTC62
    let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
782069860236atagen2026-10-10 12:44 UTC63
    let gzipped = encoder.write_all(&bytes).and_then(|()| encoder.finish());
782069860236atagen2026-10-10 12:44 UTC64
    let Ok(gzipped) = gzipped else {
782069860236atagen2026-10-10 12:44 UTC65
        return Response::from_parts(parts, Body::from(bytes));
782069860236atagen2026-10-10 12:44 UTC66
    };
782069860236atagen2026-10-10 12:44 UTC67
    parts
782069860236atagen2026-10-10 12:44 UTC68
        .headers
782069860236atagen2026-10-10 12:44 UTC69
        .insert(header::CONTENT_ENCODING, HeaderValue::from_static("gzip"));
782069860236atagen2026-10-10 12:44 UTC70
    parts.headers.remove(header::CONTENT_LENGTH);
782069860236atagen2026-10-10 12:44 UTC71
    parts
782069860236atagen2026-10-10 12:44 UTC72
        .headers
782069860236atagen2026-10-10 12:44 UTC73
        .append(header::VARY, HeaderValue::from_static("Accept-Encoding"));
782069860236atagen2026-10-10 12:44 UTC74
    Response::from_parts(parts, Body::from(gzipped))
782069860236atagen2026-10-10 12:44 UTC75
}
782069860236atagen2026-10-10 12:44 UTC76
782069860236atagen2026-10-10 12:44 UTC77
pub async fn chrome(request: Request, next: Next) -> Response {
782069860236atagen2026-10-10 12:44 UTC78
    let gzip = request
782069860236atagen2026-10-10 12:44 UTC79
        .headers()
782069860236atagen2026-10-10 12:44 UTC80
        .get(header::ACCEPT_ENCODING)
782069860236atagen2026-10-10 12:44 UTC81
        .and_then(|v| v.to_str().ok())
782069860236atagen2026-10-10 12:44 UTC82
        .is_some_and(|e| e.to_ascii_lowercase().contains("gzip"));
782069860236atagen2026-10-10 12:44 UTC83
    let mut response = match reject(&request) {
782069860236atagen2026-10-10 12:44 UTC84
        Some(problem) => problem.into_response(),
782069860236atagen2026-10-10 12:44 UTC85
        None => next.run(request).await,
782069860236atagen2026-10-10 12:44 UTC86
    };
782069860236atagen2026-10-10 12:44 UTC87
    if gzip && response.extensions().get::<Compress>().is_some() {
782069860236atagen2026-10-10 12:44 UTC88
        response = compress(response).await;
782069860236atagen2026-10-10 12:44 UTC89
    }
782069860236atagen2026-10-10 12:44 UTC90
    let headers = response.headers_mut();
782069860236atagen2026-10-10 12:44 UTC91
    headers.insert(header::SERVER, HeaderValue::from_static("jelqd/0.6.9"));
782069860236atagen2026-10-10 12:44 UTC92
    headers.insert(X_POWERED_BY, HeaderValue::from_static("blood"));
782069860236atagen2026-10-10 12:44 UTC93
    headers
782069860236atagen2026-10-10 12:44 UTC94
        .entry(header::CACHE_CONTROL)
782069860236atagen2026-10-10 12:44 UTC95
        .or_insert(HeaderValue::from_static("no-store, private"));
782069860236atagen2026-10-10 12:44 UTC96
    response
782069860236atagen2026-10-10 12:44 UTC97
}