Skip to main content

http_ip/
ohkami024.rs

1//! Ohkami 0.24 integration
2use core::{fmt, marker};
3use core::net::IpAddr;
4
5use crate::find_next_ip_after_filter;
6use crate::filter::Filter;
7use crate::forwarded::parse_forwarded_for_rev;
8use ohkami024::{FromRequest, Request};
9
10#[repr(transparent)]
11#[derive(Copy, Clone)]
12///ClientIp extractor
13///
14///Provided `F` parameter can be used to customize filter selection. Use `nil` type to only extract rightmost IP.
15pub struct ClientIp<F: Filter> {
16    ///Underlying IP addr if available
17    pub inner: Option<IpAddr>,
18    _filter: marker::PhantomData<F>
19}
20
21impl<F: Filter> ClientIp<F> {
22    #[inline(always)]
23    fn new(inner: Option<IpAddr>) -> Self {
24        Self {
25            inner,
26            _filter: marker::PhantomData,
27        }
28    }
29
30    #[inline(always)]
31    ///Access underlying value
32    pub fn into_inner(self) -> Option<IpAddr> {
33        self.inner
34    }
35}
36
37impl<F: Filter> fmt::Debug for ClientIp<F> {
38    #[inline(always)]
39    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
40        fmt::Debug::fmt(&self.inner, fmt)
41    }
42}
43
44impl<'req, F: Send + Sync + Filter + 'static> FromRequest<'req> for ClientIp<F> {
45    ///This extractor will not fail
46    type Error = core::convert::Infallible;
47    ///Extracts `Forwarded` header, or do nothing if not available
48    ///
49    ///If no IP can be determined due to filter or obfuscation, sets `ClientIp::inner` to `None`
50    fn from_request(req: &'req Request) -> Option<Result<Self, Self::Error>> {
51        let filter = req.context.get::<F>()?;
52        //This library is a mess of undocumented functionality, but this method is basically shortcut to getting Forwarded with both possible cases
53        if let Some(ip) = req.headers.forwarded().and_then(|value| find_next_ip_after_filter(parse_forwarded_for_rev(value), filter)) {
54            Some(Ok(ClientIp::new(Some(ip))))
55        } else {
56            //do not return None as it will cause 404 by default, it is best to have your control over this behaviour
57            Some(Ok(ClientIp::new(None)))
58        }
59    }
60}