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 {
129 request_path.starts_with(&self.0)
130 }
131
132 pub fn len(&self) -> usize {
134 self.0.len()
135 }
136
137 pub fn is_empty(&self) -> bool {
139 self.0.is_empty()
140 }
141
142 pub fn is_root(&self) -> bool {
144 self.0 == "/"
145 }
146}
147
148impl From<&str> for PathName {
149 fn from(s: &str) -> Self {
150 Self::new(s)
151 }
152}
153
154impl From<String> for PathName {
155 fn from(s: String) -> Self {
156 Self::new(s)
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn test_server_name_exact_match() {
166 let name = ServerName::new("example.com");
167 assert!(name.matches("example.com"));
168 assert!(name.matches("EXAMPLE.COM"));
169 assert!(!name.matches("sub.example.com"));
170 assert!(!name.matches("example.org"));
171 }
172
173 #[test]
174 fn test_server_name_wildcard() {
175 let name = ServerName::new("*.example.com");
176 assert!(name.matches("sub.example.com"));
177 assert!(name.matches("api.example.com"));
178 assert!(!name.matches("example.com"));
179 assert!(!name.matches("sub.sub.example.com")); }
181
182 #[test]
183 fn test_path_name_matching() {
184 let path = PathName::new("/api");
185 assert!(path.matches("/api"));
186 assert!(path.matches("/api/users"));
187 assert!(path.matches("/api/users/123"));
188 assert!(!path.matches("/other"));
189 assert!(!path.matches("/"));
190 }
191
192 #[test]
193 fn test_path_name_root() {
194 let path = PathName::new("/");
195 assert!(path.matches("/"));
196 assert!(path.matches("/anything"));
197 assert!(path.is_root());
198 }
199}