Skip to main content

ordinary_utils/
headers.rs

1// Copyright (C) 2026 Ordinary Labs, LLC.
2//
3// SPDX-License-Identifier: AGPL-3.0-only
4
5use crate::{
6    HeadersDebug, LatencyDisplay, WrappedRedactedHashingAlg, X_FORWARDED_FOR, X_FORWARDED_HOST,
7    X_FORWARDED_PROTO, X_VIA, get_host, get_http_version_str, get_mapped_ip_for_addr,
8};
9use axum::extract::{ConnectInfo, Request};
10use axum::http::{HeaderMap, HeaderValue, header};
11use std::net::SocketAddr;
12use std::sync::Arc;
13use std::time::Instant;
14
15pub fn log_response(
16    status: u16,
17    log_headers: bool,
18    redacted_hash: &Arc<Option<WrappedRedactedHashingAlg>>,
19    start: Instant,
20    headers: &HeaderMap,
21) {
22    let hd = log_headers.then_some(HeadersDebug(headers, redacted_hash.clone()));
23
24    #[cfg(tracing_unstable)]
25    let headers = log_headers.then_some(tracing::field::valuable(&hd));
26    #[cfg(not(tracing_unstable))]
27    let headers = log_headers.then_some(tracing::field::debug(&hd));
28
29    let latency = LatencyDisplay(start.elapsed().as_nanos() as f64);
30
31    if status >= 500 {
32        tracing::error!(status, headers, %latency, "res");
33    } else if status >= 400 {
34        tracing::warn!(status, headers, %latency, "res");
35    } else {
36        tracing::info!(status, headers, %latency, "res");
37    }
38}
39
40pub fn get_request_headers_for_forward(
41    req: &Request,
42    forwarded_by: &str,
43    forwarded_proto: &str,
44    via_domain: &str,
45) -> HeaderMap {
46    let mut headers = req.headers().clone();
47
48    let req_version = get_http_version_str(req.version());
49
50    let via = if let Some(src_via) = headers.get(header::VIA)
51        && let Ok(src_via) = src_via.to_str()
52    {
53        format!("{src_via}, {req_version} {via_domain} (ordinaryd)")
54    } else {
55        format!("{req_version} {via_domain} (ordinaryd)")
56    };
57
58    if let Ok(via) = HeaderValue::from_str(&via) {
59        headers.insert(header::VIA, via);
60    }
61
62    let connect_info = req.extensions().get::<ConnectInfo<SocketAddr>>();
63
64    let mut forwarded = if let Some(src_forwarded) = headers.get(header::FORWARDED)
65        && let Ok(src_forwarded) = src_forwarded.to_str()
66    {
67        format!("{src_forwarded}, by={forwarded_by}")
68    } else {
69        format!("by={forwarded_by}")
70    };
71
72    if let Some(addr) = connect_info {
73        let ip = get_mapped_ip_for_addr(&addr.0);
74        let ip_str = ip.to_string();
75
76        if ip.is_ipv6() {
77            forwarded = format!("{forwarded};for=\"[{ip_str}]\"");
78        } else {
79            forwarded = format!("{forwarded};for={ip_str}");
80        }
81
82        let forwarded_for = if let Some(src_forwarded_for) = headers.get("x-forwarded-for")
83            && let Ok(src_forwarded_for) = src_forwarded_for.to_str()
84        {
85            format!("{src_forwarded_for}, {ip_str}")
86        } else {
87            ip_str
88        };
89
90        if let Ok(forwarded_for) = HeaderValue::from_str(&forwarded_for) {
91            headers.insert(X_FORWARDED_FOR, forwarded_for);
92        }
93    }
94
95    if let Some(host) = get_host(req.headers(), req.uri()) {
96        forwarded = format!("{forwarded};host={host}");
97
98        if let Ok(forwarded_host) = HeaderValue::from_str(host.as_str()) {
99            headers.insert(X_FORWARDED_HOST, forwarded_host);
100        }
101    }
102
103    forwarded = format!("{forwarded};proto={forwarded_proto}");
104
105    if let Ok(forwarded_proto) = HeaderValue::from_str(forwarded_proto) {
106        headers.insert(X_FORWARDED_PROTO, forwarded_proto);
107    }
108
109    if let Ok(forwarded) = HeaderValue::from_str(&forwarded) {
110        headers.insert(header::FORWARDED, forwarded);
111    }
112
113    headers.remove(X_VIA);
114    headers
115}