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 )) }) } }