parsed_cookie_unittest.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <string>
6
7#include "net/cookies/parsed_cookie.h"
8#include "testing/gtest/include/gtest/gtest.h"
9
10namespace net {
11
12namespace {
13
14class ParsedCookieTest : public testing::Test { };
15
16}  // namespace
17
18TEST(ParsedCookieTest, TestBasic) {
19  ParsedCookie pc("a=b");
20  EXPECT_TRUE(pc.IsValid());
21  EXPECT_FALSE(pc.IsSecure());
22  EXPECT_EQ("a", pc.Name());
23  EXPECT_EQ("b", pc.Value());
24}
25
26TEST(ParsedCookieTest, TestQuoted) {
27  // These are some quoting cases which the major browsers all
28  // handle differently.  I've tested Internet Explorer 6, Opera 9.6,
29  // Firefox 3, and Safari Windows 3.2.1.  We originally tried to match
30  // Firefox closely, however we now match Internet Explorer and Safari.
31  const char* values[] = {
32    // Trailing whitespace after a quoted value.  The whitespace after
33    // the quote is stripped in all browsers.
34    "\"zzz \"  ",              "\"zzz \"",
35    // Handling a quoted value with a ';', like FOO="zz;pp"  ;
36    // IE and Safari: "zz;
37    // Firefox and Opera: "zz;pp"
38    "\"zz;pp\" ;",             "\"zz",
39    // Handling a value with multiple quoted parts, like FOO="zzz "   "ppp" ;
40    // IE and Safari: "zzz "   "ppp";
41    // Firefox: "zzz ";
42    // Opera: <rejects cookie>
43    "\"zzz \"   \"ppp\" ",     "\"zzz \"   \"ppp\"",
44    // A quote in a value that didn't start quoted.  like FOO=A"B ;
45    // IE, Safari, and Firefox: A"B;
46    // Opera: <rejects cookie>
47    "A\"B",                    "A\"B",
48  };
49
50  for (size_t i = 0; i < arraysize(values); i += 2) {
51    std::string input(values[i]);
52    std::string expected(values[i + 1]);
53
54    ParsedCookie pc("aBc=" + input + " ; path=\"/\"  ; httponly ");
55    EXPECT_TRUE(pc.IsValid());
56    EXPECT_FALSE(pc.IsSecure());
57    EXPECT_TRUE(pc.IsHttpOnly());
58    EXPECT_TRUE(pc.HasPath());
59    EXPECT_EQ("aBc", pc.Name());
60    EXPECT_EQ(expected, pc.Value());
61
62    // If a path was quoted, the path attribute keeps the quotes.  This will
63    // make the cookie effectively useless, but path parameters aren't supposed
64    // to be quoted.  Bug 1261605.
65    EXPECT_EQ("\"/\"", pc.Path());
66  }
67}
68
69TEST(ParsedCookieTest, TestNameless) {
70  ParsedCookie pc("BLAHHH; path=/; secure;");
71  EXPECT_TRUE(pc.IsValid());
72  EXPECT_TRUE(pc.IsSecure());
73  EXPECT_TRUE(pc.HasPath());
74  EXPECT_EQ("/", pc.Path());
75  EXPECT_EQ("", pc.Name());
76  EXPECT_EQ("BLAHHH", pc.Value());
77}
78
79TEST(ParsedCookieTest, TestAttributeCase) {
80  ParsedCookie pc("BLAHHH; Path=/; sECuRe; httpONLY");
81  EXPECT_TRUE(pc.IsValid());
82  EXPECT_TRUE(pc.IsSecure());
83  EXPECT_TRUE(pc.IsHttpOnly());
84  EXPECT_TRUE(pc.HasPath());
85  EXPECT_EQ("/", pc.Path());
86  EXPECT_EQ("", pc.Name());
87  EXPECT_EQ("BLAHHH", pc.Value());
88  EXPECT_EQ(3U, pc.NumberOfAttributes());
89}
90
91TEST(ParsedCookieTest, TestDoubleQuotedNameless) {
92  ParsedCookie pc("\"BLA\\\"HHH\"; path=/; secure;");
93  EXPECT_TRUE(pc.IsValid());
94  EXPECT_TRUE(pc.IsSecure());
95  EXPECT_TRUE(pc.HasPath());
96  EXPECT_EQ("/", pc.Path());
97  EXPECT_EQ("", pc.Name());
98  EXPECT_EQ("\"BLA\\\"HHH\"", pc.Value());
99  EXPECT_EQ(2U, pc.NumberOfAttributes());
100}
101
102TEST(ParsedCookieTest, QuoteOffTheEnd) {
103  ParsedCookie pc("a=\"B");
104  EXPECT_TRUE(pc.IsValid());
105  EXPECT_EQ("a", pc.Name());
106  EXPECT_EQ("\"B", pc.Value());
107  EXPECT_EQ(0U, pc.NumberOfAttributes());
108}
109
110TEST(ParsedCookieTest, MissingName) {
111  ParsedCookie pc("=ABC");
112  EXPECT_TRUE(pc.IsValid());
113  EXPECT_EQ("", pc.Name());
114  EXPECT_EQ("ABC", pc.Value());
115  EXPECT_EQ(0U, pc.NumberOfAttributes());
116}
117
118TEST(ParsedCookieTest, MissingValue) {
119  ParsedCookie pc("ABC=;  path = /wee");
120  EXPECT_TRUE(pc.IsValid());
121  EXPECT_EQ("ABC", pc.Name());
122  EXPECT_EQ("", pc.Value());
123  EXPECT_TRUE(pc.HasPath());
124  EXPECT_EQ("/wee", pc.Path());
125  EXPECT_EQ(1U, pc.NumberOfAttributes());
126}
127
128TEST(ParsedCookieTest, Whitespace) {
129  ParsedCookie pc("  A  = BC  ;secure;;;   httponly");
130  EXPECT_TRUE(pc.IsValid());
131  EXPECT_EQ("A", pc.Name());
132  EXPECT_EQ("BC", pc.Value());
133  EXPECT_FALSE(pc.HasPath());
134  EXPECT_FALSE(pc.HasDomain());
135  EXPECT_TRUE(pc.IsSecure());
136  EXPECT_TRUE(pc.IsHttpOnly());
137  // We parse anything between ; as attributes, so we end up with two
138  // attributes with an empty string name and value.
139  EXPECT_EQ(4U, pc.NumberOfAttributes());
140}
141TEST(ParsedCookieTest, MultipleEquals) {
142  ParsedCookie pc("  A=== BC  ;secure;;;   httponly");
143  EXPECT_TRUE(pc.IsValid());
144  EXPECT_EQ("A", pc.Name());
145  EXPECT_EQ("== BC", pc.Value());
146  EXPECT_FALSE(pc.HasPath());
147  EXPECT_FALSE(pc.HasDomain());
148  EXPECT_TRUE(pc.IsSecure());
149  EXPECT_TRUE(pc.IsHttpOnly());
150  EXPECT_EQ(4U, pc.NumberOfAttributes());
151}
152
153TEST(ParsedCookieTest, QuotedTrailingWhitespace) {
154  ParsedCookie pc("ANCUUID=\"zohNumRKgI0oxyhSsV3Z7D\"  ; "
155                      "expires=Sun, 18-Apr-2027 21:06:29 GMT ; "
156                      "path=/  ;  ");
157  EXPECT_TRUE(pc.IsValid());
158  EXPECT_EQ("ANCUUID", pc.Name());
159  // Stripping whitespace after the quotes matches all other major browsers.
160  EXPECT_EQ("\"zohNumRKgI0oxyhSsV3Z7D\"", pc.Value());
161  EXPECT_TRUE(pc.HasExpires());
162  EXPECT_TRUE(pc.HasPath());
163  EXPECT_EQ("/", pc.Path());
164  EXPECT_EQ(2U, pc.NumberOfAttributes());
165}
166
167TEST(ParsedCookieTest, TrailingWhitespace) {
168  ParsedCookie pc("ANCUUID=zohNumRKgI0oxyhSsV3Z7D  ; "
169                      "expires=Sun, 18-Apr-2027 21:06:29 GMT ; "
170                      "path=/  ;  ");
171  EXPECT_TRUE(pc.IsValid());
172  EXPECT_EQ("ANCUUID", pc.Name());
173  EXPECT_EQ("zohNumRKgI0oxyhSsV3Z7D", pc.Value());
174  EXPECT_TRUE(pc.HasExpires());
175  EXPECT_TRUE(pc.HasPath());
176  EXPECT_EQ("/", pc.Path());
177  EXPECT_EQ(2U, pc.NumberOfAttributes());
178}
179
180TEST(ParsedCookieTest, TooManyPairs) {
181  std::string blankpairs;
182  blankpairs.resize(ParsedCookie::kMaxPairs - 1, ';');
183
184  ParsedCookie pc1(blankpairs + "secure");
185  EXPECT_TRUE(pc1.IsValid());
186  EXPECT_TRUE(pc1.IsSecure());
187
188  ParsedCookie pc2(blankpairs + ";secure");
189  EXPECT_TRUE(pc2.IsValid());
190  EXPECT_FALSE(pc2.IsSecure());
191}
192
193// TODO(erikwright): some better test cases for invalid cookies.
194TEST(ParsedCookieTest, InvalidWhitespace) {
195  ParsedCookie pc("    ");
196  EXPECT_FALSE(pc.IsValid());
197}
198
199TEST(ParsedCookieTest, InvalidTooLong) {
200  std::string maxstr;
201  maxstr.resize(ParsedCookie::kMaxCookieSize, 'a');
202
203  ParsedCookie pc1(maxstr);
204  EXPECT_TRUE(pc1.IsValid());
205
206  ParsedCookie pc2(maxstr + "A");
207  EXPECT_FALSE(pc2.IsValid());
208}
209
210TEST(ParsedCookieTest, InvalidEmpty) {
211  ParsedCookie pc("");
212  EXPECT_FALSE(pc.IsValid());
213}
214
215TEST(ParsedCookieTest, EmbeddedTerminator) {
216  ParsedCookie pc1("AAA=BB\0ZYX");
217  ParsedCookie pc2("AAA=BB\rZYX");
218  ParsedCookie pc3("AAA=BB\nZYX");
219  EXPECT_TRUE(pc1.IsValid());
220  EXPECT_EQ("AAA", pc1.Name());
221  EXPECT_EQ("BB", pc1.Value());
222  EXPECT_TRUE(pc2.IsValid());
223  EXPECT_EQ("AAA", pc2.Name());
224  EXPECT_EQ("BB", pc2.Value());
225  EXPECT_TRUE(pc3.IsValid());
226  EXPECT_EQ("AAA", pc3.Name());
227  EXPECT_EQ("BB", pc3.Value());
228}
229
230TEST(ParsedCookieTest, ParseTokensAndValues) {
231  EXPECT_EQ("hello",
232            ParsedCookie::ParseTokenString("hello\nworld"));
233  EXPECT_EQ("fs!!@",
234            ParsedCookie::ParseTokenString("fs!!@;helloworld"));
235  EXPECT_EQ("hello world\tgood",
236            ParsedCookie::ParseTokenString("hello world\tgood\rbye"));
237  EXPECT_EQ("A",
238            ParsedCookie::ParseTokenString("A=B=C;D=E"));
239  EXPECT_EQ("hello",
240            ParsedCookie::ParseValueString("hello\nworld"));
241  EXPECT_EQ("fs!!@",
242            ParsedCookie::ParseValueString("fs!!@;helloworld"));
243  EXPECT_EQ("hello world\tgood",
244            ParsedCookie::ParseValueString("hello world\tgood\rbye"));
245  EXPECT_EQ("A=B=C",
246            ParsedCookie::ParseValueString("A=B=C;D=E"));
247}
248
249TEST(ParsedCookieTest, SerializeCookieLine) {
250  const char input[] = "ANCUUID=zohNumRKgI0oxyhSsV3Z7D  ; "
251                       "expires=Sun, 18-Apr-2027 21:06:29 GMT ; "
252                       "path=/  ;  ";
253  const char output[] = "ANCUUID=zohNumRKgI0oxyhSsV3Z7D; "
254                        "expires=Sun, 18-Apr-2027 21:06:29 GMT; "
255                        "path=/";
256  ParsedCookie pc(input);
257  EXPECT_EQ(output, pc.ToCookieLine());
258}
259
260
261TEST(ParsedCookieTest, SetNameAndValue) {
262  ParsedCookie empty("");
263  EXPECT_FALSE(empty.IsValid());
264  EXPECT_FALSE(empty.SetDomain("foobar.com"));
265  EXPECT_TRUE(empty.SetName("name"));
266  EXPECT_TRUE(empty.SetValue("value"));
267  EXPECT_EQ("name=value", empty.ToCookieLine());
268  EXPECT_TRUE(empty.IsValid());
269
270  // We don't test
271  //   ParsedCookie invalid("@foo=bar");
272  //   EXPECT_FALSE(invalid.IsValid());
273  // here because we are slightly more tolerant to invalid cookie names and
274  // values that are set by webservers. We only enforce a correct name and
275  // value if set via SetName() and SetValue().
276
277  ParsedCookie pc("name=value");
278  EXPECT_TRUE(pc.IsValid());
279
280  // Set invalid name / value.
281  EXPECT_FALSE(pc.SetName("@foobar"));
282  EXPECT_EQ("name=value", pc.ToCookieLine());
283  EXPECT_TRUE(pc.IsValid());
284
285  EXPECT_FALSE(pc.SetName(""));
286  EXPECT_EQ("name=value", pc.ToCookieLine());
287  EXPECT_TRUE(pc.IsValid());
288
289  EXPECT_FALSE(pc.SetValue("foo bar"));
290  EXPECT_EQ("name=value", pc.ToCookieLine());
291  EXPECT_TRUE(pc.IsValid());
292
293  EXPECT_FALSE(pc.SetValue("\"foobar"));
294  EXPECT_EQ("name=value", pc.ToCookieLine());
295  EXPECT_TRUE(pc.IsValid());
296
297  // Set valid name / value
298  EXPECT_TRUE(pc.SetName("test"));
299  EXPECT_EQ("test=value", pc.ToCookieLine());
300  EXPECT_TRUE(pc.IsValid());
301
302  EXPECT_TRUE(pc.SetValue("\"foobar\""));
303  EXPECT_EQ("test=\"foobar\"", pc.ToCookieLine());
304  EXPECT_TRUE(pc.IsValid());
305
306  EXPECT_TRUE(pc.SetValue(""));
307  EXPECT_EQ("test=", pc.ToCookieLine());
308  EXPECT_TRUE(pc.IsValid());
309}
310
311TEST(ParsedCookieTest, SetAttributes) {
312  ParsedCookie pc("name=value");
313  EXPECT_TRUE(pc.IsValid());
314
315  // Clear an unset attribute.
316  EXPECT_TRUE(pc.SetDomain(""));
317  EXPECT_FALSE(pc.HasDomain());
318  EXPECT_EQ("name=value", pc.ToCookieLine());
319  EXPECT_TRUE(pc.IsValid());
320
321  // Set a string containing an invalid character
322  EXPECT_FALSE(pc.SetDomain("foo;bar"));
323  EXPECT_FALSE(pc.HasDomain());
324  EXPECT_EQ("name=value", pc.ToCookieLine());
325  EXPECT_TRUE(pc.IsValid());
326
327  // Set all other attributes and check that they are appended in order.
328  EXPECT_TRUE(pc.SetDomain("domain.com"));
329  EXPECT_TRUE(pc.SetPath("/"));
330  EXPECT_TRUE(pc.SetExpires("Sun, 18-Apr-2027 21:06:29 GMT"));
331  EXPECT_TRUE(pc.SetMaxAge("12345"));
332  EXPECT_TRUE(pc.SetIsSecure(true));
333  EXPECT_TRUE(pc.SetIsHttpOnly(true));
334  EXPECT_EQ("name=value; domain=domain.com; path=/; "
335            "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
336            "httponly",
337            pc.ToCookieLine());
338  EXPECT_TRUE(pc.HasDomain());
339  EXPECT_TRUE(pc.HasPath());
340  EXPECT_TRUE(pc.HasExpires());
341  EXPECT_TRUE(pc.HasMaxAge());
342  EXPECT_TRUE(pc.IsSecure());
343  EXPECT_TRUE(pc.IsHttpOnly());
344
345  // Clear one attribute from the middle.
346  EXPECT_TRUE(pc.SetPath("/foo"));
347  EXPECT_TRUE(pc.HasDomain());
348  EXPECT_TRUE(pc.HasPath());
349  EXPECT_TRUE(pc.HasExpires());
350  EXPECT_TRUE(pc.IsSecure());
351  EXPECT_TRUE(pc.IsHttpOnly());
352  EXPECT_EQ("name=value; domain=domain.com; path=/foo; "
353            "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
354            "httponly",
355            pc.ToCookieLine());
356
357  // Clear the rest and change the name and value.
358  EXPECT_TRUE(pc.SetDomain(""));
359  EXPECT_TRUE(pc.SetPath(""));
360  EXPECT_TRUE(pc.SetExpires(""));
361  EXPECT_TRUE(pc.SetMaxAge(""));
362  EXPECT_TRUE(pc.SetIsSecure(false));
363  EXPECT_TRUE(pc.SetIsHttpOnly(false));
364  EXPECT_TRUE(pc.SetName("name2"));
365  EXPECT_TRUE(pc.SetValue("value2"));
366  EXPECT_FALSE(pc.HasDomain());
367  EXPECT_FALSE(pc.HasPath());
368  EXPECT_FALSE(pc.HasExpires());
369  EXPECT_FALSE(pc.HasMaxAge());
370  EXPECT_FALSE(pc.IsSecure());
371  EXPECT_FALSE(pc.IsHttpOnly());
372  EXPECT_EQ("name2=value2", pc.ToCookieLine());
373}
374
375}  // namespace net
376