http_ip/
http.rs

1//! HTTP extension module
2
3use core::fmt;
4use core::net::IpAddr;
5
6use crate::forwarded::{self, parse_forwarded_for, parse_forwarded_for_rev, parse_x_forwarded_for, parse_x_forwarded_for_rev};
7use crate::filter::Filter;
8use crate::shared::FALLBACK_STR;
9
10///Re-export of [http](https://crates.io/crates/http)
11pub use http as http_ext;
12use http_ext::header::FORWARDED;
13const X_FORWARDED_FOR: http_ext::header::HeaderName = http_ext::header::HeaderName::from_static("x-forwarded-for");
14
15///FMT formatter for header values
16pub struct HeaderValueFmt<'a>(http_ext::header::GetAll<'a, http_ext::header::HeaderValue>);
17
18impl fmt::Debug for HeaderValueFmt<'_> {
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 HeaderValueFmt<'_> {
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
54///`HeaderMap` extension trait
55pub trait HeaderMapClientIp {
56    ///Retrieves FMT formatter for header value matching provided `key`
57    fn get_header_value_fmt(&self, key: impl http_ext::header::AsHeaderName) -> HeaderValueFmt<'_>;
58
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}
75
76impl HeaderMapClientIp for http_ext::HeaderMap {
77    #[inline(always)]
78    fn get_header_value_fmt(&self, key: impl http_ext::header::AsHeaderName) -> HeaderValueFmt<'_> {
79        HeaderValueFmt(self.get_all(key))
80    }
81
82    #[inline(always)]
83    fn extract_leftmost_forwarded_ip(&self) -> Option<IpAddr> {
84        crate::shared::impl_extract_leftmost_forwarded_ip!(self)
85    }
86
87    #[inline(always)]
88    fn extract_rightmost_forwarded_ip(&self) -> Option<IpAddr> {
89        crate::shared::impl_extract_rightmost_forwarded_ip!(self)
90    }
91
92    fn extract_filtered_forwarded_ip(&self, filter: &impl Filter) -> Option<IpAddr> {
93        crate::shared::impl_extract_filtered_forwarded_ip!(self, filter)
94    }
95}