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