ordinary-utils 0.8.2

Utils for Ordinary
Documentation
// Copyright (C) 2026 Ordinary Labs, LLC.
//
// SPDX-License-Identifier: AGPL-3.0-only

use crate::{
    HeadersDebug, LatencyDisplay, WrappedRedactedHashingAlg, X_FORWARDED_FOR, X_FORWARDED_HOST,
    X_FORWARDED_PROTO, X_VIA, get_host, get_http_version_str, get_mapped_ip_for_addr,
};
use axum::extract::{ConnectInfo, Request};
use axum::http::{HeaderMap, HeaderValue, header};
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Instant;

pub fn log_response(
    status: u16,
    log_headers: bool,
    redacted_hash: &Arc<Option<WrappedRedactedHashingAlg>>,
    start: Instant,
    headers: &HeaderMap,
) {
    let hd = log_headers.then_some(HeadersDebug(headers, redacted_hash.clone()));

    #[cfg(tracing_unstable)]
    let headers = log_headers.then_some(tracing::field::valuable(&hd));
    #[cfg(not(tracing_unstable))]
    let headers = log_headers.then_some(tracing::field::debug(&hd));

    let latency = LatencyDisplay(start.elapsed().as_nanos() as f64);

    if status >= 500 {
        tracing::error!(status, headers, %latency, "res");
    } else if status >= 400 {
        tracing::warn!(status, headers, %latency, "res");
    } else {
        tracing::info!(status, headers, %latency, "res");
    }
}

pub fn get_request_headers_for_forward(
    req: &Request,
    forwarded_by: &str,
    forwarded_proto: &str,
    via_domain: &str,
) -> HeaderMap {
    let mut headers = req.headers().clone();

    let req_version = get_http_version_str(req.version());

    let via = if let Some(src_via) = headers.get(header::VIA)
        && let Ok(src_via) = src_via.to_str()
    {
        format!("{src_via}, {req_version} {via_domain} (ordinaryd)")
    } else {
        format!("{req_version} {via_domain} (ordinaryd)")
    };

    if let Ok(via) = HeaderValue::from_str(&via) {
        headers.insert(header::VIA, via);
    }

    let connect_info = req.extensions().get::<ConnectInfo<SocketAddr>>();

    let mut forwarded = if let Some(src_forwarded) = headers.get(header::FORWARDED)
        && let Ok(src_forwarded) = src_forwarded.to_str()
    {
        format!("{src_forwarded}, by={forwarded_by}")
    } else {
        format!("by={forwarded_by}")
    };

    if let Some(addr) = connect_info {
        let ip = get_mapped_ip_for_addr(&addr.0);
        let ip_str = ip.to_string();

        if ip.is_ipv6() {
            forwarded = format!("{forwarded};for=\"[{ip_str}]\"");
        } else {
            forwarded = format!("{forwarded};for={ip_str}");
        }

        let forwarded_for = if let Some(src_forwarded_for) = headers.get("x-forwarded-for")
            && let Ok(src_forwarded_for) = src_forwarded_for.to_str()
        {
            format!("{src_forwarded_for}, {ip_str}")
        } else {
            ip_str
        };

        if let Ok(forwarded_for) = HeaderValue::from_str(&forwarded_for) {
            headers.insert(X_FORWARDED_FOR, forwarded_for);
        }
    }

    if let Some(host) = get_host(req.headers(), req.uri()) {
        forwarded = format!("{forwarded};host={host}");

        if let Ok(forwarded_host) = HeaderValue::from_str(host.as_str()) {
            headers.insert(X_FORWARDED_HOST, forwarded_host);
        }
    }

    forwarded = format!("{forwarded};proto={forwarded_proto}");

    if let Ok(forwarded_proto) = HeaderValue::from_str(forwarded_proto) {
        headers.insert(X_FORWARDED_PROTO, forwarded_proto);
    }

    if let Ok(forwarded) = HeaderValue::from_str(&forwarded) {
        headers.insert(header::FORWARDED, forwarded);
    }

    headers.remove(X_VIA);
    headers
}