1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#![cfg_attr(docsrs, feature(doc_cfg))]
//! # cookie_store
//! Provides an implementation for storing and retrieving [`Cookie`]s per the path and domain matching
//! rules specified in [RFC6265](http://tools.ietf.org/html/rfc6265).
//!
//! ## Feature `preserve_order`
//! If enabled, [`CookieStore`] will use [`indexmap::IndexMap`] internally, and [`Cookie`]
//! insertion order will be preserved. Adds dependency `indexmap`.
//!
//! ## Feature `reqwest_impl`
//! If enabled, implementations of the [`reqwest::cookie::CookieStore`] trait are provided. As
//! these are intended for usage in async/concurrent contexts, these implementations use locking
//! primitives [`std::sync::Mutex`] ([`CookieStoreMutex`]) or [`std::sync::RwLock`]
//! ([`CookieStoreRwLock`]).
//!
//! ## Example
//! The following example demonstrates loading a [`CookieStore`] from disk, and using it within a
//! [`CookieStoreMutex`]. It then makes a series of request, examining and modifying the contents
//! of the underlying [`CookieStore`] in between.
//! ```no_run
//! # tokio_test::block_on(async {
//! // Load an existing set of cookies, serialized as json
//! let cookie_store = {
//!   let file = std::fs::File::open("cookies.json")
//!       .map(std::io::BufReader::new)
//!       .unwrap();
//!   cookie_store::CookieStore::load_json(file).unwrap()
//! };
//! let cookie_store = cookie_store::CookieStoreMutex::new(cookie_store);
//! let cookie_store = std::sync::Arc::new(cookie_store);
//! {
//!   // Examine initial contents
//!   println!("initial load");
//!   let store = cookie_store.lock().unwrap();
//!   for c in store.iter_any() {
//!     println!("{:?}", c);
//!   }
//! }
//!
//! // Build a `reqwest` Client, providing the deserialized store
//! let client = reqwest::Client::builder()
//!     .cookie_provider(std::sync::Arc::clone(&cookie_store))
//!     .build()
//!     .unwrap();
//!
//! // Make a sample request
//! client.get("https://google.com").send().await.unwrap();
//! {
//!   // Examine the contents of the store.
//!   println!("after google.com GET");
//!   let store = cookie_store.lock().unwrap();
//!   for c in store.iter_any() {
//!     println!("{:?}", c);
//!   }
//! }
//!
//! // Make another request from another domain
//! println!("GET from msn");
//! client.get("https://msn.com").send().await.unwrap();
//! {
//!   // Examine the contents of the store.
//!   println!("after msn.com GET");
//!   let mut store = cookie_store.lock().unwrap();
//!   for c in store.iter_any() {
//!     println!("{:?}", c);
//!   }
//!   // Clear the store, and examine again
//!   store.clear();
//!   println!("after clear");
//!   for c in store.iter_any() {
//!     println!("{:?}", c);
//!   }
//! }
//!
//! // Get some new cookies
//! client.get("https://google.com").send().await.unwrap();
//! {
//!   // Write store back to disk
//!   let mut writer = std::fs::File::create("cookies2.json")
//!       .map(std::io::BufWriter::new)
//!       .unwrap();
//!   let store = cookie_store.lock().unwrap();
//!   store.save_json(&mut writer).unwrap();
//! }
//! # });
//!```

use idna;

mod cookie;
pub use crate::cookie::Error as CookieError;
pub use crate::cookie::{Cookie, CookieResult};
mod cookie_domain;
mod cookie_expiration;
mod cookie_path;
mod cookie_store;
pub use crate::cookie_store::CookieStore;
mod utils;

#[cfg(feature = "reqwest_impl")]
#[cfg_attr(docsrs, doc(cfg(feature = "reqwest_impl")))]
mod reqwest_impl;
#[cfg(feature = "reqwest_impl")]
#[cfg_attr(docsrs, doc(cfg(feature = "reqwest_impl")))]
pub use reqwest_impl::{CookieStoreMutex, CookieStoreRwLock};

#[derive(Debug)]
pub struct IdnaErrors(idna::Errors);

impl std::fmt::Display for IdnaErrors {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "IDNA errors: {:#?}", self.0)
    }
}

impl std::error::Error for IdnaErrors {}

impl From<idna::Errors> for IdnaErrors {
    fn from(e: idna::Errors) -> Self {
        IdnaErrors(e)
    }
}

pub type Error = Box<dyn std::error::Error + Send + Sync>;
pub type Result<T> = std::result::Result<T, Error>;

pub(crate) mod rfc3339_fmt {
    use serde::{de::Error, Deserialize};

    pub(crate) const RFC3339_FORMAT: &'static str = "%Y-%m-%dT%H:%M:%SZ";
    pub(super) fn serialize<S>(t: &time::OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        // An explicit format string is used here, instead of time::Format::Rfc3339, to explicitly
        // utilize the 'Z' terminator instead of +00:00 format for Zulu time.
        let s = t.format(RFC3339_FORMAT);
        serializer.serialize_str(&s)
    }

    pub(super) fn deserialize<'de, D>(t: D) -> Result<time::OffsetDateTime, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let s = String::deserialize(t)?;
        time::OffsetDateTime::parse(&s, time::Format::Rfc3339).map_err(|e| {
            D::Error::custom(format!(
                "Could not parse string '{}' as RFC3339 UTC format: {}",
                s, e
            ))
        })
    }
}