1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "net/http/http_util.h"
- #include <algorithm>
- #include <limits>
- #include "base/strings/string_util.h"
- #include "base/time/time.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace net {
- TEST(HttpUtilTest, IsSafeHeader) {
- static const char* const unsafe_headers[] = {
- "sec-",
- "sEc-",
- "sec-foo",
- "sEc-FoO",
- "proxy-",
- "pRoXy-",
- "proxy-foo",
- "pRoXy-FoO",
- "accept-charset",
- "accept-encoding",
- "access-control-request-headers",
- "access-control-request-method",
- "connection",
- "content-length",
- "cookie",
- "cookie2",
- "date",
- "dnt",
- "expect",
- "host",
- "keep-alive",
- "origin",
- "referer",
- "set-cookie",
- "te",
- "trailer",
- "transfer-encoding",
- "upgrade",
- "user-agent",
- "via",
- };
- for (const auto* unsafe_header : unsafe_headers) {
- EXPECT_FALSE(HttpUtil::IsSafeHeader(unsafe_header)) << unsafe_header;
- EXPECT_FALSE(HttpUtil::IsSafeHeader(base::ToUpperASCII(unsafe_header)))
- << unsafe_header;
- }
- static const char* const safe_headers[] = {
- "foo",
- "x-",
- "x-foo",
- "content-disposition",
- "update",
- "accept-charseta",
- "accept_charset",
- "accept-encodinga",
- "accept_encoding",
- "access-control-request-headersa",
- "access-control-request-header",
- "access_control_request_header",
- "access-control-request-methoda",
- "access_control_request_method",
- "connectiona",
- "content-lengtha",
- "content_length",
- "content-transfer-encoding",
- "cookiea",
- "cookie2a",
- "cookie3",
- "content-transfer-encodinga",
- "content_transfer_encoding",
- "datea",
- "expecta",
- "hosta",
- "keep-alivea",
- "keep_alive",
- "origina",
- "referera",
- "referrer",
- "tea",
- "trailera",
- "transfer-encodinga",
- "transfer_encoding",
- "upgradea",
- "user-agenta",
- "user_agent",
- "viaa",
- };
- for (const auto* safe_header : safe_headers) {
- EXPECT_TRUE(HttpUtil::IsSafeHeader(safe_header)) << safe_header;
- EXPECT_TRUE(HttpUtil::IsSafeHeader(base::ToUpperASCII(safe_header)))
- << safe_header;
- }
- }
- TEST(HttpUtilTest, HeadersIterator) {
- std::string headers = "foo: 1\t\r\nbar: hello world\r\nbaz: 3 \r\n";
- HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
- ASSERT_TRUE(it.GetNext());
- EXPECT_EQ(std::string("foo"), it.name());
- EXPECT_EQ(std::string("1"), it.values());
- ASSERT_TRUE(it.GetNext());
- EXPECT_EQ(std::string("bar"), it.name());
- EXPECT_EQ(std::string("hello world"), it.values());
- ASSERT_TRUE(it.GetNext());
- EXPECT_EQ(std::string("baz"), it.name());
- EXPECT_EQ(std::string("3"), it.values());
- EXPECT_FALSE(it.GetNext());
- }
- TEST(HttpUtilTest, HeadersIterator_MalformedLine) {
- std::string headers = "foo: 1\n: 2\n3\nbar: 4";
- HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n");
- ASSERT_TRUE(it.GetNext());
- EXPECT_EQ(std::string("foo"), it.name());
- EXPECT_EQ(std::string("1"), it.values());
- ASSERT_TRUE(it.GetNext());
- EXPECT_EQ(std::string("bar"), it.name());
- EXPECT_EQ(std::string("4"), it.values());
- EXPECT_FALSE(it.GetNext());
- }
- TEST(HttpUtilTest, HeadersIterator_MalformedName) {
- std::string headers = "[ignore me] /: 3\r\n";
- HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
- EXPECT_FALSE(it.GetNext());
- }
- TEST(HttpUtilTest, HeadersIterator_MalformedNameFollowedByValidLine) {
- std::string headers = "[ignore me] /: 3\r\nbar: 4\n";
- HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
- ASSERT_TRUE(it.GetNext());
- EXPECT_EQ(std::string("bar"), it.name());
- EXPECT_EQ(std::string("4"), it.values());
- EXPECT_FALSE(it.GetNext());
- }
- TEST(HttpUtilTest, HeadersIterator_AdvanceTo) {
- std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4";
- HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
- EXPECT_TRUE(it.AdvanceTo("foo"));
- EXPECT_EQ("foo", it.name());
- EXPECT_TRUE(it.AdvanceTo("bar"));
- EXPECT_EQ("bar", it.name());
- EXPECT_FALSE(it.AdvanceTo("blat"));
- EXPECT_FALSE(it.GetNext()); // should be at end of headers
- }
- TEST(HttpUtilTest, HeadersIterator_Reset) {
- std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4";
- HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
- // Search past "foo".
- EXPECT_TRUE(it.AdvanceTo("bar"));
- // Now try advancing to "foo". This time it should fail since the iterator
- // position is past it.
- EXPECT_FALSE(it.AdvanceTo("foo"));
- it.Reset();
- // Now that we reset the iterator position, we should find 'foo'
- EXPECT_TRUE(it.AdvanceTo("foo"));
- }
- TEST(HttpUtilTest, ValuesIterator) {
- std::string values = " must-revalidate, no-cache=\"foo, bar\"\t, private ";
- HttpUtil::ValuesIterator it(values.begin(), values.end(), ',',
- true /* ignore_empty_values */);
- ASSERT_TRUE(it.GetNext());
- EXPECT_EQ(std::string("must-revalidate"), it.value());
- ASSERT_TRUE(it.GetNext());
- EXPECT_EQ(std::string("no-cache=\"foo, bar\""), it.value());
- ASSERT_TRUE(it.GetNext());
- EXPECT_EQ(std::string("private"), it.value());
- EXPECT_FALSE(it.GetNext());
- }
- TEST(HttpUtilTest, ValuesIterator_EmptyValues) {
- std::string values = ", foopy , \t ,,,";
- HttpUtil::ValuesIterator it(values.begin(), values.end(), ',',
- true /* ignore_empty_values */);
- ASSERT_TRUE(it.GetNext());
- EXPECT_EQ(std::string("foopy"), it.value());
- EXPECT_FALSE(it.GetNext());
- HttpUtil::ValuesIterator it_with_empty_values(
- values.begin(), values.end(), ',', false /* ignore_empty_values */);
- ASSERT_TRUE(it_with_empty_values.GetNext());
- EXPECT_EQ(std::string(""), it_with_empty_values.value());
- ASSERT_TRUE(it_with_empty_values.GetNext());
- EXPECT_EQ(std::string("foopy"), it_with_empty_values.value());
- ASSERT_TRUE(it_with_empty_values.GetNext());
- EXPECT_EQ(std::string(""), it_with_empty_values.value());
- ASSERT_TRUE(it_with_empty_values.GetNext());
- EXPECT_EQ(std::string(""), it_with_empty_values.value());
- ASSERT_TRUE(it_with_empty_values.GetNext());
- EXPECT_EQ(std::string(""), it_with_empty_values.value());
- ASSERT_TRUE(it_with_empty_values.GetNext());
- EXPECT_EQ(std::string(""), it_with_empty_values.value());
- EXPECT_FALSE(it_with_empty_values.GetNext());
- }
- TEST(HttpUtilTest, ValuesIterator_Blanks) {
- std::string values = " \t ";
- HttpUtil::ValuesIterator it(values.begin(), values.end(), ',',
- true /* ignore_empty_values */);
- EXPECT_FALSE(it.GetNext());
- HttpUtil::ValuesIterator it_with_empty_values(
- values.begin(), values.end(), ',', false /* ignore_empty_values */);
- ASSERT_TRUE(it_with_empty_values.GetNext());
- EXPECT_EQ(std::string(""), it_with_empty_values.value());
- EXPECT_FALSE(it_with_empty_values.GetNext());
- }
- TEST(HttpUtilTest, Unquote) {
- // Replace <backslash> " with ".
- EXPECT_STREQ("xyz\"abc", HttpUtil::Unquote("\"xyz\\\"abc\"").c_str());
- // Replace <backslash> <backslash> with <backslash>
- EXPECT_STREQ("xyz\\abc", HttpUtil::Unquote("\"xyz\\\\abc\"").c_str());
- EXPECT_STREQ("xyz\\\\\\abc",
- HttpUtil::Unquote("\"xyz\\\\\\\\\\\\abc\"").c_str());
- // Replace <backslash> X with X
- EXPECT_STREQ("xyzXabc", HttpUtil::Unquote("\"xyz\\Xabc\"").c_str());
- // Act as identity function on unquoted inputs.
- EXPECT_STREQ("X", HttpUtil::Unquote("X").c_str());
- EXPECT_STREQ("\"", HttpUtil::Unquote("\"").c_str());
- // Allow quotes in the middle of the input.
- EXPECT_STREQ("foo\"bar", HttpUtil::Unquote("\"foo\"bar\"").c_str());
- // Allow the final quote to be escaped.
- EXPECT_STREQ("foo", HttpUtil::Unquote("\"foo\\\"").c_str());
- }
- TEST(HttpUtilTest, StrictUnquote) {
- std::string out;
- // Replace <backslash> " with ".
- EXPECT_TRUE(HttpUtil::StrictUnquote("\"xyz\\\"abc\"", &out));
- EXPECT_STREQ("xyz\"abc", out.c_str());
- // Replace <backslash> <backslash> with <backslash>.
- EXPECT_TRUE(HttpUtil::StrictUnquote("\"xyz\\\\abc\"", &out));
- EXPECT_STREQ("xyz\\abc", out.c_str());
- EXPECT_TRUE(HttpUtil::StrictUnquote("\"xyz\\\\\\\\\\\\abc\"", &out));
- EXPECT_STREQ("xyz\\\\\\abc", out.c_str());
- // Replace <backslash> X with X.
- EXPECT_TRUE(HttpUtil::StrictUnquote("\"xyz\\Xabc\"", &out));
- EXPECT_STREQ("xyzXabc", out.c_str());
- // Empty quoted string.
- EXPECT_TRUE(HttpUtil::StrictUnquote("\"\"", &out));
- EXPECT_STREQ("", out.c_str());
- // Return false on unquoted inputs.
- EXPECT_FALSE(HttpUtil::StrictUnquote("X", &out));
- EXPECT_FALSE(HttpUtil::StrictUnquote("", &out));
- // Return false on mismatched quotes.
- EXPECT_FALSE(HttpUtil::StrictUnquote("\"", &out));
- EXPECT_FALSE(HttpUtil::StrictUnquote("\"xyz", &out));
- EXPECT_FALSE(HttpUtil::StrictUnquote("\"abc'", &out));
- // Return false on escaped terminal quote.
- EXPECT_FALSE(HttpUtil::StrictUnquote("\"abc\\\"", &out));
- EXPECT_FALSE(HttpUtil::StrictUnquote("\"\\\"", &out));
- // Allow escaped backslash before terminal quote.
- EXPECT_TRUE(HttpUtil::StrictUnquote("\"\\\\\"", &out));
- EXPECT_STREQ("\\", out.c_str());
- // Don't allow single quotes to act as quote marks.
- EXPECT_FALSE(HttpUtil::StrictUnquote("'x\"'", &out));
- EXPECT_TRUE(HttpUtil::StrictUnquote("\"x'\"", &out));
- EXPECT_STREQ("x'", out.c_str());
- EXPECT_FALSE(HttpUtil::StrictUnquote("''", &out));
- }
- TEST(HttpUtilTest, Quote) {
- EXPECT_STREQ("\"xyz\\\"abc\"", HttpUtil::Quote("xyz\"abc").c_str());
- // Replace <backslash> <backslash> with <backslash>
- EXPECT_STREQ("\"xyz\\\\abc\"", HttpUtil::Quote("xyz\\abc").c_str());
- // Replace <backslash> X with X
- EXPECT_STREQ("\"xyzXabc\"", HttpUtil::Quote("xyzXabc").c_str());
- }
- TEST(HttpUtilTest, LocateEndOfHeaders) {
- struct {
- const char* const input;
- size_t expected_result;
- } tests[] = {
- {"\r\n", std::string::npos},
- {"\n", std::string::npos},
- {"\r", std::string::npos},
- {"foo", std::string::npos},
- {"\r\n\r\n", 4},
- {"foo\r\nbar\r\n\r\n", 12},
- {"foo\nbar\n\n", 9},
- {"foo\r\nbar\r\n\r\njunk", 12},
- {"foo\nbar\n\njunk", 9},
- {"foo\nbar\n\r\njunk", 10},
- {"foo\nbar\r\n\njunk", 10},
- };
- for (const auto& test : tests) {
- size_t input_len = strlen(test.input);
- size_t eoh = HttpUtil::LocateEndOfHeaders(test.input, input_len);
- EXPECT_EQ(test.expected_result, eoh);
- }
- }
- TEST(HttpUtilTest, LocateEndOfAdditionalHeaders) {
- struct {
- const char* const input;
- size_t expected_result;
- } tests[] = {
- {"\r\n", 2},
- {"\n", 1},
- {"\r", std::string::npos},
- {"foo", std::string::npos},
- {"\r\n\r\n", 2},
- {"foo\r\nbar\r\n\r\n", 12},
- {"foo\nbar\n\n", 9},
- {"foo\r\nbar\r\n\r\njunk", 12},
- {"foo\nbar\n\njunk", 9},
- {"foo\nbar\n\r\njunk", 10},
- {"foo\nbar\r\n\njunk", 10},
- };
- for (const auto& test : tests) {
- size_t input_len = strlen(test.input);
- size_t eoh = HttpUtil::LocateEndOfAdditionalHeaders(test.input, input_len);
- EXPECT_EQ(test.expected_result, eoh);
- }
- }
- TEST(HttpUtilTest, AssembleRawHeaders) {
- // clang-format off
- struct {
- const char* const input; // with '|' representing '\0'
- const char* const expected_result; // with '\0' changed to '|'
- } tests[] = {
- { "HTTP/1.0 200 OK\r\nFoo: 1\r\nBar: 2\r\n\r\n",
- "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" },
- { "HTTP/1.0 200 OK\nFoo: 1\nBar: 2\n\n",
- "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" },
- // Valid line continuation (single SP).
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- " continuation\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- "Foo: 1 continuation|"
- "Bar: 2||"
- },
- // Valid line continuation (single HT).
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- "\tcontinuation\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- "Foo: 1 continuation|"
- "Bar: 2||"
- },
- // Valid line continuation (multiple SP).
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- " continuation\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- "Foo: 1 continuation|"
- "Bar: 2||"
- },
- // Valid line continuation (multiple HT).
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- "\t\t\tcontinuation\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- "Foo: 1 continuation|"
- "Bar: 2||"
- },
- // Valid line continuation (mixed HT, SP).
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- " \t \t continuation\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- "Foo: 1 continuation|"
- "Bar: 2||"
- },
- // Valid multi-line continuation
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- " continuation1\n"
- "\tcontinuation2\n"
- " continuation3\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- "Foo: 1 continuation1 continuation2 continuation3|"
- "Bar: 2||"
- },
- // Continuation of quoted value.
- // This is different from what Firefox does, since it
- // will preserve the LWS.
- {
- "HTTP/1.0 200 OK\n"
- "Etag: \"34534-d3\n"
- " 134q\"\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- "Etag: \"34534-d3 134q\"|"
- "Bar: 2||"
- },
- // Valid multi-line continuation, full LWS lines
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- " \n"
- "\t\t\t\t\n"
- "\t continuation\n"
- "Bar: 2\n\n",
- // One SP per continued line = 3.
- "HTTP/1.0 200 OK|"
- "Foo: 1 continuation|"
- "Bar: 2||"
- },
- // Valid multi-line continuation, all LWS
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- " \n"
- "\t\t\t\t\n"
- "\t \n"
- "Bar: 2\n\n",
- // One SP per continued line = 3.
- "HTTP/1.0 200 OK|"
- "Foo: 1 |"
- "Bar: 2||"
- },
- // Valid line continuation (No value bytes in first line).
- {
- "HTTP/1.0 200 OK\n"
- "Foo:\n"
- " value\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- "Foo: value|"
- "Bar: 2||"
- },
- // Not a line continuation (can't continue status line).
- {
- "HTTP/1.0 200 OK\n"
- " Foo: 1\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- " Foo: 1|"
- "Bar: 2||"
- },
- // Not a line continuation (can't continue status line).
- {
- "HTTP/1.0\n"
- " 200 OK\n"
- "Foo: 1\n"
- "Bar: 2\n\n",
- "HTTP/1.0|"
- " 200 OK|"
- "Foo: 1|"
- "Bar: 2||"
- },
- // Not a line continuation (can't continue status line).
- {
- "HTTP/1.0 404\n"
- " Not Found\n"
- "Foo: 1\n"
- "Bar: 2\n\n",
- "HTTP/1.0 404|"
- " Not Found|"
- "Foo: 1|"
- "Bar: 2||"
- },
- // Unterminated status line.
- {
- "HTTP/1.0 200 OK",
- "HTTP/1.0 200 OK||"
- },
- // Single terminated, with headers
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- "Bar: 2\n",
- "HTTP/1.0 200 OK|"
- "Foo: 1|"
- "Bar: 2||"
- },
- // Not terminated, with headers
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- "Bar: 2",
- "HTTP/1.0 200 OK|"
- "Foo: 1|"
- "Bar: 2||"
- },
- // Not a line continuation (VT)
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- "\vInvalidContinuation\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- "Foo: 1|"
- "\vInvalidContinuation|"
- "Bar: 2||"
- },
- // Not a line continuation (formfeed)
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- "\fInvalidContinuation\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- "Foo: 1|"
- "\fInvalidContinuation|"
- "Bar: 2||"
- },
- // Not a line continuation -- can't continue header names.
- {
- "HTTP/1.0 200 OK\n"
- "Serv\n"
- " er: Apache\n"
- "\tInvalidContinuation\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- "Serv|"
- " er: Apache|"
- "\tInvalidContinuation|"
- "Bar: 2||"
- },
- // Not a line continuation -- no value to continue.
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- "garbage\n"
- " not-a-continuation\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- "Foo: 1|"
- "garbage|"
- " not-a-continuation|"
- "Bar: 2||",
- },
- // Not a line continuation -- no valid name.
- {
- "HTTP/1.0 200 OK\n"
- ": 1\n"
- " garbage\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- ": 1|"
- " garbage|"
- "Bar: 2||",
- },
- // Not a line continuation -- no valid name (whitespace)
- {
- "HTTP/1.0 200 OK\n"
- " : 1\n"
- " garbage\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|"
- " : 1|"
- " garbage|"
- "Bar: 2||",
- },
- // Embed NULLs in the status line. They should not be understood
- // as line separators.
- {
- "HTTP/1.0 200 OK|Bar2:0|Baz2:1\r\nFoo: 1\r\nBar: 2\r\n\r\n",
- "HTTP/1.0 200 OKBar2:0Baz2:1|Foo: 1|Bar: 2||"
- },
- // Embed NULLs in a header line. They should not be understood as
- // line separators.
- {
- "HTTP/1.0 200 OK\nFoo: 1|Foo2: 3\nBar: 2\n\n",
- "HTTP/1.0 200 OK|Foo: 1Foo2: 3|Bar: 2||"
- },
- // The embedded NUL at the start of the line (before "Blah:") should not be
- // interpreted as LWS (as that would mistake it for a header line
- // continuation).
- {
- "HTTP/1.0 200 OK\n"
- "Foo: 1\n"
- "|Blah: 3\n"
- "Bar: 2\n\n",
- "HTTP/1.0 200 OK|Foo: 1|Blah: 3|Bar: 2||"
- },
- };
- // clang-format on
- for (const auto& test : tests) {
- std::string input = test.input;
- std::replace(input.begin(), input.end(), '|', '\0');
- std::string raw = HttpUtil::AssembleRawHeaders(input);
- std::replace(raw.begin(), raw.end(), '\0', '|');
- EXPECT_EQ(test.expected_result, raw);
- }
- }
- // Test SpecForRequest().
- TEST(HttpUtilTest, RequestUrlSanitize) {
- struct {
- const char* const url;
- const char* const expected_spec;
- } tests[] = {
- { // Check that #hash is removed.
- "http://www.google.com:78/foobar?query=1#hash",
- "http://www.google.com:78/foobar?query=1",
- },
- { // The reference may itself contain # -- strip all of it.
- "http://192.168.0.1?query=1#hash#10#11#13#14",
- "http://192.168.0.1/?query=1",
- },
- { // Strip username/password.
- "http://user:pass@google.com",
- "http://google.com/",
- },
- { // https scheme
- "https://www.google.com:78/foobar?query=1#hash",
- "https://www.google.com:78/foobar?query=1",
- },
- { // WebSocket's ws scheme
- "ws://www.google.com:78/foobar?query=1#hash",
- "ws://www.google.com:78/foobar?query=1",
- },
- { // WebSocket's wss scheme
- "wss://www.google.com:78/foobar?query=1#hash",
- "wss://www.google.com:78/foobar?query=1",
- }
- };
- for (size_t i = 0; i < std::size(tests); ++i) {
- SCOPED_TRACE(i);
- GURL url(GURL(tests[i].url));
- std::string expected_spec(tests[i].expected_spec);
- EXPECT_EQ(expected_spec, HttpUtil::SpecForRequest(url));
- }
- }
- TEST(HttpUtilTest, GenerateAcceptLanguageHeader) {
- std::string header = HttpUtil::GenerateAcceptLanguageHeader("");
- EXPECT_TRUE(header.empty());
- header = HttpUtil::GenerateAcceptLanguageHeader("es");
- EXPECT_EQ(std::string("es"), header);
- header = HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de");
- EXPECT_EQ(std::string("en-US,fr;q=0.9,de;q=0.8"), header);
- header = HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de,ko,zh-CN,ja");
- EXPECT_EQ(
- std::string("en-US,fr;q=0.9,de;q=0.8,ko;q=0.7,zh-CN;q=0.6,ja;q=0.5"),
- header);
- }
- // HttpResponseHeadersTest.GetMimeType also tests ParseContentType.
- TEST(HttpUtilTest, ParseContentType) {
- // clang-format off
- const struct {
- const char* const content_type;
- const char* const expected_mime_type;
- const char* const expected_charset;
- const bool expected_had_charset;
- const char* const expected_boundary;
- } tests[] = {
- { "text/html",
- "text/html",
- "",
- false,
- ""
- },
- { "text/html;",
- "text/html",
- "",
- false,
- ""
- },
- { "text/html; charset=utf-8",
- "text/html",
- "utf-8",
- true,
- ""
- },
- // Parameter name is "charset ", not "charset". See https://crbug.com/772834.
- { "text/html; charset =utf-8",
- "text/html",
- "",
- false,
- ""
- },
- { "text/html; charset= utf-8",
- "text/html",
- "utf-8",
- true,
- ""
- },
- { "text/html; charset=utf-8 ",
- "text/html",
- "utf-8",
- true,
- ""
- },
- { "text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs\"",
- "text/html",
- "",
- false,
- "WebKit-ada-df-dsf-adsfadsfs"
- },
- // Parameter name is "boundary ", not "boundary".
- // See https://crbug.com/772834.
- { "text/html; boundary =\"WebKit-ada-df-dsf-adsfadsfs\"",
- "text/html",
- "",
- false,
- ""
- },
- // Parameter value includes leading space. See https://crbug.com/772834.
- { "text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\"",
- "text/html",
- "",
- false,
- "WebKit-ada-df-dsf-adsfadsfs"
- },
- // Parameter value includes leading space. See https://crbug.com/772834.
- { "text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\" ",
- "text/html",
- "",
- false,
- "WebKit-ada-df-dsf-adsfadsfs"
- },
- { "text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs \"",
- "text/html",
- "",
- false,
- "WebKit-ada-df-dsf-adsfadsfs"
- },
- { "text/html; boundary=WebKit-ada-df-dsf-adsfadsfs",
- "text/html",
- "",
- false,
- "WebKit-ada-df-dsf-adsfadsfs"
- },
- { "text/html; charset",
- "text/html",
- "",
- false,
- ""
- },
- { "text/html; charset=",
- "text/html",
- "",
- false,
- ""
- },
- { "text/html; charset= ",
- "text/html",
- "",
- false,
- ""
- },
- { "text/html; charset= ;",
- "text/html",
- "",
- false,
- ""
- },
- // Empty quoted strings are allowed.
- { "text/html; charset=\"\"",
- "text/html",
- "",
- true,
- ""
- },
- // Leading and trailing whitespace in quotes is trimmed.
- { "text/html; charset=\" \"",
- "text/html",
- "",
- true,
- ""
- },
- { "text/html; charset=\" foo \"",
- "text/html",
- "foo",
- true,
- ""
- },
- // With multiple values, should use the first one.
- { "text/html; charset=foo; charset=utf-8",
- "text/html",
- "foo",
- true,
- ""
- },
- { "text/html; charset; charset=; charset=utf-8",
- "text/html",
- "utf-8",
- true,
- ""
- },
- { "text/html; charset=utf-8; charset=; charset",
- "text/html",
- "utf-8",
- true,
- ""
- },
- { "text/html; boundary=foo; boundary=bar",
- "text/html",
- "",
- false,
- "foo"
- },
- // Stray quotes ignored.
- { "text/html; \"; \"\"; charset=utf-8",
- "text/html",
- "utf-8",
- true,
- ""
- },
- // Non-leading quotes kept as-is.
- { "text/html; charset=u\"tf-8\"",
- "text/html",
- "u\"tf-8\"",
- true,
- ""
- },
- { "text/html; charset=\"utf-8\"",
- "text/html",
- "utf-8",
- true,
- ""
- },
- // No closing quote.
- { "text/html; charset=\"utf-8",
- "text/html",
- "utf-8",
- true,
- ""
- },
- // Check that \ is treated as an escape character.
- { "text/html; charset=\"\\utf\\-\\8\"",
- "text/html",
- "utf-8",
- true,
- ""
- },
- // More interseting escape character test - test escaped backslash, escaped
- // quote, and backslash at end of input in unterminated quoted string.
- { "text/html; charset=\"\\\\\\\"\\",
- "text/html",
- "\\\"\\",
- true,
- ""
- },
- // Check quoted semicolon.
- { "text/html; charset=\";charset=utf-8;\"",
- "text/html",
- ";charset=utf-8;",
- true,
- ""
- },
- // Unclear if this one should just return utf-8 or not.
- { "text/html; charset= \"utf-8\"",
- "text/html",
- "utf-8",
- true,
- ""
- },
- // Regression test for https://crbug.com/772350:
- // Single quotes are not delimiters but must be treated as part of charset.
- { "text/html; charset='utf-8'",
- "text/html",
- "'utf-8'",
- true,
- ""
- },
- // Empty subtype should be accepted.
- { "text/",
- "text/",
- "",
- false,
- ""
- },
- // "*/*" is ignored unless it has params, or is not an exact match.
- { "*/*", "", "", false, "" },
- { "*/*; charset=utf-8", "*/*", "utf-8", true, "" },
- { "*/* ", "*/*", "", false, "" },
- // Regression test for https://crbug.com/1326529
- { "teXT/html", "text/html", "", false, ""},
- // TODO(abarth): Add more interesting test cases.
- };
- // clang-format on
- for (const auto& test : tests) {
- std::string mime_type;
- std::string charset;
- bool had_charset = false;
- std::string boundary;
- HttpUtil::ParseContentType(test.content_type, &mime_type, &charset,
- &had_charset, &boundary);
- EXPECT_EQ(test.expected_mime_type, mime_type)
- << "content_type=" << test.content_type;
- EXPECT_EQ(test.expected_charset, charset)
- << "content_type=" << test.content_type;
- EXPECT_EQ(test.expected_had_charset, had_charset)
- << "content_type=" << test.content_type;
- EXPECT_EQ(test.expected_boundary, boundary)
- << "content_type=" << test.content_type;
- }
- }
- TEST(HttpUtilTest, ParseContentResetCharset) {
- std::string mime_type;
- std::string charset;
- bool had_charset = false;
- std::string boundary;
- // Set mime (capitalization should be ignored), but not charset.
- HttpUtil::ParseContentType("Text/Html", &mime_type, &charset, &had_charset,
- &boundary);
- EXPECT_EQ("text/html", mime_type);
- EXPECT_EQ("", charset);
- EXPECT_FALSE(had_charset);
- // The same mime, add charset.
- HttpUtil::ParseContentType("tExt/hTml;charset=utf-8", &mime_type, &charset,
- &had_charset, &boundary);
- EXPECT_EQ("text/html", mime_type);
- EXPECT_EQ("utf-8", charset);
- EXPECT_TRUE(had_charset);
- // The same mime (different capitalization), but no charset - should not clear
- // charset.
- HttpUtil::ParseContentType("teXt/htMl", &mime_type, &charset, &had_charset,
- &boundary);
- EXPECT_EQ("text/html", mime_type);
- EXPECT_EQ("utf-8", charset);
- EXPECT_TRUE(had_charset);
- // A different mime will clear charset.
- HttpUtil::ParseContentType("texT/plaiN", &mime_type, &charset, &had_charset,
- &boundary);
- EXPECT_EQ("text/plain", mime_type);
- EXPECT_EQ("", charset);
- EXPECT_TRUE(had_charset);
- }
- TEST(HttpUtilTest, ParseContentRangeHeader) {
- const struct {
- const char* const content_range_header_spec;
- bool expected_return_value;
- int64_t expected_first_byte_position;
- int64_t expected_last_byte_position;
- int64_t expected_instance_length;
- } tests[] = {
- {"", false, -1, -1, -1},
- {"megabytes 0-10/50", false, -1, -1, -1},
- {"0-10/50", false, -1, -1, -1},
- {"Bytes 0-50/51", true, 0, 50, 51},
- {"bytes 0-50/51", true, 0, 50, 51},
- {"bytes\t0-50/51", false, -1, -1, -1},
- {" bytes 0-50/51", true, 0, 50, 51},
- {" bytes 0 - 50 \t / \t51", true, 0, 50, 51},
- {"bytes 0\t-\t50\t/\t51\t", true, 0, 50, 51},
- {" \tbytes\t\t\t 0\t-\t50\t/\t51\t", true, 0, 50, 51},
- {"\t bytes \t 0 - 50 / 5 1", false, -1, -1, -1},
- {"\t bytes \t 0 - 5 0 / 51", false, -1, -1, -1},
- {"bytes 50-0/51", false, -1, -1, -1},
- {"bytes * /*", false, -1, -1, -1},
- {"bytes * / * ", false, -1, -1, -1},
- {"bytes 0-50/*", false, -1, -1, -1},
- {"bytes 0-50 / * ", false, -1, -1, -1},
- {"bytes 0-10000000000/10000000001", true, 0, 10000000000ll,
- 10000000001ll},
- {"bytes 0-10000000000/10000000000", false, -1, -1, -1},
- // 64 bit wraparound.
- {"bytes 0 - 9223372036854775807 / 100", false, -1, -1, -1},
- // 64 bit wraparound.
- {"bytes 0 - 100 / -9223372036854775808", false, -1, -1, -1},
- {"bytes */50", false, -1, -1, -1},
- {"bytes 0-50/10", false, -1, -1, -1},
- {"bytes 40-50/45", false, -1, -1, -1},
- {"bytes 0-50/-10", false, -1, -1, -1},
- {"bytes 0-0/1", true, 0, 0, 1},
- {"bytes 0-40000000000000000000/40000000000000000001", false, -1, -1, -1},
- {"bytes 1-/100", false, -1, -1, -1},
- {"bytes -/100", false, -1, -1, -1},
- {"bytes -1/100", false, -1, -1, -1},
- {"bytes 0-1233/*", false, -1, -1, -1},
- {"bytes -123 - -1/100", false, -1, -1, -1},
- };
- for (const auto& test : tests) {
- int64_t first_byte_position, last_byte_position, instance_length;
- EXPECT_EQ(test.expected_return_value,
- HttpUtil::ParseContentRangeHeaderFor206(
- test.content_range_header_spec, &first_byte_position,
- &last_byte_position, &instance_length))
- << test.content_range_header_spec;
- EXPECT_EQ(test.expected_first_byte_position, first_byte_position)
- << test.content_range_header_spec;
- EXPECT_EQ(test.expected_last_byte_position, last_byte_position)
- << test.content_range_header_spec;
- EXPECT_EQ(test.expected_instance_length, instance_length)
- << test.content_range_header_spec;
- }
- }
- TEST(HttpUtilTest, ParseRetryAfterHeader) {
- base::Time::Exploded now_exploded = {2014, 11, 4, 5, 22, 39, 30, 0};
- base::Time now;
- EXPECT_TRUE(base::Time::FromUTCExploded(now_exploded, &now));
- base::Time::Exploded later_exploded = {2015, 1, 5, 1, 12, 34, 56, 0};
- base::Time later;
- EXPECT_TRUE(base::Time::FromUTCExploded(later_exploded, &later));
- const struct {
- const char* retry_after_string;
- bool expected_return_value;
- base::TimeDelta expected_retry_after;
- } tests[] = {{"", false, base::TimeDelta()},
- {"-3", false, base::TimeDelta()},
- {"-2", false, base::TimeDelta()},
- {"-1", false, base::TimeDelta()},
- {"+0", false, base::TimeDelta()},
- {"+1", false, base::TimeDelta()},
- {"0", true, base::Seconds(0)},
- {"1", true, base::Seconds(1)},
- {"2", true, base::Seconds(2)},
- {"3", true, base::Seconds(3)},
- {"60", true, base::Seconds(60)},
- {"3600", true, base::Seconds(3600)},
- {"86400", true, base::Seconds(86400)},
- {"Thu, 1 Jan 2015 12:34:56 GMT", true, later - now},
- {"Mon, 1 Jan 1900 12:34:56 GMT", false, base::TimeDelta()}};
- for (size_t i = 0; i < std::size(tests); ++i) {
- base::TimeDelta retry_after;
- bool return_value = HttpUtil::ParseRetryAfterHeader(
- tests[i].retry_after_string, now, &retry_after);
- EXPECT_EQ(tests[i].expected_return_value, return_value)
- << "Test case " << i << ": expected " << tests[i].expected_return_value
- << " but got " << return_value << ".";
- if (tests[i].expected_return_value && return_value) {
- EXPECT_EQ(tests[i].expected_retry_after, retry_after)
- << "Test case " << i << ": expected "
- << tests[i].expected_retry_after.InSeconds() << "s but got "
- << retry_after.InSeconds() << "s.";
- }
- }
- }
- namespace {
- void CheckCurrentNameValuePair(HttpUtil::NameValuePairsIterator* parser,
- bool expect_valid,
- std::string expected_name,
- std::string expected_value) {
- ASSERT_EQ(expect_valid, parser->valid());
- if (!expect_valid) {
- return;
- }
- // Let's make sure that these never change (i.e., when a quoted value is
- // unquoted, it should be cached on the first calls and not regenerated
- // later).
- std::string::const_iterator first_value_begin = parser->value_begin();
- std::string::const_iterator first_value_end = parser->value_end();
- ASSERT_EQ(expected_name, std::string(parser->name_begin(),
- parser->name_end()));
- ASSERT_EQ(expected_name, parser->name());
- ASSERT_EQ(expected_value, std::string(parser->value_begin(),
- parser->value_end()));
- ASSERT_EQ(expected_value, parser->value());
- // Make sure they didn't/don't change.
- ASSERT_TRUE(first_value_begin == parser->value_begin());
- ASSERT_TRUE(first_value_end == parser->value_end());
- }
- void CheckNextNameValuePair(HttpUtil::NameValuePairsIterator* parser,
- bool expect_next,
- bool expect_valid,
- std::string expected_name,
- std::string expected_value) {
- ASSERT_EQ(expect_next, parser->GetNext());
- ASSERT_EQ(expect_valid, parser->valid());
- if (!expect_next || !expect_valid) {
- return;
- }
- CheckCurrentNameValuePair(parser,
- expect_valid,
- expected_name,
- expected_value);
- }
- void CheckInvalidNameValuePair(std::string valid_part,
- std::string invalid_part) {
- std::string whole_string = valid_part + invalid_part;
- HttpUtil::NameValuePairsIterator valid_parser(valid_part.begin(),
- valid_part.end(),
- ';');
- HttpUtil::NameValuePairsIterator invalid_parser(whole_string.begin(),
- whole_string.end(),
- ';');
- ASSERT_TRUE(valid_parser.valid());
- ASSERT_TRUE(invalid_parser.valid());
- // Both parsers should return all the same values until "valid_parser" is
- // exhausted.
- while (valid_parser.GetNext()) {
- ASSERT_TRUE(invalid_parser.GetNext());
- ASSERT_TRUE(valid_parser.valid());
- ASSERT_TRUE(invalid_parser.valid());
- ASSERT_EQ(valid_parser.name(), invalid_parser.name());
- ASSERT_EQ(valid_parser.value(), invalid_parser.value());
- }
- // valid_parser is exhausted and remains 'valid'
- ASSERT_TRUE(valid_parser.valid());
- // invalid_parser's corresponding call to GetNext also returns false...
- ASSERT_FALSE(invalid_parser.GetNext());
- // ...but the parser is in an invalid state.
- ASSERT_FALSE(invalid_parser.valid());
- }
- } // namespace
- TEST(HttpUtilTest, NameValuePairsIteratorCopyAndAssign) {
- std::string data =
- "alpha=\"\\\"a\\\"\"; beta=\" b \"; cappa=\"c;\"; delta=\"d\"";
- HttpUtil::NameValuePairsIterator parser_a(data.begin(), data.end(), ';');
- EXPECT_TRUE(parser_a.valid());
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser_a, true, true, "alpha", "\"a\""));
- HttpUtil::NameValuePairsIterator parser_b(parser_a);
- // a and b now point to same location
- ASSERT_NO_FATAL_FAILURE(
- CheckCurrentNameValuePair(&parser_b, true, "alpha", "\"a\""));
- ASSERT_NO_FATAL_FAILURE(
- CheckCurrentNameValuePair(&parser_a, true, "alpha", "\"a\""));
- // advance a, no effect on b
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser_a, true, true, "beta", " b "));
- ASSERT_NO_FATAL_FAILURE(
- CheckCurrentNameValuePair(&parser_b, true, "alpha", "\"a\""));
- // assign b the current state of a, no effect on a
- parser_b = parser_a;
- ASSERT_NO_FATAL_FAILURE(
- CheckCurrentNameValuePair(&parser_b, true, "beta", " b "));
- ASSERT_NO_FATAL_FAILURE(
- CheckCurrentNameValuePair(&parser_a, true, "beta", " b "));
- // advance b, no effect on a
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser_b, true, true, "cappa", "c;"));
- ASSERT_NO_FATAL_FAILURE(
- CheckCurrentNameValuePair(&parser_a, true, "beta", " b "));
- }
- TEST(HttpUtilTest, NameValuePairsIteratorEmptyInput) {
- std::string data;
- HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
- EXPECT_TRUE(parser.valid());
- ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
- &parser, false, true, std::string(), std::string()));
- }
- TEST(HttpUtilTest, NameValuePairsIterator) {
- std::string data =
- "alpha=1; beta= 2 ;"
- "cappa =' 3; foo=';"
- "cappa =\" 3; foo=\";"
- "delta= \" \\\"4\\\" \"; e= \" '5'\"; e=6;"
- "f=\"\\\"\\h\\e\\l\\l\\o\\ \\w\\o\\r\\l\\d\\\"\";"
- "g=\"\"; h=\"hello\"";
- HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
- EXPECT_TRUE(parser.valid());
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "beta", "2"));
- // Single quotes shouldn't be treated as quotes.
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "cappa", "' 3"));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "foo", "'"));
- // But double quotes should be, and can contain semi-colons and equal signs.
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "cappa", " 3; foo="));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "delta", " \"4\" "));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "e", " '5'"));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "e", "6"));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "f", "\"hello world\""));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "g", std::string()));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "h", "hello"));
- ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
- &parser, false, true, std::string(), std::string()));
- }
- TEST(HttpUtilTest, NameValuePairsIteratorOptionalValues) {
- std::string data = "alpha=1; beta;cappa ; delta; e ; f=1";
- // Test that the default parser requires values.
- HttpUtil::NameValuePairsIterator default_parser(data.begin(), data.end(),
- ';');
- EXPECT_TRUE(default_parser.valid());
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&default_parser, true, true, "alpha", "1"));
- ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&default_parser, false, false,
- std::string(), std::string()));
- HttpUtil::NameValuePairsIterator values_required_parser(
- data.begin(), data.end(), ';',
- HttpUtil::NameValuePairsIterator::Values::REQUIRED,
- HttpUtil::NameValuePairsIterator::Quotes::NOT_STRICT);
- EXPECT_TRUE(values_required_parser.valid());
- ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&values_required_parser, true,
- true, "alpha", "1"));
- ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
- &values_required_parser, false, false, std::string(), std::string()));
- HttpUtil::NameValuePairsIterator parser(
- data.begin(), data.end(), ';',
- HttpUtil::NameValuePairsIterator::Values::NOT_REQUIRED,
- HttpUtil::NameValuePairsIterator::Quotes::NOT_STRICT);
- EXPECT_TRUE(parser.valid());
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "beta", std::string()));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "cappa", std::string()));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "delta", std::string()));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "e", std::string()));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "f", "1"));
- ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&parser, false, true,
- std::string(), std::string()));
- EXPECT_TRUE(parser.valid());
- }
- TEST(HttpUtilTest, NameValuePairsIteratorIllegalInputs) {
- ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; beta"));
- ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair(std::string(), "beta"));
- ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; \"beta\"=2"));
- ASSERT_NO_FATAL_FAILURE(
- CheckInvalidNameValuePair(std::string(), "\"beta\"=2"));
- ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";beta="));
- ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1",
- ";beta=;cappa=2"));
- // According to the spec this is an error, but it doesn't seem appropriate to
- // change our behaviour to be less permissive at this time.
- // See NameValuePairsIteratorExtraSeparators test
- // ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";; beta=2"));
- }
- // If we are going to support extra separators against the spec, let's just make
- // sure they work rationally.
- TEST(HttpUtilTest, NameValuePairsIteratorExtraSeparators) {
- std::string data = " ; ;;alpha=1; ;; ; beta= 2;cappa=3;;; ; ";
- HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
- EXPECT_TRUE(parser.valid());
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "beta", "2"));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "cappa", "3"));
- ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
- &parser, false, true, std::string(), std::string()));
- }
- // See comments on the implementation of NameValuePairsIterator::GetNext
- // regarding this derogation from the spec.
- TEST(HttpUtilTest, NameValuePairsIteratorMissingEndQuote) {
- std::string data = "name=\"value";
- HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
- EXPECT_TRUE(parser.valid());
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "name", "value"));
- ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
- &parser, false, true, std::string(), std::string()));
- }
- TEST(HttpUtilTest, NameValuePairsIteratorStrictQuotesEscapedEndQuote) {
- std::string data = "foo=bar; name=\"value\\\"";
- HttpUtil::NameValuePairsIterator parser(
- data.begin(), data.end(), ';',
- HttpUtil::NameValuePairsIterator::Values::REQUIRED,
- HttpUtil::NameValuePairsIterator::Quotes::STRICT_QUOTES);
- EXPECT_TRUE(parser.valid());
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "foo", "bar"));
- ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&parser, false, false,
- std::string(), std::string()));
- }
- TEST(HttpUtilTest, NameValuePairsIteratorStrictQuotesQuoteInValue) {
- std::string data = "foo=\"bar\"; name=\"va\"lue\"";
- HttpUtil::NameValuePairsIterator parser(
- data.begin(), data.end(), ';',
- HttpUtil::NameValuePairsIterator::Values::REQUIRED,
- HttpUtil::NameValuePairsIterator::Quotes::STRICT_QUOTES);
- EXPECT_TRUE(parser.valid());
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "foo", "bar"));
- ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&parser, false, false,
- std::string(), std::string()));
- }
- TEST(HttpUtilTest, NameValuePairsIteratorStrictQuotesMissingEndQuote) {
- std::string data = "foo=\"bar\"; name=\"value";
- HttpUtil::NameValuePairsIterator parser(
- data.begin(), data.end(), ';',
- HttpUtil::NameValuePairsIterator::Values::REQUIRED,
- HttpUtil::NameValuePairsIterator::Quotes::STRICT_QUOTES);
- EXPECT_TRUE(parser.valid());
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "foo", "bar"));
- ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&parser, false, false,
- std::string(), std::string()));
- }
- TEST(HttpUtilTest, NameValuePairsIteratorStrictQuotesSingleQuotes) {
- std::string data = "foo=\"bar\"; name='value; ok=it'";
- HttpUtil::NameValuePairsIterator parser(
- data.begin(), data.end(), ';',
- HttpUtil::NameValuePairsIterator::Values::REQUIRED,
- HttpUtil::NameValuePairsIterator::Quotes::STRICT_QUOTES);
- EXPECT_TRUE(parser.valid());
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "foo", "bar"));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "name", "'value"));
- ASSERT_NO_FATAL_FAILURE(
- CheckNextNameValuePair(&parser, true, true, "ok", "it'"));
- }
- TEST(HttpUtilTest, HasValidators) {
- const char* const kMissing = "";
- const char* const kEtagEmpty = "\"\"";
- const char* const kEtagStrong = "\"strong\"";
- const char* const kEtagWeak = "W/\"weak\"";
- const char* const kLastModified = "Tue, 15 Nov 1994 12:45:26 GMT";
- const char* const kLastModifiedInvalid = "invalid";
- const HttpVersion v0_9 = HttpVersion(0, 9);
- EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kMissing, kMissing));
- EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagStrong, kMissing));
- EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagWeak, kMissing));
- EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagEmpty, kMissing));
- EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kMissing, kLastModified));
- EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagStrong, kLastModified));
- EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagWeak, kLastModified));
- EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagEmpty, kLastModified));
- EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kMissing, kLastModifiedInvalid));
- EXPECT_FALSE(
- HttpUtil::HasValidators(v0_9, kEtagStrong, kLastModifiedInvalid));
- EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagWeak, kLastModifiedInvalid));
- EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagEmpty, kLastModifiedInvalid));
- const HttpVersion v1_0 = HttpVersion(1, 0);
- EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kMissing, kMissing));
- EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kEtagStrong, kMissing));
- EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kEtagWeak, kMissing));
- EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kEtagEmpty, kMissing));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_0, kMissing, kLastModified));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_0, kEtagStrong, kLastModified));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_0, kEtagWeak, kLastModified));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_0, kEtagEmpty, kLastModified));
- EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kMissing, kLastModifiedInvalid));
- EXPECT_FALSE(
- HttpUtil::HasValidators(v1_0, kEtagStrong, kLastModifiedInvalid));
- EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kEtagWeak, kLastModifiedInvalid));
- EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kEtagEmpty, kLastModifiedInvalid));
- const HttpVersion v1_1 = HttpVersion(1, 1);
- EXPECT_FALSE(HttpUtil::HasValidators(v1_1, kMissing, kMissing));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagStrong, kMissing));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagWeak, kMissing));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagEmpty, kMissing));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kMissing, kLastModified));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagStrong, kLastModified));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagWeak, kLastModified));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagEmpty, kLastModified));
- EXPECT_FALSE(HttpUtil::HasValidators(v1_1, kMissing, kLastModifiedInvalid));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagStrong, kLastModifiedInvalid));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagWeak, kLastModifiedInvalid));
- EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagEmpty, kLastModifiedInvalid));
- }
- TEST(HttpUtilTest, IsValidHeaderValue) {
- const char* const invalid_values[] = {
- "X-Requested-With: chrome${NUL}Sec-Unsafe: injected",
- "X-Requested-With: chrome\r\nSec-Unsafe: injected",
- "X-Requested-With: chrome\nSec-Unsafe: injected",
- "X-Requested-With: chrome\rSec-Unsafe: injected",
- };
- for (const std::string& value : invalid_values) {
- std::string replaced = value;
- base::ReplaceSubstringsAfterOffset(&replaced, 0, "${NUL}",
- std::string(1, '\0'));
- EXPECT_FALSE(HttpUtil::IsValidHeaderValue(replaced)) << replaced;
- }
- // Check that all characters permitted by RFC7230 3.2.6 are allowed.
- std::string allowed = "\t";
- for (char c = '\x20'; c < '\x7F'; ++c) {
- allowed.append(1, c);
- }
- for (int c = 0x80; c <= 0xFF; ++c) {
- allowed.append(1, static_cast<char>(c));
- }
- EXPECT_TRUE(HttpUtil::IsValidHeaderValue(allowed));
- }
- TEST(HttpUtilTest, IsToken) {
- EXPECT_TRUE(HttpUtil::IsToken("valid"));
- EXPECT_TRUE(HttpUtil::IsToken("!"));
- EXPECT_TRUE(HttpUtil::IsToken("~"));
- EXPECT_FALSE(HttpUtil::IsToken(""));
- EXPECT_FALSE(HttpUtil::IsToken(base::StringPiece()));
- EXPECT_FALSE(HttpUtil::IsToken("hello, world"));
- EXPECT_FALSE(HttpUtil::IsToken(" "));
- EXPECT_FALSE(HttpUtil::IsToken(base::StringPiece("\0", 1)));
- EXPECT_FALSE(HttpUtil::IsToken("\x01"));
- EXPECT_FALSE(HttpUtil::IsToken("\x7F"));
- EXPECT_FALSE(HttpUtil::IsToken("\x80"));
- EXPECT_FALSE(HttpUtil::IsToken("\xff"));
- }
- TEST(HttpUtilTest, IsLWS) {
- EXPECT_FALSE(HttpUtil::IsLWS('\v'));
- EXPECT_FALSE(HttpUtil::IsLWS('\0'));
- EXPECT_FALSE(HttpUtil::IsLWS('1'));
- EXPECT_FALSE(HttpUtil::IsLWS('a'));
- EXPECT_FALSE(HttpUtil::IsLWS('.'));
- EXPECT_FALSE(HttpUtil::IsLWS('\n'));
- EXPECT_FALSE(HttpUtil::IsLWS('\r'));
- EXPECT_TRUE(HttpUtil::IsLWS('\t'));
- EXPECT_TRUE(HttpUtil::IsLWS(' '));
- }
- TEST(HttpUtilTest, IsControlChar) {
- EXPECT_FALSE(HttpUtil::IsControlChar('1'));
- EXPECT_FALSE(HttpUtil::IsControlChar('a'));
- EXPECT_FALSE(HttpUtil::IsControlChar('.'));
- EXPECT_FALSE(HttpUtil::IsControlChar('$'));
- EXPECT_FALSE(HttpUtil::IsControlChar('\x7E'));
- EXPECT_FALSE(HttpUtil::IsControlChar('\x80'));
- EXPECT_FALSE(HttpUtil::IsControlChar('\xFF'));
- EXPECT_TRUE(HttpUtil::IsControlChar('\0'));
- EXPECT_TRUE(HttpUtil::IsControlChar('\v'));
- EXPECT_TRUE(HttpUtil::IsControlChar('\n'));
- EXPECT_TRUE(HttpUtil::IsControlChar('\r'));
- EXPECT_TRUE(HttpUtil::IsControlChar('\t'));
- EXPECT_TRUE(HttpUtil::IsControlChar('\x01'));
- EXPECT_TRUE(HttpUtil::IsControlChar('\x7F'));
- }
- TEST(HttpUtilTest, ParseAcceptEncoding) {
- const struct {
- const char* const value;
- const char* const expected;
- } tests[] = {
- {"", "*"},
- {"identity;q=1, *;q=0", "identity"},
- {"identity", "identity"},
- {"FOO, Bar", "bar|foo|identity"},
- {"foo; q=1", "foo|identity"},
- {"abc, foo; Q=1.0", "abc|foo|identity"},
- {"abc, foo;q= 1.00 , bar", "abc|bar|foo|identity"},
- {"abc, foo; q=1.000, bar", "abc|bar|foo|identity"},
- {"abc, foo ; q = 0 , bar", "abc|bar|identity"},
- {"abc, foo; q=0.0, bar", "abc|bar|identity"},
- {"abc, foo; q=0.00, bar", "abc|bar|identity"},
- {"abc, foo; q=0.000, bar", "abc|bar|identity"},
- {"abc, foo; q=0.001, bar", "abc|bar|foo|identity"},
- {"gzip", "gzip|identity|x-gzip"},
- {"x-gzip", "gzip|identity|x-gzip"},
- {"compress", "compress|identity|x-compress"},
- {"x-compress", "compress|identity|x-compress"},
- {"x-compress", "compress|identity|x-compress"},
- {"foo bar", "INVALID"},
- {"foo;", "INVALID"},
- {"foo;w=1", "INVALID"},
- {"foo;q+1", "INVALID"},
- {"foo;q=2", "INVALID"},
- {"foo;q=1.001", "INVALID"},
- {"foo;q=0.", "INVALID"},
- {"foo,\"bar\"", "INVALID"},
- };
- for (const auto& test : tests) {
- std::string value(test.value);
- std::string reformatted;
- std::set<std::string> allowed_encodings;
- if (!HttpUtil::ParseAcceptEncoding(value, &allowed_encodings)) {
- reformatted = "INVALID";
- } else {
- std::vector<std::string> encodings_list;
- for (auto const& encoding : allowed_encodings)
- encodings_list.push_back(encoding);
- reformatted = base::JoinString(encodings_list, "|");
- }
- EXPECT_STREQ(test.expected, reformatted.c_str())
- << "value=\"" << value << "\"";
- }
- }
- TEST(HttpUtilTest, ParseContentEncoding) {
- const struct {
- const char* const value;
- const char* const expected;
- } tests[] = {
- {"", ""},
- {"identity;q=1, *;q=0", "INVALID"},
- {"identity", "identity"},
- {"FOO, zergli , Bar", "bar|foo|zergli"},
- {"foo, *", "INVALID"},
- {"foo,\"bar\"", "INVALID"},
- };
- for (const auto& test : tests) {
- std::string value(test.value);
- std::string reformatted;
- std::set<std::string> used_encodings;
- if (!HttpUtil::ParseContentEncoding(value, &used_encodings)) {
- reformatted = "INVALID";
- } else {
- std::vector<std::string> encodings_list;
- for (auto const& encoding : used_encodings)
- encodings_list.push_back(encoding);
- reformatted = base::JoinString(encodings_list, "|");
- }
- EXPECT_STREQ(test.expected, reformatted.c_str())
- << "value=\"" << value << "\"";
- }
- }
- // Test the expansion of the Language List.
- TEST(HttpUtilTest, ExpandLanguageList) {
- EXPECT_EQ("", HttpUtil::ExpandLanguageList(""));
- EXPECT_EQ("en-US,en", HttpUtil::ExpandLanguageList("en-US"));
- EXPECT_EQ("fr", HttpUtil::ExpandLanguageList("fr"));
- // The base language is added after all regional codes...
- EXPECT_EQ("en-US,en-CA,en", HttpUtil::ExpandLanguageList("en-US,en-CA"));
- // ... but before other language families.
- EXPECT_EQ("en-US,en-CA,en,fr",
- HttpUtil::ExpandLanguageList("en-US,en-CA,fr"));
- EXPECT_EQ("en-US,en-CA,en,fr,en-AU",
- HttpUtil::ExpandLanguageList("en-US,en-CA,fr,en-AU"));
- EXPECT_EQ("en-US,en-CA,en,fr-CA,fr",
- HttpUtil::ExpandLanguageList("en-US,en-CA,fr-CA"));
- // Add a base language even if it's already in the list.
- EXPECT_EQ("en-US,en,fr-CA,fr,it,es-AR,es,it-IT",
- HttpUtil::ExpandLanguageList("en-US,fr-CA,it,fr,es-AR,it-IT"));
- // Trims a whitespace.
- EXPECT_EQ("en-US,en,fr", HttpUtil::ExpandLanguageList("en-US, fr"));
- // Do not expand the single character subtag 'x' as a language.
- EXPECT_EQ("x-private-agreement-subtags",
- HttpUtil::ExpandLanguageList("x-private-agreement-subtags"));
- // Do not expand the single character subtag 'i' as a language.
- EXPECT_EQ("i-klingon", HttpUtil::ExpandLanguageList("i-klingon"));
- }
- } // namespace net
|