diff --git a/.travis.yml b/.travis.yml index df783a2..9edd3e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,11 @@ matrix: - rust: nightly env: FEATURES="--features rustls-tls" + # optional cookies + #- rust: stable + - rust: nightly + env: FEATURES="--features cookies" + # socks #- rust: stable #- rust: nightly diff --git a/Cargo.toml b/Cargo.toml index d773e6a..91466bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ readme = "README.md" license = "MIT/Apache-2.0" categories = ["web-programming::http-client"] edition = "2018" +autotests = true publish = false @@ -38,8 +39,7 @@ time = "0.1.42" # TODO: candidates for optional features async-compression = { version = "0.1.0-alpha.4", default-features = false, features = ["gzip", "stream"] } -cookie_store = "0.9.0" -cookie = "0.12.0" + serde = "1.0" serde_json = "1.0" serde_urlencoded = "0.6.1" @@ -57,6 +57,10 @@ rustls = { version = "0.16", features = ["dangerous_configuration"], optional = tokio-rustls = { version = "=0.12.0-alpha.2", optional = true } webpki-roots = { version = "0.17", optional = true } +## cookies +cookie_crate = { version = "0.12", package = "cookie", optional = true } +cookie_store = { version = "0.9", optional = true } + ## socks #socks = { version = "0.3.2", optional = true } @@ -81,7 +85,15 @@ default-tls-vendored = ["default-tls", "native-tls/vendored"] rustls-tls = ["hyper-rustls", "tokio-rustls", "webpki-roots", "rustls", "tls"] +cookies = ["cookie_crate", "cookie_store"] + #trust-dns = ["trust-dns-resolver"] [target.'cfg(windows)'.dependencies] winreg = "0.6" + +[[test]] +name = "cookie" +path = "tests/cookie.rs" +required-features = ["cookies"] + diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index 120b873..650341b 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -1,5 +1,7 @@ use std::net::IpAddr; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; +#[cfg(feature = "cookies")] +use std::sync::RwLock; use std::time::Duration; use std::{fmt, str}; @@ -23,6 +25,7 @@ use log::debug; use super::request::{Request, RequestBuilder}; use super::response::Response; use crate::connect::Connector; +#[cfg(feature = "cookies")] use crate::cookie; use crate::into_url::{expect_uri, try_uri}; use crate::proxy::get_proxies; @@ -76,6 +79,7 @@ struct Config { http1_title_case_headers: bool, local_address: Option, nodelay: bool, + #[cfg(feature = "cookies")] cookie_store: Option, } @@ -115,6 +119,7 @@ impl ClientBuilder { http1_title_case_headers: false, local_address: None, nodelay: false, + #[cfg(feature = "cookies")] cookie_store: None, }, } @@ -210,11 +215,10 @@ impl ClientBuilder { let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth()); - let cookie_store = config.cookie_store.map(RwLock::new); - Ok(Client { inner: Arc::new(ClientRef { - cookie_store, + #[cfg(feature = "cookies")] + cookie_store: config.cookie_store.map(RwLock::new), gzip: config.gzip, hyper: hyper_client, headers: config.headers, @@ -430,6 +434,11 @@ impl ClientBuilder { /// additional requests. /// /// By default, no cookie store is used. + /// + /// # Optional + /// + /// This requires the optional `cookies` feature to be enabled. + #[cfg(feature = "cookies")] pub fn cookie_store(mut self, enable: bool) -> ClientBuilder { self.config.cookie_store = if enable { Some(cookie::CookieStore::default()) @@ -561,10 +570,13 @@ impl Client { } // Add cookies from the cookie store. - if let Some(cookie_store_wrapper) = 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); + #[cfg(feature = "cookies")] + { + if let Some(cookie_store_wrapper) = 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); + } } } @@ -662,6 +674,7 @@ impl fmt::Debug for ClientBuilder { } struct ClientRef { + #[cfg(feature = "cookies")] cookie_store: Option>, gzip: bool, headers: HeaderMap, @@ -760,12 +773,16 @@ impl Future for PendingRequest { Poll::Ready(Ok(res)) => res, Poll::Pending => return Poll::Pending, }; - if let Some(store_wrapper) = self.client.cookie_store.as_ref() { - let mut store = store_wrapper.write().unwrap(); - let cookies = cookie::extract_response_cookies(&res.headers()) - .filter_map(|res| res.ok()) - .map(|cookie| cookie.into_inner().into_owned()); - store.0.store_response_cookies(cookies, &self.url); + + #[cfg(feature = "cookies")] + { + if let Some(store_wrapper) = self.client.cookie_store.as_ref() { + let mut store = store_wrapper.write().unwrap(); + let cookies = cookie::extract_response_cookies(&res.headers()) + .filter_map(|res| res.ok()) + .map(|cookie| cookie.into_inner().into_owned()); + store.0.store_response_cookies(cookies, &self.url); + } } let should_redirect = match res.status() { StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => { @@ -854,9 +871,14 @@ impl Future for PendingRequest { .expect("valid request parts"); // Add cookies from the cookie store. - 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); + #[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); + } } *req.headers_mut() = headers.clone(); @@ -909,6 +931,7 @@ fn make_referer(next: &Url, previous: &Url) -> Option { referer.as_str().parse().ok() } +#[cfg(feature = "cookies")] fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &cookie::CookieStore, url: &Url) { let header = cookie_store .0 diff --git a/src/async_impl/response.rs b/src/async_impl/response.rs index 8617584..34b494d 100644 --- a/src/async_impl/response.rs +++ b/src/async_impl/response.rs @@ -18,6 +18,7 @@ use url::Url; use super::body::Body; use super::Decoder; +#[cfg(feature = "cookies")] use crate::cookie; /// A Response to a submitted `Request`. @@ -99,6 +100,11 @@ impl Response { /// Retrieve the cookies contained in the response. /// /// Note that invalid 'Set-Cookie' headers will be ignored. + /// + /// # Optional + /// + /// This requires the optional `cookies` feature to be enabled. + #[cfg(feature = "cookies")] pub fn cookies<'a>(&'a self) -> impl Iterator> + 'a { cookie::extract_response_cookies(&self.headers).filter_map(Result::ok) } diff --git a/src/blocking/client.rs b/src/blocking/client.rs index ab4379d..86918c4 100644 --- a/src/blocking/client.rs +++ b/src/blocking/client.rs @@ -392,6 +392,11 @@ impl ClientBuilder { /// .build() /// .unwrap(); /// ``` + /// + /// # Optional + /// + /// This requires the optional `cookies` feature to be enabled. + #[cfg(feature = "cookies")] pub fn cookie_store(self, enable: bool) -> ClientBuilder { self.with_inner(|inner| inner.cookie_store(enable)) } diff --git a/src/blocking/response.rs b/src/blocking/response.rs index 5827b53..d5f5a76 100644 --- a/src/blocking/response.rs +++ b/src/blocking/response.rs @@ -11,6 +11,7 @@ use serde::de::DeserializeOwned; use super::client::KeepCoreThreadAlive; use super::wait; +#[cfg(feature = "cookies")] use crate::cookie; use crate::{async_impl, StatusCode, Url, Version}; @@ -121,6 +122,11 @@ impl Response { /// Retrieve the cookies contained in the response. /// /// Note that invalid 'Set-Cookie' headers will be ignored. + /// + /// # Optional + /// + /// This requires the optional `cookies` feature to be enabled. + #[cfg(feature = "cookies")] pub fn cookies<'a>(&'a self) -> impl Iterator> + 'a { cookie::extract_response_cookies(self.headers()).filter_map(Result::ok) } diff --git a/src/cookie.rs b/src/cookie.rs index f217884..20f6990 100644 --- a/src/cookie.rs +++ b/src/cookie.rs @@ -1,6 +1,5 @@ //! The cookies module contains types for working with request and response cookies. -use crate::cookie_crate; use crate::header; use std::borrow::Cow; use std::fmt; @@ -18,7 +17,7 @@ fn tm_to_systemtime(tm: time::Tm) -> SystemTime { } /// Error representing a parse failure of a 'Set-Cookie' header. -pub struct CookieParseError(cookie::ParseError); +pub struct CookieParseError(cookie_crate::ParseError); impl<'a> fmt::Debug for CookieParseError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -35,7 +34,7 @@ impl<'a> fmt::Display for CookieParseError { impl std::error::Error for CookieParseError {} /// A single HTTP cookie. -pub struct Cookie<'a>(cookie::Cookie<'a>); +pub struct Cookie<'a>(cookie_crate::Cookie<'a>); impl<'a> fmt::Debug for Cookie<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -50,20 +49,20 @@ impl Cookie<'static> { N: Into>, V: Into>, { - Cookie(cookie::Cookie::new(name, value)) + Cookie(cookie_crate::Cookie::new(name, value)) } } impl<'a> Cookie<'a> { fn parse(value: &'a crate::header::HeaderValue) -> Result, CookieParseError> { std::str::from_utf8(value.as_bytes()) - .map_err(cookie::ParseError::from) - .and_then(cookie::Cookie::parse) + .map_err(cookie_crate::ParseError::from) + .and_then(cookie_crate::Cookie::parse) .map_err(CookieParseError) .map(Cookie) } - pub(crate) fn into_inner(self) -> cookie::Cookie<'a> { + pub(crate) fn into_inner(self) -> cookie_crate::Cookie<'a> { self.0 } diff --git a/src/lib.rs b/src/lib.rs index c45ae78..1bfa050 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,6 +156,7 @@ //! `native-tls` library to connect over HTTPS. //! - **default-tls-vendored**: Enables the `vendored` feature of `native-tls`. //! - **rustls-tls**: Provides TLS support via the `rustls` library. +//! - **cookies**: Provides cookie session support. //! //! //! [hyper]: http://hyper.rs @@ -172,8 +173,6 @@ ////! - **trust-dns**: Enables a trust-dns async resolver instead of default ////! threadpool using `getaddrinfo`. -extern crate cookie as cookie_crate; - #[cfg(test)] #[macro_use] extern crate doc_comment; @@ -208,6 +207,7 @@ mod error; mod async_impl; pub mod blocking; mod connect; +#[cfg(feature = "cookies")] pub mod cookie; //#[cfg(feature = "trust-dns")] //mod dns; diff --git a/tests/redirect.rs b/tests/redirect.rs index eef1653..78a0ae4 100644 --- a/tests/redirect.rs +++ b/tests/redirect.rs @@ -397,6 +397,7 @@ async fn test_invalid_location_stops_redirect_gh484() { ); } +#[cfg(feature = "cookies")] #[tokio::test] async fn test_redirect_302_with_set_cookies() { let code = 302;