http_ip/
axum08.rs

1//! Axum 0.8 extension module
2//!
3//! Provides [ClientIp](struct.ClientIp.html) to extract client's ip using filters
4
5use core::{fmt, marker};
6use core::net::{IpAddr, SocketAddr};
7
8pub use axum08::*;
9use axum08::extract::FromRequestParts;
10
11use crate::filter::Filter;
12use crate::http::HeaderMapClientIp;
13
14#[repr(transparent)]
15#[derive(Copy, Clone)]
16///ClientIp extractor
17///
18///Provided `F` parameter can be used to customize filter selection. Use `nil` type to only extract rightmost IP.
19///
20///Defaults to `axum::extract::ConnectInfo` if corresponding header cannot provide ip
21///
22///## Usage
23///
24///```rust
25///use std::net::{IpAddr, Ipv4Addr};
26///
27///use http_ip::axum08::{
28///    routing::get,
29///    handler::Handler,
30///    Router,
31///};
32///use http_ip::axum08::ClientIp;
33///
34///#[derive(Clone)]
35///struct MyState {
36///    local_ip: IpAddr,
37///}
38///
39/////Alternatively use derive macro
40///impl http_ip::axum08::extract::FromRef<MyState> for IpAddr {
41///    #[inline(always)]
42///    fn from_ref(state: &MyState) -> Self {
43///        state.local_ip
44///    }
45///}
46///
47///async fn create_user(client_ip: ClientIp<IpAddr>) {
48///    // Do whatever you want with client_ip now
49///}
50///
51///let state = MyState {
52///    local_ip: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
53///};
54///let app: Router<MyState> = Router::new().route("/users", get(create_user)).with_state(state);
55///```
56pub struct ClientIp<F: Filter> {
57    ///Underlying IP addr if available
58    pub inner: Option<IpAddr>,
59    _filter: marker::PhantomData<F>
60}
61
62impl<F: Filter> ClientIp<F> {
63    #[inline(always)]
64    fn new(inner: Option<IpAddr>) -> Self {
65        Self {
66            inner,
67            _filter: marker::PhantomData,
68        }
69    }
70
71    #[inline(always)]
72    ///Access underlying value
73    pub fn into_inner(self) -> Option<IpAddr> {
74        self.inner
75    }
76}
77
78impl<F: Filter> fmt::Debug for ClientIp<F> {
79    #[inline(always)]
80    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
81        fmt::Debug::fmt(&self.inner, fmt)
82    }
83}
84
85impl<S: Send + Sync, F: Send + Sync + Filter + Clone + extract::FromRef<S>> FromRequestParts<S> for ClientIp<F> {
86    type Rejection = core::convert::Infallible;
87
88    async fn from_request_parts(parts: &mut http::request::Parts, state: &S) -> Result<Self, Self::Rejection> {
89        let filter: F = extract::FromRef::from_ref(state);
90        let ip = if let Some(ip) = parts.headers.extract_filtered_forwarded_ip(&filter) {
91            Some(ip)
92        } else if let Ok(addr) = extract::ConnectInfo::<SocketAddr>::from_request_parts(parts, state).await {
93            Some(addr.ip())
94        } else {
95            None
96        };
97        Ok(ClientIp::new(ip))
98    }
99}