Skip to main content

http_ip/
tonic014.rs

1//! Tonic 0.14 extension module
2
3use 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, parse_x_forwarded_for, parse_x_forwarded_for_rev};
10use crate::filter::Filter;
11use crate::shared::FALLBACK_STR;
12
13const FORWARDED: &str = "forwarded";
14const X_FORWARDED_FOR: &str = "x-forwarded-for";
15
16///FMT formatter for header values
17pub struct MetadataValueFmt<'a>(tonic::metadata::GetAll<'a, tonic::metadata::Ascii>);
18
19impl fmt::Debug for MetadataValueFmt<'_> {
20    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
21        let mut out = fmt.debug_list();
22        for header in self.0.iter() {
23            match header.to_str() {
24                Ok(header) => out.entry(&header),
25                Err(_) => out.entry(header),
26            };
27        }
28
29        out.finish()
30    }
31}
32
33impl fmt::Display for MetadataValueFmt<'_> {
34    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
35        let mut headers = self.0.iter();
36        if let Some(header) = headers.next() {
37            match header.to_str() {
38                Ok(header) => fmt.write_str(header)?,
39                Err(_) => fmt.write_str(FALLBACK_STR)?,
40            }
41
42            for header in headers {
43                fmt.write_str(" ,")?;
44                match header.to_str() {
45                    Ok(header) => fmt.write_str(header)?,
46                    Err(_) => fmt.write_str(FALLBACK_STR)?,
47                }
48            }
49        }
50
51        Ok(())
52    }
53}
54
55///`MetadataMap` extension trait
56pub trait MetadataMapClientIp {
57    ///Retrieves FMT formatter for header value matching provided `key`
58    fn get_header_value_fmt(&self, key: &str) -> MetadataValueFmt<'_>;
59    ///Extracts leftmost client IP with no assumption.
60    ///
61    ///Note that this is generally not reliable as your client might be behind proxy
62    ///Prefer to use `extract_client_ip_with` by filtering out your proxies to find correct IP
63    ///
64    ///Returns `None` if IP is not provided or obfuscated
65    fn extract_leftmost_forwarded_ip(&self) -> Option<IpAddr>;
66    ///Extracts rightmost client IP with no assumption.
67    ///
68    ///Returns `None` if IP is not provided or obfuscated
69    fn extract_rightmost_forwarded_ip(&self) -> Option<IpAddr>;
70    ///Extracts client ip taking rightmost, after filtering out any IP matching `filter`
71    ///
72    ///Returns `None` if IP is not provided or obfuscated
73    fn extract_filtered_forwarded_ip(&self, filter: &impl Filter) -> Option<IpAddr>;
74    ///Extracts client ip taking rightmost, after filtering out any IP matching `filter` after skipping `skip` amount of IPs
75    ///
76    ///Returns `None` if IP is not provided or obfuscated
77    fn extract_filtered_forwarded_ip_after(&self, skip: usize, filter: &impl Filter) -> Option<IpAddr>;
78}
79
80impl MetadataMapClientIp for MetadataMap {
81    #[inline(always)]
82    fn get_header_value_fmt(&self, key: &str) -> MetadataValueFmt<'_> {
83        MetadataValueFmt(self.get_all(key))
84    }
85
86    #[inline(always)]
87    fn extract_leftmost_forwarded_ip(&self) -> Option<IpAddr> {
88        crate::shared::impl_extract_leftmost_forwarded_ip!(self)
89    }
90
91    #[inline(always)]
92    fn extract_rightmost_forwarded_ip(&self) -> Option<IpAddr> {
93        crate::shared::impl_extract_rightmost_forwarded_ip!(self)
94    }
95
96    #[inline(always)]
97    fn extract_filtered_forwarded_ip(&self, filter: &impl Filter) -> Option<IpAddr> {
98        self.extract_filtered_forwarded_ip_after(0, filter)
99    }
100
101    fn extract_filtered_forwarded_ip_after(&self, skip: usize, filter: &impl Filter) -> Option<IpAddr> {
102        crate::shared::impl_extract_filtered_forwarded_ip!(self, filter, skip)
103    }
104}