jsonrpsee_server/middleware/http/
authority.rs1use crate::HttpRequest;
30use http::uri::{InvalidUri, Uri};
31use jsonrpsee_core::http_helpers;
32
33#[derive(Clone, Hash, PartialEq, Eq, Debug)]
37pub struct Authority {
38 pub host: String,
40 pub port: Port,
42}
43
44#[derive(Debug, thiserror::Error)]
46pub enum AuthorityError {
47 #[error(transparent)]
49 InvalidUri(InvalidUri),
50 #[error("Invalid port: {0}")]
52 InvalidPort(String),
53 #[error("The host was not found")]
55 MissingHost,
56}
57
58#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
60pub enum Port {
61 Default,
63 Any,
65 Fixed(u16),
67}
68
69impl Authority {
70 fn inner_from_str(value: &str) -> Result<Self, AuthorityError> {
71 let uri: Uri = value.parse().map_err(AuthorityError::InvalidUri)?;
72 let authority = uri.authority().ok_or(AuthorityError::MissingHost)?;
73 let host = authority.host();
74 let maybe_port = &authority.as_str()[host.len()..];
75
76 let port = match maybe_port.split_once(':') {
78 Some((_, "*")) => Port::Any,
79 Some((_, p)) => {
80 let port_u16: u16 =
81 p.parse().map_err(|e: std::num::ParseIntError| AuthorityError::InvalidPort(e.to_string()))?;
82
83 match default_port(uri.scheme_str()) {
85 Some(p) if p == port_u16 => Port::Default,
86 _ => port_u16.into(),
87 }
88 }
89 None => Port::Default,
90 };
91
92 Ok(Self { host: host.to_owned(), port })
93 }
94
95 pub fn from_http_request<T>(request: &HttpRequest<T>) -> Option<Self> {
100 let host_header =
103 http_helpers::read_header_value(request.headers(), hyper::header::HOST).map(Authority::try_from);
104 let uri = request.uri().authority().map(|v| Authority::try_from(v.as_str()));
105
106 match (host_header, uri) {
107 (Some(Ok(a1)), Some(Ok(a2))) => {
108 if a1 == a2 {
109 Some(a1)
110 } else {
111 None
112 }
113 }
114 (Some(Ok(a)), _) => Some(a),
115 (_, Some(Ok(a))) => Some(a),
116 _ => None,
117 }
118 }
119}
120
121impl<'a> TryFrom<&'a str> for Authority {
122 type Error = AuthorityError;
123
124 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
125 Self::inner_from_str(value)
126 }
127}
128
129impl TryFrom<String> for Authority {
130 type Error = AuthorityError;
131
132 fn try_from(value: String) -> Result<Self, Self::Error> {
133 Self::inner_from_str(&value)
134 }
135}
136
137impl TryFrom<std::net::SocketAddr> for Authority {
138 type Error = AuthorityError;
139
140 fn try_from(sockaddr: std::net::SocketAddr) -> Result<Self, Self::Error> {
141 Self::inner_from_str(&sockaddr.to_string())
142 }
143}
144
145impl From<u16> for Port {
146 fn from(port: u16) -> Port {
147 Port::Fixed(port)
148 }
149}
150
151fn default_port(scheme: Option<&str>) -> Option<u16> {
152 match scheme {
153 Some("http") | Some("ws") => Some(80),
154 Some("https") | Some("wss") => Some(443),
155 Some("ftp") => Some(21),
156 _ => None,
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::{Authority, HttpRequest, Port};
163 use hyper::header::HOST;
164
165 fn authority(host: &str, port: Port) -> Authority {
166 Authority { host: host.to_owned(), port }
167 }
168
169 type EmptyBody = http_body_util::Empty<hyper::body::Bytes>;
170
171 #[test]
172 fn should_parse_valid_authority() {
173 assert_eq!(Authority::try_from("http://parity.io").unwrap(), authority("parity.io", Port::Default));
174 assert_eq!(Authority::try_from("https://parity.io:8443").unwrap(), authority("parity.io", Port::Fixed(8443)));
175 assert_eq!(Authority::try_from("chrome-extension://124.0.0.1").unwrap(), authority("124.0.0.1", Port::Default));
176 assert_eq!(Authority::try_from("http://*.domain:*/somepath").unwrap(), authority("*.domain", Port::Any));
177 assert_eq!(Authority::try_from("parity.io").unwrap(), authority("parity.io", Port::Default));
178 assert_eq!(Authority::try_from("127.0.0.1:8845").unwrap(), authority("127.0.0.1", Port::Fixed(8845)));
179 assert_eq!(
180 Authority::try_from("http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]:9933/").unwrap(),
181 authority("[2001:db8:85a3:8d3:1319:8a2e:370:7348]", Port::Fixed(9933))
182 );
183 assert_eq!(
184 Authority::try_from("http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]/").unwrap(),
185 authority("[2001:db8:85a3:8d3:1319:8a2e:370:7348]", Port::Default)
186 );
187 assert_eq!(
188 Authority::try_from("https://user:password@example.com/tmp/foo").unwrap(),
189 authority("example.com", Port::Default)
190 );
191 }
192
193 #[test]
194 fn should_not_parse_invalid_authority() {
195 assert!(Authority::try_from("/foo/bar").is_err());
196 assert!(Authority::try_from("user:password").is_err());
197 assert!(Authority::try_from("parity.io/somepath").is_err());
198 assert!(Authority::try_from("127.0.0.1:8545/somepath").is_err());
199 assert!(Authority::try_from("127.0.0.1:-1337").is_err());
200 }
201
202 #[test]
203 fn authority_from_http_only_host_works() {
204 let req = HttpRequest::builder().header(HOST, "example.com").body(EmptyBody::new()).unwrap();
205 assert!(Authority::from_http_request(&req).is_some());
206 }
207
208 #[test]
209 fn authority_only_uri_works() {
210 let req = HttpRequest::builder().uri("example.com").body(EmptyBody::new()).unwrap();
211 assert!(Authority::from_http_request(&req).is_some());
212 }
213
214 #[test]
215 fn authority_host_and_uri_works() {
216 let req = HttpRequest::builder()
217 .header(HOST, "example.com:9999")
218 .uri("example.com:9999")
219 .body(EmptyBody::new())
220 .unwrap();
221 assert!(Authority::from_http_request(&req).is_some());
222 }
223
224 #[test]
225 fn authority_host_and_uri_mismatch() {
226 let req =
227 HttpRequest::builder().header(HOST, "example.com:9999").uri("example.com").body(EmptyBody::new()).unwrap();
228 assert!(Authority::from_http_request(&req).is_none());
229 }
230
231 #[test]
232 fn authority_missing_host_and_uri() {
233 let req = HttpRequest::builder().body(EmptyBody::new()).unwrap();
234 assert!(Authority::from_http_request(&req).is_none());
235 }
236}