cookie_store/
cookie_path.rs1#[cfg(feature = "serde")]
2use serde_derive::{Deserialize, Serialize};
3use std::cmp::max;
4use std::ops::Deref;
5use url::Url;
6
7pub fn is_match(path: &str, request_url: &Url) -> bool {
10 CookiePath::parse(path).map_or(false, |cp| cp.matches(request_url))
11}
12
13#[derive(PartialEq, Eq, Clone, Debug, Hash, PartialOrd, Ord)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16pub struct CookiePath(String, bool);
17impl CookiePath {
18 pub fn matches(&self, request_url: &Url) -> bool {
21 if request_url.cannot_be_a_base() {
22 false
23 } else {
24 let request_path = request_url.path();
25 let cookie_path = &*self.0;
26 cookie_path == request_path
28 || (request_path.starts_with(cookie_path)
29 && (cookie_path.ends_with('/')
30 || &request_path[cookie_path.len()..=cookie_path.len()] == "/"))
31 }
32 }
33
34 pub fn is_from_path_attr(&self) -> bool {
37 self.1
38 }
39
40 pub fn default_path(request_url: &Url) -> CookiePath {
62 let cp = if request_url.cannot_be_a_base() {
63 "/".into()
65 } else {
66 let path = request_url.path();
67 match path.rfind('/') {
68 None => "/".into(), Some(i) => path[0..max(i, 1)].into(), }
71 };
72 CookiePath(cp, false)
73 }
74
75 pub fn new(path: &str, request_url: &Url) -> CookiePath {
78 match CookiePath::parse(path) {
79 Some(cp) => cp,
80 None => CookiePath::default_path(request_url),
81 }
82 }
83
84 pub fn parse(path: &str) -> Option<CookiePath> {
87 if path.starts_with('/') {
88 Some(CookiePath(String::from(path), true))
89 } else {
90 None
91 }
92 }
93}
94
95impl AsRef<str> for CookiePath {
96 fn as_ref(&self) -> &str {
97 &self.0
98 }
99}
100
101impl Deref for CookiePath {
102 type Target = str;
103 fn deref(&self) -> &Self::Target {
104 &self.0
105 }
106}
107
108impl<'a> From<&'a CookiePath> for String {
109 fn from(cp: &CookiePath) -> String {
110 cp.0.clone()
111 }
112}
113
114impl From<CookiePath> for String {
115 fn from(cp: CookiePath) -> String {
116 cp.0
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::CookiePath;
123 use url::Url;
124
125 #[test]
126 fn default_path() {
127 fn get_path(url: &str) -> String {
128 CookiePath::default_path(&Url::parse(url).expect("unable to parse url in default_path"))
129 .into()
130 }
131 assert_eq!(get_path("data:foobusbar"), "/");
132 assert_eq!(get_path("http://example.com"), "/");
133 assert_eq!(get_path("http://example.com/"), "/");
134 assert_eq!(get_path("http://example.com/foo"), "/");
135 assert_eq!(get_path("http://example.com/foo/"), "/foo");
136 assert_eq!(get_path("http://example.com//foo/"), "//foo");
137 assert_eq!(get_path("http://example.com/foo//"), "/foo/");
138 assert_eq!(get_path("http://example.com/foo/bus/bar"), "/foo/bus");
139 assert_eq!(get_path("http://example.com/foo//bus/bar"), "/foo//bus");
140 assert_eq!(get_path("http://example.com/foo/bus/bar/"), "/foo/bus/bar");
141 }
142
143 fn do_match(exp: bool, cp: &str, rp: &str) {
144 let url = Url::parse(&format!("http://example.com{}", rp))
145 .expect("unable to parse url in do_match");
146 let cp = CookiePath::parse(cp).expect("unable to parse CookiePath in do_match");
147 assert!(
148 exp == cp.matches(&url),
149 "\n>> {:?}\nshould{}match\n>> {:?}\n>> {:?}\n",
150 cp,
151 if exp { " " } else { " NOT " },
152 url,
153 url.path()
154 );
155 }
156 fn is_match(cp: &str, rp: &str) {
157 do_match(true, cp, rp);
158 }
159 fn is_mismatch(cp: &str, rp: &str) {
160 do_match(false, cp, rp);
161 }
162
163 #[test]
164 fn bad_paths() {
165 assert!(CookiePath::parse("").is_none());
166 assert!(CookiePath::parse("a/foo").is_none());
167 }
168
169 #[test]
170 fn bad_path_defaults() {
171 fn get_path(cp: &str, url: &str) -> String {
172 CookiePath::new(
173 cp,
174 &Url::parse(url).expect("unable to parse url in bad_path_defaults"),
175 )
176 .into()
177 }
178 assert_eq!(get_path("", "http://example.com/"), "/");
179 assert_eq!(get_path("a/foo", "http://example.com/"), "/");
180 assert_eq!(get_path("", "http://example.com/foo/bar"), "/foo");
181 assert_eq!(get_path("a/foo", "http://example.com/foo/bar"), "/foo");
182 assert_eq!(get_path("", "http://example.com/foo/bar/"), "/foo/bar");
183 assert_eq!(get_path("a/foo", "http://example.com/foo/bar/"), "/foo/bar");
184 }
185
186 #[test]
187 fn shortest_path() {
188 is_match("/", "/");
189 }
190
191 #[test]
194 fn identical_paths() {
195 is_match("/foo/bus", "/foo/bus"); is_mismatch("/foo/bus", "/foo/buss"); is_mismatch("/foo/bus", "/zoo/bus"); is_mismatch("/foo/bus", "/zfoo/bus"); }
201
202 #[test]
203 fn cookie_path_prefix1() {
204 is_match("/foo/", "/foo/bus"); is_mismatch("/bar", "/foo/bus"); is_mismatch("/foo/bus/bar", "/foo/bus"); is_mismatch("/fo", "/foo/bus"); }
211
212 #[test]
213 fn cookie_path_prefix2() {
214 is_match("/foo", "/foo/bus"); is_mismatch("/bar", "/foo/bus"); is_mismatch("/foo/bus/bar", "/foo/bus"); is_mismatch("/fo", "/foo/bus"); }
222}