Differentiate between HTTP and non-HTTP APIs for cookie operations. Fix some incorrect cookie removal operation logic. Order the returned cookies according to the spec. Make cookie unit tests pass.

This commit is contained in:
Josh Matthews 2015-01-15 01:51:06 -05:00
parent 24c8896f88
commit 14df9f8a70
7 changed files with 142 additions and 137 deletions

View file

@ -7,6 +7,16 @@
use url::Url;
use cookie::Cookie;
use std::cmp::Ordering;
/// The creator of a given cookie
#[derive(PartialEq, Copy)]
pub enum CookieSource {
/// An HTTP API
HTTP,
/// A non-HTTP API
NonHTTP,
}
pub struct CookieStorage {
cookies: Vec<Cookie>
@ -20,7 +30,7 @@ impl CookieStorage {
}
// http://tools.ietf.org/html/rfc6265#section-5.3
pub fn remove(&mut self, cookie: &Cookie) -> Option<Cookie> {
pub fn remove(&mut self, cookie: &Cookie, source: CookieSource) -> Result<Option<Cookie>, ()> {
// Step 1
let position = self.cookies.iter().position(|c| {
c.cookie.domain == cookie.cookie.domain &&
@ -29,42 +39,68 @@ impl CookieStorage {
});
if let Some(ind) = position {
Some(self.cookies.remove(ind))
let c = self.cookies.remove(ind);
// http://tools.ietf.org/html/rfc6265#section-5.3 step 11.2
if !c.cookie.httponly || source == CookieSource::HTTP {
Ok(Some(c))
} else {
// Undo the removal.
self.cookies.push(c);
Err(())
}
} else {
None
Ok(None)
}
}
// http://tools.ietf.org/html/rfc6265#section-5.3
pub fn push(&mut self, mut cookie: Cookie, request: &Url) {
// Step 11
if let Some(old_cookie) = self.remove(&cookie) {
// Step 11.2
if old_cookie.cookie.httponly && !request.scheme.starts_with("http") {
self.cookies.push(old_cookie);
} else {
// Step 11.3
cookie.created_at = old_cookie.created_at;
// Step 12
self.cookies.push(cookie);
}
pub fn push(&mut self, mut cookie: Cookie, source: CookieSource) {
let old_cookie = self.remove(&cookie, source);
if old_cookie.is_err() {
// This new cookie is not allowed to overwrite an existing one.
return;
}
if cookie.cookie.value.is_empty() {
return;
}
// Step 11
if let Some(old_cookie) = old_cookie.unwrap() {
// Step 11.3
cookie.creation_time = old_cookie.creation_time;
}
// Step 12
self.cookies.push(cookie);
}
// http://tools.ietf.org/html/rfc6265#section-5.4
pub fn cookies_for_url(&mut self, url: Url) -> Option<String> {
pub fn cookies_for_url(&mut self, url: &Url, source: CookieSource) -> Option<String> {
let filterer = |&:c: &&mut Cookie| -> bool {
info!(" === SENT COOKIE : {} {} {:?} {:?}", c.cookie.name, c.cookie.value, c.cookie.domain, c.cookie.path);
info!(" === SENT COOKIE RESULT {}", c.appropriate_for_url(url.clone()));
info!(" === SENT COOKIE RESULT {}", c.appropriate_for_url(url, source));
// Step 1
c.appropriate_for_url(url.clone())
c.appropriate_for_url(url, source)
};
let mut url_cookies = self.cookies.iter_mut().filter(filterer);
// Step 2
let mut url_cookies: Vec<&mut Cookie> = self.cookies.iter_mut().filter(filterer).collect();
url_cookies.sort_by(|a, b| {
let a_path_len = a.cookie.path.as_ref().map(|p| p.len()).unwrap_or(0);
let b_path_len = b.cookie.path.as_ref().map(|p| p.len()).unwrap_or(0);
match a_path_len.cmp(&b_path_len) {
Ordering::Equal => {
let a_creation_time = a.creation_time.to_timespec();
let b_creation_time = b.creation_time.to_timespec();
a_creation_time.cmp(&b_creation_time)
}
result => result
}
});
// TODO Step 2
let reducer = |&:acc: String, c: &mut Cookie| -> String {
let reducer = |&:acc: String, c: &mut &mut Cookie| -> String {
// Step 3
c.touch();
@ -74,7 +110,7 @@ impl CookieStorage {
_ => acc + ";"
}) + c.cookie.name.as_slice() + "=" + c.cookie.value.as_slice()
};
let result = url_cookies.fold("".to_string(), reducer);
let result = url_cookies.iter_mut().fold("".to_string(), reducer);
info!(" === COOKIES SENT: {}", result);
match result.len() {