Make Cookie header RFC6265 compliant (#130)

This commit is contained in:
cazgp
2020-08-07 18:04:15 +01:00
committed by GitHub
parent 75d5e52a45
commit 22d815e5e7
2 changed files with 77 additions and 36 deletions

View File

@@ -1,7 +1,7 @@
use crate::error::Error;
use std::str::FromStr;
#[derive(Clone)]
#[derive(Clone, PartialEq)]
/// Wrapper type for a header field.
/// https://tools.ietf.org/html/rfc7230#section-3.2
pub struct Header {

View File

@@ -61,7 +61,7 @@ impl Unit {
let query_string = combine_query(&url, &req.query, mix_queries);
let cookie_headers: Vec<_> = extract_cookies(&req.agent, &url);
let cookie_header: Option<Header> = extract_cookies(&req.agent, &url);
let extra_headers = {
let mut extra = vec![];
@@ -86,7 +86,7 @@ impl Unit {
let headers: Vec<_> = req
.headers
.iter()
.chain(cookie_headers.iter())
.chain(cookie_header.iter())
.chain(extra_headers.iter())
.cloned()
.collect();
@@ -239,25 +239,26 @@ pub(crate) fn connect(
}
#[cfg(feature = "cookie")]
fn extract_cookies(state: &std::sync::Mutex<Option<AgentState>>, url: &Url) -> Vec<Header> {
fn extract_cookies(state: &std::sync::Mutex<Option<AgentState>>, url: &Url) -> Option<Header> {
let state = state.lock().unwrap();
let is_secure = url.scheme().eq_ignore_ascii_case("https");
let hostname = url.host_str().unwrap_or(DEFAULT_HOST).to_string();
match state.as_ref().map(|state| &state.jar) {
None => vec![],
Some(jar) => match_cookies(jar, &hostname, url.path(), is_secure),
}
state
.as_ref()
.map(|state| &state.jar)
.and_then(|jar| match_cookies(jar, &hostname, url.path(), is_secure))
}
#[cfg(not(feature = "cookie"))]
fn extract_cookies(_state: &std::sync::Mutex<Option<AgentState>>, _url: &Url) -> Vec<Header> {
vec![]
fn extract_cookies(_state: &std::sync::Mutex<Option<AgentState>>, _url: &Url) -> Option<Header> {
None
}
// TODO check so cookies can't be set for tld:s
#[cfg(feature = "cookie")]
fn match_cookies(jar: &CookieJar, domain: &str, path: &str, is_secure: bool) -> Vec<Header> {
fn match_cookies(jar: &CookieJar, domain: &str, path: &str, is_secure: bool) -> Option<Header> {
Some(
jar.iter()
.filter(|c| {
// if there is a domain, it must be matched.
@@ -281,9 +282,13 @@ fn match_cookies(jar: &CookieJar, domain: &str, path: &str, is_secure: bool) ->
let name = c.name().to_string();
let value = c.value().to_string();
let nameval = Cookie::new(name, value).encoded().to_string();
Header::new("Cookie", &nameval)
nameval
})
.collect()
.collect::<Vec<_>>()
.join(";"),
)
.filter(|x| !x.is_empty())
.map(|s| Header::new("Cookie", &s))
}
/// Combine the query of the url and the query options set on the request object.
@@ -419,3 +424,39 @@ fn save_cookies(unit: &Unit, resp: &Response) {
}
}
}
#[cfg(test)]
#[cfg(feature = "cookies")]
mod tests {
use super::*;
///////////////////// COOKIE TESTS //////////////////////////////
#[test]
fn match_cookies_returns_nothing_when_no_cookies() {
let jar = CookieJar::new();
let result = match_cookies(&jar, "crates.io", "/", false);
assert_eq!(result, None);
}
#[test]
fn match_cookies_returns_one_header() {
let mut jar = CookieJar::new();
let cookie1 = Cookie::parse("cookie1=value1; Domain=crates.io").unwrap();
let cookie2 = Cookie::parse("cookie2=value2; Domain=crates.io").unwrap();
jar.add(cookie1);
jar.add(cookie2);
// There's no guarantee to the order in which cookies are defined.
// Ensure that they're either in one order or the other.
let result = match_cookies(&jar, "crates.io", "/", false);
let order1 = "cookie1=value1;cookie2=value2";
let order2 = "cookie2=value2;cookie1=value1";
assert!(
result == Some(Header::new("Cookie", order1))
|| result == Some(Header::new("Cookie", order2))
);
}
}