bios_basic/helper/
request_helper.rs1use std::{
5 collections::HashMap,
6 net::{IpAddr, Ipv4Addr},
7 str::FromStr,
8};
9
10use itertools::Itertools;
11use tardis::{
12 basic::{dto::TardisContext, result::TardisResult},
13 web::poem::{http::header::FORWARDED, Request},
14};
15
16pub const REMOTE_ADDR: &str = "remote-addr";
17
18pub async fn add_ip(ip: Option<String>, ctx: &TardisContext) -> TardisResult<()> {
20 if let Some(ip) = ip {
21 ctx.add_ext(REMOTE_ADDR, &ip).await?;
22 }
23 Ok(())
24}
25
26pub async fn try_set_real_ip_from_req_to_ctx(request: &Request, ctx: &TardisContext) -> TardisResult<()> {
28 ctx.add_ext(REMOTE_ADDR, &try_get_real_ip_from_req(request).await?.unwrap_or_default()).await?;
29 Ok(())
30}
31
32pub fn parse_forwarded_ip(forwarded_value: &str) -> Option<IpAddr> {
45 forwarded_value.strip_prefix("Forwarded: ").and_then(|forwarded_value| {
46 forwarded_value
47 .split(';')
48 .find(|part| part.trim().starts_with("for="))
49 .and_then(|part| part.trim()[4..].split(',').next().and_then(|ip_str| IpAddr::from_str(ip_str).ok()))
50 })
51}
52
53pub fn mapped_ipv6_to_ipv4(ip_addr: IpAddr) -> IpAddr {
69 match ip_addr {
70 IpAddr::V6(ip_addr) => {
71 let segments = ip_addr.segments();
72 if segments[0] == 0 && segments[1] == 0 && segments[2] == 0 && segments[3] == 0 && segments[4] == 0 && segments[5] == 0xffff {
73 IpAddr::V4(Ipv4Addr::new(ip_addr.octets()[12], ip_addr.octets()[13], ip_addr.octets()[14], ip_addr.octets()[15]))
75 } else {
76 IpAddr::V6(ip_addr)
77 }
78 }
79 IpAddr::V4(ip_addr) => IpAddr::V4(ip_addr),
80 }
81}
82
83pub async fn try_get_real_ip_from_req(request: &Request) -> TardisResult<Option<String>> {
87 if let Some(forwarded_header) = request.headers().get(FORWARDED) {
89 if let Ok(forwarded_value) = forwarded_header.to_str() {
90 if let Some(ip) = parse_forwarded_ip(forwarded_value.trim()) {
91 return Ok(Some(mapped_ipv6_to_ipv4(ip).to_string()));
92 }
93 }
94 }
95 if let Some(xff_header) = request.headers().get("X-Forwarded-For") {
96 if let Ok(xff_value) = xff_header.to_str() {
97 if let Some(ip) = xff_value.split(',').next().and_then(|s| IpAddr::from_str(s.trim()).ok()) {
98 return Ok(Some(mapped_ipv6_to_ipv4(ip).to_string()));
99 }
100 }
101 }
102 if let Some(xrp_header) = request.headers().get("X-Real-IP") {
103 if let Ok(xrp_value) = xrp_header.to_str() {
104 if let Some(ip) = xrp_value.split(',').next().and_then(|s| IpAddr::from_str(s.trim()).ok()) {
105 return Ok(Some(mapped_ipv6_to_ipv4(ip).to_string()));
106 }
107 }
108 }
109 Ok(request.remote_addr().as_socket_addr().map(|addr| mapped_ipv6_to_ipv4(addr.ip()).to_string()))
110}
111
112pub async fn get_real_ip_from_ctx(ctx: &TardisContext) -> TardisResult<Option<String>> {
114 ctx.get_ext(REMOTE_ADDR).await
115}
116
117pub fn sort_query(query: &str) -> String {
119 if query.is_empty() {
120 return "".to_string();
121 }
122 query.split('&').sorted_by(|a, b| Ord::cmp(&a.to_lowercase(), &b.to_lowercase())).join("&")
123}
124
125pub fn sort_hashmap_query(query: HashMap<String, String>) -> String {
127 if query.is_empty() {
128 return "".to_string();
129 }
130 query.iter().map(|a| format!("{}={}", a.0, a.1)).sorted_by(|a, b| Ord::cmp(&a.to_lowercase(), &b.to_lowercase())).join("&")
131}