1
- use header :: { Header , Raw } ;
2
- use std:: fmt:: { self , Display } ;
1
+ use std :: borrow :: Cow ;
2
+ use std:: fmt;
3
3
use std:: str:: from_utf8;
4
4
5
+ use header:: { Header , Raw } ;
6
+ use header:: internals:: VecMap ;
7
+
5
8
/// `Cookie` header, defined in [RFC6265](http://tools.ietf.org/html/rfc6265#section-5.4)
6
9
///
7
10
/// If the user agent does attach a Cookie header field to an HTTP
@@ -20,17 +23,53 @@ use std::str::from_utf8;
20
23
/// use hyper::header::{Headers, Cookie};
21
24
///
22
25
/// let mut headers = Headers::new();
26
+ /// let mut cookie = Cookie::new();
27
+ /// cookie.append("foo", "bar");
23
28
///
24
- /// headers.set(
25
- /// Cookie(vec![
26
- /// String::from("foo=bar")
27
- /// ])
28
- /// );
29
+ /// assert_eq!(cookie.get("foo"), Some("bar"));
30
+ ///
31
+ /// headers.set(cookie);
29
32
/// ```
30
- #[ derive( Clone , PartialEq , Debug ) ]
31
- pub struct Cookie ( pub Vec < String > ) ;
33
+ #[ derive( Clone ) ]
34
+ pub struct Cookie ( VecMap < Cow < ' static , str > , Cow < ' static , str > > ) ;
32
35
33
- __hyper__deref ! ( Cookie => Vec <String >) ;
36
+ impl Cookie {
37
+ /// Creates a new `Cookie` header.
38
+ pub fn new ( ) -> Cookie {
39
+ Cookie ( VecMap :: with_capacity ( 0 ) )
40
+ }
41
+
42
+ /// Append a name and value for the `Cookie`.
43
+ ///
44
+ /// # Note
45
+ ///
46
+ /// Cookies are allowed to set a name with a
47
+ /// a value multiple times. For example:
48
+ ///
49
+ /// ```
50
+ /// use hyper::header::Cookie;
51
+ /// let mut cookie = Cookie::new();
52
+ /// cookie.append("foo", "bar");
53
+ /// cookie.append("foo", "quux");
54
+ /// assert_eq!(cookie.to_string(), "foo=bar; foo=quux");
55
+ pub fn append < K , V > ( & mut self , key : K , value : V )
56
+ where K : Into < Cow < ' static , str > > ,
57
+ V : Into < Cow < ' static , str > > ,
58
+ {
59
+ self . 0 . append ( key. into ( ) , value. into ( ) ) ;
60
+ }
61
+
62
+ /// Get a value for the name, if it exists.
63
+ ///
64
+ /// # Note
65
+ ///
66
+ /// Only returns the first instance found. To access
67
+ /// any other values associated with the name, parse
68
+ /// the `str` representation.
69
+ pub fn get ( & self , key : & str ) -> Option < & str > {
70
+ self . 0 . get ( key) . map ( AsRef :: as_ref)
71
+ }
72
+ }
34
73
35
74
impl Header for Cookie {
36
75
fn header_name ( ) -> & ' static str {
@@ -39,16 +78,22 @@ impl Header for Cookie {
39
78
}
40
79
41
80
fn parse_header ( raw : & Raw ) -> :: Result < Cookie > {
42
- let mut cookies = Vec :: with_capacity ( raw. len ( ) ) ;
81
+ let mut vec_map = VecMap :: with_capacity ( raw. len ( ) ) ;
43
82
for cookies_raw in raw. iter ( ) {
44
83
let cookies_str = try!( from_utf8 ( & cookies_raw[ ..] ) ) ;
45
84
for cookie_str in cookies_str. split ( ';' ) {
46
- cookies. push ( cookie_str. trim ( ) . to_owned ( ) )
85
+ let mut key_val = cookie_str. splitn ( 2 , '=' ) ;
86
+ let key_val = ( key_val. next ( ) , key_val. next ( ) ) ;
87
+ if let ( Some ( key) , Some ( val) ) = key_val {
88
+ vec_map. insert ( key. trim ( ) . to_owned ( ) . into ( ) , val. trim ( ) . to_owned ( ) . into ( ) ) ;
89
+ } else {
90
+ return Err ( :: Error :: Header ) ;
91
+ }
47
92
}
48
93
}
49
94
50
- if !cookies . is_empty ( ) {
51
- Ok ( Cookie ( cookies ) )
95
+ if vec_map . len ( ) != 0 {
96
+ Ok ( Cookie ( vec_map ) )
52
97
} else {
53
98
Err ( :: Error :: Header )
54
99
}
@@ -59,16 +104,106 @@ impl Header for Cookie {
59
104
}
60
105
}
61
106
107
+ impl PartialEq for Cookie {
108
+ fn eq ( & self , other : & Cookie ) -> bool {
109
+ if self . 0 . len ( ) == other. 0 . len ( ) {
110
+ for & ( ref k, ref v) in self . 0 . iter ( ) {
111
+ if other. get ( k) != Some ( v) {
112
+ return false ;
113
+ }
114
+ }
115
+ true
116
+ } else {
117
+ false
118
+ }
119
+ }
120
+ }
121
+
122
+ impl fmt:: Debug for Cookie {
123
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
124
+ f. debug_map ( )
125
+ . entries ( self . 0 . iter ( ) . map ( |& ( ref k, ref v) | ( k, v) ) )
126
+ . finish ( )
127
+ }
128
+ }
129
+
62
130
impl fmt:: Display for Cookie {
63
131
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
64
- let cookies = & self . 0 ;
65
- for ( i , cookie ) in cookies . iter ( ) . enumerate ( ) {
66
- if i != 0 {
67
- try! ( f . write_str ( "; " ) ) ;
68
- }
69
- try!( Display :: fmt ( & cookie , f ) ) ;
132
+ let mut iter = self . 0 . iter ( ) ;
133
+ if let Some ( & ( ref key , ref val ) ) = iter. next ( ) {
134
+ try! ( write ! ( f , "{}={}" , key , val ) ) ;
135
+ }
136
+ for & ( ref key , ref val ) in iter {
137
+ try!( write ! ( f , "; {}={}" , key , val ) ) ;
70
138
}
71
139
Ok ( ( ) )
140
+ }
141
+ }
142
+
143
+ #[ cfg( test) ]
144
+ mod tests {
145
+ use :: header:: Header ;
146
+ use super :: Cookie ;
147
+
148
+ #[ test]
149
+ fn test_set_and_get ( ) {
150
+ let mut cookie = Cookie :: new ( ) ;
151
+ cookie. append ( "foo" , "bar" ) ;
152
+ cookie. append ( String :: from ( "dyn" ) , String :: from ( "amic" ) ) ;
153
+
154
+ assert_eq ! ( cookie. get( "foo" ) , Some ( "bar" ) ) ;
155
+ assert_eq ! ( cookie. get( "dyn" ) , Some ( "amic" ) ) ;
156
+ assert ! ( cookie. get( "nope" ) . is_none( ) ) ;
157
+
158
+ cookie. append ( "foo" , "notbar" ) ;
159
+ assert_eq ! ( cookie. get( "foo" ) , Some ( "bar" ) ) ;
160
+ }
161
+
162
+ #[ test]
163
+ fn test_eq ( ) {
164
+ let mut cookie = Cookie :: new ( ) ;
165
+ let mut cookie2 = Cookie :: new ( ) ;
166
+
167
+ // empty is equal
168
+ assert_eq ! ( cookie, cookie2) ;
169
+
170
+ // left has more params
171
+ cookie. append ( "foo" , "bar" ) ;
172
+ assert ! ( cookie != cookie2) ;
173
+
174
+ // same len, different params
175
+ cookie2. append ( "bar" , "foo" ) ;
176
+ assert ! ( cookie != cookie2) ;
177
+
178
+
179
+ // right has more params, and matching KV
180
+ cookie2. append ( "foo" , "bar" ) ;
181
+ assert ! ( cookie != cookie2) ;
182
+
183
+ // same params, different order
184
+ cookie. append ( "bar" , "foo" ) ;
185
+ assert_eq ! ( cookie, cookie2) ;
186
+ }
187
+
188
+ #[ test]
189
+ fn test_parse ( ) {
190
+ let mut cookie = Cookie :: new ( ) ;
191
+
192
+ let parsed = Cookie :: parse_header ( & b"foo=bar" . to_vec ( ) . into ( ) ) . unwrap ( ) ;
193
+ cookie. append ( "foo" , "bar" ) ;
194
+ assert_eq ! ( cookie, parsed) ;
195
+
196
+ let parsed = Cookie :: parse_header ( & b"foo=bar; baz=quux" . to_vec ( ) . into ( ) ) . unwrap ( ) ;
197
+ cookie. append ( "baz" , "quux" ) ;
198
+ assert_eq ! ( cookie, parsed) ;
199
+
200
+ let parsed = Cookie :: parse_header ( & b" foo = bar;baz= quux " . to_vec ( ) . into ( ) ) . unwrap ( ) ;
201
+ assert_eq ! ( cookie, parsed) ;
202
+
203
+ let parsed = Cookie :: parse_header ( & vec ! [ b"foo = bar" . to_vec( ) , b"baz= quux " . to_vec( ) ] . into ( ) ) . unwrap ( ) ;
204
+ assert_eq ! ( cookie, parsed) ;
205
+
206
+ Cookie :: parse_header ( & b"foo;bar=baz;quux" . to_vec ( ) . into ( ) ) . unwrap_err ( ) ;
72
207
73
208
}
74
209
}
0 commit comments