mirror of
https://github.com/cfal/shoes.git
synced 2026-11-06 16:14:11 +00:00
Return bool from XHTTP matching header check
check_matching_headers built an io::Error with PermissionDenied that no caller inspected: dispatch discarded it for a 404 and the HTTP/1 pre-check only needed some error to close the connection. Returning a bool makes the callers branch on the result directly, and the HTTP/1 path now builds the error hyper needs at its own call site.
This commit is contained in:
+7
-4
@@ -3907,9 +3907,10 @@ impl State {
|
||||
request: &Request<()>,
|
||||
body: Reader,
|
||||
) -> Result<(Body, bool), Rejection> {
|
||||
wire::check_matching_headers(&self.config, request.headers())
|
||||
.inspect_err(|error| log::debug!("XHTTP request rejected: {error}"))
|
||||
.map_err(|_| StatusCode::NOT_FOUND)?;
|
||||
if !wire::check_matching_headers(&self.config, request.headers()) {
|
||||
log::debug!("XHTTP request rejected: headers do not match");
|
||||
return Err(StatusCode::NOT_FOUND.into());
|
||||
}
|
||||
match wire::classify(&self.config, &self.path, request)? {
|
||||
Kind::Options => Ok((Body::Empty, false)),
|
||||
Kind::One => self.stream_one(body).map_err(Into::into),
|
||||
@@ -4387,7 +4388,9 @@ impl State {
|
||||
if !self.config.close_on_http1_error {
|
||||
return Ok(());
|
||||
}
|
||||
wire::check_matching_headers(&self.config, request.headers())?;
|
||||
if !wire::check_matching_headers(&self.config, request.headers()) {
|
||||
return Err(io::Error::other("XHTTP request headers do not match"));
|
||||
}
|
||||
if !percent_encoding::percent_decode_str(request.uri().path())
|
||||
.take(self.path.base.len())
|
||||
.eq(self.path.base.bytes())
|
||||
|
||||
+9
-21
@@ -108,21 +108,14 @@ pub enum Kind {
|
||||
Packet(String, u64),
|
||||
}
|
||||
|
||||
pub fn check_matching_headers(config: &XhttpServerConfig, headers: &HeaderMap) -> io::Result<()> {
|
||||
for (name, expected) in &config.matching_headers {
|
||||
pub fn check_matching_headers(config: &XhttpServerConfig, headers: &HeaderMap) -> bool {
|
||||
config.matching_headers.iter().all(|(name, expected)| {
|
||||
let mut values = headers.get_all(name.as_str()).iter();
|
||||
let matches = values
|
||||
values
|
||||
.next()
|
||||
.is_some_and(|value| bool::from(value.as_bytes().ct_eq(expected.as_bytes())))
|
||||
&& values.next().is_none();
|
||||
if !matches {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::PermissionDenied,
|
||||
"XHTTP request headers do not match",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
&& values.next().is_none()
|
||||
})
|
||||
}
|
||||
|
||||
fn query_padding(query: &str) -> Option<String> {
|
||||
@@ -308,7 +301,7 @@ mod tests {
|
||||
"protocol": {"type": "socks"}
|
||||
}))
|
||||
.unwrap();
|
||||
assert!(check_matching_headers(&config, &HeaderMap::new()).is_ok());
|
||||
assert!(check_matching_headers(&config, &HeaderMap::new()));
|
||||
config
|
||||
.matching_headers
|
||||
.insert("X-Token".into(), "Secret".into());
|
||||
@@ -319,10 +312,10 @@ mod tests {
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("x-token", "Secret".parse().unwrap());
|
||||
assert!(check_matching_headers(&config, &headers).is_err());
|
||||
assert!(!check_matching_headers(&config, &headers));
|
||||
headers.insert("x-empty", "".parse().unwrap());
|
||||
headers.insert("x-extra", "allowed".parse().unwrap());
|
||||
assert!(check_matching_headers(&config, &headers).is_ok());
|
||||
assert!(check_matching_headers(&config, &headers));
|
||||
for values in [
|
||||
vec![],
|
||||
vec![""],
|
||||
@@ -337,12 +330,7 @@ mod tests {
|
||||
for value in values {
|
||||
headers.append("x-token", value.parse().unwrap());
|
||||
}
|
||||
assert_eq!(
|
||||
check_matching_headers(&config, &headers)
|
||||
.unwrap_err()
|
||||
.kind(),
|
||||
io::ErrorKind::PermissionDenied
|
||||
);
|
||||
assert!(!check_matching_headers(&config, &headers));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user