diff options
Diffstat (limited to 'vendor/reqwest/src/cookie.rs')
-rw-r--r-- | vendor/reqwest/src/cookie.rs | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/vendor/reqwest/src/cookie.rs b/vendor/reqwest/src/cookie.rs new file mode 100644 index 000000000..4363301ea --- /dev/null +++ b/vendor/reqwest/src/cookie.rs @@ -0,0 +1,191 @@ +//! HTTP Cookies + +use std::convert::TryInto; +use std::fmt; +use std::sync::RwLock; +use std::time::SystemTime; + +use crate::header::{HeaderValue, SET_COOKIE}; +use bytes::Bytes; + +/// Actions for a persistent cookie store providing session support. +pub trait CookieStore: Send + Sync { + /// Store a set of Set-Cookie header values received from `url` + fn set_cookies(&self, cookie_headers: &mut dyn Iterator<Item = &HeaderValue>, url: &url::Url); + /// Get any Cookie values in the store for `url` + fn cookies(&self, url: &url::Url) -> Option<HeaderValue>; +} + +/// A single HTTP cookie. +pub struct Cookie<'a>(cookie_crate::Cookie<'a>); + +/// A good default `CookieStore` implementation. +/// +/// This is the implementation used when simply calling `cookie_store(true)`. +/// This type is exposed to allow creating one and filling it with some +/// existing cookies more easily, before creating a `Client`. +/// +/// For more advanced scenarios, such as needing to serialize the store or +/// manipulate it between requests, you may refer to the +/// [reqwest_cookie_store crate](https://crates.io/crates/reqwest_cookie_store). +#[derive(Debug, Default)] +pub struct Jar(RwLock<cookie_store::CookieStore>); + +// ===== impl Cookie ===== + +impl<'a> Cookie<'a> { + fn parse(value: &'a HeaderValue) -> Result<Cookie<'a>, CookieParseError> { + std::str::from_utf8(value.as_bytes()) + .map_err(cookie_crate::ParseError::from) + .and_then(cookie_crate::Cookie::parse) + .map_err(CookieParseError) + .map(Cookie) + } + + /// The name of the cookie. + pub fn name(&self) -> &str { + self.0.name() + } + + /// The value of the cookie. + pub fn value(&self) -> &str { + self.0.value() + } + + /// Returns true if the 'HttpOnly' directive is enabled. + pub fn http_only(&self) -> bool { + self.0.http_only().unwrap_or(false) + } + + /// Returns true if the 'Secure' directive is enabled. + pub fn secure(&self) -> bool { + self.0.secure().unwrap_or(false) + } + + /// Returns true if 'SameSite' directive is 'Lax'. + pub fn same_site_lax(&self) -> bool { + self.0.same_site() == Some(cookie_crate::SameSite::Lax) + } + + /// Returns true if 'SameSite' directive is 'Strict'. + pub fn same_site_strict(&self) -> bool { + self.0.same_site() == Some(cookie_crate::SameSite::Strict) + } + + /// Returns the path directive of the cookie, if set. + pub fn path(&self) -> Option<&str> { + self.0.path() + } + + /// Returns the domain directive of the cookie, if set. + pub fn domain(&self) -> Option<&str> { + self.0.domain() + } + + /// Get the Max-Age information. + pub fn max_age(&self) -> Option<std::time::Duration> { + self.0.max_age().map(|d| { + d.try_into() + .expect("time::Duration into std::time::Duration") + }) + } + + /// The cookie expiration time. + pub fn expires(&self) -> Option<SystemTime> { + match self.0.expires() { + Some(cookie_crate::Expiration::DateTime(offset)) => Some(SystemTime::from(offset)), + None | Some(cookie_crate::Expiration::Session) => None, + } + } +} + +impl<'a> fmt::Debug for Cookie<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +pub(crate) fn extract_response_cookie_headers<'a>( + headers: &'a hyper::HeaderMap, +) -> impl Iterator<Item = &'a HeaderValue> + 'a { + headers.get_all(SET_COOKIE).iter() +} + +pub(crate) fn extract_response_cookies<'a>( + headers: &'a hyper::HeaderMap, +) -> impl Iterator<Item = Result<Cookie<'a>, CookieParseError>> + 'a { + headers + .get_all(SET_COOKIE) + .iter() + .map(|value| Cookie::parse(value)) +} + +/// Error representing a parse failure of a 'Set-Cookie' header. +pub(crate) struct CookieParseError(cookie_crate::ParseError); + +impl<'a> fmt::Debug for CookieParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl<'a> fmt::Display for CookieParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl std::error::Error for CookieParseError {} + +// ===== impl Jar ===== + +impl Jar { + /// Add a cookie to this jar. + /// + /// # Example + /// + /// ``` + /// use reqwest::{cookie::Jar, Url}; + /// + /// let cookie = "foo=bar; Domain=yolo.local"; + /// let url = "https://yolo.local".parse::<Url>().unwrap(); + /// + /// let jar = Jar::default(); + /// jar.add_cookie_str(cookie, &url); + /// + /// // and now add to a `ClientBuilder`? + /// ``` + pub fn add_cookie_str(&self, cookie: &str, url: &url::Url) { + let cookies = cookie_crate::Cookie::parse(cookie) + .ok() + .map(|c| c.into_owned()) + .into_iter(); + self.0.write().unwrap().store_response_cookies(cookies, url); + } +} + +impl CookieStore for Jar { + fn set_cookies(&self, cookie_headers: &mut dyn Iterator<Item = &HeaderValue>, url: &url::Url) { + let iter = + cookie_headers.filter_map(|val| Cookie::parse(val).map(|c| c.0.into_owned()).ok()); + + self.0.write().unwrap().store_response_cookies(iter, url); + } + + fn cookies(&self, url: &url::Url) -> Option<HeaderValue> { + let s = self + .0 + .read() + .unwrap() + .get_request_values(url) + .map(|(name, value)| format!("{}={}", name, value)) + .collect::<Vec<_>>() + .join("; "); + + if s.is_empty() { + return None; + } + + HeaderValue::from_maybe_shared(Bytes::from(s)).ok() + } +} |