1use core::fmt;
4use core::net::IpAddr;
5
6pub use tonic014 as tonic;
7pub use tonic::metadata::MetadataMap;
8
9use crate::forwarded::{self, parse_forwarded_for, parse_forwarded_for_rev};
10use crate::filter::Filter;
11use crate::shared::FALLBACK_STR;
12
13const FORWARDED: &str = "forwarded";
14
15pub struct MetadataValueFmt<'a>(tonic::metadata::GetAll<'a, tonic::metadata::Ascii>);
17
18impl fmt::Debug for MetadataValueFmt<'_> {
19 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
20 let mut out = fmt.debug_list();
21 for header in self.0.iter() {
22 match header.to_str() {
23 Ok(header) => out.entry(&header),
24 Err(_) => out.entry(header),
25 };
26 }
27
28 out.finish()
29 }
30}
31
32impl fmt::Display for MetadataValueFmt<'_> {
33 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
34 let mut headers = self.0.iter();
35 if let Some(header) = headers.next() {
36 match header.to_str() {
37 Ok(header) => fmt.write_str(header)?,
38 Err(_) => fmt.write_str(FALLBACK_STR)?,
39 }
40
41 for header in headers {
42 fmt.write_str(" ,")?;
43 match header.to_str() {
44 Ok(header) => fmt.write_str(header)?,
45 Err(_) => fmt.write_str(FALLBACK_STR)?,
46 }
47 }
48 }
49
50 Ok(())
51 }
52}
53
54pub trait MetadataMapClientIp {
56 fn get_header_value_fmt(&self, key: &str) -> MetadataValueFmt<'_>;
58 fn extract_leftmost_forwarded_ip(&self) -> Option<IpAddr>;
65 fn extract_rightmost_forwarded_ip(&self) -> Option<IpAddr>;
69 fn extract_filtered_forwarded_ip(&self, filter: &impl Filter) -> Option<IpAddr>;
73}
74
75impl MetadataMapClientIp for MetadataMap {
76 #[inline(always)]
77 fn get_header_value_fmt(&self, key: &str) -> MetadataValueFmt<'_> {
78 MetadataValueFmt(self.get_all(key))
79 }
80
81 #[inline(always)]
82 fn extract_leftmost_forwarded_ip(&self) -> Option<IpAddr> {
83 self.get_all(FORWARDED)
84 .into_iter()
85 .next()
86 .and_then(|header| header.to_str().ok())
87 .and_then(|header| parse_forwarded_for(header).next())
88 .and_then(|node| match node {
89 forwarded::ForwardedNode::Ip(ip) => Some(ip),
90 _ => None
91 })
92 }
93
94 #[inline(always)]
95 fn extract_rightmost_forwarded_ip(&self) -> Option<IpAddr> {
96 self.get_all(FORWARDED)
97 .into_iter()
98 .next_back()
99 .and_then(|header| header.to_str().ok())
100 .and_then(|header| parse_forwarded_for_rev(header).next())
101 .and_then(|node| match node {
102 forwarded::ForwardedNode::Ip(ip) => Some(ip),
103 _ => None
104 })
105 }
106
107 fn extract_filtered_forwarded_ip(&self, filter: &impl Filter) -> Option<IpAddr> {
108 let forwarded = self.get_all(FORWARDED)
109 .into_iter()
110 .rev()
111 .filter_map(|header| header.to_str().ok()).flat_map(|header| parse_forwarded_for_rev(header));
112
113 for node in forwarded {
114 match node {
115 forwarded::ForwardedNode::Ip(ip) => if filter.is_match(ip) {
116 continue
117 } else {
118 return Some(ip)
119 },
120 _ => return None,
121 }
122 }
123
124 None
125 }
126}