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
//! A crate to handle HTTP cookies in a Rust server.
//!
//! # Overview
//!
//! You can use `biscotti` to handle cookies in your server.  
//!
//! It has support for:
//!
//! - Handling cookies attached to incoming requests, via [`RequestCookies`]
//! - Building cookies for outgoing responses, via [`ResponseCookies`]
//! - Encrypting, signing or encoding cookies, via [`Processor`]
//!
//! In particular:
//!
//! - It can handle multiple request cookies with the same name
//! - It lets you add multiple cookies with the same name but different paths or domains
//! - Cookies are percent-encoded/decoded by default (but you can opt out)
//! - It has built-in support for rotating signing/encryption keys over time
//!
//! # Non-goals
//!
//! `biscotti` is not designed to handle cookies on the client side.  
//! It doesn't provide any logic to parse the `Set-Cookie` headers returned in a server response.
//!
//! # Quickstart
//!
//! ## Incoming cookies
//!
//! ```rust
//! use biscotti::{Processor, config::Config, RequestCookies};
//!
//! // Start by creating a `Processor` instance from a `Config`.
//! // It determines if (and which) cookies should be decrypted, verified or percent-decoded.
//! let processor: Processor = Config::default().into();
//! // You can then use `RequestCookies::parse_header` to parse the `Cookie` header
//! // you received from the client.
//! let cookies = RequestCookies::parse_header(
//!     "name=first%20value; name2=val; name=another%20value",
//!     &processor
//! ).unwrap();
//!
//! // You can now access the cookies!
//!
//! // You can access the first cookie with a given name...
//! assert_eq!(cookies.get("name").unwrap().value(), "first value");
//! // ...or opt to retrieve all values associated with that cookie name.
//! assert_eq!(cookies.get_all("name").unwrap().len(), 2);
//!
//! assert_eq!(cookies.get("name2").unwrap().value(), "val");
//! assert_eq!(cookies.get_all("name2").unwrap().len(), 1);
//! ```
//!
//! ## Outgoing cookies
//!
//! ```rust
//! use std::collections::HashSet;
//! use biscotti::{Processor, config::Config, ResponseCookies, RemovalCookie, ResponseCookie};
//! use biscotti::SameSite;
//!
//! // Start by creating a `ResponseCookies` instance to hold the cookies you want to send.
//! let mut cookies = ResponseCookies::new();
//!
//! // You can add cookies to the `ResponseCookies` instance via the `insert` method.
//! cookies.insert(ResponseCookie::new("name", "a value"));
//! cookies.insert(ResponseCookie::new("name", "a value").set_path("/"));
//! // If you want to remove a cookie from the client's machine, you can use a `RemovalCookie`.
//! cookies.insert(RemovalCookie::new("another name"));
//!
//! // You then convert obtain the respective `Set-Cookie` header values.
//! // A processor is required: it determines if (and which) cookies should be encrypted,
//! // signed or percent-encoded.
//! let processor: Processor = Config::default().into();
//! let header_values: HashSet<_> = cookies.header_values(&processor).collect();
//! assert_eq!(header_values, HashSet::from([
//!     "name=a%20value".to_string(),
//!     // Both `name` cookies are kept since they have different path attributes.
//!     "name=a%20value; Path=/".to_string(),
//!     // A removal cookie is a cookie with an empty value and an expiry in the past.
//!     "another%20name=; Expires=Thu, 01 Jan 1970 00:00:00 GMT".to_string(),
//! ]));
//! ```
//!
//! ## Credits
//!
//! `biscotti` is heavily inspired by the [`cookie` crate](https://crates.io/crates/cookie) [Copyright (c) 2017 Sergio Benitez,
//! Copyright (c) 2014 Alex Crichton].  
//! `biscotti` started as a `cookie` fork and it includes non-negligible portions of its
//! code.
//!
//! [`Config`]: crate::config::Config
//! [`Processor`]: crate::Processor
//! [`RequestCookies`]: crate::RequestCookies
//! [`ResponseCookies`]: crate::ResponseCookies

pub mod config;
mod crypto;
mod encoding;
mod expiration;
mod processor;
mod removal;
mod request_cookie;
mod request_cookies;
mod response_cookie;
mod response_cookie_id;
mod response_cookies;
mod same_site;

pub mod request;

pub use crate::expiration::*;
pub use crate::same_site::*;
pub use crypto::Key;
pub use processor::Processor;
pub use removal::RemovalCookie;
pub use request_cookie::RequestCookie;
pub use request_cookies::RequestCookies;
pub use response_cookie::ResponseCookie;
pub use response_cookie_id::ResponseCookieId;
pub use response_cookies::ResponseCookies;
pub use time;

/// Errors that can occur when using `biscotti`.
pub mod errors {
    pub use crate::crypto::KeyError;
    pub use crate::processor::{CryptoError, DecodingError, ProcessIncomingError};
    pub use crate::request_cookies::ParseError;
}