rama_http_headers/common/cookie.rs
1use crate::util::{FlatCsv, SemiColon};
2
3/// `Cookie` header, defined in [RFC6265](https://datatracker.ietf.org/doc/html/rfc6265#section-5.4)
4///
5/// If the user agent does attach a Cookie header field to an HTTP
6/// request, the user agent must send the cookie-string
7/// as the value of the header field.
8///
9/// When the user agent generates an HTTP request, the user agent MUST NOT
10/// attach more than one Cookie header field.
11///
12/// # Example values
13/// * `SID=31d4d96e407aad42`
14/// * `SID=31d4d96e407aad42; lang=en-US`
15///
16#[derive(Clone, Debug)]
17pub struct Cookie(FlatCsv<SemiColon>);
18
19derive_header! {
20 Cookie(_),
21 name: COOKIE
22}
23
24impl Cookie {
25 /// Lookup a value for a cookie name.
26 ///
27 /// # Example
28 ///
29 /// ```
30 /// use rama_http_headers::{HeaderMapExt, Cookie};
31 /// use rama_http_types::{HeaderMap, HeaderValue};
32 ///
33 /// // Setup the header map with strings...
34 /// let mut headers = HeaderMap::new();
35 /// headers.insert("cookie", HeaderValue::from_static("lang=en-US"));
36 ///
37 /// // Parse a `Cookie` so we can play with it...
38 /// let cookie = headers
39 /// .typed_get::<Cookie>()
40 /// .expect("we just inserted a valid Cookie");
41 ///
42 /// assert_eq!(cookie.get("lang"), Some("en-US"));
43 /// assert_eq!(cookie.get("SID"), None);
44 /// ```
45 pub fn get(&self, name: &str) -> Option<&str> {
46 self.iter()
47 .find(|&(key, _)| key == name)
48 .map(|(_, val)| val)
49 }
50
51 /// Get the number of key-value pairs this `Cookie` contains.
52 pub fn len(&self) -> usize {
53 self.iter().count()
54 }
55
56 /// Returns true if this doesn't contain any cookies
57 pub fn is_empty(&self) -> bool {
58 self.len() == 0
59 }
60
61 /// Iterator the key-value pairs of this `Cookie` header.
62 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
63 self.0.iter().filter_map(|kv| {
64 let mut iter = kv.splitn(2, '=');
65 let key = iter.next()?.trim();
66 let val = iter.next()?.trim();
67 Some((key, val))
68 })
69 }
70}
71
72/*
73impl PartialEq for Cookie {
74 fn eq(&self, other: &Cookie) -> bool {
75 if self.0.len() == other.0.len() {
76 for &(ref k, ref v) in self.0.iter() {
77 if other.get(k) != Some(v) {
78 return false;
79 }
80 }
81 true
82 } else {
83 false
84 }
85 }
86}
87*/
88
89#[cfg(test)]
90mod tests {
91 use super::super::test_decode;
92 use super::Cookie;
93
94 #[test]
95 fn test_parse() {
96 let cookie = test_decode::<Cookie>(&["foo=bar"]).unwrap();
97
98 assert_eq!(cookie.get("foo"), Some("bar"));
99 assert_eq!(cookie.get("bar"), None);
100 }
101
102 #[test]
103 fn test_multipe_same_name() {
104 let cookie = test_decode::<Cookie>(&["foo=bar; foo=baz"]).unwrap();
105
106 assert_eq!(cookie.get("foo"), Some("bar"));
107 }
108
109 #[test]
110 fn test_multipe_lines() {
111 let cookie = test_decode::<Cookie>(&["foo=bar", "lol = cat"]).unwrap();
112
113 assert_eq!(cookie.get("foo"), Some("bar"));
114 assert_eq!(cookie.get("lol"), Some("cat"));
115 }
116
117 /*
118 #[test]
119 fn test_set_and_get() {
120 let mut cookie = Cookie::new();
121 cookie.append("foo", "bar");
122 cookie.append(String::from("dyn"), String::from("amic"));
123
124 assert_eq!(cookie.get("foo"), Some("bar"));
125 assert_eq!(cookie.get("dyn"), Some("amic"));
126 assert!(cookie.get("nope").is_none());
127
128 cookie.append("foo", "notbar");
129 assert_eq!(cookie.get("foo"), Some("bar"));
130
131 cookie.set("foo", "hi");
132 assert_eq!(cookie.get("foo"), Some("hi"));
133 assert_eq!(cookie.get("dyn"), Some("amic"));
134 }
135
136 #[test]
137 fn test_eq() {
138 let mut cookie = Cookie::new();
139 let mut cookie2 = Cookie::new();
140
141 // empty is equal
142 assert_eq!(cookie, cookie2);
143
144 // left has more params
145 cookie.append("foo", "bar");
146 assert_ne!(cookie, cookie2);
147
148 // same len, different params
149 cookie2.append("bar", "foo");
150 assert_ne!(cookie, cookie2);
151
152
153 // right has more params, and matching KV
154 cookie2.append("foo", "bar");
155 assert_ne!(cookie, cookie2);
156
157 // same params, different order
158 cookie.append("bar", "foo");
159 assert_eq!(cookie, cookie2);
160 }
161
162 #[test]
163 fn test_parse() {
164 let mut cookie = Cookie::new();
165
166 let parsed = Cookie::parse_header(&b"foo=bar".to_vec().into()).unwrap();
167 cookie.append("foo", "bar");
168 assert_eq!(cookie, parsed);
169
170 let parsed = Cookie::parse_header(&b"foo=bar;".to_vec().into()).unwrap();
171 assert_eq!(cookie, parsed);
172
173 let parsed = Cookie::parse_header(&b"foo=bar; baz=quux".to_vec().into()).unwrap();
174 cookie.append("baz", "quux");
175 assert_eq!(cookie, parsed);
176
177 let parsed = Cookie::parse_header(&b"foo=bar;; baz=quux".to_vec().into()).unwrap();
178 assert_eq!(cookie, parsed);
179
180 let parsed = Cookie::parse_header(&b"foo=bar; invalid ; bad; ;; baz=quux".to_vec().into())
181 .unwrap();
182 assert_eq!(cookie, parsed);
183
184 let parsed = Cookie::parse_header(&b" foo = bar;baz= quux ".to_vec().into()).unwrap();
185 assert_eq!(cookie, parsed);
186
187 let parsed =
188 Cookie::parse_header(&vec![b"foo = bar".to_vec(), b"baz= quux ".to_vec()].into())
189 .unwrap();
190 assert_eq!(cookie, parsed);
191
192 let parsed = Cookie::parse_header(&b"foo=bar; baz=quux ; empty=".to_vec().into()).unwrap();
193 cookie.append("empty", "");
194 assert_eq!(cookie, parsed);
195
196
197 let mut cookie = Cookie::new();
198
199 let parsed = Cookie::parse_header(&b"middle=equals=in=the=middle".to_vec().into()).unwrap();
200 cookie.append("middle", "equals=in=the=middle");
201 assert_eq!(cookie, parsed);
202
203 let parsed =
204 Cookie::parse_header(&b"middle=equals=in=the=middle; double==2".to_vec().into())
205 .unwrap();
206 cookie.append("double", "=2");
207 assert_eq!(cookie, parsed);
208 }
209 */
210}