o_sfu/runtime/
request_origin.rs1use std::{convert::Infallible, net::SocketAddr, str};
2
3use axum::{
4 extract::{ConnectInfo, FromRequestParts},
5 http::{HeaderMap, header, request::Parts},
6};
7
8use crate::runtime::RuntimeState;
9
10const UNKNOWN_REMOTE_ADDRESS: &str = "unknown";
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct RequestOrigin {
15 pub base_url: String,
16 pub remote_address: String,
17}
18
19impl FromRequestParts<RuntimeState> for RequestOrigin {
20 type Rejection = Infallible;
21
22 async fn from_request_parts(
23 parts: &mut Parts,
24 state: &RuntimeState,
25 ) -> Result<Self, Self::Rejection> {
26 let connect_info = parts
27 .extensions
28 .get::<ConnectInfo<SocketAddr>>()
29 .map(|ConnectInfo(addr)| *addr);
30 Ok(resolve_request_origin(
31 &parts.headers,
32 state.config.http.trust_proxy_headers,
33 state.config.http.bind_address,
34 connect_info,
35 ))
36 }
37}
38
39#[must_use]
45pub fn resolve_request_origin(
46 headers: &HeaderMap,
47 trust_proxy_headers: bool,
48 fallback_bind_address: SocketAddr,
49 connect_info: Option<SocketAddr>,
50) -> RequestOrigin {
51 RequestOrigin {
52 base_url: request_base_url(headers, trust_proxy_headers, fallback_bind_address),
53 remote_address: resolve_remote_address(headers, trust_proxy_headers, connect_info),
54 }
55}
56
57pub(crate) fn resolve_remote_address(
58 headers: &HeaderMap,
59 trust_proxy_headers: bool,
60 connect_info: Option<SocketAddr>,
61) -> String {
62 trusted_forwarded_header(headers, trust_proxy_headers, "x-forwarded-for")
63 .map(str::to_owned)
64 .or_else(|| connect_info.map(|addr| addr.ip().to_string()))
65 .unwrap_or_else(|| UNKNOWN_REMOTE_ADDRESS.to_owned())
66}
67
68pub(crate) fn trusted_forwarded_header<'headers>(
69 headers: &'headers HeaderMap,
70 trust_proxy_headers: bool,
71 name: &str,
72) -> Option<&'headers str> {
73 if !trust_proxy_headers {
74 return None;
75 }
76 forwarded_header(headers, name)
77}
78
79fn forwarded_header<'headers>(headers: &'headers HeaderMap, name: &str) -> Option<&'headers str> {
80 let value = headers.get(name)?.to_str().ok()?;
81 value.split(',').next().map(str::trim)
82}
83
84pub(crate) fn request_base_url(
85 headers: &HeaderMap,
86 trust_proxy_headers: bool,
87 fallback_bind_address: SocketAddr,
88) -> String {
89 let scheme = trusted_forwarded_header(headers, trust_proxy_headers, "x-forwarded-proto")
90 .unwrap_or("http");
91 let host = trusted_forwarded_header(headers, trust_proxy_headers, "x-forwarded-host")
92 .map(str::to_owned)
93 .or_else(|| {
94 headers
95 .get(header::HOST)
96 .and_then(|value| value.to_str().ok())
97 .map(str::to_owned)
98 })
99 .unwrap_or_else(|| fallback_bind_address.to_string());
100 format!("{scheme}://{host}")
101}