• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
11 using net::HttpUtil;
12 
13 namespace {
14 class HttpUtilTest : public testing::Test {};
15 }
16 
TEST(HttpUtilTest,HasHeader)17 TEST(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 
TEST(HttpUtilTest,StripHeaders)37 TEST(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 
TEST(HttpUtilTest,HeadersIterator)60 TEST(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 
TEST(HttpUtilTest,HeadersIterator_MalformedLine)80 TEST(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 
TEST(HttpUtilTest,HeadersIterator_AdvanceTo)96 TEST(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 
TEST(HttpUtilTest,HeadersIterator_Reset)108 TEST(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 
TEST(HttpUtilTest,ValuesIterator)121 TEST(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 
TEST(HttpUtilTest,ValuesIterator_Blanks)138 TEST(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 
TEST(HttpUtilTest,Unquote)146 TEST(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 
TEST(HttpUtilTest,Quote)167 TEST(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 
TEST(HttpUtilTest,LocateEndOfHeaders)177 TEST(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 
TEST(HttpUtilTest,AssembleRawHeaders)196 TEST(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().
TEST(HttpUtilTest,RequestUrlSanitize)495 TEST(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 
TEST(HttpUtilTest,GenerateAcceptLanguageHeader)527 TEST(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 
TEST(HttpUtilTest,GenerateAcceptCharsetHeader)535 TEST(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 
TEST(HttpUtilTest,ParseRanges)542 TEST(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 
675 namespace {
CheckCurrentNameValuePair(HttpUtil::NameValuePairsIterator * parser,bool expect_valid,std::string expected_name,std::string expected_value)676 void 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 
CheckNextNameValuePair(HttpUtil::NameValuePairsIterator * parser,bool expect_next,bool expect_valid,std::string expected_name,std::string expected_value)703 void 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 
CheckInvalidNameValuePair(std::string valid_part,std::string invalid_part)720 void 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 
TEST(HttpUtilTest,NameValuePairsIteratorCopyAndAssign)755 TEST(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 
TEST(HttpUtilTest,NameValuePairsIteratorEmptyInput)790 TEST(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 
TEST(HttpUtilTest,NameValuePairsIterator)799 TEST(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 
TEST(HttpUtilTest,NameValuePairsIteratorIllegalInputs)829 TEST(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.
TEST(HttpUtilTest,NameValuePairsIteratorExtraSeparators)847 TEST(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.
TEST(HttpUtilTest,NameValuePairsIteratorMissingEndQuote)864 TEST(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