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