123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379 |
- // 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_response_headers.h"
- #include <stdint.h>
- #include <algorithm>
- #include <iostream>
- #include <limits>
- #include <memory>
- #include <unordered_set>
- #include "base/pickle.h"
- #include "base/time/time.h"
- #include "base/trace_event/trace_event.h"
- #include "base/values.h"
- #include "net/http/http_byte_range.h"
- #include "net/http/http_util.h"
- #include "net/log/net_log_capture_mode.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "third_party/perfetto/include/perfetto/test/traced_value_test_support.h"
- namespace net {
- namespace {
- struct TestData {
- const char* raw_headers;
- const char* expected_headers;
- HttpVersion expected_version;
- int expected_response_code;
- const char* expected_status_text;
- };
- class HttpResponseHeadersTest : public testing::Test {
- };
- // Transform "normal"-looking headers (\n-separated) to the appropriate
- // input format for ParseRawHeaders (\0-separated).
- void HeadersToRaw(std::string* headers) {
- std::replace(headers->begin(), headers->end(), '\n', '\0');
- if (!headers->empty())
- *headers += '\0';
- }
- class HttpResponseHeadersCacheControlTest : public HttpResponseHeadersTest {
- protected:
- // Make tests less verbose.
- typedef base::TimeDelta TimeDelta;
- // Initilise the headers() value with a Cache-Control header set to
- // |cache_control|. |cache_control| is copied and so can safely be a
- // temporary.
- void InitializeHeadersWithCacheControl(const char* cache_control) {
- std::string raw_headers("HTTP/1.1 200 OK\n");
- raw_headers += "Cache-Control: ";
- raw_headers += cache_control;
- raw_headers += "\n";
- HeadersToRaw(&raw_headers);
- headers_ = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
- }
- const scoped_refptr<HttpResponseHeaders>& headers() { return headers_; }
- // Return a pointer to a TimeDelta object. For use when the value doesn't
- // matter.
- TimeDelta* TimeDeltaPointer() { return &delta_; }
- // Get the max-age value. This should only be used in tests where a valid
- // max-age parameter is expected to be present.
- TimeDelta GetMaxAgeValue() {
- DCHECK(headers_.get()) << "Call InitializeHeadersWithCacheControl() first";
- TimeDelta max_age_value;
- EXPECT_TRUE(headers()->GetMaxAgeValue(&max_age_value));
- return max_age_value;
- }
- // Get the stale-while-revalidate value. This should only be used in tests
- // where a valid max-age parameter is expected to be present.
- TimeDelta GetStaleWhileRevalidateValue() {
- DCHECK(headers_.get()) << "Call InitializeHeadersWithCacheControl() first";
- TimeDelta stale_while_revalidate_value;
- EXPECT_TRUE(
- headers()->GetStaleWhileRevalidateValue(&stale_while_revalidate_value));
- return stale_while_revalidate_value;
- }
- private:
- scoped_refptr<HttpResponseHeaders> headers_;
- TimeDelta delta_;
- };
- class CommonHttpResponseHeadersTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<TestData> {
- };
- // Returns a simple text serialization of the given
- // |HttpResponseHeaders|. This is used by tests to verify that an
- // |HttpResponseHeaders| matches an expectation string.
- //
- // * One line per header, written as:
- // HEADER_NAME: HEADER_VALUE\n
- // * The original case of header names is preserved.
- // * Whitespace around head names/values is stripped.
- // * Repeated headers are not aggregated.
- // * Headers are listed in their original order.
- std::string ToSimpleString(const scoped_refptr<HttpResponseHeaders>& parsed) {
- std::string result = parsed->GetStatusLine() + "\n";
- size_t iter = 0;
- std::string name;
- std::string value;
- while (parsed->EnumerateHeaderLines(&iter, &name, &value)) {
- std::string new_line = name + ": " + value + "\n";
- // Verify that |name| and |value| do not contain ':' or '\n' (if they did
- // it would make this serialized format ambiguous).
- if (std::count(new_line.begin(), new_line.end(), '\n') != 1 ||
- std::count(new_line.begin(), new_line.end(), ':') != 1) {
- ADD_FAILURE() << "Unexpected characters in the header name or value: "
- << new_line;
- return result;
- }
- result += new_line;
- }
- return result;
- }
- TEST_P(CommonHttpResponseHeadersTest, TestCommon) {
- const TestData test = GetParam();
- std::string raw_headers(test.raw_headers);
- HeadersToRaw(&raw_headers);
- std::string expected_headers(test.expected_headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
- std::string headers = ToSimpleString(parsed);
- // Transform to readable output format (so it's easier to see diffs).
- std::replace(headers.begin(), headers.end(), ' ', '_');
- std::replace(headers.begin(), headers.end(), '\n', '\\');
- std::replace(expected_headers.begin(), expected_headers.end(), ' ', '_');
- std::replace(expected_headers.begin(), expected_headers.end(), '\n', '\\');
- EXPECT_EQ(expected_headers, headers);
- EXPECT_TRUE(test.expected_version == parsed->GetHttpVersion());
- EXPECT_EQ(test.expected_response_code, parsed->response_code());
- EXPECT_EQ(test.expected_status_text, parsed->GetStatusText());
- }
- TestData response_headers_tests[] = {
- {// Normalize whitespace.
- "HTTP/1.1 202 Accepted \n"
- "Content-TYPE : text/html; charset=utf-8 \n"
- "Set-Cookie: a \n"
- "Set-Cookie: b \n",
- "HTTP/1.1 202 Accepted\n"
- "Content-TYPE: text/html; charset=utf-8\n"
- "Set-Cookie: a\n"
- "Set-Cookie: b\n",
- HttpVersion(1, 1), 202, "Accepted"},
- {// Normalize leading whitespace.
- "HTTP/1.1 202 Accepted \n"
- // Starts with space -- will be skipped as invalid.
- " Content-TYPE : text/html; charset=utf-8 \n"
- "Set-Cookie: a \n"
- "Set-Cookie: b \n",
- "HTTP/1.1 202 Accepted\n"
- "Set-Cookie: a\n"
- "Set-Cookie: b\n",
- HttpVersion(1, 1), 202, "Accepted"},
- {// Keep whitespace within status text.
- "HTTP/1.0 404 Not found \n",
- "HTTP/1.0 404 Not found\n",
- HttpVersion(1, 0), 404, "Not found"},
- {// Normalize blank headers.
- "HTTP/1.1 200 OK\n"
- "Header1 : \n"
- "Header2: \n"
- "Header3:\n"
- "Header4\n"
- "Header5 :\n",
- "HTTP/1.1 200 OK\n"
- "Header1: \n"
- "Header2: \n"
- "Header3: \n"
- "Header5: \n",
- HttpVersion(1, 1), 200, "OK"},
- {// Don't believe the http/0.9 version if there are headers!
- "hTtP/0.9 201\n"
- "Content-TYPE: text/html; charset=utf-8\n",
- "HTTP/1.0 201\n"
- "Content-TYPE: text/html; charset=utf-8\n",
- HttpVersion(1, 0), 201, ""},
- {// Accept the HTTP/0.9 version number if there are no headers.
- // This is how HTTP/0.9 responses get constructed from
- // HttpNetworkTransaction.
- "hTtP/0.9 200 OK\n",
- "HTTP/0.9 200 OK\n",
- HttpVersion(0, 9), 200, "OK"},
- {// Do not add missing status text.
- "HTTP/1.1 201\n"
- "Content-TYPE: text/html; charset=utf-8\n",
- "HTTP/1.1 201\n"
- "Content-TYPE: text/html; charset=utf-8\n",
- HttpVersion(1, 1), 201, ""},
- {// Normalize bad status line.
- "SCREWED_UP_STATUS_LINE\n"
- "Content-TYPE: text/html; charset=utf-8\n",
- "HTTP/1.0 200 OK\n"
- "Content-TYPE: text/html; charset=utf-8\n",
- HttpVersion(1, 0), 200, "OK"},
- {// Normalize bad status line.
- "Foo bar.",
- "HTTP/1.0 200\n",
- HttpVersion(1, 0), 200, ""},
- {// Normalize invalid status code.
- "HTTP/1.1 -1 Unknown\n",
- "HTTP/1.1 200\n",
- HttpVersion(1, 1), 200, ""},
- {// Normalize empty header.
- "",
- "HTTP/1.0 200 OK\n",
- HttpVersion(1, 0), 200, "OK"},
- {// Normalize headers that start with a colon.
- "HTTP/1.1 202 Accepted \n"
- "foo: bar\n"
- ": a \n"
- " : b\n"
- "baz: blat \n",
- "HTTP/1.1 202 Accepted\n"
- "foo: bar\n"
- "baz: blat\n",
- HttpVersion(1, 1), 202, "Accepted"},
- {// Normalize headers that end with a colon.
- "HTTP/1.1 202 Accepted \n"
- "foo: \n"
- "bar:\n"
- "baz: blat \n"
- "zip:\n",
- "HTTP/1.1 202 Accepted\n"
- "foo: \n"
- "bar: \n"
- "baz: blat\n"
- "zip: \n",
- HttpVersion(1, 1), 202, "Accepted"},
- {// Normalize whitespace headers.
- "\n \n",
- "HTTP/1.0 200 OK\n",
- HttpVersion(1, 0), 200, "OK"},
- {// Has multiple Set-Cookie headers.
- "HTTP/1.1 200 OK\n"
- "Set-Cookie: x=1\n"
- "Set-Cookie: y=2\n",
- "HTTP/1.1 200 OK\n"
- "Set-Cookie: x=1\n"
- "Set-Cookie: y=2\n",
- HttpVersion(1, 1), 200, "OK"},
- {// Has multiple cache-control headers.
- "HTTP/1.1 200 OK\n"
- "Cache-control: private\n"
- "cache-Control: no-store\n",
- "HTTP/1.1 200 OK\n"
- "Cache-control: private\n"
- "cache-Control: no-store\n",
- HttpVersion(1, 1), 200, "OK"},
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- CommonHttpResponseHeadersTest,
- testing::ValuesIn(response_headers_tests));
- struct PersistData {
- HttpResponseHeaders::PersistOptions options;
- const char* raw_headers;
- const char* expected_headers;
- };
- class PersistenceTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<PersistData> {
- };
- TEST_P(PersistenceTest, Persist) {
- const PersistData test = GetParam();
- std::string headers = test.raw_headers;
- HeadersToRaw(&headers);
- auto parsed1 = base::MakeRefCounted<HttpResponseHeaders>(headers);
- base::Pickle pickle;
- parsed1->Persist(&pickle, test.options);
- base::PickleIterator iter(pickle);
- auto parsed2 = base::MakeRefCounted<HttpResponseHeaders>(&iter);
- EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed2));
- }
- const struct PersistData persistence_tests[] = {
- {HttpResponseHeaders::PERSIST_ALL,
- "HTTP/1.1 200 OK\n"
- "Cache-control:private\n"
- "cache-Control:no-store\n",
- "HTTP/1.1 200 OK\n"
- "Cache-control: private\n"
- "cache-Control: no-store\n"},
- {HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "server: blah\n",
- "HTTP/1.1 200 OK\n"
- "server: blah\n"},
- {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
- HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
- "HTTP/1.1 200 OK\n"
- "fOo: 1\n"
- "Foo: 2\n"
- "Transfer-Encoding: chunked\n"
- "CoNnection: keep-alive\n"
- "cache-control: private, no-cache=\"foo\"\n",
- "HTTP/1.1 200 OK\n"
- "cache-control: private, no-cache=\"foo\"\n"},
- {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Cache-Control: private,no-cache=\"foo, bar\"\n"
- "bar",
- "HTTP/1.1 200 OK\n"
- "Cache-Control: private,no-cache=\"foo, bar\"\n"},
- // Ignore bogus no-cache value.
- {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Cache-Control: private,no-cache=foo\n",
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Cache-Control: private,no-cache=foo\n"},
- // Ignore bogus no-cache value.
- {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Cache-Control: private, no-cache=\n",
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Cache-Control: private, no-cache=\n"},
- // Ignore empty no-cache value.
- {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Cache-Control: private, no-cache=\"\"\n",
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Cache-Control: private, no-cache=\"\"\n"},
- // Ignore wrong quotes no-cache value.
- {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Cache-Control: private, no-cache=\'foo\'\n",
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Cache-Control: private, no-cache=\'foo\'\n"},
- // Ignore unterminated quotes no-cache value.
- {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Cache-Control: private, no-cache=\"foo\n",
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Cache-Control: private, no-cache=\"foo\n"},
- // Accept sloppy LWS.
- {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Cache-Control: private, no-cache=\" foo\t, bar\"\n",
- "HTTP/1.1 200 OK\n"
- "Cache-Control: private, no-cache=\" foo\t, bar\"\n"},
- // Header name appears twice, separated by another header.
- {HttpResponseHeaders::PERSIST_ALL,
- "HTTP/1.1 200 OK\n"
- "Foo: 1\n"
- "Bar: 2\n"
- "Foo: 3\n",
- "HTTP/1.1 200 OK\n"
- "Foo: 1\n"
- "Bar: 2\n"
- "Foo: 3\n"},
- // Header name appears twice, separated by another header (type 2).
- {HttpResponseHeaders::PERSIST_ALL,
- "HTTP/1.1 200 OK\n"
- "Foo: 1, 3\n"
- "Bar: 2\n"
- "Foo: 4\n",
- "HTTP/1.1 200 OK\n"
- "Foo: 1, 3\n"
- "Bar: 2\n"
- "Foo: 4\n"},
- // Test filtering of cookie headers.
- {HttpResponseHeaders::PERSIST_SANS_COOKIES,
- "HTTP/1.1 200 OK\n"
- "Set-Cookie: foo=bar; httponly\n"
- "Set-Cookie: bar=foo\n"
- "Bar: 1\n"
- "Set-Cookie2: bar2=foo2\n",
- "HTTP/1.1 200 OK\n"
- "Bar: 1\n"},
- {HttpResponseHeaders::PERSIST_SANS_COOKIES,
- "HTTP/1.1 200 OK\n"
- "Set-Cookie: foo=bar\n"
- "Foo: 2\n"
- "Clear-Site-Data: { \"types\" : [ \"cookies\" ] }\n"
- "Bar: 3\n",
- "HTTP/1.1 200 OK\n"
- "Foo: 2\n"
- "Bar: 3\n"},
- // Test LWS at the end of a header.
- {HttpResponseHeaders::PERSIST_ALL,
- "HTTP/1.1 200 OK\n"
- "Content-Length: 450 \n"
- "Content-Encoding: gzip\n",
- "HTTP/1.1 200 OK\n"
- "Content-Length: 450\n"
- "Content-Encoding: gzip\n"},
- // Test LWS at the end of a header.
- {HttpResponseHeaders::PERSIST_RAW,
- "HTTP/1.1 200 OK\n"
- "Content-Length: 450 \n"
- "Content-Encoding: gzip\n",
- "HTTP/1.1 200 OK\n"
- "Content-Length: 450\n"
- "Content-Encoding: gzip\n"},
- // Test filtering of transport security state headers.
- {HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE,
- "HTTP/1.1 200 OK\n"
- "Strict-Transport-Security: max-age=1576800\n"
- "Bar: 1\n",
- "HTTP/1.1 200 OK\n"
- "Bar: 1\n"},
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- PersistenceTest,
- testing::ValuesIn(persistence_tests));
- TEST(HttpResponseHeadersTest, EnumerateHeader_Coalesced) {
- // Ensure that commas in quoted strings are not regarded as value separators.
- // Ensure that whitespace following a value is trimmed properly.
- std::string headers =
- "HTTP/1.1 200 OK\n"
- "Cache-control:,,private , no-cache=\"set-cookie,server\",\n"
- "cache-Control: no-store\n"
- "cache-Control:\n";
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- size_t iter = 0;
- std::string value;
- ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
- EXPECT_EQ("", value);
- ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
- EXPECT_EQ("", value);
- ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
- EXPECT_EQ("private", value);
- ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
- EXPECT_EQ("no-cache=\"set-cookie,server\"", value);
- ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
- EXPECT_EQ("", value);
- ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
- EXPECT_EQ("no-store", value);
- ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
- EXPECT_EQ("", value);
- EXPECT_FALSE(parsed->EnumerateHeader(&iter, "cache-control", &value));
- }
- TEST(HttpResponseHeadersTest, EnumerateHeader_Challenge) {
- // Even though WWW-Authenticate has commas, it should not be treated as
- // coalesced values.
- std::string headers =
- "HTTP/1.1 401 OK\n"
- "WWW-Authenticate:Digest realm=foobar, nonce=x, domain=y\n"
- "WWW-Authenticate:Basic realm=quatar\n";
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- size_t iter = 0;
- std::string value;
- EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
- EXPECT_EQ("Digest realm=foobar, nonce=x, domain=y", value);
- EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
- EXPECT_EQ("Basic realm=quatar", value);
- EXPECT_FALSE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
- }
- TEST(HttpResponseHeadersTest, EnumerateHeader_DateValued) {
- // The comma in a date valued header should not be treated as a
- // field-value separator.
- std::string headers =
- "HTTP/1.1 200 OK\n"
- "Date: Tue, 07 Aug 2007 23:10:55 GMT\n"
- "Last-Modified: Wed, 01 Aug 2007 23:23:45 GMT\n";
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- std::string value;
- EXPECT_TRUE(parsed->EnumerateHeader(nullptr, "date", &value));
- EXPECT_EQ("Tue, 07 Aug 2007 23:10:55 GMT", value);
- EXPECT_TRUE(parsed->EnumerateHeader(nullptr, "last-modified", &value));
- EXPECT_EQ("Wed, 01 Aug 2007 23:23:45 GMT", value);
- }
- TEST(HttpResponseHeadersTest, DefaultDateToGMT) {
- // Verify we make the best interpretation when parsing dates that incorrectly
- // do not end in "GMT" as RFC2616 requires.
- std::string headers =
- "HTTP/1.1 200 OK\n"
- "Date: Tue, 07 Aug 2007 23:10:55\n"
- "Last-Modified: Tue, 07 Aug 2007 19:10:55 EDT\n"
- "Expires: Tue, 07 Aug 2007 23:10:55 UTC\n";
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- base::Time expected_value;
- ASSERT_TRUE(base::Time::FromString("Tue, 07 Aug 2007 23:10:55 GMT",
- &expected_value));
- base::Time value;
- // When the timezone is missing, GMT is a good guess as its what RFC2616
- // requires.
- EXPECT_TRUE(parsed->GetDateValue(&value));
- EXPECT_EQ(expected_value, value);
- // If GMT is missing but an RFC822-conforming one is present, use that.
- EXPECT_TRUE(parsed->GetLastModifiedValue(&value));
- EXPECT_EQ(expected_value, value);
- // If an unknown timezone is present, treat like a missing timezone and
- // default to GMT. The only example of a web server not specifying "GMT"
- // used "UTC" which is equivalent to GMT.
- if (parsed->GetExpiresValue(&value))
- EXPECT_EQ(expected_value, value);
- }
- TEST(HttpResponseHeadersTest, GetAgeValue10) {
- std::string headers =
- "HTTP/1.1 200 OK\n"
- "Age: 10\n";
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- base::TimeDelta age;
- ASSERT_TRUE(parsed->GetAgeValue(&age));
- EXPECT_EQ(10, age.InSeconds());
- }
- TEST(HttpResponseHeadersTest, GetAgeValue0) {
- std::string headers =
- "HTTP/1.1 200 OK\n"
- "Age: 0\n";
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- base::TimeDelta age;
- ASSERT_TRUE(parsed->GetAgeValue(&age));
- EXPECT_EQ(0, age.InSeconds());
- }
- TEST(HttpResponseHeadersTest, GetAgeValueBogus) {
- std::string headers =
- "HTTP/1.1 200 OK\n"
- "Age: donkey\n";
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- base::TimeDelta age;
- ASSERT_FALSE(parsed->GetAgeValue(&age));
- }
- TEST(HttpResponseHeadersTest, GetAgeValueNegative) {
- std::string headers =
- "HTTP/1.1 200 OK\n"
- "Age: -10\n";
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- base::TimeDelta age;
- ASSERT_FALSE(parsed->GetAgeValue(&age));
- }
- TEST(HttpResponseHeadersTest, GetAgeValueLeadingPlus) {
- std::string headers =
- "HTTP/1.1 200 OK\n"
- "Age: +10\n";
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- base::TimeDelta age;
- ASSERT_FALSE(parsed->GetAgeValue(&age));
- }
- TEST(HttpResponseHeadersTest, GetAgeValueOverflow) {
- std::string headers =
- "HTTP/1.1 200 OK\n"
- "Age: 999999999999999999999999999999999999999999\n";
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- base::TimeDelta age;
- ASSERT_TRUE(parsed->GetAgeValue(&age));
- // Should have saturated to 2^32 - 1.
- EXPECT_EQ(static_cast<int64_t>(0xFFFFFFFFL), age.InSeconds());
- }
- struct ContentTypeTestData {
- const std::string raw_headers;
- const std::string mime_type;
- const bool has_mimetype;
- const std::string charset;
- const bool has_charset;
- const std::string all_content_type;
- };
- class ContentTypeTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<ContentTypeTestData> {
- };
- TEST_P(ContentTypeTest, GetMimeType) {
- const ContentTypeTestData test = GetParam();
- std::string headers(test.raw_headers);
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- std::string value;
- EXPECT_EQ(test.has_mimetype, parsed->GetMimeType(&value));
- EXPECT_EQ(test.mime_type, value);
- value.clear();
- EXPECT_EQ(test.has_charset, parsed->GetCharset(&value));
- EXPECT_EQ(test.charset, value);
- EXPECT_TRUE(parsed->GetNormalizedHeader("content-type", &value));
- EXPECT_EQ(test.all_content_type, value);
- }
- // clang-format off
- const ContentTypeTestData mimetype_tests[] = {
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html\n",
- "text/html", true,
- "", false,
- "text/html" },
- // Multiple content-type headers should give us the last one.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html\n"
- "Content-type: text/html\n",
- "text/html", true,
- "", false,
- "text/html, text/html" },
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/plain\n"
- "Content-type: text/html\n"
- "Content-type: text/plain\n"
- "Content-type: text/html\n",
- "text/html", true,
- "", false,
- "text/plain, text/html, text/plain, text/html" },
- // Test charset parsing.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html\n"
- "Content-type: text/html; charset=ISO-8859-1\n",
- "text/html", true,
- "iso-8859-1", true,
- "text/html, text/html; charset=ISO-8859-1" },
- // Test charset in double quotes.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html\n"
- "Content-type: text/html; charset=\"ISO-8859-1\"\n",
- "text/html", true,
- "iso-8859-1", true,
- "text/html, text/html; charset=\"ISO-8859-1\"" },
- // If there are multiple matching content-type headers, we carry
- // over the charset value.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html;charset=utf-8\n"
- "Content-type: text/html\n",
- "text/html", true,
- "utf-8", true,
- "text/html;charset=utf-8, text/html" },
- // Regression test for https://crbug.com/772350:
- // Single quotes are not delimiters but must be treated as part of charset.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html;charset='utf-8'\n"
- "Content-type: text/html\n",
- "text/html", true,
- "'utf-8'", true,
- "text/html;charset='utf-8', text/html" },
- // First charset wins if matching content-type.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html;charset=utf-8\n"
- "Content-type: text/html;charset=iso-8859-1\n",
- "text/html", true,
- "iso-8859-1", true,
- "text/html;charset=utf-8, text/html;charset=iso-8859-1" },
- // Charset is ignored if the content types change.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/plain;charset=utf-8\n"
- "Content-type: text/html\n",
- "text/html", true,
- "", false,
- "text/plain;charset=utf-8, text/html" },
- // Empty content-type.
- { "HTTP/1.1 200 OK\n"
- "Content-type: \n",
- "", false,
- "", false,
- "" },
- // Emtpy charset.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html;charset=\n",
- "text/html", true,
- "", false,
- "text/html;charset=" },
- // Multiple charsets, first one wins.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n",
- "text/html", true,
- "utf-8", true,
- "text/html;charset=utf-8; charset=iso-8859-1" },
- // Multiple params.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n",
- "text/html", true,
- "iso-8859-1", true,
- "text/html; foo=utf-8; charset=iso-8859-1" },
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n",
- "text/html", true,
- "utf-8", true,
- "text/html ; charset=utf-8 ; bar=iso-8859-1" },
- // Comma embeded in quotes.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html ; charset=\"utf-8,text/plain\" ;\n",
- "text/html", true,
- "utf-8,text/plain", true,
- "text/html ; charset=\"utf-8,text/plain\" ;" },
- // Charset with leading spaces.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html ; charset= \"utf-8\" ;\n",
- "text/html", true,
- "utf-8", true,
- "text/html ; charset= \"utf-8\" ;" },
- // Media type comments in mime-type.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html (html)\n",
- "text/html", true,
- "", false,
- "text/html (html)" },
- // Incomplete charset= param.
- { "HTTP/1.1 200 OK\n"
- "Content-type: text/html; char=\n",
- "text/html", true,
- "", false,
- "text/html; char=" },
- // Invalid media type: no slash.
- { "HTTP/1.1 200 OK\n"
- "Content-type: texthtml\n",
- "", false,
- "", false,
- "texthtml" },
- // Invalid media type: "*/*".
- { "HTTP/1.1 200 OK\n"
- "Content-type: */*\n",
- "", false,
- "", false,
- "*/*" },
- };
- // clang-format on
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- ContentTypeTest,
- testing::ValuesIn(mimetype_tests));
- struct RequiresValidationTestData {
- const char* headers;
- ValidationType validation_type;
- };
- class RequiresValidationTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<RequiresValidationTestData> {
- };
- TEST_P(RequiresValidationTest, RequiresValidation) {
- const RequiresValidationTestData test = GetParam();
- base::Time request_time, response_time, current_time;
- ASSERT_TRUE(
- base::Time::FromString("Wed, 28 Nov 2007 00:40:09 GMT", &request_time));
- ASSERT_TRUE(
- base::Time::FromString("Wed, 28 Nov 2007 00:40:12 GMT", &response_time));
- ASSERT_TRUE(
- base::Time::FromString("Wed, 28 Nov 2007 00:45:20 GMT", ¤t_time));
- std::string headers(test.headers);
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- ValidationType validation_type =
- parsed->RequiresValidation(request_time, response_time, current_time);
- EXPECT_EQ(test.validation_type, validation_type);
- }
- const struct RequiresValidationTestData requires_validation_tests[] = {
- // No expiry info: expires immediately.
- {"HTTP/1.1 200 OK\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- // No expiry info: expires immediately.
- {"HTTP/1.1 200 OK\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- // Valid for a little while.
- {"HTTP/1.1 200 OK\n"
- "cache-control: max-age=10000\n"
- "\n",
- VALIDATION_NONE},
- // Expires in the future.
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "expires: Wed, 28 Nov 2007 01:00:00 GMT\n"
- "\n",
- VALIDATION_NONE},
- // Already expired.
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- // Max-age trumps expires.
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
- "cache-control: max-age=10000\n"
- "\n",
- VALIDATION_NONE},
- // Last-modified heuristic: modified a while ago.
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
- "\n",
- VALIDATION_NONE},
- {"HTTP/1.1 203 Non-Authoritative Information\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
- "\n",
- VALIDATION_NONE},
- {"HTTP/1.1 206 Partial Content\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
- "\n",
- VALIDATION_NONE},
- // Last-modified heuristic: modified recently.
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- {"HTTP/1.1 203 Non-Authoritative Information\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- {"HTTP/1.1 206 Partial Content\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- // Cached permanent redirect.
- {"HTTP/1.1 301 Moved Permanently\n"
- "\n",
- VALIDATION_NONE},
- // Another cached permanent redirect.
- {"HTTP/1.1 308 Permanent Redirect\n"
- "\n",
- VALIDATION_NONE},
- // Cached redirect: not reusable even though by default it would be.
- {"HTTP/1.1 300 Multiple Choices\n"
- "Cache-Control: no-cache\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- // Cached forever by default.
- {"HTTP/1.1 410 Gone\n"
- "\n",
- VALIDATION_NONE},
- // Cached temporary redirect: not reusable.
- {"HTTP/1.1 302 Found\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- // Cached temporary redirect: reusable.
- {"HTTP/1.1 302 Found\n"
- "cache-control: max-age=10000\n"
- "\n",
- VALIDATION_NONE},
- // Cache-control: max-age=N overrides expires: date in the past.
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "expires: Wed, 28 Nov 2007 00:20:11 GMT\n"
- "cache-control: max-age=10000\n"
- "\n",
- VALIDATION_NONE},
- // Cache-control: no-store overrides expires: in the future.
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "expires: Wed, 29 Nov 2007 00:40:11 GMT\n"
- "cache-control: no-store,private,no-cache=\"foo\"\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- // Pragma: no-cache overrides last-modified heuristic.
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
- "pragma: no-cache\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- // max-age has expired, needs synchronous revalidation
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "cache-control: max-age=300\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- // max-age has expired, stale-while-revalidate has not, eligible for
- // asynchronous revalidation
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "cache-control: max-age=300, stale-while-revalidate=3600\n"
- "\n",
- VALIDATION_ASYNCHRONOUS},
- // max-age and stale-while-revalidate have expired, needs synchronous
- // revalidation
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "cache-control: max-age=300, stale-while-revalidate=5\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- // max-age is 0, stale-while-revalidate is large enough to permit
- // asynchronous revalidation
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "cache-control: max-age=0, stale-while-revalidate=360\n"
- "\n",
- VALIDATION_ASYNCHRONOUS},
- // stale-while-revalidate must not override no-cache or similar directives.
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "cache-control: no-cache, stale-while-revalidate=360\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- // max-age has not expired, so no revalidation is needed.
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "cache-control: max-age=3600, stale-while-revalidate=3600\n"
- "\n",
- VALIDATION_NONE},
- // must-revalidate overrides stale-while-revalidate, so synchronous
- // validation
- // is needed.
- {"HTTP/1.1 200 OK\n"
- "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
- "cache-control: must-revalidate, max-age=300, "
- "stale-while-revalidate=3600\n"
- "\n",
- VALIDATION_SYNCHRONOUS},
- // TODO(darin): Add many many more tests here.
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- RequiresValidationTest,
- testing::ValuesIn(requires_validation_tests));
- struct UpdateTestData {
- const char* orig_headers;
- const char* new_headers;
- const char* expected_headers;
- };
- class UpdateTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<UpdateTestData> {
- };
- TEST_P(UpdateTest, Update) {
- const UpdateTestData test = GetParam();
- std::string orig_headers(test.orig_headers);
- HeadersToRaw(&orig_headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
- std::string new_headers(test.new_headers);
- HeadersToRaw(&new_headers);
- auto new_parsed = base::MakeRefCounted<HttpResponseHeaders>(new_headers);
- parsed->Update(*new_parsed.get());
- EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
- }
- const UpdateTestData update_tests[] = {
- {"HTTP/1.1 200 OK\n",
- "HTTP/1/1 304 Not Modified\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n",
- "HTTP/1.1 200 OK\n"
- "Cache-control: max-age=10000\n"},
- {"HTTP/1.1 200 OK\n"
- "Foo: 1\n"
- "Cache-control: private\n",
- "HTTP/1/1 304 Not Modified\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n",
- "HTTP/1.1 200 OK\n"
- "Cache-control: max-age=10000\n"
- "Foo: 1\n"},
- {"HTTP/1.1 200 OK\n"
- "Foo: 1\n"
- "Cache-control: private\n",
- "HTTP/1/1 304 Not Modified\n"
- "connection: keep-alive\n"
- "Cache-CONTROL: max-age=10000\n",
- "HTTP/1.1 200 OK\n"
- "Cache-CONTROL: max-age=10000\n"
- "Foo: 1\n"},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: 450\n",
- "HTTP/1/1 304 Not Modified\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10001 \n",
- "HTTP/1.1 200 OK\n"
- "Cache-control: max-age=10001\n"
- "Content-Length: 450\n"},
- {
- "HTTP/1.1 200 OK\n"
- "X-Frame-Options: DENY\n",
- "HTTP/1/1 304 Not Modified\n"
- "X-Frame-Options: ALLOW\n",
- "HTTP/1.1 200 OK\n"
- "X-Frame-Options: DENY\n",
- },
- {
- "HTTP/1.1 200 OK\n"
- "X-WebKit-CSP: default-src 'none'\n",
- "HTTP/1/1 304 Not Modified\n"
- "X-WebKit-CSP: default-src *\n",
- "HTTP/1.1 200 OK\n"
- "X-WebKit-CSP: default-src 'none'\n",
- },
- {
- "HTTP/1.1 200 OK\n"
- "X-XSS-Protection: 1\n",
- "HTTP/1/1 304 Not Modified\n"
- "X-XSS-Protection: 0\n",
- "HTTP/1.1 200 OK\n"
- "X-XSS-Protection: 1\n",
- },
- {"HTTP/1.1 200 OK\n",
- "HTTP/1/1 304 Not Modified\n"
- "X-Content-Type-Options: nosniff\n",
- "HTTP/1.1 200 OK\n"},
- {"HTTP/1.1 200 OK\n"
- "Content-Encoding: identity\n"
- "Content-Length: 100\n"
- "Content-Type: text/html\n"
- "Content-Security-Policy: default-src 'none'\n",
- "HTTP/1/1 304 Not Modified\n"
- "Content-Encoding: gzip\n"
- "Content-Length: 200\n"
- "Content-Type: text/xml\n"
- "Content-Security-Policy: default-src 'self'\n",
- "HTTP/1.1 200 OK\n"
- "Content-Security-Policy: default-src 'self'\n"
- "Content-Encoding: identity\n"
- "Content-Length: 100\n"
- "Content-Type: text/html\n"},
- {"HTTP/1.1 200 OK\n"
- "Content-Location: /example_page.html\n",
- "HTTP/1/1 304 Not Modified\n"
- "Content-Location: /not_example_page.html\n",
- "HTTP/1.1 200 OK\n"
- "Content-Location: /example_page.html\n"},
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- UpdateTest,
- testing::ValuesIn(update_tests));
- struct EnumerateHeaderTestData {
- const char* headers;
- const char* expected_lines;
- };
- class EnumerateHeaderLinesTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<EnumerateHeaderTestData> {
- };
- TEST_P(EnumerateHeaderLinesTest, EnumerateHeaderLines) {
- const EnumerateHeaderTestData test = GetParam();
- std::string headers(test.headers);
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- std::string name, value, lines;
- size_t iter = 0;
- while (parsed->EnumerateHeaderLines(&iter, &name, &value)) {
- lines.append(name);
- lines.append(": ");
- lines.append(value);
- lines.append("\n");
- }
- EXPECT_EQ(std::string(test.expected_lines), lines);
- }
- const EnumerateHeaderTestData enumerate_header_tests[] = {
- {"HTTP/1.1 200 OK\n",
- ""},
- {"HTTP/1.1 200 OK\n"
- "Foo: 1\n",
- "Foo: 1\n"},
- {"HTTP/1.1 200 OK\n"
- "Foo: 1\n"
- "Bar: 2\n"
- "Foo: 3\n",
- "Foo: 1\nBar: 2\nFoo: 3\n"},
- {"HTTP/1.1 200 OK\n"
- "Foo: 1, 2, 3\n",
- "Foo: 1, 2, 3\n"},
- {"HTTP/1.1 200 OK\n"
- "Foo: ,, 1,, 2, 3,, \n",
- "Foo: ,, 1,, 2, 3,,\n"},
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- EnumerateHeaderLinesTest,
- testing::ValuesIn(enumerate_header_tests));
- struct IsRedirectTestData {
- const char* headers;
- const char* location;
- bool is_redirect;
- };
- class IsRedirectTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<IsRedirectTestData> {
- };
- TEST_P(IsRedirectTest, IsRedirect) {
- const IsRedirectTestData test = GetParam();
- std::string headers(test.headers);
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- std::string location;
- EXPECT_EQ(parsed->IsRedirect(&location), test.is_redirect);
- EXPECT_EQ(location, test.location);
- }
- const IsRedirectTestData is_redirect_tests[] = {
- { "HTTP/1.1 200 OK\n",
- "",
- false
- },
- { "HTTP/1.1 301 Moved\n"
- "Location: http://foopy/\n",
- "http://foopy/",
- true
- },
- { "HTTP/1.1 301 Moved\n"
- "Location: \t \n",
- "",
- false
- },
- // We use the first location header as the target of the redirect.
- { "HTTP/1.1 301 Moved\n"
- "Location: http://foo/\n"
- "Location: http://bar/\n",
- "http://foo/",
- true
- },
- // We use the first _valid_ location header as the target of the redirect.
- { "HTTP/1.1 301 Moved\n"
- "Location: \n"
- "Location: http://bar/\n",
- "http://bar/",
- true
- },
- // Bug 1050541 (location header with an unescaped comma).
- { "HTTP/1.1 301 Moved\n"
- "Location: http://foo/bar,baz.html\n",
- "http://foo/bar,baz.html",
- true
- },
- // Bug 1224617 (location header with non-ASCII bytes).
- { "HTTP/1.1 301 Moved\n"
- "Location: http://foo/bar?key=\xE4\xF6\xFC\n",
- "http://foo/bar?key=%E4%F6%FC",
- true
- },
- // Shift_JIS, Big5, and GBK contain multibyte characters with the trailing
- // byte falling in the ASCII range.
- { "HTTP/1.1 301 Moved\n"
- "Location: http://foo/bar?key=\x81\x5E\xD8\xBF\n",
- "http://foo/bar?key=%81^%D8%BF",
- true
- },
- { "HTTP/1.1 301 Moved\n"
- "Location: http://foo/bar?key=\x82\x40\xBD\xC4\n",
- "http://foo/bar?key=%82@%BD%C4",
- true
- },
- { "HTTP/1.1 301 Moved\n"
- "Location: http://foo/bar?key=\x83\x5C\x82\x5D\xCB\xD7\n",
- "http://foo/bar?key=%83\\%82]%CB%D7",
- true
- },
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- IsRedirectTest,
- testing::ValuesIn(is_redirect_tests));
- struct ContentLengthTestData {
- const char* headers;
- int64_t expected_len;
- };
- class GetContentLengthTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<ContentLengthTestData> {
- };
- TEST_P(GetContentLengthTest, GetContentLength) {
- const ContentLengthTestData test = GetParam();
- std::string headers(test.headers);
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- EXPECT_EQ(test.expected_len, parsed->GetContentLength());
- }
- const ContentLengthTestData content_length_tests[] = {
- {"HTTP/1.1 200 OK\n", -1},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: 10\n",
- 10},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: \n",
- -1},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: abc\n",
- -1},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: -10\n",
- -1},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: +10\n",
- -1},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: 23xb5\n",
- -1},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: 0xA\n",
- -1},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: 010\n",
- 10},
- // Content-Length too big, will overflow an int64_t.
- {"HTTP/1.1 200 OK\n"
- "Content-Length: 40000000000000000000\n",
- -1},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: 10\n",
- 10},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: 10 \n",
- 10},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: \t10\n",
- 10},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: \v10\n",
- -1},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: \f10\n",
- -1},
- {"HTTP/1.1 200 OK\n"
- "cOnTeNt-LENgth: 33\n",
- 33},
- {"HTTP/1.1 200 OK\n"
- "Content-Length: 34\r\n",
- -1},
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- GetContentLengthTest,
- testing::ValuesIn(content_length_tests));
- struct ContentRangeTestData {
- const char* headers;
- bool expected_return_value;
- int64_t expected_first_byte_position;
- int64_t expected_last_byte_position;
- int64_t expected_instance_size;
- };
- class ContentRangeTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<ContentRangeTestData> {
- };
- TEST_P(ContentRangeTest, GetContentRangeFor206) {
- const ContentRangeTestData test = GetParam();
- std::string headers(test.headers);
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- int64_t first_byte_position;
- int64_t last_byte_position;
- int64_t instance_size;
- bool return_value = parsed->GetContentRangeFor206(
- &first_byte_position, &last_byte_position, &instance_size);
- EXPECT_EQ(test.expected_return_value, return_value);
- EXPECT_EQ(test.expected_first_byte_position, first_byte_position);
- EXPECT_EQ(test.expected_last_byte_position, last_byte_position);
- EXPECT_EQ(test.expected_instance_size, instance_size);
- }
- const ContentRangeTestData content_range_tests[] = {
- {"HTTP/1.1 206 Partial Content", false, -1, -1, -1},
- {"HTTP/1.1 206 Partial Content\n"
- "Content-Range:",
- false, -1, -1, -1},
- {"HTTP/1.1 206 Partial Content\n"
- "Content-Range: bytes 0-50/51",
- true, 0, 50, 51},
- {"HTTP/1.1 206 Partial Content\n"
- "Content-Range: bytes 50-0/51",
- false, -1, -1, -1},
- {"HTTP/1.1 416 Requested range not satisfiable\n"
- "Content-Range: bytes */*",
- false, -1, -1, -1},
- {"HTTP/1.1 206 Partial Content\n"
- "Content-Range: bytes 0-50/*",
- false, -1, -1, -1},
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- ContentRangeTest,
- testing::ValuesIn(content_range_tests));
- struct KeepAliveTestData {
- const char* headers;
- bool expected_keep_alive;
- };
- // Enable GTest to print KeepAliveTestData in an intelligible way if the test
- // fails.
- void PrintTo(const KeepAliveTestData& keep_alive_test_data,
- std::ostream* os) {
- *os << "{\"" << keep_alive_test_data.headers << "\", " << std::boolalpha
- << keep_alive_test_data.expected_keep_alive << "}";
- }
- class IsKeepAliveTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<KeepAliveTestData> {
- };
- TEST_P(IsKeepAliveTest, IsKeepAlive) {
- const KeepAliveTestData test = GetParam();
- std::string headers(test.headers);
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- EXPECT_EQ(test.expected_keep_alive, parsed->IsKeepAlive());
- }
- const KeepAliveTestData keepalive_tests[] = {
- // The status line fabricated by HttpNetworkTransaction for a 0.9 response.
- // Treated as 0.9.
- { "HTTP/0.9 200 OK",
- false
- },
- // This could come from a broken server. Treated as 1.0 because it has a
- // header.
- { "HTTP/0.9 200 OK\n"
- "connection: keep-alive\n",
- true
- },
- { "HTTP/1.1 200 OK\n",
- true
- },
- { "HTTP/1.0 200 OK\n",
- false
- },
- { "HTTP/1.0 200 OK\n"
- "connection: close\n",
- false
- },
- { "HTTP/1.0 200 OK\n"
- "connection: keep-alive\n",
- true
- },
- { "HTTP/1.0 200 OK\n"
- "connection: kEeP-AliVe\n",
- true
- },
- { "HTTP/1.0 200 OK\n"
- "connection: keep-aliveX\n",
- false
- },
- { "HTTP/1.1 200 OK\n"
- "connection: close\n",
- false
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n",
- true
- },
- { "HTTP/1.0 200 OK\n"
- "proxy-connection: close\n",
- false
- },
- { "HTTP/1.0 200 OK\n"
- "proxy-connection: keep-alive\n",
- true
- },
- { "HTTP/1.1 200 OK\n"
- "proxy-connection: close\n",
- false
- },
- { "HTTP/1.1 200 OK\n"
- "proxy-connection: keep-alive\n",
- true
- },
- { "HTTP/1.1 200 OK\n"
- "Connection: Upgrade, close\n",
- false
- },
- { "HTTP/1.1 200 OK\n"
- "Connection: Upgrade, keep-alive\n",
- true
- },
- { "HTTP/1.1 200 OK\n"
- "Connection: Upgrade\n"
- "Connection: close\n",
- false
- },
- { "HTTP/1.1 200 OK\n"
- "Connection: Upgrade\n"
- "Connection: keep-alive\n",
- true
- },
- { "HTTP/1.1 200 OK\n"
- "Connection: close, Upgrade\n",
- false
- },
- { "HTTP/1.1 200 OK\n"
- "Connection: keep-alive, Upgrade\n",
- true
- },
- { "HTTP/1.1 200 OK\n"
- "Connection: Upgrade\n"
- "Proxy-Connection: close\n",
- false
- },
- { "HTTP/1.1 200 OK\n"
- "Connection: Upgrade\n"
- "Proxy-Connection: keep-alive\n",
- true
- },
- // In situations where the response headers conflict with themselves, use the
- // first one for backwards-compatibility.
- { "HTTP/1.1 200 OK\n"
- "Connection: close\n"
- "Connection: keep-alive\n",
- false
- },
- { "HTTP/1.1 200 OK\n"
- "Connection: keep-alive\n"
- "Connection: close\n",
- true
- },
- { "HTTP/1.0 200 OK\n"
- "Connection: close\n"
- "Connection: keep-alive\n",
- false
- },
- { "HTTP/1.0 200 OK\n"
- "Connection: keep-alive\n"
- "Connection: close\n",
- true
- },
- // Ignore the Proxy-Connection header if at all possible.
- { "HTTP/1.0 200 OK\n"
- "Proxy-Connection: keep-alive\n"
- "Connection: close\n",
- false
- },
- { "HTTP/1.1 200 OK\n"
- "Proxy-Connection: close\n"
- "Connection: keep-alive\n",
- true
- },
- // Older versions of Chrome would have ignored Proxy-Connection in this case,
- // but it doesn't seem safe.
- { "HTTP/1.1 200 OK\n"
- "Proxy-Connection: close\n"
- "Connection: Transfer-Encoding\n",
- false
- },
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- IsKeepAliveTest,
- testing::ValuesIn(keepalive_tests));
- struct HasStrongValidatorsTestData {
- const char* headers;
- bool expected_result;
- };
- class HasStrongValidatorsTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<HasStrongValidatorsTestData> {
- };
- TEST_P(HasStrongValidatorsTest, HasStrongValidators) {
- const HasStrongValidatorsTestData test = GetParam();
- std::string headers(test.headers);
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- EXPECT_EQ(test.expected_result, parsed->HasStrongValidators());
- }
- const HasStrongValidatorsTestData strong_validators_tests[] = {
- { "HTTP/0.9 200 OK",
- false
- },
- { "HTTP/1.0 200 OK\n"
- "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
- "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
- "ETag: \"foo\"\n",
- false
- },
- { "HTTP/1.1 200 OK\n"
- "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
- "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
- "ETag: \"foo\"\n",
- true
- },
- { "HTTP/1.1 200 OK\n"
- "Date: Wed, 28 Nov 2007 00:41:10 GMT\n"
- "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
- true
- },
- { "HTTP/1.1 200 OK\n"
- "Date: Wed, 28 Nov 2007 00:41:09 GMT\n"
- "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
- false
- },
- { "HTTP/1.1 200 OK\n"
- "ETag: \"foo\"\n",
- true
- },
- // This is not really a weak etag:
- { "HTTP/1.1 200 OK\n"
- "etag: \"w/foo\"\n",
- true
- },
- // This is a weak etag:
- { "HTTP/1.1 200 OK\n"
- "etag: w/\"foo\"\n",
- false
- },
- { "HTTP/1.1 200 OK\n"
- "etag: W / \"foo\"\n",
- false
- }
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- HasStrongValidatorsTest,
- testing::ValuesIn(strong_validators_tests));
- TEST(HttpResponseHeadersTest, HasValidatorsNone) {
- std::string headers("HTTP/1.1 200 OK");
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- EXPECT_FALSE(parsed->HasValidators());
- }
- TEST(HttpResponseHeadersTest, HasValidatorsEtag) {
- std::string headers(
- "HTTP/1.1 200 OK\n"
- "etag: \"anything\"");
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- EXPECT_TRUE(parsed->HasValidators());
- }
- TEST(HttpResponseHeadersTest, HasValidatorsLastModified) {
- std::string headers(
- "HTTP/1.1 200 OK\n"
- "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT");
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- EXPECT_TRUE(parsed->HasValidators());
- }
- TEST(HttpResponseHeadersTest, HasValidatorsWeakEtag) {
- std::string headers(
- "HTTP/1.1 200 OK\n"
- "etag: W/\"anything\"");
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- EXPECT_TRUE(parsed->HasValidators());
- }
- TEST(HttpResponseHeadersTest, GetNormalizedHeaderWithEmptyValues) {
- std::string headers(
- "HTTP/1.1 200 OK\n"
- "a:\n"
- "b: \n"
- "c:*\n"
- "d: *\n"
- "e: \n"
- "a: \n"
- "b:*\n"
- "c:\n"
- "d:*\n"
- "a:\n");
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- std::string value;
- EXPECT_TRUE(parsed->GetNormalizedHeader("a", &value));
- EXPECT_EQ(value, ", , ");
- EXPECT_TRUE(parsed->GetNormalizedHeader("b", &value));
- EXPECT_EQ(value, ", *");
- EXPECT_TRUE(parsed->GetNormalizedHeader("c", &value));
- EXPECT_EQ(value, "*, ");
- EXPECT_TRUE(parsed->GetNormalizedHeader("d", &value));
- EXPECT_EQ(value, "*, *");
- EXPECT_TRUE(parsed->GetNormalizedHeader("e", &value));
- EXPECT_EQ(value, "");
- EXPECT_FALSE(parsed->GetNormalizedHeader("f", &value));
- }
- TEST(HttpResponseHeadersTest, GetNormalizedHeaderWithCommas) {
- std::string headers(
- "HTTP/1.1 200 OK\n"
- "a: foo, bar\n"
- "b: , foo, bar,\n"
- "c: ,,,\n"
- "d: , , , \n"
- "e:\t,\t,\t,\t\n"
- "a: ,");
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- std::string value;
- // TODO(mmenke): "Normalized" headers probably should preserve the
- // leading/trailing whitespace from the original headers.
- ASSERT_TRUE(parsed->GetNormalizedHeader("a", &value));
- EXPECT_EQ("foo, bar, ,", value);
- ASSERT_TRUE(parsed->GetNormalizedHeader("b", &value));
- EXPECT_EQ(", foo, bar,", value);
- ASSERT_TRUE(parsed->GetNormalizedHeader("c", &value));
- EXPECT_EQ(",,,", value);
- ASSERT_TRUE(parsed->GetNormalizedHeader("d", &value));
- EXPECT_EQ(", , ,", value);
- ASSERT_TRUE(parsed->GetNormalizedHeader("e", &value));
- EXPECT_EQ(",\t,\t,", value);
- EXPECT_FALSE(parsed->GetNormalizedHeader("f", &value));
- }
- TEST(HttpResponseHeadersTest, AddHeader) {
- scoped_refptr<HttpResponseHeaders> headers = HttpResponseHeaders::TryToCreate(
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n");
- ASSERT_TRUE(headers);
- headers->AddHeader("Content-Length", "450");
- EXPECT_EQ(
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- "Content-Length: 450\n",
- ToSimpleString(headers));
- // Add a second Content-Length header with extra spaces in the value. It
- // should be added to the end, and the extra spaces removed.
- headers->AddHeader("Content-Length", " 42 ");
- EXPECT_EQ(
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- "Content-Length: 450\n"
- "Content-Length: 42\n",
- ToSimpleString(headers));
- }
- TEST(HttpResponseHeadersTest, SetHeader) {
- scoped_refptr<HttpResponseHeaders> headers = HttpResponseHeaders::TryToCreate(
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n");
- ASSERT_TRUE(headers);
- headers->SetHeader("Content-Length", "450");
- EXPECT_EQ(
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- "Content-Length: 450\n",
- ToSimpleString(headers));
- headers->SetHeader("Content-Length", " 42 ");
- EXPECT_EQ(
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- "Content-Length: 42\n",
- ToSimpleString(headers));
- headers->SetHeader("connection", "close");
- EXPECT_EQ(
- "HTTP/1.1 200 OK\n"
- "Cache-control: max-age=10000\n"
- "Content-Length: 42\n"
- "connection: close\n",
- ToSimpleString(headers));
- }
- TEST(HttpResponseHeadersTest, TracingSupport) {
- scoped_refptr<HttpResponseHeaders> headers = HttpResponseHeaders::TryToCreate(
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n");
- ASSERT_TRUE(headers);
- EXPECT_EQ(perfetto::TracedValueToString(headers),
- "{response_code:200,headers:[{name:connection,value:keep-alive}]}");
- }
- struct RemoveHeaderTestData {
- const char* orig_headers;
- const char* to_remove;
- const char* expected_headers;
- };
- class RemoveHeaderTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<RemoveHeaderTestData> {
- };
- TEST_P(RemoveHeaderTest, RemoveHeader) {
- const RemoveHeaderTestData test = GetParam();
- std::string orig_headers(test.orig_headers);
- HeadersToRaw(&orig_headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
- std::string name(test.to_remove);
- parsed->RemoveHeader(name);
- EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
- }
- const RemoveHeaderTestData remove_header_tests[] = {
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- "Content-Length: 450\n",
- "Content-Length",
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive \n"
- "Content-Length : 450 \n"
- "Cache-control: max-age=10000\n",
- "Content-Length",
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- },
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- RemoveHeaderTest,
- testing::ValuesIn(remove_header_tests));
- struct RemoveHeadersTestData {
- const char* orig_headers;
- const char* to_remove[2];
- const char* expected_headers;
- };
- class RemoveHeadersTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<RemoveHeadersTestData> {};
- TEST_P(RemoveHeadersTest, RemoveHeaders) {
- const RemoveHeadersTestData test = GetParam();
- std::string orig_headers(test.orig_headers);
- HeadersToRaw(&orig_headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
- std::unordered_set<std::string> to_remove;
- for (auto* header : test.to_remove) {
- if (header)
- to_remove.insert(header);
- }
- parsed->RemoveHeaders(to_remove);
- EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
- }
- const RemoveHeadersTestData remove_headers_tests[] = {
- {"HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- "Content-Length: 450\n",
- {"Content-Length", "CACHE-control"},
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"},
- {"HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Content-Length: 450\n",
- {"foo", "bar"},
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Content-Length: 450\n"},
- {"HTTP/1.1 404 Kinda not OK\n"
- "connection: keep-alive \n",
- {},
- "HTTP/1.1 404 Kinda not OK\n"
- "connection: keep-alive\n"},
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- RemoveHeadersTest,
- testing::ValuesIn(remove_headers_tests));
- struct RemoveIndividualHeaderTestData {
- const char* orig_headers;
- const char* to_remove_name;
- const char* to_remove_value;
- const char* expected_headers;
- };
- class RemoveIndividualHeaderTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<RemoveIndividualHeaderTestData> {
- };
- TEST_P(RemoveIndividualHeaderTest, RemoveIndividualHeader) {
- const RemoveIndividualHeaderTestData test = GetParam();
- std::string orig_headers(test.orig_headers);
- HeadersToRaw(&orig_headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
- std::string name(test.to_remove_name);
- std::string value(test.to_remove_value);
- parsed->RemoveHeaderLine(name, value);
- EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
- }
- const RemoveIndividualHeaderTestData remove_individual_header_tests[] = {
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- "Content-Length: 450\n",
- "Content-Length",
- "450",
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive \n"
- "Content-Length : 450 \n"
- "Cache-control: max-age=10000\n",
- "Content-Length",
- "450",
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive \n"
- "Content-Length: 450\n"
- "Cache-control: max-age=10000\n",
- "Content-Length", // Matching name.
- "999", // Mismatching value.
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Content-Length: 450\n"
- "Cache-control: max-age=10000\n"
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive \n"
- "Foo: bar, baz\n"
- "Foo: bar\n"
- "Cache-control: max-age=10000\n",
- "Foo",
- "bar, baz", // Space in value.
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Foo: bar\n"
- "Cache-control: max-age=10000\n"
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive \n"
- "Foo: bar, baz\n"
- "Cache-control: max-age=10000\n",
- "Foo",
- "baz", // Only partial match -> ignored.
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Foo: bar, baz\n"
- "Cache-control: max-age=10000\n"
- },
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- RemoveIndividualHeaderTest,
- testing::ValuesIn(remove_individual_header_tests));
- struct ReplaceStatusTestData {
- const char* orig_headers;
- const char* new_status;
- const char* expected_headers;
- };
- class ReplaceStatusTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<ReplaceStatusTestData> {
- };
- TEST_P(ReplaceStatusTest, ReplaceStatus) {
- const ReplaceStatusTestData test = GetParam();
- std::string orig_headers(test.orig_headers);
- HeadersToRaw(&orig_headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
- std::string name(test.new_status);
- parsed->ReplaceStatusLine(name);
- EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
- }
- const ReplaceStatusTestData replace_status_tests[] = {
- { "HTTP/1.1 206 Partial Content\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- "Content-Length: 450\n",
- "HTTP/1.1 200 OK",
- "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n"
- "Cache-control: max-age=10000\n"
- "Content-Length: 450\n"
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive\n",
- "HTTP/1.1 304 Not Modified",
- "HTTP/1.1 304 Not Modified\n"
- "connection: keep-alive\n"
- },
- { "HTTP/1.1 200 OK\n"
- "connection: keep-alive \n"
- "Content-Length : 450 \n"
- "Cache-control: max-age=10000\n",
- "HTTP/1//1 304 Not Modified",
- "HTTP/1.0 304 Not Modified\n"
- "connection: keep-alive\n"
- "Content-Length: 450\n"
- "Cache-control: max-age=10000\n"
- },
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- ReplaceStatusTest,
- testing::ValuesIn(replace_status_tests));
- struct UpdateWithNewRangeTestData {
- const char* orig_headers;
- const char* expected_headers;
- const char* expected_headers_with_replaced_status;
- };
- class UpdateWithNewRangeTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<UpdateWithNewRangeTestData> {
- };
- TEST_P(UpdateWithNewRangeTest, UpdateWithNewRange) {
- const UpdateWithNewRangeTestData test = GetParam();
- const HttpByteRange range = HttpByteRange::Bounded(3, 5);
- std::string orig_headers(test.orig_headers);
- std::replace(orig_headers.begin(), orig_headers.end(), '\n', '\0');
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers + '\0');
- int64_t content_size = parsed->GetContentLength();
- // Update headers without replacing status line.
- parsed->UpdateWithNewRange(range, content_size, false);
- EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
- // Replace status line too.
- parsed->UpdateWithNewRange(range, content_size, true);
- EXPECT_EQ(std::string(test.expected_headers_with_replaced_status),
- ToSimpleString(parsed));
- }
- const UpdateWithNewRangeTestData update_range_tests[] = {
- { "HTTP/1.1 200 OK\n"
- "Content-Length: 450\n",
- "HTTP/1.1 200 OK\n"
- "Content-Range: bytes 3-5/450\n"
- "Content-Length: 3\n",
- "HTTP/1.1 206 Partial Content\n"
- "Content-Range: bytes 3-5/450\n"
- "Content-Length: 3\n",
- },
- { "HTTP/1.1 200 OK\n"
- "Content-Length: 5\n",
- "HTTP/1.1 200 OK\n"
- "Content-Range: bytes 3-5/5\n"
- "Content-Length: 3\n",
- "HTTP/1.1 206 Partial Content\n"
- "Content-Range: bytes 3-5/5\n"
- "Content-Length: 3\n",
- },
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- UpdateWithNewRangeTest,
- testing::ValuesIn(update_range_tests));
- TEST_F(HttpResponseHeadersCacheControlTest, AbsentMaxAgeReturnsFalse) {
- InitializeHeadersWithCacheControl("nocache");
- EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
- }
- TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithNoParameterRejected) {
- InitializeHeadersWithCacheControl("max-age=,private");
- EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
- }
- TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithSpaceParameterRejected) {
- InitializeHeadersWithCacheControl("max-age= ,private");
- EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
- }
- TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithInterimSpaceIsRejected) {
- InitializeHeadersWithCacheControl("max-age=1 2");
- EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
- }
- TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithMinusSignIsRejected) {
- InitializeHeadersWithCacheControl("max-age=-7");
- EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
- }
- TEST_F(HttpResponseHeadersCacheControlTest,
- MaxAgeWithSpaceBeforeEqualsIsRejected) {
- InitializeHeadersWithCacheControl("max-age = 7");
- EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
- }
- TEST_F(HttpResponseHeadersCacheControlTest,
- MaxAgeWithLeadingandTrailingSpaces) {
- InitializeHeadersWithCacheControl("max-age= 7 ");
- EXPECT_EQ(base::Seconds(7), GetMaxAgeValue());
- }
- TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeFirstMatchUsed) {
- InitializeHeadersWithCacheControl("max-age=10, max-age=20");
- EXPECT_EQ(base::Seconds(10), GetMaxAgeValue());
- }
- TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeBogusFirstMatchUsed) {
- // "max-age10" isn't parsed as "max-age"; "max-age=now" is bogus and
- // ignored and so "max-age=20" is used.
- InitializeHeadersWithCacheControl(
- "max-age10, max-age=now, max-age=20, max-age=30");
- EXPECT_EQ(base::Seconds(20), GetMaxAgeValue());
- }
- TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeCaseInsensitive) {
- InitializeHeadersWithCacheControl("Max-aGe=15");
- EXPECT_EQ(base::Seconds(15), GetMaxAgeValue());
- }
- TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeOverflow) {
- InitializeHeadersWithCacheControl("max-age=99999999999999999999");
- EXPECT_EQ(base::TimeDelta::FiniteMax().InSeconds(),
- GetMaxAgeValue().InSeconds());
- }
- struct MaxAgeTestData {
- const char* max_age_string;
- const absl::optional<int64_t> expected_seconds;
- };
- class MaxAgeEdgeCasesTest
- : public HttpResponseHeadersCacheControlTest,
- public ::testing::WithParamInterface<MaxAgeTestData> {
- };
- TEST_P(MaxAgeEdgeCasesTest, MaxAgeEdgeCases) {
- const MaxAgeTestData test = GetParam();
- std::string max_age = "max-age=";
- InitializeHeadersWithCacheControl(
- (max_age + test.max_age_string).c_str());
- if (test.expected_seconds.has_value()) {
- EXPECT_EQ(test.expected_seconds.value(), GetMaxAgeValue().InSeconds())
- << " for max-age=" << test.max_age_string;
- } else {
- EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
- }
- }
- const MaxAgeTestData max_age_tests[] = {
- {" 1 ", 1}, // Spaces are ignored.
- {"-1", absl::nullopt},
- {"--1", absl::nullopt},
- {"2s", absl::nullopt},
- {"3 days", absl::nullopt},
- {"'4'", absl::nullopt},
- {"\"5\"", absl::nullopt},
- {"0x6", absl::nullopt}, // Hex not parsed as hex.
- {"7F", absl::nullopt}, // Hex without 0x still not parsed as hex.
- {"010", 10}, // Octal not parsed as octal.
- {"9223372036853", 9223372036853},
- {"9223372036854", 9223372036854},
- {"9223372036855", 9223372036854},
- {"9223372036854775806", 9223372036854},
- {"9223372036854775807", 9223372036854},
- {"20000000000000000000", 9223372036854}, // Overflow int64_t.
- };
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeadersCacheControl,
- MaxAgeEdgeCasesTest,
- testing::ValuesIn(max_age_tests));
- TEST_F(HttpResponseHeadersCacheControlTest,
- AbsentStaleWhileRevalidateReturnsFalse) {
- InitializeHeadersWithCacheControl("max-age=3600");
- EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
- }
- TEST_F(HttpResponseHeadersCacheControlTest,
- StaleWhileRevalidateWithoutValueRejected) {
- InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=");
- EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
- }
- TEST_F(HttpResponseHeadersCacheControlTest,
- StaleWhileRevalidateWithInvalidValueIgnored) {
- InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=true");
- EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
- }
- TEST_F(HttpResponseHeadersCacheControlTest, StaleWhileRevalidateValueReturned) {
- InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=7200");
- EXPECT_EQ(base::Seconds(7200), GetStaleWhileRevalidateValue());
- }
- TEST_F(HttpResponseHeadersCacheControlTest,
- FirstStaleWhileRevalidateValueUsed) {
- InitializeHeadersWithCacheControl(
- "stale-while-revalidate=1,stale-while-revalidate=7200");
- EXPECT_EQ(base::Seconds(1), GetStaleWhileRevalidateValue());
- }
- struct GetCurrentAgeTestData {
- const char* headers;
- const char* request_time;
- const char* response_time;
- const char* current_time;
- const int expected_age;
- };
- class GetCurrentAgeTest
- : public HttpResponseHeadersTest,
- public ::testing::WithParamInterface<GetCurrentAgeTestData> {
- };
- TEST_P(GetCurrentAgeTest, GetCurrentAge) {
- const GetCurrentAgeTestData test = GetParam();
- base::Time request_time, response_time, current_time;
- ASSERT_TRUE(base::Time::FromString(test.request_time, &request_time));
- ASSERT_TRUE(base::Time::FromString(test.response_time, &response_time));
- ASSERT_TRUE(base::Time::FromString(test.current_time, ¤t_time));
- std::string headers(test.headers);
- HeadersToRaw(&headers);
- auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
- base::TimeDelta age =
- parsed->GetCurrentAge(request_time, response_time, current_time);
- EXPECT_EQ(test.expected_age, age.InSeconds());
- }
- const struct GetCurrentAgeTestData get_current_age_tests[] = {
- // Without Date header.
- {"HTTP/1.1 200 OK\n"
- "Age: 2",
- "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
- "Fri, 20 Jan 2011 10:40:14 GMT", 8},
- // Without Age header.
- {"HTTP/1.1 200 OK\n"
- "Date: Fri, 20 Jan 2011 10:40:10 GMT\n",
- "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
- "Fri, 20 Jan 2011 10:40:14 GMT", 6},
- // date_value > response_time with Age header.
- {"HTTP/1.1 200 OK\n"
- "Date: Fri, 20 Jan 2011 10:40:14 GMT\n"
- "Age: 2\n",
- "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
- "Fri, 20 Jan 2011 10:40:14 GMT", 8},
- // date_value > response_time without Age header.
- {"HTTP/1.1 200 OK\n"
- "Date: Fri, 20 Jan 2011 10:40:14 GMT\n",
- "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
- "Fri, 20 Jan 2011 10:40:14 GMT", 6},
- // apparent_age > corrected_age_value
- {"HTTP/1.1 200 OK\n"
- "Date: Fri, 20 Jan 2011 10:40:07 GMT\n"
- "Age: 0\n",
- "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
- "Fri, 20 Jan 2011 10:40:14 GMT", 7}};
- INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
- GetCurrentAgeTest,
- testing::ValuesIn(get_current_age_tests));
- } // namespace
- } // namespace net
|