Merge pull request #375 from jbr/forwarded-header-bugs

address three bugs in forwarded header parsing
This commit is contained in:
Jeremiah Senkpiel 2021-08-05 09:42:12 -07:00 committed by GitHub
commit cb62676859
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 35 additions and 11 deletions

View File

@ -5,7 +5,7 @@ pub(crate) fn parse_token(input: &str) -> (Option<&str>, &str) {
let mut end_of_token = 0;
for (i, c) in input.char_indices() {
if tchar(c) {
end_of_token = i;
end_of_token = i + 1;
} else {
break;
}
@ -14,7 +14,7 @@ pub(crate) fn parse_token(input: &str) -> (Option<&str>, &str) {
if end_of_token == 0 {
(None, input)
} else {
(Some(&input[..end_of_token + 1]), &input[end_of_token + 1..])
(Some(&input[..end_of_token]), &input[end_of_token..])
}
}
@ -125,7 +125,7 @@ mod test {
assert_eq!(parse_token("key=value"), (Some("key"), "=value"));
assert_eq!(parse_token("KEY=value"), (Some("KEY"), "=value"));
assert_eq!(parse_token("0123)=value"), (Some("0123"), ")=value"));
assert_eq!(parse_token("a=b"), (Some("a"), "=b"));
assert_eq!(
parse_token("!#$%&'*+-.^_`|~=value"),
(Some("!#$%&'*+-.^_`|~"), "=value",)

View File

@ -12,7 +12,7 @@ const X_FORWARDED_HOST: HeaderName = HeaderName::from_lowercase_str("x-forwarded
/// A rust representation of the [forwarded
/// header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded).
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Forwarded<'a> {
by: Option<Cow<'a, str>>,
forwarded_for: Vec<Cow<'a, str>>,
@ -208,12 +208,12 @@ impl<'a> Forwarded<'a> {
let mut input = input;
let mut forwarded = Forwarded::new();
if starts_with_ignore_case("for=", input) {
input = forwarded.parse_for(input)?;
}
while !input.is_empty() {
input = forwarded.parse_forwarded_pair(input)?;
input = if starts_with_ignore_case("for=", input) {
forwarded.parse_for(input)?
} else {
forwarded.parse_forwarded_pair(input)?
}
}
Ok(forwarded)
@ -429,8 +429,12 @@ fn match_ignore_case<'a>(start: &'static str, input: &'a str) -> (bool, &'a str)
}
fn starts_with_ignore_case(start: &'static str, input: &str) -> bool {
let len = start.len();
input[..len].eq_ignore_ascii_case(start)
if start.len() <= input.len() {
let len = start.len();
input[..len].eq_ignore_ascii_case(start)
} else {
false
}
}
impl std::fmt::Display for Forwarded<'_> {
@ -467,6 +471,11 @@ mod tests {
use crate::{Method::Get, Request, Response, Result};
use url::Url;
#[test]
fn starts_with_ignore_case_can_handle_short_inputs() {
assert!(!starts_with_ignore_case("helloooooo", "h"));
}
#[test]
fn parsing_for() -> Result<()> {
assert_eq!(
@ -641,4 +650,19 @@ mod tests {
assert_eq!(forwarded.by(), Some("by"));
Ok(())
}
#[test]
fn round_trip() -> Result<()> {
let inputs = [
"for=client,for=b,for=c;by=proxy.com;host=example.com;proto=https",
"by=proxy.com;proto=https;host=example.com;for=a,for=b",
];
for input in inputs {
let forwarded = Forwarded::parse(input).map_err(|_| crate::Error::new_adhoc(input))?;
let header = forwarded.header_value();
let parsed = Forwarded::parse(header.as_str())?;
assert_eq!(forwarded, parsed);
}
Ok(())
}
}