chadtmiller.com/knotless

Git hosted in your atproto account, on any stock PDS, fast enough for agents.

git clone knotless://chadtmiller.com/knotless

needs the knotless git helper. set it up

88 lines · 2.8 KB Raw History
//! The pages: every request this server does not answer itself, handed to
//! the SvelteKit app that draws them, which reads back under `/_data`.

use axum::Router;
use axum::body::Body;
use axum::extract::Request;
use axum::http::{HeaderMap, HeaderValue, StatusCode, header};
use axum::response::{IntoResponse, Response};
use std::sync::LazyLock;

/// The largest request body handed on: the pages take none to speak of.
const BODY_LIMIT: usize = 1 << 20;

/// Headers about one hop of a connection, which are not handed on.
const HOP: [header::HeaderName; 6] = [
    header::CONNECTION,
    header::HOST,
    header::TRANSFER_ENCODING,
    header::CONTENT_LENGTH,
    header::UPGRADE,
    header::TE,
];

static CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
    reqwest::Client::builder()
        .redirect(reqwest::redirect::Policy::none())
        .build()
        .expect("a client builds")
});

/// A router that hands every request to the app at `web`.
pub fn pages(web: String) -> Router {
    let web: &'static str = web.trim_end_matches('/').to_string().leak();
    Router::new().fallback(move |req: Request| page(web, req))
}

async fn page(web: &str, req: Request) -> Response {
    match handed(web, req).await {
        Ok(res) => res,
        Err(e) => (
            StatusCode::BAD_GATEWAY,
            format!("The pages are not being drawn: {e:#}"),
        )
            .into_response(),
    }
}

/// `req`, as the app at `web` answers it. The app sees the host it was
/// asked for, as `x-forwarded-host`.
async fn handed(web: &str, req: Request) -> anyhow::Result<Response> {
    let (parts, body) = req.into_parts();
    let at = parts.uri.path_and_query().map_or("/", |p| p.as_str());
    let mut headers = kept(&parts.headers);
    if let Some(host) = parts.headers.get(header::HOST) {
        headers.insert("x-forwarded-host", host.clone());
    }
    headers
        .entry("x-forwarded-proto")
        .or_insert(HeaderValue::from_static("http"));
    let body = axum::body::to_bytes(body, BODY_LIMIT).await?;
    let answer = CLIENT
        .request(parts.method, format!("{web}{at}"))
        .headers(headers)
        .body(body)
        .send()
        .await?;
    let mut res = Response::builder().status(answer.status());
    let mut headers = kept(answer.headers());
    for (name, value) in [
        (header::X_CONTENT_TYPE_OPTIONS, "nosniff"),
        (header::REFERRER_POLICY, "same-origin"),
    ] {
        headers
            .entry(name)
            .or_insert(HeaderValue::from_static(value));
    }
    *res.headers_mut().unwrap() = headers;
    Ok(res.body(Body::from(answer.bytes().await?))?)
}

/// `headers`, but for those about one hop.
fn kept(headers: &HeaderMap) -> HeaderMap {
    let mut out = headers.clone();
    for name in HOP {
        out.remove(name);
    }
    out
}