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