Skip to content

Commit

Permalink
sample implementation of new CookieStorage trait
Browse files Browse the repository at this point in the history
  • Loading branch information
pfernie authored and seanmonstar committed Mar 4, 2021
1 parent a856638 commit 74947ec
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 42 deletions.
67 changes: 37 additions & 30 deletions src/async_impl/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ use std::any::Any;
use std::convert::TryInto;
use std::net::IpAddr;
use std::sync::Arc;
#[cfg(feature = "cookies")]
use std::sync::RwLock;
use std::time::Duration;
use std::{fmt, str};

Expand Down Expand Up @@ -105,7 +103,7 @@ struct Config {
local_address: Option<IpAddr>,
nodelay: bool,
#[cfg(feature = "cookies")]
cookie_store: Option<cookie::CookieStore>,
cookie_store: Option<Arc<dyn cookie::CookieStore>>,
trust_dns: bool,
error: Option<crate::Error>,
https_only: bool,
Expand Down Expand Up @@ -350,7 +348,7 @@ impl ClientBuilder {
inner: Arc::new(ClientRef {
accepts: config.accepts,
#[cfg(feature = "cookies")]
cookie_store: config.cookie_store.map(RwLock::new),
cookie_store: config.cookie_store,
hyper: hyper_client,
headers: config.headers,
redirect_policy: config.redirect_policy,
Expand Down Expand Up @@ -451,10 +449,10 @@ impl ClientBuilder {
self
}

/// Enable a persistent cookie store for the client.
/// Set the persistent cookie store for the client.
///
/// Cookies received in responses will be preserved and included in
/// additional requests.
/// Cookies received in responses will be passed to this store, and
/// additional requests will query this store for cookies.
///
/// By default, no cookie store is used.
///
Expand All @@ -472,6 +470,26 @@ impl ClientBuilder {
self
}

/// Set the persistent cookie store for the client.
///
/// Cookies received in responses will be passed to this store, and
/// additional requests will query this store for cookies.
///
/// By default, no cookie store is used.
///
/// # Optional
///
/// This requires the optional `cookies` feature to be enabled.
#[cfg(feature = "cookies")]
#[cfg_attr(docsrs, doc(cfg(feature = "cookies")))]
pub fn cookie_provider<C: cookie::CookieStore + 'static>(
mut self,
cookie_store: Option<Arc<C>>,
) -> ClientBuilder {
self.config.cookie_store = cookie_store.map(|a| a as _);
self
}

/// Enable auto gzip decompression by checking the `Content-Encoding` response header.
///
/// If auto gzip decompression is turned on:
Expand Down Expand Up @@ -1109,10 +1127,9 @@ impl Client {
// Add cookies from the cookie store.
#[cfg(feature = "cookies")]
{
if let Some(cookie_store_wrapper) = self.inner.cookie_store.as_ref() {
if let Some(cookie_store) = self.inner.cookie_store.as_ref() {
if headers.get(crate::header::COOKIE).is_none() {
let cookie_store = cookie_store_wrapper.read().unwrap();
add_cookie_header(&mut headers, &cookie_store, &url);
add_cookie_header(&mut headers, &**cookie_store, &url);
}
}
}
Expand Down Expand Up @@ -1289,7 +1306,7 @@ impl Config {
struct ClientRef {
accepts: Accepts,
#[cfg(feature = "cookies")]
cookie_store: Option<RwLock<cookie::CookieStore>>,
cookie_store: Option<Arc<dyn cookie::CookieStore>>,
headers: HeaderMap,
hyper: HyperClient,
redirect_policy: redirect::Policy,
Expand Down Expand Up @@ -1431,14 +1448,12 @@ impl Future for PendingRequest {

#[cfg(feature = "cookies")]
{
if let Some(store_wrapper) = self.client.cookie_store.as_ref() {
let mut cookies = cookie::extract_response_cookies(&res.headers())
.filter_map(|res| res.ok())
.map(|cookie| cookie.into_inner().into_owned())
.peekable();
if let Some(ref cookie_store) = self.client.cookie_store {
let mut cookies =
cookie::extract_response_cookie_headers(&res.headers()).peekable();
if cookies.peek().is_some() {
let mut store = store_wrapper.write().unwrap();
store.0.store_response_cookies(cookies, &self.url);
let cookies = cookies.collect();
cookie_store.set_cookies(cookies, &self.url);
}
}
}
Expand Down Expand Up @@ -1531,11 +1546,8 @@ impl Future for PendingRequest {
// Add cookies from the cookie store.
#[cfg(feature = "cookies")]
{
if let Some(cookie_store_wrapper) =
self.client.cookie_store.as_ref()
{
let cookie_store = cookie_store_wrapper.read().unwrap();
add_cookie_header(&mut headers, &cookie_store, &self.url);
if let Some(ref cookie_store) = self.client.cookie_store {
add_cookie_header(&mut headers, &**cookie_store, &self.url);
}
}

Expand Down Expand Up @@ -1592,13 +1604,8 @@ fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
}

#[cfg(feature = "cookies")]
fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &cookie::CookieStore, url: &Url) {
let header = cookie_store
.0
.get_request_cookies(url)
.map(|c| format!("{}={}", c.name(), c.value()))
.collect::<Vec<_>>()
.join("; ");
fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &dyn cookie::CookieStore, url: &Url) {
let header = cookie_store.cookies(url).join("; ");
if !header.is_empty() {
headers.insert(
crate::header::COOKIE,
Expand Down
27 changes: 15 additions & 12 deletions src/cookie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ impl<'a> Cookie<'a> {
.map(Cookie)
}

pub(crate) fn into_inner(self) -> cookie_crate::Cookie<'a> {
self.0
}

/// The name of the cookie.
pub fn name(&self) -> &str {
self.0.name()
Expand Down Expand Up @@ -82,6 +78,15 @@ impl<'a> fmt::Debug for Cookie<'a> {
}
}

pub(crate) fn extract_response_cookie_headers<'a>(
headers: &'a hyper::HeaderMap,
) -> impl Iterator<Item = &'a str> + 'a {
headers
.get_all(header::SET_COOKIE)
.iter()
.filter_map(|value| std::str::from_utf8(value.as_bytes()).ok())
}

pub(crate) fn extract_response_cookies<'a>(
headers: &'a hyper::HeaderMap,
) -> impl Iterator<Item = Result<Cookie<'a>, CookieParseError>> + 'a {
Expand All @@ -91,14 +96,12 @@ pub(crate) fn extract_response_cookies<'a>(
.map(|value| Cookie::parse(value))
}

/// A persistent cookie store that provides session support.
#[derive(Default)]
pub(crate) struct CookieStore(pub(crate) cookie_store::CookieStore);

impl<'a> fmt::Debug for CookieStore {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
/// Actions for a persistent cookie store providing session supprt.
pub trait CookieStore: Send + Sync {
/// Store a set of Set-Cookie header values recevied from `url`
fn set_cookies(&self, cookie_headers: Vec<&str>, url: &url::Url);
/// Get any Cookie values in the store for `url`
fn cookies(&self, url: &url::Url) -> Vec<String>;
}

/// Error representing a parse failure of a 'Set-Cookie' header.
Expand Down

0 comments on commit 74947ec

Please sign in to comment.