Skip to main content

rocket_client_addr/
errors.rs

1use std::{error::Error, fmt};
2
3use crate::TrustedProxyRule;
4
5/// An error returned while building a [`crate::ClientIpConfig`].
6#[derive(Clone, Debug, Eq, PartialEq)]
7#[non_exhaustive]
8pub enum ClientIpConfigBuildError {
9    /// Trusted proxy CIDRs cover a common address but do not agree on the client IP header.
10    ///
11    /// One socket peer IP has to mean one policy, so the config cannot be built until the CIDRs are changed to agree or to stop overlapping.
12    OverlappingTrustedProxyRules {
13        /// The first of the two rules that overlap.
14        left: Box<TrustedProxyRule>,
15
16        /// The second of the two rules that overlap.
17        right: Box<TrustedProxyRule>,
18    },
19}
20
21impl fmt::Display for ClientIpConfigBuildError {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        match self {
24            Self::OverlappingTrustedProxyRules {
25                left,
26                right,
27            } => write!(
28                f,
29                "trusted proxy CIDRs overlap but use different client IP headers: {} ({}) \
30                 overlaps {} ({})",
31                left.cidr(),
32                describe_client_ip_header(left),
33                right.cidr(),
34                describe_client_ip_header(right),
35            ),
36        }
37    }
38}
39
40/// Name the client IP header of a rule, for an error message.
41fn describe_client_ip_header(rule: &TrustedProxyRule) -> &str {
42    match rule.client_ip_header() {
43        Some(header) => header.as_str(),
44        None => "no client IP header",
45    }
46}
47
48impl Error for ClientIpConfigBuildError {}
49
50/// The error returned by the [`crate::ClientIp`] request guard.
51///
52/// Both variants are server setup mistakes rather than bad requests, so the guard fails with [`rocket::http::Status::InternalServerError`].
53#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
54#[non_exhaustive]
55pub enum ClientIpRejection {
56    /// No [`crate::ClientIpConfig`] is in Rocket's managed state.
57    ///
58    /// Pass a built config to `rocket::build().manage(config)` so that the guard knows which trust model to use.
59    MissingConfig,
60
61    /// The request carries no remote address.
62    ///
63    /// Every answer falls back to the socket peer IP, so a request without one cannot be resolved at all. Rocket records the address of every real connection, so this only shows up on a local test client that was not given one with `LocalRequest::remote`.
64    MissingRemoteAddr,
65}
66
67impl fmt::Display for ClientIpRejection {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        match self {
70            Self::MissingConfig => {
71                write!(f, "no ClientIpConfig is managed; pass one to rocket::build().manage()")
72            },
73            Self::MissingRemoteAddr => write!(f, "the request has no remote address"),
74        }
75    }
76}
77
78impl Error for ClientIpRejection {}