Skip to content

Commit

Permalink
Preserve leading '.' prefix for 'Domain'.
Browse files Browse the repository at this point in the history
A leading `.` in the `Domain` of a cookie was previously removed during
parsing. While this was correct behavior, as stipulated by the RFC, it
raised an incongruity with manually set `Domain`s (via `set_domain()`,
and `CookieBuilder::domain()`), which did not strip a leading `.`.

This commit resolves this incongruity by always preserving a leading `.`
and omitting instead only when the `Domain` value is read. This
preserves the original behavior during parsing but changes the observed
behavior for manually set `Domain`s with a leading dot.

Resolves #207.
  • Loading branch information
SergioBenitez committed Jan 16, 2023
1 parent d5ce056 commit 87d0396
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 8 deletions.
35 changes: 32 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,10 @@ impl<'c> Cookie<'c> {

/// Returns the `Domain` of the cookie if one was specified.
///
/// This does not consider whether the `Domain` is valid; validation is left
/// to higher-level libraries, as needed. However, if the `Domain` starts
/// with a leading `.`, the leading `.` is stripped.
///
/// # Example
///
/// ```
Expand All @@ -511,11 +515,21 @@ impl<'c> Cookie<'c> {
///
/// let c = Cookie::parse("name=value; Domain=crates.io").unwrap();
/// assert_eq!(c.domain(), Some("crates.io"));
///
/// let c = Cookie::parse("name=value; Domain=.crates.io").unwrap();
/// assert_eq!(c.domain(), Some("crates.io"));
///
/// // Note that `..crates.io` is not a valid domain.
/// let c = Cookie::parse("name=value; Domain=..crates.io").unwrap();
/// assert_eq!(c.domain(), Some(".crates.io"));
/// ```
#[inline]
pub fn domain(&self) -> Option<&str> {
match self.domain {
Some(ref c) => Some(c.to_str(self.cookie_string.as_ref())),
Some(ref c) => {
let domain = c.to_str(self.cookie_string.as_ref());
domain.strip_prefix(".").or(Some(domain))
},
None => None,
}
}
Expand Down Expand Up @@ -1029,6 +1043,10 @@ impl<'c> Cookie<'c> {
/// from a raw string, or if `self` doesn't contain a `Domain`, or if the
/// `Domain` has changed since parsing, returns `None`.
///
/// Like [`Cookie::domain()`], this does not consider whether `Domain` is
/// valid; validation is left to higher-level libraries, as needed. However,
/// if `Domain` starts with a leading `.`, the leading `.` is stripped.
///
/// This method differs from [`Cookie::domain()`] in that it returns a
/// string with the same lifetime as the originally parsed string. This
/// lifetime may outlive `self` struct. If a longer lifetime is not
Expand All @@ -1040,7 +1058,7 @@ impl<'c> Cookie<'c> {
/// ```
/// use cookie::Cookie;
///
/// let cookie_string = format!("{}={}; Domain=crates.io", "foo", "bar");
/// let cookie_string = format!("{}={}; Domain=.crates.io", "foo", "bar");
///
/// //`c` will be dropped at the end of the scope, but `domain` will live on
/// let domain = {
Expand All @@ -1053,7 +1071,10 @@ impl<'c> Cookie<'c> {
#[inline]
pub fn domain_raw(&self) -> Option<&'c str> {
match (self.domain.as_ref(), self.cookie_string.as_ref()) {
(Some(domain), Some(string)) => domain.to_raw_str(string),
(Some(domain), Some(string)) => match domain.to_raw_str(string) {
Some(s) => s.strip_prefix(".").or(Some(s)),
None => None,
}
_ => None,
}
}
Expand Down Expand Up @@ -1320,6 +1341,14 @@ mod tests {
.domain("www.rust-lang.org").finish();
assert_eq!(&cookie.to_string(), "foo=bar; Domain=www.rust-lang.org");

let cookie = Cookie::build("foo", "bar")
.domain(".rust-lang.org").finish();
assert_eq!(&cookie.to_string(), "foo=bar; Domain=rust-lang.org");

let cookie = Cookie::build("foo", "bar")
.domain("rust-lang.org").finish();
assert_eq!(&cookie.to_string(), "foo=bar; Domain=rust-lang.org");

let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
let expires = parse_date(time_str, &crate::parse::FMT1).unwrap();
let cookie = Cookie::build("foo", "bar")
Expand Down
12 changes: 7 additions & 5 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,7 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
.unwrap_or_else(|_| Duration::seconds(i64::max_value())))
}
},
("domain", Some(mut domain)) if !domain.is_empty() => {
if domain.starts_with('.') {
domain = &domain[1..];
}

("domain", Some(domain)) if !domain.is_empty() => {
let (i, j) = indexes_of(domain, s).expect("domain sub");
cookie.domain = Some(CookieStr::Indexed(i, j));
}
Expand Down Expand Up @@ -442,6 +438,12 @@ mod tests {
assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
Domain=FOO.COM", expected);

expected.set_domain(".foo.com");
assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
Domain=.foo.com", expected);
assert_eq_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
Domain=.FOO.COM", expected);

unexpected.set_path("/foo");
unexpected.set_domain("bar.com");
assert_ne_parse!(" foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
Expand Down

0 comments on commit 87d0396

Please sign in to comment.