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}