rocket_client_addr/
guard.rs1use std::{fmt, net::IpAddr};
2
3use rocket::{
4 http::{Status, uncased::Uncased},
5 outcome::Outcome,
6 request::{self, FromRequest, Request},
7};
8
9use crate::{
10 ClientIpConfig,
11 ClientIpRejection::{self, MissingConfig, MissingRemoteAddr},
12};
13
14#[derive(Clone, Debug, Eq, Hash, PartialEq)]
18pub struct ClientIp {
19 ip: IpAddr,
20 source: ClientIpSource,
21}
22
23impl ClientIp {
24 #[inline]
25 pub(crate) const fn new(ip: IpAddr, source: ClientIpSource) -> Self {
26 Self {
27 ip,
28 source,
29 }
30 }
31
32 #[inline]
34 pub const fn ip(&self) -> IpAddr {
35 self.ip
36 }
37
38 #[inline]
42 pub const fn source(&self) -> &ClientIpSource {
43 &self.source
44 }
45
46 #[inline]
48 pub fn into_parts(self) -> (IpAddr, ClientIpSource) {
49 (self.ip, self.source)
50 }
51}
52
53impl fmt::Display for ClientIp {
54 #[inline]
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 fmt::Display::fmt(&self.ip, f)
57 }
58}
59
60impl From<ClientIp> for IpAddr {
61 #[inline]
62 fn from(client_ip: ClientIp) -> Self {
63 client_ip.ip
64 }
65}
66
67#[derive(Clone, Debug, Eq, Hash, PartialEq)]
69pub enum ClientIpSource {
70 ConfiguredHeader(Uncased<'static>),
74
75 ChainHeader(Uncased<'static>),
79
80 Socket,
84}
85
86impl ClientIpSource {
87 #[inline]
89 pub const fn header_name(&self) -> Option<&Uncased<'static>> {
90 match self {
91 Self::ConfiguredHeader(header) | Self::ChainHeader(header) => Some(header),
92 Self::Socket => None,
93 }
94 }
95}
96
97fn from_request(request: &Request<'_>) -> Result<ClientIp, ClientIpRejection> {
99 let config = request.rocket().state::<ClientIpConfig>().ok_or(MissingConfig)?;
100 let remote = request.remote().ok_or(MissingRemoteAddr)?;
101
102 Ok(config.resolve_client_ip(request.headers(), remote.ip()))
103}
104
105#[rocket::async_trait]
106impl<'r> FromRequest<'r> for ClientIp {
107 type Error = ClientIpRejection;
108
109 async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
110 match from_request(request) {
111 Ok(client_ip) => Outcome::Success(client_ip),
112 Err(rejection) => Outcome::Error((Status::InternalServerError, rejection)),
114 }
115 }
116}
117
118#[rocket::async_trait]
119impl<'r> FromRequest<'r> for &'r ClientIp {
120 type Error = ClientIpRejection;
121
122 async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
123 let cache: &Result<ClientIp, ClientIpRejection> =
125 request.local_cache(|| from_request(request));
126
127 match cache {
128 Ok(client_ip) => Outcome::Success(client_ip),
129 Err(rejection) => Outcome::Error((Status::InternalServerError, *rejection)),
130 }
131 }
132}