Skip to content

Commit

Permalink
support persisting cookies from multiple domains
Browse files Browse the repository at this point in the history
  • Loading branch information
ducaale committed May 29, 2023
1 parent a17c9fb commit 8ed83f9
Showing 1 changed file with 102 additions and 53 deletions.
155 changes: 102 additions & 53 deletions src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,43 @@ struct Auth {
}

// Unlike xh, HTTPie serializes path, secure and expires with defaults of "/", false, and null respectively.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct LegacyCookie {
value: String,
#[serde(skip_serializing_if = "Option::is_none")]
expires: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
secure: Option<bool>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Cookie {
name: String,
value: String,
#[serde(skip_serializing_if = "Option::is_none")]
expires: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
secure: Option<bool>,
// TODO: store cookie domain as well
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum Cookies {
// old cookie format kept for backward compatibility
Map(HashMap<String, LegacyCookie>),
// new cookie format that closely resembles a cookie jar
List(Vec<Cookie>),
}

impl Default for Cookies {
fn default() -> Self {
Cookies::List(Vec::new())
}
}

#[derive(Debug, Serialize, Deserialize)]
Expand Down Expand Up @@ -75,7 +103,7 @@ struct Content {
#[serde(rename = "__meta__")]
meta: Meta,
auth: Auth,
cookies: HashMap<String, Cookie>,
cookies: Cookies,
headers: Headers,
}

Expand All @@ -90,6 +118,20 @@ impl Content {
.collect(),
);
}
if let Cookies::Map(cookies) = self.cookies {
self.cookies = Cookies::List(
cookies
.into_iter()
.map(|(name, legacy_cookie)| Cookie {
name,
value: legacy_cookie.value,
expires: legacy_cookie.expires,
path: legacy_cookie.path,
secure: legacy_cookie.secure,
})
.collect(),
)
}

self
}
Expand Down Expand Up @@ -211,39 +253,48 @@ impl Session {
}

pub fn cookies(&self) -> Vec<cookie_crate::Cookie> {
let mut cookies = vec![];
for (name, c) in &self.content.cookies {
let mut cookie_builder = cookie_crate::Cookie::build(name, &c.value);
if let Some(expires) = c.expires {
cookie_builder =
cookie_builder.expires(time::OffsetDateTime::from_unix_timestamp(expires));
}
if let Some(ref path) = c.path {
cookie_builder = cookie_builder.path(path);
}
if let Some(secure) = c.secure {
cookie_builder = cookie_builder.secure(secure);
}
cookies.push(cookie_builder.finish());
match &self.content.cookies {
Cookies::Map(_) => unreachable!(),
Cookies::List(cookies) => cookies
.iter()
.map(|cookie| {
let mut cookie_builder =
cookie_crate::Cookie::build(cookie.name.clone(), cookie.value.clone());
if let Some(expires) = cookie.expires {
cookie_builder = cookie_builder
.expires(time::OffsetDateTime::from_unix_timestamp(expires));
}
if let Some(path) = &cookie.path {
cookie_builder = cookie_builder.path(path.clone());
}
if let Some(secure) = cookie.secure {
cookie_builder = cookie_builder.secure(secure);
}
cookie_builder.finish()
})
.collect(),
}
cookies
}

pub fn save_cookies(&mut self, cookies: Vec<cookie_crate::Cookie>) {
self.content.cookies.clear();
let session_cookies = match self.content.cookies {
Cookies::Map(_) => unreachable!(),
Cookies::List(ref mut cookies) => cookies,
};

session_cookies.clear();

for cookie in cookies {
self.content.cookies.insert(
cookie.name().into(),
Cookie {
value: cookie.value().into(),
expires: cookie
.expires()
.and_then(|v| v.datetime())
.map(|v| v.unix_timestamp()),
path: cookie.path().map(Into::into),
secure: cookie.secure(),
},
);
session_cookies.push(Cookie {
name: cookie.name().into(),
value: cookie.value().into(),
expires: cookie
.expires()
.and_then(|v| v.datetime())
.map(|v| v.unix_timestamp()),
path: cookie.path().map(Into::into),
secure: cookie.secure(),
});
}
}

Expand Down Expand Up @@ -346,19 +397,18 @@ mod tests {
},
);

let expected_cookie = serde_json::from_str::<Cookie>(
r#"
{
"expires": 1620239688,
"path": "/",
"secure": false,
"value": "d090ada9c629fc7b8bbc6dba3dde1149d1617647688"
}
"#,
)?;
let cookies = session.cookies();
assert_eq!(
cookies[0].name_value(),
("__cfduid", "d090ada9c629fc7b8bbc6dba3dde1149d1617647688")
);
assert_eq!(cookies[0].path(), Some("/"));
assert_eq!(cookies[0].secure(), Some(false));
assert_eq!(
session.content.cookies.get("__cfduid"),
Some(&expected_cookie)
cookies[0].expires(),
Some(cookie_crate::Expiration::from(
time::OffsetDateTime::from_unix_timestamp(1620239688)
))
);
Ok(())
}
Expand Down Expand Up @@ -414,19 +464,18 @@ mod tests {
},
);

let expected_cookie = serde_json::from_str::<Cookie>(
r#"
{
"expires": 1620239688,
"path": "/",
"secure": false,
"value": "d090ada9c629fc7b8bbc6dba3dde1149d1617647688"
}
"#,
)?;
let cookies = session.cookies();
assert_eq!(
cookies[0].name_value(),
("__cfduid", "d090ada9c629fc7b8bbc6dba3dde1149d1617647688")
);
assert_eq!(cookies[0].path(), Some("/"));
assert_eq!(cookies[0].secure(), Some(false));
assert_eq!(
session.content.cookies.get("__cfduid"),
Some(&expected_cookie)
cookies[0].expires(),
Some(cookie_crate::Expiration::from(
time::OffsetDateTime::from_unix_timestamp(1620239688)
))
);
Ok(())
}
Expand Down

0 comments on commit 8ed83f9

Please sign in to comment.