1// Copyright (c) 2006-2009 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 <algorithm>
6
7#include "base/basictypes.h"
8#include "net/http/http_util.h"
9#include "testing/gtest/include/gtest/gtest.h"
10
11using net::HttpUtil;
12
13namespace {
14class HttpUtilTest : public testing::Test {};
15}
16
17TEST(HttpUtilTest, HasHeader) {
18  static const struct {
19    const char* headers;
20    const char* name;
21    bool expected_result;
22  } tests[] = {
23    { "", "foo", false },
24    { "foo\r\nbar", "foo", false },
25    { "ffoo: 1", "foo", false },
26    { "foo: 1", "foo", true },
27    { "foo: 1\r\nbar: 2", "foo", true },
28    { "fOO: 1\r\nbar: 2", "foo", true },
29    { "g: 0\r\nfoo: 1\r\nbar: 2", "foo", true },
30  };
31  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
32    bool result = HttpUtil::HasHeader(tests[i].headers, tests[i].name);
33    EXPECT_EQ(tests[i].expected_result, result);
34  }
35}
36
37TEST(HttpUtilTest, StripHeaders) {
38  static const char* headers =
39      "Origin: origin\r\n"
40      "Content-Type: text/plain\r\n"
41      "Cookies: foo1\r\n"
42      "Custom: baz\r\n"
43      "COOKIES: foo2\r\n"
44      "Server: Apache\r\n"
45      "OrIGin: origin2\r\n";
46
47  static const char* header_names[] = {
48    "origin", "content-type", "cookies"
49  };
50
51  static const char* expected_stripped_headers =
52      "Custom: baz\r\n"
53      "Server: Apache\r\n";
54
55  EXPECT_EQ(expected_stripped_headers,
56            HttpUtil::StripHeaders(headers, header_names,
57                                   arraysize(header_names)));
58}
59
60TEST(HttpUtilTest, HeadersIterator) {
61  std::string headers = "foo: 1\t\r\nbar: hello world\r\nbaz: 3 \r\n";
62
63  HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
64
65  ASSERT_TRUE(it.GetNext());
66  EXPECT_EQ(std::string("foo"), it.name());
67  EXPECT_EQ(std::string("1"), it.values());
68
69  ASSERT_TRUE(it.GetNext());
70  EXPECT_EQ(std::string("bar"), it.name());
71  EXPECT_EQ(std::string("hello world"), it.values());
72
73  ASSERT_TRUE(it.GetNext());
74  EXPECT_EQ(std::string("baz"), it.name());
75  EXPECT_EQ(std::string("3"), it.values());
76
77  EXPECT_FALSE(it.GetNext());
78}
79
80TEST(HttpUtilTest, HeadersIterator_MalformedLine) {
81  std::string headers = "foo: 1\n: 2\n3\nbar: 4";
82
83  HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n");
84
85  ASSERT_TRUE(it.GetNext());
86  EXPECT_EQ(std::string("foo"), it.name());
87  EXPECT_EQ(std::string("1"), it.values());
88
89  ASSERT_TRUE(it.GetNext());
90  EXPECT_EQ(std::string("bar"), it.name());
91  EXPECT_EQ(std::string("4"), it.values());
92
93  EXPECT_FALSE(it.GetNext());
94}
95
96TEST(HttpUtilTest, HeadersIterator_AdvanceTo) {
97  std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4";
98
99  HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
100  EXPECT_TRUE(it.AdvanceTo("foo"));
101  EXPECT_EQ("foo", it.name());
102  EXPECT_TRUE(it.AdvanceTo("bar"));
103  EXPECT_EQ("bar", it.name());
104  EXPECT_FALSE(it.AdvanceTo("blat"));
105  EXPECT_FALSE(it.GetNext());  // should be at end of headers
106}
107
108TEST(HttpUtilTest, HeadersIterator_Reset) {
109  std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4";
110  HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
111  // Search past "foo".
112  EXPECT_TRUE(it.AdvanceTo("bar"));
113  // Now try advancing to "foo".  This time it should fail since the iterator
114  // position is past it.
115  EXPECT_FALSE(it.AdvanceTo("foo"));
116  it.Reset();
117  // Now that we reset the iterator position, we should find 'foo'
118  EXPECT_TRUE(it.AdvanceTo("foo"));
119}
120
121TEST(HttpUtilTest, ValuesIterator) {
122  std::string values = " must-revalidate,   no-cache=\"foo, bar\"\t, private ";
123
124  HttpUtil::ValuesIterator it(values.begin(), values.end(), ',');
125
126  ASSERT_TRUE(it.GetNext());
127  EXPECT_EQ(std::string("must-revalidate"), it.value());
128
129  ASSERT_TRUE(it.GetNext());
130  EXPECT_EQ(std::string("no-cache=\"foo, bar\""), it.value());
131
132  ASSERT_TRUE(it.GetNext());
133  EXPECT_EQ(std::string("private"), it.value());
134
135  EXPECT_FALSE(it.GetNext());
136}
137
138TEST(HttpUtilTest, ValuesIterator_Blanks) {
139  std::string values = " \t ";
140
141  HttpUtil::ValuesIterator it(values.begin(), values.end(), ',');
142
143  EXPECT_FALSE(it.GetNext());
144}
145
146TEST(HttpUtilTest, Unquote) {
147  // Replace <backslash> " with ".
148  EXPECT_STREQ("xyz\"abc", HttpUtil::Unquote("\"xyz\\\"abc\"").c_str());
149
150  // Replace <backslash> <backslash> with <backslash>
151  EXPECT_STREQ("xyz\\abc", HttpUtil::Unquote("\"xyz\\\\abc\"").c_str());
152  EXPECT_STREQ("xyz\\\\\\abc",
153               HttpUtil::Unquote("\"xyz\\\\\\\\\\\\abc\"").c_str());
154
155  // Replace <backslash> X with X
156  EXPECT_STREQ("xyzXabc", HttpUtil::Unquote("\"xyz\\Xabc\"").c_str());
157
158  // Act as identity function on unquoted inputs.
159  EXPECT_STREQ("X", HttpUtil::Unquote("X").c_str());
160  EXPECT_STREQ("\"", HttpUtil::Unquote("\"").c_str());
161
162  // Allow single quotes to act as quote marks.
163  // Not part of RFC 2616.
164  EXPECT_STREQ("x\"", HttpUtil::Unquote("'x\"'").c_str());
165}
166
167TEST(HttpUtilTest, Quote) {
168  EXPECT_STREQ("\"xyz\\\"abc\"", HttpUtil::Quote("xyz\"abc").c_str());
169
170  // Replace <backslash> <backslash> with <backslash>
171  EXPECT_STREQ("\"xyz\\\\abc\"", HttpUtil::Quote("xyz\\abc").c_str());
172
173  // Replace <backslash> X with X
174  EXPECT_STREQ("\"xyzXabc\"", HttpUtil::Quote("xyzXabc").c_str());
175}
176
177TEST(HttpUtilTest, LocateEndOfHeaders) {
178  struct {
179    const char* input;
180    int expected_result;
181  } tests[] = {
182    { "foo\r\nbar\r\n\r\n", 12 },
183    { "foo\nbar\n\n", 9 },
184    { "foo\r\nbar\r\n\r\njunk", 12 },
185    { "foo\nbar\n\njunk", 9 },
186    { "foo\nbar\n\r\njunk", 10 },
187    { "foo\nbar\r\n\njunk", 10 },
188  };
189  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
190    int input_len = static_cast<int>(strlen(tests[i].input));
191    int eoh = HttpUtil::LocateEndOfHeaders(tests[i].input, input_len);
192    EXPECT_EQ(tests[i].expected_result, eoh);
193  }
194}
195
196TEST(HttpUtilTest, AssembleRawHeaders) {
197  struct {
198    const char* input;
199    const char* expected_result;  // with '\0' changed to '|'
200  } tests[] = {
201    { "HTTP/1.0 200 OK\r\nFoo: 1\r\nBar: 2\r\n\r\n",
202      "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" },
203
204    { "HTTP/1.0 200 OK\nFoo: 1\nBar: 2\n\n",
205      "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" },
206
207    // Valid line continuation (single SP).
208    {
209      "HTTP/1.0 200 OK\n"
210      "Foo: 1\n"
211      " continuation\n"
212      "Bar: 2\n\n",
213
214      "HTTP/1.0 200 OK|"
215      "Foo: 1 continuation|"
216      "Bar: 2||"
217    },
218
219    // Valid line continuation (single HT).
220    {
221      "HTTP/1.0 200 OK\n"
222      "Foo: 1\n"
223      "\tcontinuation\n"
224      "Bar: 2\n\n",
225
226      "HTTP/1.0 200 OK|"
227      "Foo: 1 continuation|"
228      "Bar: 2||"
229    },
230
231    // Valid line continuation (multiple SP).
232    {
233      "HTTP/1.0 200 OK\n"
234      "Foo: 1\n"
235      "   continuation\n"
236      "Bar: 2\n\n",
237
238      "HTTP/1.0 200 OK|"
239      "Foo: 1 continuation|"
240      "Bar: 2||"
241    },
242
243    // Valid line continuation (multiple HT).
244    {
245      "HTTP/1.0 200 OK\n"
246      "Foo: 1\n"
247      "\t\t\tcontinuation\n"
248      "Bar: 2\n\n",
249
250      "HTTP/1.0 200 OK|"
251      "Foo: 1 continuation|"
252      "Bar: 2||"
253    },
254
255    // Valid line continuation (mixed HT, SP).
256    {
257      "HTTP/1.0 200 OK\n"
258      "Foo: 1\n"
259      " \t \t continuation\n"
260      "Bar: 2\n\n",
261
262      "HTTP/1.0 200 OK|"
263      "Foo: 1 continuation|"
264      "Bar: 2||"
265    },
266
267    // Valid multi-line continuation
268    {
269      "HTTP/1.0 200 OK\n"
270      "Foo: 1\n"
271      " continuation1\n"
272      "\tcontinuation2\n"
273      "  continuation3\n"
274      "Bar: 2\n\n",
275
276      "HTTP/1.0 200 OK|"
277      "Foo: 1 continuation1 continuation2 continuation3|"
278      "Bar: 2||"
279    },
280
281    // Continuation of quoted value.
282    // This is different from what Firefox does, since it
283    // will preserve the LWS.
284    {
285      "HTTP/1.0 200 OK\n"
286      "Etag: \"34534-d3\n"
287      "    134q\"\n"
288      "Bar: 2\n\n",
289
290      "HTTP/1.0 200 OK|"
291      "Etag: \"34534-d3 134q\"|"
292      "Bar: 2||"
293    },
294
295    // Valid multi-line continuation, full LWS lines
296    {
297      "HTTP/1.0 200 OK\n"
298      "Foo: 1\n"
299      "         \n"
300      "\t\t\t\t\n"
301      "\t  continuation\n"
302      "Bar: 2\n\n",
303
304      // One SP per continued line = 3.
305      "HTTP/1.0 200 OK|"
306      "Foo: 1   continuation|"
307      "Bar: 2||"
308    },
309
310    // Valid multi-line continuation, all LWS
311    {
312      "HTTP/1.0 200 OK\n"
313      "Foo: 1\n"
314      "         \n"
315      "\t\t\t\t\n"
316      "\t  \n"
317      "Bar: 2\n\n",
318
319      // One SP per continued line = 3.
320      "HTTP/1.0 200 OK|"
321      "Foo: 1   |"
322      "Bar: 2||"
323    },
324
325    // Valid line continuation (No value bytes in first line).
326    {
327      "HTTP/1.0 200 OK\n"
328      "Foo:\n"
329      " value\n"
330      "Bar: 2\n\n",
331
332      "HTTP/1.0 200 OK|"
333      "Foo: value|"
334      "Bar: 2||"
335    },
336
337    // Not a line continuation (can't continue status line).
338    {
339      "HTTP/1.0 200 OK\n"
340      " Foo: 1\n"
341      "Bar: 2\n\n",
342
343      "HTTP/1.0 200 OK|"
344      " Foo: 1|"
345      "Bar: 2||"
346    },
347
348    // Not a line continuation (can't continue status line).
349    {
350      "HTTP/1.0\n"
351      " 200 OK\n"
352      "Foo: 1\n"
353      "Bar: 2\n\n",
354
355      "HTTP/1.0|"
356      " 200 OK|"
357      "Foo: 1|"
358      "Bar: 2||"
359    },
360
361    // Not a line continuation (can't continue status line).
362    {
363      "HTTP/1.0 404\n"
364      " Not Found\n"
365      "Foo: 1\n"
366      "Bar: 2\n\n",
367
368      "HTTP/1.0 404|"
369      " Not Found|"
370      "Foo: 1|"
371      "Bar: 2||"
372    },
373
374    // Unterminated status line.
375    {
376      "HTTP/1.0 200 OK",
377
378      "HTTP/1.0 200 OK||"
379    },
380
381    // Single terminated, with headers
382    {
383      "HTTP/1.0 200 OK\n"
384      "Foo: 1\n"
385      "Bar: 2\n",
386
387      "HTTP/1.0 200 OK|"
388      "Foo: 1|"
389      "Bar: 2||"
390    },
391
392    // Not terminated, with headers
393    {
394      "HTTP/1.0 200 OK\n"
395      "Foo: 1\n"
396      "Bar: 2",
397
398      "HTTP/1.0 200 OK|"
399      "Foo: 1|"
400      "Bar: 2||"
401    },
402
403    // Not a line continuation (VT)
404    {
405      "HTTP/1.0 200 OK\n"
406      "Foo: 1\n"
407      "\vInvalidContinuation\n"
408      "Bar: 2\n\n",
409
410      "HTTP/1.0 200 OK|"
411      "Foo: 1|"
412      "\vInvalidContinuation|"
413      "Bar: 2||"
414    },
415
416    // Not a line continuation (formfeed)
417    {
418      "HTTP/1.0 200 OK\n"
419      "Foo: 1\n"
420      "\fInvalidContinuation\n"
421      "Bar: 2\n\n",
422
423      "HTTP/1.0 200 OK|"
424      "Foo: 1|"
425      "\fInvalidContinuation|"
426      "Bar: 2||"
427    },
428
429    // Not a line continuation -- can't continue header names.
430    {
431      "HTTP/1.0 200 OK\n"
432      "Serv\n"
433      " er: Apache\n"
434      "\tInvalidContinuation\n"
435      "Bar: 2\n\n",
436
437      "HTTP/1.0 200 OK|"
438      "Serv|"
439      " er: Apache|"
440      "\tInvalidContinuation|"
441      "Bar: 2||"
442    },
443
444    // Not a line continuation -- no value to continue.
445    {
446      "HTTP/1.0 200 OK\n"
447      "Foo: 1\n"
448      "garbage\n"
449      "  not-a-continuation\n"
450      "Bar: 2\n\n",
451
452      "HTTP/1.0 200 OK|"
453      "Foo: 1|"
454      "garbage|"
455      "  not-a-continuation|"
456      "Bar: 2||",
457    },
458
459    // Not a line continuation -- no valid name.
460    {
461      "HTTP/1.0 200 OK\n"
462      ": 1\n"
463      "  garbage\n"
464      "Bar: 2\n\n",
465
466      "HTTP/1.0 200 OK|"
467      ": 1|"
468      "  garbage|"
469      "Bar: 2||",
470    },
471
472    // Not a line continuation -- no valid name (whitespace)
473    {
474      "HTTP/1.0 200 OK\n"
475      "   : 1\n"
476      "  garbage\n"
477      "Bar: 2\n\n",
478
479      "HTTP/1.0 200 OK|"
480      "   : 1|"
481      "  garbage|"
482      "Bar: 2||",
483    },
484
485  };
486  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
487    int input_len = static_cast<int>(strlen(tests[i].input));
488    std::string raw = HttpUtil::AssembleRawHeaders(tests[i].input, input_len);
489    std::replace(raw.begin(), raw.end(), '\0', '|');
490    EXPECT_TRUE(raw == tests[i].expected_result);
491  }
492}
493
494// Test SpecForRequest() and PathForRequest().
495TEST(HttpUtilTest, RequestUrlSanitize) {
496  struct {
497    const char* url;
498    const char* expected_spec;
499    const char* expected_path;
500  } tests[] = {
501    { // Check that #hash is removed.
502      "http://www.google.com:78/foobar?query=1#hash",
503      "http://www.google.com:78/foobar?query=1",
504      "/foobar?query=1"
505    },
506    { // The reference may itself contain # -- strip all of it.
507      "http://192.168.0.1?query=1#hash#10#11#13#14",
508      "http://192.168.0.1/?query=1",
509      "/?query=1"
510    },
511    { // Strip username/password.
512      "http://user:pass@google.com",
513      "http://google.com/",
514      "/"
515    }
516  };
517  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
518    GURL url(GURL(tests[i].url));
519    std::string expected_spec(tests[i].expected_spec);
520    std::string expected_path(tests[i].expected_path);
521
522    EXPECT_EQ(expected_spec, HttpUtil::SpecForRequest(url));
523    EXPECT_EQ(expected_path, HttpUtil::PathForRequest(url));
524  }
525}
526
527TEST(HttpUtilTest, GenerateAcceptLanguageHeader) {
528  EXPECT_EQ(std::string("en-US,fr;q=0.8,de;q=0.6"),
529            HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de"));
530  EXPECT_EQ(std::string("en-US,fr;q=0.8,de;q=0.6,ko;q=0.4,zh-CN;q=0.2,"
531                        "ja;q=0.2"),
532            HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de,ko,zh-CN,ja"));
533}
534
535TEST(HttpUtilTest, GenerateAcceptCharsetHeader) {
536  EXPECT_EQ(std::string("utf-8,*;q=0.5"),
537            HttpUtil::GenerateAcceptCharsetHeader("utf-8"));
538  EXPECT_EQ(std::string("EUC-JP,utf-8;q=0.7,*;q=0.3"),
539            HttpUtil::GenerateAcceptCharsetHeader("EUC-JP"));
540}
541
542TEST(HttpUtilTest, ParseRanges) {
543  const struct {
544    const char* headers;
545    bool expected_return_value;
546    size_t expected_ranges_size;
547    const struct {
548      int64 expected_first_byte_position;
549      int64 expected_last_byte_position;
550      int64 expected_suffix_length;
551    } expected_ranges[10];
552  } tests[] = {
553    { "Range: bytes=0-10",
554      true,
555      1,
556      { {0, 10, -1}, }
557    },
558    { "Range: bytes=10-0",
559      false,
560      0,
561      {}
562    },
563    { "Range: BytES=0-10",
564      true,
565      1,
566      { {0, 10, -1}, }
567    },
568    { "Range: megabytes=0-10",
569      false,
570      0,
571      {}
572    },
573    { "Range: bytes0-10",
574      false,
575      0,
576      {}
577    },
578    { "Range: bytes=0-0,0-10,10-20,100-200,100-,-200",
579      true,
580      6,
581      { {0, 0, -1},
582        {0, 10, -1},
583        {10, 20, -1},
584        {100, 200, -1},
585        {100, -1, -1},
586        {-1, -1, 200},
587      }
588    },
589    { "Range: bytes=0-10\r\n"
590      "Range: bytes=0-10,10-20,100-200,100-,-200",
591      true,
592      1,
593      { {0, 10, -1}
594      }
595    },
596    { "Range: bytes=",
597      false,
598      0,
599      {}
600    },
601    { "Range: bytes=-",
602      false,
603      0,
604      {}
605    },
606    { "Range: bytes=0-10-",
607      false,
608      0,
609      {}
610    },
611    { "Range: bytes=-0-10",
612      false,
613      0,
614      {}
615    },
616    { "Range: bytes =0-10\r\n",
617      true,
618      1,
619      { {0, 10, -1}
620      }
621    },
622    { "Range: bytes=  0-10      \r\n",
623      true,
624      1,
625      { {0, 10, -1}
626      }
627    },
628    { "Range: bytes  =   0  -   10      \r\n",
629      true,
630      1,
631      { {0, 10, -1}
632      }
633    },
634    { "Range: bytes=   0-1   0\r\n",
635      false,
636      0,
637      {}
638    },
639    { "Range: bytes=   0-     -10\r\n",
640      false,
641      0,
642      {}
643    },
644    { "Range: bytes=   0  -  1   ,   10 -20,   100- 200 ,  100-,  -200 \r\n",
645      true,
646      5,
647      { {0, 1, -1},
648        {10, 20, -1},
649        {100, 200, -1},
650        {100, -1, -1},
651        {-1, -1, 200},
652      }
653    },
654  };
655
656  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
657    std::vector<net::HttpByteRange> ranges;
658    bool return_value = HttpUtil::ParseRanges(std::string(tests[i].headers),
659                                              &ranges);
660    EXPECT_EQ(tests[i].expected_return_value, return_value);
661    if (return_value) {
662      EXPECT_EQ(tests[i].expected_ranges_size, ranges.size());
663      for (size_t j = 0; j < ranges.size(); ++j) {
664        EXPECT_EQ(tests[i].expected_ranges[j].expected_first_byte_position,
665                  ranges[j].first_byte_position());
666        EXPECT_EQ(tests[i].expected_ranges[j].expected_last_byte_position,
667                  ranges[j].last_byte_position());
668        EXPECT_EQ(tests[i].expected_ranges[j].expected_suffix_length,
669                  ranges[j].suffix_length());
670      }
671    }
672  }
673}
674
675namespace {
676void CheckCurrentNameValuePair(HttpUtil::NameValuePairsIterator* parser,
677                               bool expect_valid,
678                               std::string expected_name,
679                               std::string expected_value) {
680  ASSERT_EQ(expect_valid, parser->valid());
681  if (!expect_valid) {
682    return;
683  }
684
685  // Let's make sure that these never change (i.e., when a quoted value is
686  // unquoted, it should be cached on the first calls and not regenerated
687  // later).
688  std::string::const_iterator first_value_begin = parser->value_begin();
689  std::string::const_iterator first_value_end = parser->value_end();
690
691  ASSERT_EQ(expected_name, std::string(parser->name_begin(),
692                                       parser->name_end()));
693  ASSERT_EQ(expected_name, parser->name());
694  ASSERT_EQ(expected_value, std::string(parser->value_begin(),
695                                        parser->value_end()));
696  ASSERT_EQ(expected_value, parser->value());
697
698  // Make sure they didn't/don't change.
699  ASSERT_TRUE(first_value_begin == parser->value_begin());
700  ASSERT_TRUE(first_value_end == parser->value_end());
701}
702
703void CheckNextNameValuePair(HttpUtil::NameValuePairsIterator* parser,
704                            bool expect_next,
705                            bool expect_valid,
706                            std::string expected_name,
707                            std::string expected_value) {
708  ASSERT_EQ(expect_next, parser->GetNext());
709  ASSERT_EQ(expect_valid, parser->valid());
710  if (!expect_next || !expect_valid) {
711    return;
712  }
713
714  CheckCurrentNameValuePair(parser,
715                            expect_valid,
716                            expected_name,
717                            expected_value);
718}
719
720void CheckInvalidNameValuePair(std::string valid_part,
721                               std::string invalid_part) {
722  std::string whole_string = valid_part + invalid_part;
723
724  HttpUtil::NameValuePairsIterator valid_parser(valid_part.begin(),
725                                                valid_part.end(),
726                                                ';');
727  HttpUtil::NameValuePairsIterator invalid_parser(whole_string.begin(),
728                                                  whole_string.end(),
729                                                  ';');
730
731  ASSERT_TRUE(valid_parser.valid());
732  ASSERT_TRUE(invalid_parser.valid());
733
734  // Both parsers should return all the same values until "valid_parser" is
735  // exhausted.
736  while (valid_parser.GetNext()) {
737    ASSERT_TRUE(invalid_parser.GetNext());
738    ASSERT_TRUE(valid_parser.valid());
739    ASSERT_TRUE(invalid_parser.valid());
740    ASSERT_EQ(valid_parser.name(), invalid_parser.name());
741    ASSERT_EQ(valid_parser.value(), invalid_parser.value());
742  }
743
744  // valid_parser is exhausted and remains 'valid'
745  ASSERT_TRUE(valid_parser.valid());
746
747  // invalid_parser's corresponding call to GetNext also returns false...
748  ASSERT_FALSE(invalid_parser.GetNext());
749  // ...but the parser is in an invalid state.
750  ASSERT_FALSE(invalid_parser.valid());
751}
752
753}  // anonymous namespace
754
755TEST(HttpUtilTest, NameValuePairsIteratorCopyAndAssign) {
756  std::string data = "alpha='\\'a\\''; beta=\" b \"; cappa='c;'; delta=\"d\"";
757  HttpUtil::NameValuePairsIterator parser_a(data.begin(), data.end(), ';');
758
759  EXPECT_TRUE(parser_a.valid());
760  ASSERT_NO_FATAL_FAILURE(
761      CheckNextNameValuePair(&parser_a, true, true, "alpha", "'a'"));
762
763  HttpUtil::NameValuePairsIterator parser_b(parser_a);
764  // a and b now point to same location
765  ASSERT_NO_FATAL_FAILURE(
766      CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'"));
767  ASSERT_NO_FATAL_FAILURE(
768      CheckCurrentNameValuePair(&parser_a, true, "alpha", "'a'"));
769
770  // advance a, no effect on b
771  ASSERT_NO_FATAL_FAILURE(
772      CheckNextNameValuePair(&parser_a, true, true, "beta", " b "));
773  ASSERT_NO_FATAL_FAILURE(
774      CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'"));
775
776  // assign b the current state of a, no effect on a
777  parser_b = parser_a;
778  ASSERT_NO_FATAL_FAILURE(
779      CheckCurrentNameValuePair(&parser_b, true, "beta", " b "));
780  ASSERT_NO_FATAL_FAILURE(
781      CheckCurrentNameValuePair(&parser_a, true, "beta", " b "));
782
783  // advance b, no effect on a
784  ASSERT_NO_FATAL_FAILURE(
785      CheckNextNameValuePair(&parser_b, true, true, "cappa", "c;"));
786  ASSERT_NO_FATAL_FAILURE(
787      CheckCurrentNameValuePair(&parser_a, true, "beta", " b "));
788}
789
790TEST(HttpUtilTest, NameValuePairsIteratorEmptyInput) {
791  std::string data = "";
792  HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
793
794  EXPECT_TRUE(parser.valid());
795  ASSERT_NO_FATAL_FAILURE(
796      CheckNextNameValuePair(&parser, false, true, "", ""));
797}
798
799TEST(HttpUtilTest, NameValuePairsIterator) {
800  std::string data = "alpha=1; beta= 2 ;cappa =' 3; ';"
801                     "delta= \" \\\"4\\\" \"; e= \" '5'\"; e=6;"
802                     "f='\\'\\h\\e\\l\\l\\o\\ \\w\\o\\r\\l\\d\\'';"
803                     "g=''; h='hello'";
804  HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
805  EXPECT_TRUE(parser.valid());
806
807  ASSERT_NO_FATAL_FAILURE(
808      CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
809  ASSERT_NO_FATAL_FAILURE(
810      CheckNextNameValuePair(&parser, true, true, "beta", "2"));
811  ASSERT_NO_FATAL_FAILURE(
812      CheckNextNameValuePair(&parser, true, true, "cappa", " 3; "));
813  ASSERT_NO_FATAL_FAILURE(
814      CheckNextNameValuePair(&parser, true, true, "delta", " \"4\" "));
815  ASSERT_NO_FATAL_FAILURE(
816      CheckNextNameValuePair(&parser, true, true, "e", " '5'"));
817  ASSERT_NO_FATAL_FAILURE(
818      CheckNextNameValuePair(&parser, true, true, "e", "6"));
819  ASSERT_NO_FATAL_FAILURE(
820      CheckNextNameValuePair(&parser, true, true, "f", "'hello world'"));
821  ASSERT_NO_FATAL_FAILURE(
822      CheckNextNameValuePair(&parser, true, true, "g", ""));
823  ASSERT_NO_FATAL_FAILURE(
824      CheckNextNameValuePair(&parser, true, true, "h", "hello"));
825  ASSERT_NO_FATAL_FAILURE(
826      CheckNextNameValuePair(&parser, false, true, "", ""));
827}
828
829TEST(HttpUtilTest, NameValuePairsIteratorIllegalInputs) {
830  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; beta"));
831  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("", "beta"));
832
833  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; 'beta'=2"));
834  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("", "'beta'=2"));
835  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";beta="));
836  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1",
837                                                    ";beta=;cappa=2"));
838
839  // According to the spec this is an error, but it doesn't seem appropriate to
840  // change our behaviour to be less permissive at this time.
841  // See NameValuePairsIteratorExtraSeparators test
842  // ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";; beta=2"));
843}
844
845// If we are going to support extra separators against the spec, let's just make
846// sure they work rationally.
847TEST(HttpUtilTest, NameValuePairsIteratorExtraSeparators) {
848  std::string data = " ; ;;alpha=1; ;; ; beta= 2;cappa=3;;; ; ";
849  HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
850  EXPECT_TRUE(parser.valid());
851
852  ASSERT_NO_FATAL_FAILURE(
853      CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
854  ASSERT_NO_FATAL_FAILURE(
855      CheckNextNameValuePair(&parser, true, true, "beta", "2"));
856  ASSERT_NO_FATAL_FAILURE(
857      CheckNextNameValuePair(&parser, true, true, "cappa", "3"));
858  ASSERT_NO_FATAL_FAILURE(
859      CheckNextNameValuePair(&parser, false, true, "", ""));
860}
861
862// See comments on the implementation of NameValuePairsIterator::GetNext
863// regarding this derogation from the spec.
864TEST(HttpUtilTest, NameValuePairsIteratorMissingEndQuote) {
865  std::string data = "name='value";
866  HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
867  EXPECT_TRUE(parser.valid());
868
869  ASSERT_NO_FATAL_FAILURE(
870      CheckNextNameValuePair(&parser, true, true, "name", "value"));
871  ASSERT_NO_FATAL_FAILURE(
872      CheckNextNameValuePair(&parser, false, true, "", ""));
873}
874