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::tcp::Sni;
6use crate::{
7    HeadersDebug, LatencyDisplay, WrappedRedactedHashingAlg, X_FORWARDED_FOR, X_FORWARDED_HOST,
8    X_FORWARDED_PROTO, get_host_fwd, get_http_version_str, get_mapped_ip_for_addr,
9};
10use axum::extract::{ConnectInfo, Request};
11use axum::http::{HeaderMap, HeaderValue, header};
12use hyper::{Method, Version};
13use std::net::SocketAddr;
14use std::sync::Arc;
15use std::time::Instant;
16
17pub fn log_request(
18    log_headers: bool,
19    headers: &HeaderMap,
20    redacted_hash: &Arc<Option<WrappedRedactedHashingAlg>>,
21    method: &Method,
22) {
23    {
24        let hd = log_headers.then_some(HeadersDebug(headers, redacted_hash.clone()));
25
26        #[cfg(tracing_unstable)]
27        let headers = log_headers.then_some(tracing::field::valuable(&hd));
28        #[cfg(not(tracing_unstable))]
29        let headers = log_headers.then_some(tracing::field::debug(&hd));
30
31        tracing::info!(
32            %method,
33            headers,
34            "req"
35        );
36    }
37}
38
39pub fn log_response(
40    status: u16,
41    log_headers: bool,
42    redacted_hash: &Arc<Option<WrappedRedactedHashingAlg>>,
43    start: Instant,
44    headers: &HeaderMap,
45    version: Version,
46) {
47    let hd = log_headers.then_some(HeadersDebug(headers, redacted_hash.clone()));
48
49    #[cfg(tracing_unstable)]
50    let headers = log_headers.then_some(tracing::field::valuable(&hd));
51    #[cfg(not(tracing_unstable))]
52    let headers = log_headers.then_some(tracing::field::debug(&hd));
53
54    let latency = LatencyDisplay(start.elapsed().as_nanos() as f64);
55
56    if status >= 500 {
57        tracing::error!(version = ?version, status, headers, %latency, "res");
58    } else if status >= 400 {
59        tracing::warn!(version = ?version, status, headers, %latency, "res");
60    } else {
61        tracing::info!(version = ?version, status, headers, %latency, "res");
62    }
63}
64
65pub fn get_request_headers_for_forward(
66    req: &Request,
67    forwarded_by: &str,
68    forwarded_proto: &str,
69    via_domain: &str,
70) -> HeaderMap {
71    let mut headers = req.headers().clone();
72
73    let req_version = get_http_version_str(req.version());
74
75    let via = if let Some(src_via) = headers.get(header::VIA)
76        && let Ok(src_via) = src_via.to_str()
77    {
78        format!("{src_via}, {req_version} {via_domain} (ordinaryd)")
79    } else {
80        format!("{req_version} {via_domain} (ordinaryd)")
81    };
82
83    if let Ok(via) = HeaderValue::from_str(&via) {
84        headers.insert(header::VIA, via);
85    }
86
87    let connect_info = req.extensions().get::<ConnectInfo<SocketAddr>>();
88    let sni = req.extensions().get::<Sni>();
89
90    let mut forwarded = if let Some(src_forwarded) = headers.get(header::FORWARDED)
91        && let Ok(src_forwarded) = src_forwarded.to_str()
92    {
93        format!("{src_forwarded}, by={forwarded_by}")
94    } else {
95        format!("by={forwarded_by}")
96    };
97
98    if let Some(addr) = connect_info {
99        let ip = get_mapped_ip_for_addr(&addr.0);
100        let ip_str = ip.to_string();
101
102        if ip.is_ipv6() {
103            forwarded = format!("{forwarded};for=\"[{ip_str}]\"");
104        } else {
105            forwarded = format!("{forwarded};for={ip_str}");
106        }
107
108        let forwarded_for = if let Some(src_forwarded_for) = headers.get("x-forwarded-for")
109            && let Ok(src_forwarded_for) = src_forwarded_for.to_str()
110        {
111            format!("{src_forwarded_for}, {ip_str}")
112        } else {
113            ip_str
114        };
115
116        if let Ok(forwarded_for) = HeaderValue::from_str(&forwarded_for) {
117            headers.insert(X_FORWARDED_FOR, forwarded_for);
118        }
119    }
120
121    if let Some(host) = get_host_fwd(req.headers(), req.uri(), sni) {
122        forwarded = format!("{forwarded};host={host}");
123
124        if let Ok(forwarded_host) = HeaderValue::from_str(host.as_str()) {
125            headers.insert(X_FORWARDED_HOST, forwarded_host);
126        }
127    }
128
129    forwarded = format!("{forwarded};proto={forwarded_proto}");
130
131    if let Ok(forwarded_proto) = HeaderValue::from_str(forwarded_proto) {
132        headers.insert(X_FORWARDED_PROTO, forwarded_proto);
133    }
134
135    if let Ok(forwarded) = HeaderValue::from_str(&forwarded) {
136        headers.insert(header::FORWARDED, forwarded);
137    }
138
139    headers
140}