postrust_proxy/vendored/
types.rs1use thiserror::Error;
4
5#[derive(Error, Debug)]
7pub enum ProxyError {
8 #[error("Backend not found: {0}")]
10 BackendNotFound(String),
11
12 #[error("Upstream not found: {0}")]
14 UpstreamNotFound(String),
15
16 #[error("Connection error: {0}")]
18 Connection(String),
19
20 #[error("Request error: {0}")]
22 Request(String),
23
24 #[error("Response error: {0}")]
26 Response(String),
27
28 #[error("Timeout")]
30 Timeout,
31
32 #[error("Hyper error: {0}")]
34 Hyper(#[from] hyper::Error),
35
36 #[error("HTTP error: {0}")]
39 Http(#[from] hyper::http::Error),
40
41 #[error("IO error: {0}")]
43 Io(#[from] std::io::Error),
44}
45
46#[derive(Clone, Debug, PartialEq, Eq, Hash)]
48pub struct ServerName(
49 pub String,
51);
52
53impl ServerName {
54 pub fn new(name: impl Into<String>) -> Self {
60 Self(name.into().to_lowercase())
61 }
62
63 pub fn matches(&self, host: &str) -> bool {
65 let host = host.to_lowercase();
66
67 if self.0 == host {
69 return true;
70 }
71
72 if self.0.starts_with("*.") {
76 let suffix = &self.0[2..];
77 if host.ends_with(suffix) {
78 let prefix_len = host.len() - suffix.len();
79 if prefix_len > 0
82 && host.chars().nth(prefix_len - 1) == Some('.')
83 && !host[..prefix_len - 1].contains('.')
84 {
85 return true;
86 }
87 }
88 }
89
90 false
91 }
92}
93
94impl From<&str> for ServerName {
95 fn from(s: &str) -> Self {
96 Self::new(s)
97 }
98}
99
100impl From<String> for ServerName {
101 fn from(s: String) -> Self {
102 Self::new(s)
103 }
104}
105
106#[derive(Clone, Debug, PartialEq, Eq, Hash)]
108pub struct PathName(
109 pub String,
111);
112
113impl PathName {
114 pub fn new(path: impl Into<String>) -> Self {
119 let mut path = path.into();
120 if !path.starts_with('/') {
122 path = format!("/{}", path);
123 }
124 Self(path)
125 }
126
127 pub fn matches(&self, request_path: &str) -> bool {
130 crate::config::path_has_prefix(request_path, &self.0)
131 }
132
133 pub fn len(&self) -> usize {
135 self.0.len()
136 }
137
138 pub fn is_empty(&self) -> bool {
140 self.0.is_empty()
141 }
142
143 pub fn is_root(&self) -> bool {
145 self.0 == "/"
146 }
147}
148
149impl From<&str> for PathName {
150 fn from(s: &str) -> Self {
151 Self::new(s)
152 }
153}
154
155impl From<String> for PathName {
156 fn from(s: String) -> Self {
157 Self::new(s)
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn test_server_name_exact_match() {
167 let name = ServerName::new("example.com");
168 assert!(name.matches("example.com"));
169 assert!(name.matches("EXAMPLE.COM"));
170 assert!(!name.matches("sub.example.com"));
171 assert!(!name.matches("example.org"));
172 }
173
174 #[test]
175 fn test_server_name_wildcard() {
176 let name = ServerName::new("*.example.com");
177 assert!(name.matches("sub.example.com"));
178 assert!(name.matches("api.example.com"));
179 assert!(!name.matches("example.com"));
180 assert!(!name.matches("sub.sub.example.com")); }
182
183 #[test]
184 fn test_path_name_matching() {
185 let path = PathName::new("/api");
186 assert!(path.matches("/api"));
187 assert!(path.matches("/api/users"));
188 assert!(path.matches("/api/users/123"));
189 assert!(!path.matches("/other"));
190 assert!(!path.matches("/"));
191 assert!(!path.matches("/apis"));
192 }
193
194 #[test]
195 fn test_path_name_root() {
196 let path = PathName::new("/");
197 assert!(path.matches("/"));
198 assert!(path.matches("/anything"));
199 assert!(path.is_root());
200 }
201}