postrust_proxy/vendored/
types.rs1use thiserror::Error;
4
5#[derive(Error, Debug)]
7pub enum ProxyError {
8 #[error("Backend not found: {0}")]
9 BackendNotFound(String),
10
11 #[error("Upstream not found: {0}")]
12 UpstreamNotFound(String),
13
14 #[error("Connection error: {0}")]
15 Connection(String),
16
17 #[error("Request error: {0}")]
18 Request(String),
19
20 #[error("Response error: {0}")]
21 Response(String),
22
23 #[error("Timeout")]
24 Timeout,
25
26 #[error("Hyper error: {0}")]
27 Hyper(#[from] hyper::Error),
28
29 #[error("HTTP error: {0}")]
30 Http(#[from] hyper::http::Error),
31
32 #[error("IO error: {0}")]
33 Io(#[from] std::io::Error),
34}
35
36#[derive(Clone, Debug, PartialEq, Eq, Hash)]
38pub struct ServerName(pub String);
39
40impl ServerName {
41 pub fn new(name: impl Into<String>) -> Self {
42 Self(name.into().to_lowercase())
43 }
44
45 pub fn matches(&self, host: &str) -> bool {
47 let host = host.to_lowercase();
48
49 if self.0 == host {
51 return true;
52 }
53
54 if self.0.starts_with("*.") {
58 let suffix = &self.0[2..];
59 if host.ends_with(suffix) {
60 let prefix_len = host.len() - suffix.len();
61 if prefix_len > 0
64 && host.chars().nth(prefix_len - 1) == Some('.')
65 && !host[..prefix_len - 1].contains('.')
66 {
67 return true;
68 }
69 }
70 }
71
72 false
73 }
74}
75
76impl From<&str> for ServerName {
77 fn from(s: &str) -> Self {
78 Self::new(s)
79 }
80}
81
82impl From<String> for ServerName {
83 fn from(s: String) -> Self {
84 Self::new(s)
85 }
86}
87
88#[derive(Clone, Debug, PartialEq, Eq, Hash)]
90pub struct PathName(pub String);
91
92impl PathName {
93 pub fn new(path: impl Into<String>) -> Self {
94 let mut path = path.into();
95 if !path.starts_with('/') {
97 path = format!("/{}", path);
98 }
99 Self(path)
100 }
101
102 pub fn matches(&self, request_path: &str) -> bool {
104 request_path.starts_with(&self.0)
105 }
106
107 pub fn len(&self) -> usize {
109 self.0.len()
110 }
111
112 pub fn is_empty(&self) -> bool {
114 self.0.is_empty()
115 }
116
117 pub fn is_root(&self) -> bool {
119 self.0 == "/"
120 }
121}
122
123impl From<&str> for PathName {
124 fn from(s: &str) -> Self {
125 Self::new(s)
126 }
127}
128
129impl From<String> for PathName {
130 fn from(s: String) -> Self {
131 Self::new(s)
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_server_name_exact_match() {
141 let name = ServerName::new("example.com");
142 assert!(name.matches("example.com"));
143 assert!(name.matches("EXAMPLE.COM"));
144 assert!(!name.matches("sub.example.com"));
145 assert!(!name.matches("example.org"));
146 }
147
148 #[test]
149 fn test_server_name_wildcard() {
150 let name = ServerName::new("*.example.com");
151 assert!(name.matches("sub.example.com"));
152 assert!(name.matches("api.example.com"));
153 assert!(!name.matches("example.com"));
154 assert!(!name.matches("sub.sub.example.com")); }
156
157 #[test]
158 fn test_path_name_matching() {
159 let path = PathName::new("/api");
160 assert!(path.matches("/api"));
161 assert!(path.matches("/api/users"));
162 assert!(path.matches("/api/users/123"));
163 assert!(!path.matches("/other"));
164 assert!(!path.matches("/"));
165 }
166
167 #[test]
168 fn test_path_name_root() {
169 let path = PathName::new("/");
170 assert!(path.matches("/"));
171 assert!(path.matches("/anything"));
172 assert!(path.is_root());
173 }
174}