OLD | NEW |
| (Empty) |
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 <algorithm> | |
6 | |
7 #include "base/basictypes.h" | |
8 #include "base/strings/string_util.h" | |
9 #include "net/http/http_util.h" | |
10 #include "testing/gtest/include/gtest/gtest.h" | |
11 | |
12 using net::HttpUtil; | |
13 | |
14 namespace { | |
15 class HttpUtilTest : public testing::Test {}; | |
16 } | |
17 | |
18 TEST(HttpUtilTest, IsSafeHeader) { | |
19 static const char* const unsafe_headers[] = { | |
20 "sec-", | |
21 "sEc-", | |
22 "sec-foo", | |
23 "sEc-FoO", | |
24 "proxy-", | |
25 "pRoXy-", | |
26 "proxy-foo", | |
27 "pRoXy-FoO", | |
28 "accept-charset", | |
29 "accept-encoding", | |
30 "access-control-request-headers", | |
31 "access-control-request-method", | |
32 "connection", | |
33 "content-length", | |
34 "cookie", | |
35 "cookie2", | |
36 "content-transfer-encoding", | |
37 "date", | |
38 "expect", | |
39 "host", | |
40 "keep-alive", | |
41 "origin", | |
42 "referer", | |
43 "te", | |
44 "trailer", | |
45 "transfer-encoding", | |
46 "upgrade", | |
47 "user-agent", | |
48 "via", | |
49 }; | |
50 for (size_t i = 0; i < arraysize(unsafe_headers); ++i) { | |
51 EXPECT_FALSE(HttpUtil::IsSafeHeader(unsafe_headers[i])) | |
52 << unsafe_headers[i]; | |
53 EXPECT_FALSE(HttpUtil::IsSafeHeader(StringToUpperASCII(std::string( | |
54 unsafe_headers[i])))) << unsafe_headers[i]; | |
55 } | |
56 static const char* const safe_headers[] = { | |
57 "foo", | |
58 "x-", | |
59 "x-foo", | |
60 "content-disposition", | |
61 "update", | |
62 "accept-charseta", | |
63 "accept_charset", | |
64 "accept-encodinga", | |
65 "accept_encoding", | |
66 "access-control-request-headersa", | |
67 "access-control-request-header", | |
68 "access_control_request_header", | |
69 "access-control-request-methoda", | |
70 "access_control_request_method", | |
71 "connectiona", | |
72 "content-lengtha", | |
73 "content_length", | |
74 "cookiea", | |
75 "cookie2a", | |
76 "cookie3", | |
77 "content-transfer-encodinga", | |
78 "content_transfer_encoding", | |
79 "datea", | |
80 "expecta", | |
81 "hosta", | |
82 "keep-alivea", | |
83 "keep_alive", | |
84 "origina", | |
85 "referera", | |
86 "referrer", | |
87 "tea", | |
88 "trailera", | |
89 "transfer-encodinga", | |
90 "transfer_encoding", | |
91 "upgradea", | |
92 "user-agenta", | |
93 "user_agent", | |
94 "viaa", | |
95 }; | |
96 for (size_t i = 0; i < arraysize(safe_headers); ++i) { | |
97 EXPECT_TRUE(HttpUtil::IsSafeHeader(safe_headers[i])) << safe_headers[i]; | |
98 EXPECT_TRUE(HttpUtil::IsSafeHeader(StringToUpperASCII(std::string( | |
99 safe_headers[i])))) << safe_headers[i]; | |
100 } | |
101 } | |
102 | |
103 TEST(HttpUtilTest, HasHeader) { | |
104 static const struct { | |
105 const char* const headers; | |
106 const char* const name; | |
107 bool expected_result; | |
108 } tests[] = { | |
109 { "", "foo", false }, | |
110 { "foo\r\nbar", "foo", false }, | |
111 { "ffoo: 1", "foo", false }, | |
112 { "foo: 1", "foo", true }, | |
113 { "foo: 1\r\nbar: 2", "foo", true }, | |
114 { "fOO: 1\r\nbar: 2", "foo", true }, | |
115 { "g: 0\r\nfoo: 1\r\nbar: 2", "foo", true }, | |
116 }; | |
117 for (size_t i = 0; i < arraysize(tests); ++i) { | |
118 bool result = HttpUtil::HasHeader(tests[i].headers, tests[i].name); | |
119 EXPECT_EQ(tests[i].expected_result, result); | |
120 } | |
121 } | |
122 | |
123 TEST(HttpUtilTest, StripHeaders) { | |
124 static const char* const headers = | |
125 "Origin: origin\r\n" | |
126 "Content-Type: text/plain\r\n" | |
127 "Cookies: foo1\r\n" | |
128 "Custom: baz\r\n" | |
129 "COOKIES: foo2\r\n" | |
130 "Server: Apache\r\n" | |
131 "OrIGin: origin2\r\n"; | |
132 | |
133 static const char* const header_names[] = { | |
134 "origin", "content-type", "cookies" | |
135 }; | |
136 | |
137 static const char* const expected_stripped_headers = | |
138 "Custom: baz\r\n" | |
139 "Server: Apache\r\n"; | |
140 | |
141 EXPECT_EQ(expected_stripped_headers, | |
142 HttpUtil::StripHeaders(headers, header_names, | |
143 arraysize(header_names))); | |
144 } | |
145 | |
146 TEST(HttpUtilTest, HeadersIterator) { | |
147 std::string headers = "foo: 1\t\r\nbar: hello world\r\nbaz: 3 \r\n"; | |
148 | |
149 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n"); | |
150 | |
151 ASSERT_TRUE(it.GetNext()); | |
152 EXPECT_EQ(std::string("foo"), it.name()); | |
153 EXPECT_EQ(std::string("1"), it.values()); | |
154 | |
155 ASSERT_TRUE(it.GetNext()); | |
156 EXPECT_EQ(std::string("bar"), it.name()); | |
157 EXPECT_EQ(std::string("hello world"), it.values()); | |
158 | |
159 ASSERT_TRUE(it.GetNext()); | |
160 EXPECT_EQ(std::string("baz"), it.name()); | |
161 EXPECT_EQ(std::string("3"), it.values()); | |
162 | |
163 EXPECT_FALSE(it.GetNext()); | |
164 } | |
165 | |
166 TEST(HttpUtilTest, HeadersIterator_MalformedLine) { | |
167 std::string headers = "foo: 1\n: 2\n3\nbar: 4"; | |
168 | |
169 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n"); | |
170 | |
171 ASSERT_TRUE(it.GetNext()); | |
172 EXPECT_EQ(std::string("foo"), it.name()); | |
173 EXPECT_EQ(std::string("1"), it.values()); | |
174 | |
175 ASSERT_TRUE(it.GetNext()); | |
176 EXPECT_EQ(std::string("bar"), it.name()); | |
177 EXPECT_EQ(std::string("4"), it.values()); | |
178 | |
179 EXPECT_FALSE(it.GetNext()); | |
180 } | |
181 | |
182 TEST(HttpUtilTest, HeadersIterator_AdvanceTo) { | |
183 std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4"; | |
184 | |
185 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n"); | |
186 EXPECT_TRUE(it.AdvanceTo("foo")); | |
187 EXPECT_EQ("foo", it.name()); | |
188 EXPECT_TRUE(it.AdvanceTo("bar")); | |
189 EXPECT_EQ("bar", it.name()); | |
190 EXPECT_FALSE(it.AdvanceTo("blat")); | |
191 EXPECT_FALSE(it.GetNext()); // should be at end of headers | |
192 } | |
193 | |
194 TEST(HttpUtilTest, HeadersIterator_Reset) { | |
195 std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4"; | |
196 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n"); | |
197 // Search past "foo". | |
198 EXPECT_TRUE(it.AdvanceTo("bar")); | |
199 // Now try advancing to "foo". This time it should fail since the iterator | |
200 // position is past it. | |
201 EXPECT_FALSE(it.AdvanceTo("foo")); | |
202 it.Reset(); | |
203 // Now that we reset the iterator position, we should find 'foo' | |
204 EXPECT_TRUE(it.AdvanceTo("foo")); | |
205 } | |
206 | |
207 TEST(HttpUtilTest, ValuesIterator) { | |
208 std::string values = " must-revalidate, no-cache=\"foo, bar\"\t, private "; | |
209 | |
210 HttpUtil::ValuesIterator it(values.begin(), values.end(), ','); | |
211 | |
212 ASSERT_TRUE(it.GetNext()); | |
213 EXPECT_EQ(std::string("must-revalidate"), it.value()); | |
214 | |
215 ASSERT_TRUE(it.GetNext()); | |
216 EXPECT_EQ(std::string("no-cache=\"foo, bar\""), it.value()); | |
217 | |
218 ASSERT_TRUE(it.GetNext()); | |
219 EXPECT_EQ(std::string("private"), it.value()); | |
220 | |
221 EXPECT_FALSE(it.GetNext()); | |
222 } | |
223 | |
224 TEST(HttpUtilTest, ValuesIterator_Blanks) { | |
225 std::string values = " \t "; | |
226 | |
227 HttpUtil::ValuesIterator it(values.begin(), values.end(), ','); | |
228 | |
229 EXPECT_FALSE(it.GetNext()); | |
230 } | |
231 | |
232 TEST(HttpUtilTest, Unquote) { | |
233 // Replace <backslash> " with ". | |
234 EXPECT_STREQ("xyz\"abc", HttpUtil::Unquote("\"xyz\\\"abc\"").c_str()); | |
235 | |
236 // Replace <backslash> <backslash> with <backslash> | |
237 EXPECT_STREQ("xyz\\abc", HttpUtil::Unquote("\"xyz\\\\abc\"").c_str()); | |
238 EXPECT_STREQ("xyz\\\\\\abc", | |
239 HttpUtil::Unquote("\"xyz\\\\\\\\\\\\abc\"").c_str()); | |
240 | |
241 // Replace <backslash> X with X | |
242 EXPECT_STREQ("xyzXabc", HttpUtil::Unquote("\"xyz\\Xabc\"").c_str()); | |
243 | |
244 // Act as identity function on unquoted inputs. | |
245 EXPECT_STREQ("X", HttpUtil::Unquote("X").c_str()); | |
246 EXPECT_STREQ("\"", HttpUtil::Unquote("\"").c_str()); | |
247 | |
248 // Allow single quotes to act as quote marks. | |
249 // Not part of RFC 2616. | |
250 EXPECT_STREQ("x\"", HttpUtil::Unquote("'x\"'").c_str()); | |
251 } | |
252 | |
253 TEST(HttpUtilTest, Quote) { | |
254 EXPECT_STREQ("\"xyz\\\"abc\"", HttpUtil::Quote("xyz\"abc").c_str()); | |
255 | |
256 // Replace <backslash> <backslash> with <backslash> | |
257 EXPECT_STREQ("\"xyz\\\\abc\"", HttpUtil::Quote("xyz\\abc").c_str()); | |
258 | |
259 // Replace <backslash> X with X | |
260 EXPECT_STREQ("\"xyzXabc\"", HttpUtil::Quote("xyzXabc").c_str()); | |
261 } | |
262 | |
263 TEST(HttpUtilTest, LocateEndOfHeaders) { | |
264 struct { | |
265 const char* const input; | |
266 int expected_result; | |
267 } tests[] = { | |
268 { "foo\r\nbar\r\n\r\n", 12 }, | |
269 { "foo\nbar\n\n", 9 }, | |
270 { "foo\r\nbar\r\n\r\njunk", 12 }, | |
271 { "foo\nbar\n\njunk", 9 }, | |
272 { "foo\nbar\n\r\njunk", 10 }, | |
273 { "foo\nbar\r\n\njunk", 10 }, | |
274 }; | |
275 for (size_t i = 0; i < arraysize(tests); ++i) { | |
276 int input_len = static_cast<int>(strlen(tests[i].input)); | |
277 int eoh = HttpUtil::LocateEndOfHeaders(tests[i].input, input_len); | |
278 EXPECT_EQ(tests[i].expected_result, eoh); | |
279 } | |
280 } | |
281 | |
282 TEST(HttpUtilTest, AssembleRawHeaders) { | |
283 struct { | |
284 const char* const input; // with '|' representing '\0' | |
285 const char* const expected_result; // with '\0' changed to '|' | |
286 } tests[] = { | |
287 { "HTTP/1.0 200 OK\r\nFoo: 1\r\nBar: 2\r\n\r\n", | |
288 "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" }, | |
289 | |
290 { "HTTP/1.0 200 OK\nFoo: 1\nBar: 2\n\n", | |
291 "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" }, | |
292 | |
293 // Valid line continuation (single SP). | |
294 { | |
295 "HTTP/1.0 200 OK\n" | |
296 "Foo: 1\n" | |
297 " continuation\n" | |
298 "Bar: 2\n\n", | |
299 | |
300 "HTTP/1.0 200 OK|" | |
301 "Foo: 1 continuation|" | |
302 "Bar: 2||" | |
303 }, | |
304 | |
305 // Valid line continuation (single HT). | |
306 { | |
307 "HTTP/1.0 200 OK\n" | |
308 "Foo: 1\n" | |
309 "\tcontinuation\n" | |
310 "Bar: 2\n\n", | |
311 | |
312 "HTTP/1.0 200 OK|" | |
313 "Foo: 1 continuation|" | |
314 "Bar: 2||" | |
315 }, | |
316 | |
317 // Valid line continuation (multiple SP). | |
318 { | |
319 "HTTP/1.0 200 OK\n" | |
320 "Foo: 1\n" | |
321 " continuation\n" | |
322 "Bar: 2\n\n", | |
323 | |
324 "HTTP/1.0 200 OK|" | |
325 "Foo: 1 continuation|" | |
326 "Bar: 2||" | |
327 }, | |
328 | |
329 // Valid line continuation (multiple HT). | |
330 { | |
331 "HTTP/1.0 200 OK\n" | |
332 "Foo: 1\n" | |
333 "\t\t\tcontinuation\n" | |
334 "Bar: 2\n\n", | |
335 | |
336 "HTTP/1.0 200 OK|" | |
337 "Foo: 1 continuation|" | |
338 "Bar: 2||" | |
339 }, | |
340 | |
341 // Valid line continuation (mixed HT, SP). | |
342 { | |
343 "HTTP/1.0 200 OK\n" | |
344 "Foo: 1\n" | |
345 " \t \t continuation\n" | |
346 "Bar: 2\n\n", | |
347 | |
348 "HTTP/1.0 200 OK|" | |
349 "Foo: 1 continuation|" | |
350 "Bar: 2||" | |
351 }, | |
352 | |
353 // Valid multi-line continuation | |
354 { | |
355 "HTTP/1.0 200 OK\n" | |
356 "Foo: 1\n" | |
357 " continuation1\n" | |
358 "\tcontinuation2\n" | |
359 " continuation3\n" | |
360 "Bar: 2\n\n", | |
361 | |
362 "HTTP/1.0 200 OK|" | |
363 "Foo: 1 continuation1 continuation2 continuation3|" | |
364 "Bar: 2||" | |
365 }, | |
366 | |
367 // Continuation of quoted value. | |
368 // This is different from what Firefox does, since it | |
369 // will preserve the LWS. | |
370 { | |
371 "HTTP/1.0 200 OK\n" | |
372 "Etag: \"34534-d3\n" | |
373 " 134q\"\n" | |
374 "Bar: 2\n\n", | |
375 | |
376 "HTTP/1.0 200 OK|" | |
377 "Etag: \"34534-d3 134q\"|" | |
378 "Bar: 2||" | |
379 }, | |
380 | |
381 // Valid multi-line continuation, full LWS lines | |
382 { | |
383 "HTTP/1.0 200 OK\n" | |
384 "Foo: 1\n" | |
385 " \n" | |
386 "\t\t\t\t\n" | |
387 "\t continuation\n" | |
388 "Bar: 2\n\n", | |
389 | |
390 // One SP per continued line = 3. | |
391 "HTTP/1.0 200 OK|" | |
392 "Foo: 1 continuation|" | |
393 "Bar: 2||" | |
394 }, | |
395 | |
396 // Valid multi-line continuation, all LWS | |
397 { | |
398 "HTTP/1.0 200 OK\n" | |
399 "Foo: 1\n" | |
400 " \n" | |
401 "\t\t\t\t\n" | |
402 "\t \n" | |
403 "Bar: 2\n\n", | |
404 | |
405 // One SP per continued line = 3. | |
406 "HTTP/1.0 200 OK|" | |
407 "Foo: 1 |" | |
408 "Bar: 2||" | |
409 }, | |
410 | |
411 // Valid line continuation (No value bytes in first line). | |
412 { | |
413 "HTTP/1.0 200 OK\n" | |
414 "Foo:\n" | |
415 " value\n" | |
416 "Bar: 2\n\n", | |
417 | |
418 "HTTP/1.0 200 OK|" | |
419 "Foo: value|" | |
420 "Bar: 2||" | |
421 }, | |
422 | |
423 // Not a line continuation (can't continue status line). | |
424 { | |
425 "HTTP/1.0 200 OK\n" | |
426 " Foo: 1\n" | |
427 "Bar: 2\n\n", | |
428 | |
429 "HTTP/1.0 200 OK|" | |
430 " Foo: 1|" | |
431 "Bar: 2||" | |
432 }, | |
433 | |
434 // Not a line continuation (can't continue status line). | |
435 { | |
436 "HTTP/1.0\n" | |
437 " 200 OK\n" | |
438 "Foo: 1\n" | |
439 "Bar: 2\n\n", | |
440 | |
441 "HTTP/1.0|" | |
442 " 200 OK|" | |
443 "Foo: 1|" | |
444 "Bar: 2||" | |
445 }, | |
446 | |
447 // Not a line continuation (can't continue status line). | |
448 { | |
449 "HTTP/1.0 404\n" | |
450 " Not Found\n" | |
451 "Foo: 1\n" | |
452 "Bar: 2\n\n", | |
453 | |
454 "HTTP/1.0 404|" | |
455 " Not Found|" | |
456 "Foo: 1|" | |
457 "Bar: 2||" | |
458 }, | |
459 | |
460 // Unterminated status line. | |
461 { | |
462 "HTTP/1.0 200 OK", | |
463 | |
464 "HTTP/1.0 200 OK||" | |
465 }, | |
466 | |
467 // Single terminated, with headers | |
468 { | |
469 "HTTP/1.0 200 OK\n" | |
470 "Foo: 1\n" | |
471 "Bar: 2\n", | |
472 | |
473 "HTTP/1.0 200 OK|" | |
474 "Foo: 1|" | |
475 "Bar: 2||" | |
476 }, | |
477 | |
478 // Not terminated, with headers | |
479 { | |
480 "HTTP/1.0 200 OK\n" | |
481 "Foo: 1\n" | |
482 "Bar: 2", | |
483 | |
484 "HTTP/1.0 200 OK|" | |
485 "Foo: 1|" | |
486 "Bar: 2||" | |
487 }, | |
488 | |
489 // Not a line continuation (VT) | |
490 { | |
491 "HTTP/1.0 200 OK\n" | |
492 "Foo: 1\n" | |
493 "\vInvalidContinuation\n" | |
494 "Bar: 2\n\n", | |
495 | |
496 "HTTP/1.0 200 OK|" | |
497 "Foo: 1|" | |
498 "\vInvalidContinuation|" | |
499 "Bar: 2||" | |
500 }, | |
501 | |
502 // Not a line continuation (formfeed) | |
503 { | |
504 "HTTP/1.0 200 OK\n" | |
505 "Foo: 1\n" | |
506 "\fInvalidContinuation\n" | |
507 "Bar: 2\n\n", | |
508 | |
509 "HTTP/1.0 200 OK|" | |
510 "Foo: 1|" | |
511 "\fInvalidContinuation|" | |
512 "Bar: 2||" | |
513 }, | |
514 | |
515 // Not a line continuation -- can't continue header names. | |
516 { | |
517 "HTTP/1.0 200 OK\n" | |
518 "Serv\n" | |
519 " er: Apache\n" | |
520 "\tInvalidContinuation\n" | |
521 "Bar: 2\n\n", | |
522 | |
523 "HTTP/1.0 200 OK|" | |
524 "Serv|" | |
525 " er: Apache|" | |
526 "\tInvalidContinuation|" | |
527 "Bar: 2||" | |
528 }, | |
529 | |
530 // Not a line continuation -- no value to continue. | |
531 { | |
532 "HTTP/1.0 200 OK\n" | |
533 "Foo: 1\n" | |
534 "garbage\n" | |
535 " not-a-continuation\n" | |
536 "Bar: 2\n\n", | |
537 | |
538 "HTTP/1.0 200 OK|" | |
539 "Foo: 1|" | |
540 "garbage|" | |
541 " not-a-continuation|" | |
542 "Bar: 2||", | |
543 }, | |
544 | |
545 // Not a line continuation -- no valid name. | |
546 { | |
547 "HTTP/1.0 200 OK\n" | |
548 ": 1\n" | |
549 " garbage\n" | |
550 "Bar: 2\n\n", | |
551 | |
552 "HTTP/1.0 200 OK|" | |
553 ": 1|" | |
554 " garbage|" | |
555 "Bar: 2||", | |
556 }, | |
557 | |
558 // Not a line continuation -- no valid name (whitespace) | |
559 { | |
560 "HTTP/1.0 200 OK\n" | |
561 " : 1\n" | |
562 " garbage\n" | |
563 "Bar: 2\n\n", | |
564 | |
565 "HTTP/1.0 200 OK|" | |
566 " : 1|" | |
567 " garbage|" | |
568 "Bar: 2||", | |
569 }, | |
570 | |
571 // Embed NULLs in the status line. They should not be understood | |
572 // as line separators. | |
573 { | |
574 "HTTP/1.0 200 OK|Bar2:0|Baz2:1\r\nFoo: 1\r\nBar: 2\r\n\r\n", | |
575 "HTTP/1.0 200 OKBar2:0Baz2:1|Foo: 1|Bar: 2||" | |
576 }, | |
577 | |
578 // Embed NULLs in a header line. They should not be understood as | |
579 // line separators. | |
580 { | |
581 "HTTP/1.0 200 OK\nFoo: 1|Foo2: 3\nBar: 2\n\n", | |
582 "HTTP/1.0 200 OK|Foo: 1Foo2: 3|Bar: 2||" | |
583 }, | |
584 }; | |
585 for (size_t i = 0; i < arraysize(tests); ++i) { | |
586 std::string input = tests[i].input; | |
587 std::replace(input.begin(), input.end(), '|', '\0'); | |
588 std::string raw = HttpUtil::AssembleRawHeaders(input.data(), input.size()); | |
589 std::replace(raw.begin(), raw.end(), '\0', '|'); | |
590 EXPECT_EQ(tests[i].expected_result, raw); | |
591 } | |
592 } | |
593 | |
594 // Test SpecForRequest() and PathForRequest(). | |
595 TEST(HttpUtilTest, RequestUrlSanitize) { | |
596 struct { | |
597 const char* const url; | |
598 const char* const expected_spec; | |
599 const char* const expected_path; | |
600 } tests[] = { | |
601 { // Check that #hash is removed. | |
602 "http://www.google.com:78/foobar?query=1#hash", | |
603 "http://www.google.com:78/foobar?query=1", | |
604 "/foobar?query=1" | |
605 }, | |
606 { // The reference may itself contain # -- strip all of it. | |
607 "http://192.168.0.1?query=1#hash#10#11#13#14", | |
608 "http://192.168.0.1/?query=1", | |
609 "/?query=1" | |
610 }, | |
611 { // Strip username/password. | |
612 "http://user:pass@google.com", | |
613 "http://google.com/", | |
614 "/" | |
615 }, | |
616 { // https scheme | |
617 "https://www.google.com:78/foobar?query=1#hash", | |
618 "https://www.google.com:78/foobar?query=1", | |
619 "/foobar?query=1" | |
620 }, | |
621 { // WebSocket's ws scheme | |
622 "ws://www.google.com:78/foobar?query=1#hash", | |
623 "ws://www.google.com:78/foobar?query=1", | |
624 "/foobar?query=1" | |
625 }, | |
626 { // WebSocket's wss scheme | |
627 "wss://www.google.com:78/foobar?query=1#hash", | |
628 "wss://www.google.com:78/foobar?query=1", | |
629 "/foobar?query=1" | |
630 } | |
631 }; | |
632 for (size_t i = 0; i < arraysize(tests); ++i) { | |
633 GURL url(GURL(tests[i].url)); | |
634 std::string expected_spec(tests[i].expected_spec); | |
635 std::string expected_path(tests[i].expected_path); | |
636 | |
637 EXPECT_EQ(expected_spec, HttpUtil::SpecForRequest(url)); | |
638 EXPECT_EQ(expected_path, HttpUtil::PathForRequest(url)); | |
639 } | |
640 } | |
641 | |
642 // Test SpecForRequest() for "ftp" scheme. | |
643 TEST(HttpUtilTest, SpecForRequestForUrlWithFtpScheme) { | |
644 GURL ftp_url("ftp://user:pass@google.com/pub/chromium/"); | |
645 EXPECT_EQ("ftp://google.com/pub/chromium/", | |
646 HttpUtil::SpecForRequest(ftp_url)); | |
647 } | |
648 | |
649 TEST(HttpUtilTest, GenerateAcceptLanguageHeader) { | |
650 EXPECT_EQ(std::string("en-US,fr;q=0.8,de;q=0.6"), | |
651 HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de")); | |
652 EXPECT_EQ(std::string("en-US,fr;q=0.8,de;q=0.6,ko;q=0.4,zh-CN;q=0.2," | |
653 "ja;q=0.2"), | |
654 HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de,ko,zh-CN,ja")); | |
655 } | |
656 | |
657 // HttpResponseHeadersTest.GetMimeType also tests ParseContentType. | |
658 TEST(HttpUtilTest, ParseContentType) { | |
659 const struct { | |
660 const char* const content_type; | |
661 const char* const expected_mime_type; | |
662 const char* const expected_charset; | |
663 const bool expected_had_charset; | |
664 const char* const expected_boundary; | |
665 } tests[] = { | |
666 { "text/html; charset=utf-8", | |
667 "text/html", | |
668 "utf-8", | |
669 true, | |
670 "" | |
671 }, | |
672 { "text/html; charset =utf-8", | |
673 "text/html", | |
674 "utf-8", | |
675 true, | |
676 "" | |
677 }, | |
678 { "text/html; charset= utf-8", | |
679 "text/html", | |
680 "utf-8", | |
681 true, | |
682 "" | |
683 }, | |
684 { "text/html; charset=utf-8 ", | |
685 "text/html", | |
686 "utf-8", | |
687 true, | |
688 "" | |
689 }, | |
690 { "text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs\"", | |
691 "text/html", | |
692 "", | |
693 false, | |
694 "\"WebKit-ada-df-dsf-adsfadsfs\"" | |
695 }, | |
696 { "text/html; boundary =\"WebKit-ada-df-dsf-adsfadsfs\"", | |
697 "text/html", | |
698 "", | |
699 false, | |
700 "\"WebKit-ada-df-dsf-adsfadsfs\"" | |
701 }, | |
702 { "text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\"", | |
703 "text/html", | |
704 "", | |
705 false, | |
706 "\"WebKit-ada-df-dsf-adsfadsfs\"" | |
707 }, | |
708 { "text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\" ", | |
709 "text/html", | |
710 "", | |
711 false, | |
712 "\"WebKit-ada-df-dsf-adsfadsfs\"" | |
713 }, | |
714 { "text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs \"", | |
715 "text/html", | |
716 "", | |
717 false, | |
718 "\"WebKit-ada-df-dsf-adsfadsfs \"" | |
719 }, | |
720 { "text/html; boundary=WebKit-ada-df-dsf-adsfadsfs", | |
721 "text/html", | |
722 "", | |
723 false, | |
724 "WebKit-ada-df-dsf-adsfadsfs" | |
725 }, | |
726 // TODO(abarth): Add more interesting test cases. | |
727 }; | |
728 for (size_t i = 0; i < arraysize(tests); ++i) { | |
729 std::string mime_type; | |
730 std::string charset; | |
731 bool had_charset = false; | |
732 std::string boundary; | |
733 net::HttpUtil::ParseContentType(tests[i].content_type, &mime_type, | |
734 &charset, &had_charset, &boundary); | |
735 EXPECT_EQ(tests[i].expected_mime_type, mime_type) << "i=" << i; | |
736 EXPECT_EQ(tests[i].expected_charset, charset) << "i=" << i; | |
737 EXPECT_EQ(tests[i].expected_had_charset, had_charset) << "i=" << i; | |
738 EXPECT_EQ(tests[i].expected_boundary, boundary) << "i=" << i; | |
739 } | |
740 } | |
741 | |
742 TEST(HttpUtilTest, ParseRanges) { | |
743 const struct { | |
744 const char* const headers; | |
745 bool expected_return_value; | |
746 size_t expected_ranges_size; | |
747 const struct { | |
748 int64 expected_first_byte_position; | |
749 int64 expected_last_byte_position; | |
750 int64 expected_suffix_length; | |
751 } expected_ranges[10]; | |
752 } tests[] = { | |
753 { "Range: bytes=0-10", | |
754 true, | |
755 1, | |
756 { {0, 10, -1}, } | |
757 }, | |
758 { "Range: bytes=10-0", | |
759 false, | |
760 0, | |
761 {} | |
762 }, | |
763 { "Range: BytES=0-10", | |
764 true, | |
765 1, | |
766 { {0, 10, -1}, } | |
767 }, | |
768 { "Range: megabytes=0-10", | |
769 false, | |
770 0, | |
771 {} | |
772 }, | |
773 { "Range: bytes0-10", | |
774 false, | |
775 0, | |
776 {} | |
777 }, | |
778 { "Range: bytes=0-0,0-10,10-20,100-200,100-,-200", | |
779 true, | |
780 6, | |
781 { {0, 0, -1}, | |
782 {0, 10, -1}, | |
783 {10, 20, -1}, | |
784 {100, 200, -1}, | |
785 {100, -1, -1}, | |
786 {-1, -1, 200}, | |
787 } | |
788 }, | |
789 { "Range: bytes=0-10\r\n" | |
790 "Range: bytes=0-10,10-20,100-200,100-,-200", | |
791 true, | |
792 1, | |
793 { {0, 10, -1} | |
794 } | |
795 }, | |
796 { "Range: bytes=", | |
797 false, | |
798 0, | |
799 {} | |
800 }, | |
801 { "Range: bytes=-", | |
802 false, | |
803 0, | |
804 {} | |
805 }, | |
806 { "Range: bytes=0-10-", | |
807 false, | |
808 0, | |
809 {} | |
810 }, | |
811 { "Range: bytes=-0-10", | |
812 false, | |
813 0, | |
814 {} | |
815 }, | |
816 { "Range: bytes =0-10\r\n", | |
817 true, | |
818 1, | |
819 { {0, 10, -1} | |
820 } | |
821 }, | |
822 { "Range: bytes= 0-10 \r\n", | |
823 true, | |
824 1, | |
825 { {0, 10, -1} | |
826 } | |
827 }, | |
828 { "Range: bytes = 0 - 10 \r\n", | |
829 true, | |
830 1, | |
831 { {0, 10, -1} | |
832 } | |
833 }, | |
834 { "Range: bytes= 0-1 0\r\n", | |
835 false, | |
836 0, | |
837 {} | |
838 }, | |
839 { "Range: bytes= 0- -10\r\n", | |
840 false, | |
841 0, | |
842 {} | |
843 }, | |
844 { "Range: bytes= 0 - 1 , 10 -20, 100- 200 , 100-, -200 \r\n", | |
845 true, | |
846 5, | |
847 { {0, 1, -1}, | |
848 {10, 20, -1}, | |
849 {100, 200, -1}, | |
850 {100, -1, -1}, | |
851 {-1, -1, 200}, | |
852 } | |
853 }, | |
854 }; | |
855 | |
856 for (size_t i = 0; i < arraysize(tests); ++i) { | |
857 std::vector<net::HttpByteRange> ranges; | |
858 bool return_value = HttpUtil::ParseRanges(std::string(tests[i].headers), | |
859 &ranges); | |
860 EXPECT_EQ(tests[i].expected_return_value, return_value); | |
861 if (return_value) { | |
862 EXPECT_EQ(tests[i].expected_ranges_size, ranges.size()); | |
863 for (size_t j = 0; j < ranges.size(); ++j) { | |
864 EXPECT_EQ(tests[i].expected_ranges[j].expected_first_byte_position, | |
865 ranges[j].first_byte_position()); | |
866 EXPECT_EQ(tests[i].expected_ranges[j].expected_last_byte_position, | |
867 ranges[j].last_byte_position()); | |
868 EXPECT_EQ(tests[i].expected_ranges[j].expected_suffix_length, | |
869 ranges[j].suffix_length()); | |
870 } | |
871 } | |
872 } | |
873 } | |
874 | |
875 TEST(HttpUtilTest, ParseRetryAfterHeader) { | |
876 base::Time::Exploded now_exploded = { 2014, 11, -1, 5, 22, 39, 30, 0 }; | |
877 base::Time now = base::Time::FromUTCExploded(now_exploded); | |
878 | |
879 base::Time::Exploded later_exploded = { 2015, 1, -1, 1, 12, 34, 56, 0 }; | |
880 base::Time later = base::Time::FromUTCExploded(later_exploded); | |
881 | |
882 const struct { | |
883 const char* retry_after_string; | |
884 bool expected_return_value; | |
885 base::TimeDelta expected_retry_after; | |
886 } tests[] = { | |
887 { "", false, base::TimeDelta() }, | |
888 { "-3", false, base::TimeDelta() }, | |
889 { "-2", false, base::TimeDelta() }, | |
890 { "-1", false, base::TimeDelta() }, | |
891 { "0", true, base::TimeDelta::FromSeconds(0) }, | |
892 { "1", true, base::TimeDelta::FromSeconds(1) }, | |
893 { "2", true, base::TimeDelta::FromSeconds(2) }, | |
894 { "3", true, base::TimeDelta::FromSeconds(3) }, | |
895 { "60", true, base::TimeDelta::FromSeconds(60) }, | |
896 { "3600", true, base::TimeDelta::FromSeconds(3600) }, | |
897 { "86400", true, base::TimeDelta::FromSeconds(86400) }, | |
898 { "Thu, 1 Jan 2015 12:34:56 GMT", true, later - now }, | |
899 { "Mon, 1 Jan 1900 12:34:56 GMT", false, base::TimeDelta() } | |
900 }; | |
901 | |
902 for (size_t i = 0; i < arraysize(tests); ++i) { | |
903 base::TimeDelta retry_after; | |
904 bool return_value = HttpUtil::ParseRetryAfterHeader( | |
905 tests[i].retry_after_string, now, &retry_after); | |
906 EXPECT_EQ(tests[i].expected_return_value, return_value) | |
907 << "Test case " << i << ": expected " << tests[i].expected_return_value | |
908 << " but got " << return_value << "."; | |
909 if (tests[i].expected_return_value && return_value) { | |
910 EXPECT_EQ(tests[i].expected_retry_after, retry_after) | |
911 << "Test case " << i << ": expected " | |
912 << tests[i].expected_retry_after.InSeconds() << "s but got " | |
913 << retry_after.InSeconds() << "s."; | |
914 } | |
915 } | |
916 } | |
917 | |
918 namespace { | |
919 void CheckCurrentNameValuePair(HttpUtil::NameValuePairsIterator* parser, | |
920 bool expect_valid, | |
921 std::string expected_name, | |
922 std::string expected_value) { | |
923 ASSERT_EQ(expect_valid, parser->valid()); | |
924 if (!expect_valid) { | |
925 return; | |
926 } | |
927 | |
928 // Let's make sure that these never change (i.e., when a quoted value is | |
929 // unquoted, it should be cached on the first calls and not regenerated | |
930 // later). | |
931 std::string::const_iterator first_value_begin = parser->value_begin(); | |
932 std::string::const_iterator first_value_end = parser->value_end(); | |
933 | |
934 ASSERT_EQ(expected_name, std::string(parser->name_begin(), | |
935 parser->name_end())); | |
936 ASSERT_EQ(expected_name, parser->name()); | |
937 ASSERT_EQ(expected_value, std::string(parser->value_begin(), | |
938 parser->value_end())); | |
939 ASSERT_EQ(expected_value, parser->value()); | |
940 | |
941 // Make sure they didn't/don't change. | |
942 ASSERT_TRUE(first_value_begin == parser->value_begin()); | |
943 ASSERT_TRUE(first_value_end == parser->value_end()); | |
944 } | |
945 | |
946 void CheckNextNameValuePair(HttpUtil::NameValuePairsIterator* parser, | |
947 bool expect_next, | |
948 bool expect_valid, | |
949 std::string expected_name, | |
950 std::string expected_value) { | |
951 ASSERT_EQ(expect_next, parser->GetNext()); | |
952 ASSERT_EQ(expect_valid, parser->valid()); | |
953 if (!expect_next || !expect_valid) { | |
954 return; | |
955 } | |
956 | |
957 CheckCurrentNameValuePair(parser, | |
958 expect_valid, | |
959 expected_name, | |
960 expected_value); | |
961 } | |
962 | |
963 void CheckInvalidNameValuePair(std::string valid_part, | |
964 std::string invalid_part) { | |
965 std::string whole_string = valid_part + invalid_part; | |
966 | |
967 HttpUtil::NameValuePairsIterator valid_parser(valid_part.begin(), | |
968 valid_part.end(), | |
969 ';'); | |
970 HttpUtil::NameValuePairsIterator invalid_parser(whole_string.begin(), | |
971 whole_string.end(), | |
972 ';'); | |
973 | |
974 ASSERT_TRUE(valid_parser.valid()); | |
975 ASSERT_TRUE(invalid_parser.valid()); | |
976 | |
977 // Both parsers should return all the same values until "valid_parser" is | |
978 // exhausted. | |
979 while (valid_parser.GetNext()) { | |
980 ASSERT_TRUE(invalid_parser.GetNext()); | |
981 ASSERT_TRUE(valid_parser.valid()); | |
982 ASSERT_TRUE(invalid_parser.valid()); | |
983 ASSERT_EQ(valid_parser.name(), invalid_parser.name()); | |
984 ASSERT_EQ(valid_parser.value(), invalid_parser.value()); | |
985 } | |
986 | |
987 // valid_parser is exhausted and remains 'valid' | |
988 ASSERT_TRUE(valid_parser.valid()); | |
989 | |
990 // invalid_parser's corresponding call to GetNext also returns false... | |
991 ASSERT_FALSE(invalid_parser.GetNext()); | |
992 // ...but the parser is in an invalid state. | |
993 ASSERT_FALSE(invalid_parser.valid()); | |
994 } | |
995 | |
996 } // anonymous namespace | |
997 | |
998 TEST(HttpUtilTest, NameValuePairsIteratorCopyAndAssign) { | |
999 std::string data = "alpha='\\'a\\''; beta=\" b \"; cappa='c;'; delta=\"d\""; | |
1000 HttpUtil::NameValuePairsIterator parser_a(data.begin(), data.end(), ';'); | |
1001 | |
1002 EXPECT_TRUE(parser_a.valid()); | |
1003 ASSERT_NO_FATAL_FAILURE( | |
1004 CheckNextNameValuePair(&parser_a, true, true, "alpha", "'a'")); | |
1005 | |
1006 HttpUtil::NameValuePairsIterator parser_b(parser_a); | |
1007 // a and b now point to same location | |
1008 ASSERT_NO_FATAL_FAILURE( | |
1009 CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'")); | |
1010 ASSERT_NO_FATAL_FAILURE( | |
1011 CheckCurrentNameValuePair(&parser_a, true, "alpha", "'a'")); | |
1012 | |
1013 // advance a, no effect on b | |
1014 ASSERT_NO_FATAL_FAILURE( | |
1015 CheckNextNameValuePair(&parser_a, true, true, "beta", " b ")); | |
1016 ASSERT_NO_FATAL_FAILURE( | |
1017 CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'")); | |
1018 | |
1019 // assign b the current state of a, no effect on a | |
1020 parser_b = parser_a; | |
1021 ASSERT_NO_FATAL_FAILURE( | |
1022 CheckCurrentNameValuePair(&parser_b, true, "beta", " b ")); | |
1023 ASSERT_NO_FATAL_FAILURE( | |
1024 CheckCurrentNameValuePair(&parser_a, true, "beta", " b ")); | |
1025 | |
1026 // advance b, no effect on a | |
1027 ASSERT_NO_FATAL_FAILURE( | |
1028 CheckNextNameValuePair(&parser_b, true, true, "cappa", "c;")); | |
1029 ASSERT_NO_FATAL_FAILURE( | |
1030 CheckCurrentNameValuePair(&parser_a, true, "beta", " b ")); | |
1031 } | |
1032 | |
1033 TEST(HttpUtilTest, NameValuePairsIteratorEmptyInput) { | |
1034 std::string data; | |
1035 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';'); | |
1036 | |
1037 EXPECT_TRUE(parser.valid()); | |
1038 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair( | |
1039 &parser, false, true, std::string(), std::string())); | |
1040 } | |
1041 | |
1042 TEST(HttpUtilTest, NameValuePairsIterator) { | |
1043 std::string data = "alpha=1; beta= 2 ;cappa =' 3; ';" | |
1044 "delta= \" \\\"4\\\" \"; e= \" '5'\"; e=6;" | |
1045 "f='\\'\\h\\e\\l\\l\\o\\ \\w\\o\\r\\l\\d\\'';" | |
1046 "g=''; h='hello'"; | |
1047 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';'); | |
1048 EXPECT_TRUE(parser.valid()); | |
1049 | |
1050 ASSERT_NO_FATAL_FAILURE( | |
1051 CheckNextNameValuePair(&parser, true, true, "alpha", "1")); | |
1052 ASSERT_NO_FATAL_FAILURE( | |
1053 CheckNextNameValuePair(&parser, true, true, "beta", "2")); | |
1054 ASSERT_NO_FATAL_FAILURE( | |
1055 CheckNextNameValuePair(&parser, true, true, "cappa", " 3; ")); | |
1056 ASSERT_NO_FATAL_FAILURE( | |
1057 CheckNextNameValuePair(&parser, true, true, "delta", " \"4\" ")); | |
1058 ASSERT_NO_FATAL_FAILURE( | |
1059 CheckNextNameValuePair(&parser, true, true, "e", " '5'")); | |
1060 ASSERT_NO_FATAL_FAILURE( | |
1061 CheckNextNameValuePair(&parser, true, true, "e", "6")); | |
1062 ASSERT_NO_FATAL_FAILURE( | |
1063 CheckNextNameValuePair(&parser, true, true, "f", "'hello world'")); | |
1064 ASSERT_NO_FATAL_FAILURE( | |
1065 CheckNextNameValuePair(&parser, true, true, "g", std::string())); | |
1066 ASSERT_NO_FATAL_FAILURE( | |
1067 CheckNextNameValuePair(&parser, true, true, "h", "hello")); | |
1068 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair( | |
1069 &parser, false, true, std::string(), std::string())); | |
1070 } | |
1071 | |
1072 TEST(HttpUtilTest, NameValuePairsIteratorIllegalInputs) { | |
1073 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; beta")); | |
1074 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair(std::string(), "beta")); | |
1075 | |
1076 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; 'beta'=2")); | |
1077 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair(std::string(), "'beta'=2")); | |
1078 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";beta=")); | |
1079 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", | |
1080 ";beta=;cappa=2")); | |
1081 | |
1082 // According to the spec this is an error, but it doesn't seem appropriate to | |
1083 // change our behaviour to be less permissive at this time. | |
1084 // See NameValuePairsIteratorExtraSeparators test | |
1085 // ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";; beta=2")); | |
1086 } | |
1087 | |
1088 // If we are going to support extra separators against the spec, let's just make | |
1089 // sure they work rationally. | |
1090 TEST(HttpUtilTest, NameValuePairsIteratorExtraSeparators) { | |
1091 std::string data = " ; ;;alpha=1; ;; ; beta= 2;cappa=3;;; ; "; | |
1092 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';'); | |
1093 EXPECT_TRUE(parser.valid()); | |
1094 | |
1095 ASSERT_NO_FATAL_FAILURE( | |
1096 CheckNextNameValuePair(&parser, true, true, "alpha", "1")); | |
1097 ASSERT_NO_FATAL_FAILURE( | |
1098 CheckNextNameValuePair(&parser, true, true, "beta", "2")); | |
1099 ASSERT_NO_FATAL_FAILURE( | |
1100 CheckNextNameValuePair(&parser, true, true, "cappa", "3")); | |
1101 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair( | |
1102 &parser, false, true, std::string(), std::string())); | |
1103 } | |
1104 | |
1105 // See comments on the implementation of NameValuePairsIterator::GetNext | |
1106 // regarding this derogation from the spec. | |
1107 TEST(HttpUtilTest, NameValuePairsIteratorMissingEndQuote) { | |
1108 std::string data = "name='value"; | |
1109 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';'); | |
1110 EXPECT_TRUE(parser.valid()); | |
1111 | |
1112 ASSERT_NO_FATAL_FAILURE( | |
1113 CheckNextNameValuePair(&parser, true, true, "name", "value")); | |
1114 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair( | |
1115 &parser, false, true, std::string(), std::string())); | |
1116 } | |
OLD | NEW |