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 {}