123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953 |
- // 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 <cmath>
- #include <utility>
- #include <vector>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/files/file_util.h"
- #include "base/files/scoped_temp_dir.h"
- #include "base/memory/raw_ptr.h"
- #include "base/run_loop.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/string_piece.h"
- #include "base/test/metrics/histogram_tester.h"
- #include "base/test/scoped_feature_list.h"
- #include "base/test/test_file_util.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "build/build_config.h"
- #include "net/base/auth.h"
- #include "net/base/chunked_upload_data_stream.h"
- #include "net/base/completion_once_callback.h"
- #include "net/base/elements_upload_data_stream.h"
- #include "net/base/features.h"
- #include "net/base/ip_endpoint.h"
- #include "net/base/network_isolation_key.h"
- #include "net/base/proxy_delegate.h"
- #include "net/base/proxy_server.h"
- #include "net/base/proxy_string_util.h"
- #include "net/base/request_priority.h"
- #include "net/base/schemeful_site.h"
- #include "net/base/test_proxy_delegate.h"
- #include "net/base/upload_bytes_element_reader.h"
- #include "net/base/upload_file_element_reader.h"
- #include "net/dns/mock_host_resolver.h"
- #include "net/dns/public/secure_dns_policy.h"
- #include "net/http/http_auth_scheme.h"
- #include "net/http/http_network_session.h"
- #include "net/http/http_network_session_peer.h"
- #include "net/http/http_network_transaction.h"
- #include "net/http/http_proxy_connect_job.h"
- #include "net/http/http_response_info.h"
- #include "net/http/http_server_properties.h"
- #include "net/http/http_transaction_test_util.h"
- #include "net/http/test_upload_data_stream_not_allow_http1.h"
- #include "net/http/transport_security_state.h"
- #include "net/log/net_log_event_type.h"
- #include "net/log/net_log_with_source.h"
- #include "net/log/test_net_log.h"
- #include "net/log/test_net_log_util.h"
- #include "net/proxy_resolution/configured_proxy_resolution_service.h"
- #include "net/socket/next_proto.h"
- #include "net/socket/socket_tag.h"
- #include "net/spdy/alps_decoder.h"
- #include "net/spdy/buffered_spdy_framer.h"
- #include "net/spdy/spdy_http_stream.h"
- #include "net/spdy/spdy_http_utils.h"
- #include "net/spdy/spdy_session.h"
- #include "net/spdy/spdy_session_pool.h"
- #include "net/spdy/spdy_test_util_common.h"
- #include "net/ssl/ssl_connection_status_flags.h"
- #include "net/test/cert_test_util.h"
- #include "net/test/gtest_util.h"
- #include "net/test/test_data_directory.h"
- #include "net/test/test_with_task_environment.h"
- #include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h"
- #include "net/third_party/quiche/src/quiche/spdy/test_tools/spdy_test_utils.h"
- #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
- #include "net/url_request/url_request_context.h"
- #include "net/url_request/url_request_context_builder.h"
- #include "net/url_request/url_request_test_util.h"
- #include "net/websockets/websocket_test_util.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/platform_test.h"
- #include "url/gurl.h"
- using net::test::IsError;
- using net::test::IsOk;
- //-----------------------------------------------------------------------------
- namespace net {
- namespace {
- using testing::Each;
- using testing::Eq;
- const int32_t kBufferSize = SpdyHttpStream::kRequestBodyBufferSize;
- } // namespace
- const char kPushedUrl[] = "https://www.example.org/foo.dat";
- class SpdyNetworkTransactionTest : public TestWithTaskEnvironment {
- protected:
- SpdyNetworkTransactionTest()
- : TestWithTaskEnvironment(
- base::test::TaskEnvironment::TimeSource::MOCK_TIME),
- default_url_(kDefaultUrl),
- host_port_pair_(HostPortPair::FromURL(default_url_)) {}
- ~SpdyNetworkTransactionTest() override {
- // UploadDataStream may post a deletion task back to the message loop on
- // destruction.
- upload_data_stream_.reset();
- base::RunLoop().RunUntilIdle();
- }
- void SetUp() override {
- request_.method = "GET";
- request_.url = GURL(kDefaultUrl);
- request_.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- }
- struct TransactionHelperResult {
- int rv;
- std::string status_line;
- std::string response_data;
- HttpResponseInfo response_info;
- };
- // A helper class that handles all the initial npn/ssl setup.
- class NormalSpdyTransactionHelper {
- public:
- NormalSpdyTransactionHelper(
- const HttpRequestInfo& request,
- RequestPriority priority,
- const NetLogWithSource& log,
- std::unique_ptr<SpdySessionDependencies> session_deps)
- : request_(request),
- priority_(priority),
- session_deps_(session_deps.get() == nullptr
- ? std::make_unique<SpdySessionDependencies>()
- : std::move(session_deps)),
- log_(log) {
- session_deps_->net_log = log.net_log();
- session_ =
- SpdySessionDependencies::SpdyCreateSession(session_deps_.get());
- }
- ~NormalSpdyTransactionHelper() {
- // Any test which doesn't close the socket by sending it an EOF will
- // have a valid session left open, which leaks the entire session pool.
- // This is just fine - in fact, some of our tests intentionally do this
- // so that we can check consistency of the SpdySessionPool as the test
- // finishes. If we had put an EOF on the socket, the SpdySession would
- // have closed and we wouldn't be able to check the consistency.
- // Forcefully close existing sessions here.
- session()->spdy_session_pool()->CloseAllSessions();
- }
- void RunPreTestSetup() {
- // We're now ready to use SSL-npn SPDY.
- trans_ =
- std::make_unique<HttpNetworkTransaction>(priority_, session_.get());
- }
- // Start the transaction, read some data, finish.
- void RunDefaultTest() {
- if (!StartDefaultTest())
- return;
- FinishDefaultTest();
- }
- bool StartDefaultTest() {
- output_.rv = trans_->Start(&request_, callback_.callback(), log_);
- // We expect an IO Pending or some sort of error.
- EXPECT_LT(output_.rv, 0);
- return output_.rv == ERR_IO_PENDING;
- }
- void FinishDefaultTest() {
- output_.rv = callback_.WaitForResult();
- // Finish async network reads/writes.
- base::RunLoop().RunUntilIdle();
- if (output_.rv != OK) {
- session_->spdy_session_pool()->CloseCurrentSessions(ERR_ABORTED);
- return;
- }
- // Verify responses.
- const HttpResponseInfo* response = trans_->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
- response->connection_info);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_TRUE(response->was_alpn_negotiated);
- EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
- EXPECT_EQ(443, response->remote_endpoint.port());
- output_.status_line = response->headers->GetStatusLine();
- output_.response_info = *response; // Make a copy so we can verify.
- output_.rv = ReadTransaction(trans_.get(), &output_.response_data);
- }
- void FinishDefaultTestWithoutVerification() {
- output_.rv = callback_.WaitForResult();
- // Finish async network reads/writes.
- base::RunLoop().RunUntilIdle();
- if (output_.rv != OK)
- session_->spdy_session_pool()->CloseCurrentSessions(ERR_ABORTED);
- }
- void WaitForCallbackToComplete() { output_.rv = callback_.WaitForResult(); }
- // Most tests will want to call this function. In particular, the MockReads
- // should end with an empty read, and that read needs to be processed to
- // ensure proper deletion of the spdy_session_pool.
- void VerifyDataConsumed() {
- for (const SocketDataProvider* provider : data_vector_) {
- EXPECT_TRUE(provider->AllReadDataConsumed());
- EXPECT_TRUE(provider->AllWriteDataConsumed());
- }
- }
- // Occasionally a test will expect to error out before certain reads are
- // processed. In that case we want to explicitly ensure that the reads were
- // not processed.
- void VerifyDataNotConsumed() {
- for (const SocketDataProvider* provider : data_vector_) {
- EXPECT_FALSE(provider->AllReadDataConsumed());
- EXPECT_FALSE(provider->AllWriteDataConsumed());
- }
- }
- void RunToCompletion(SocketDataProvider* data) {
- RunPreTestSetup();
- AddData(data);
- RunDefaultTest();
- VerifyDataConsumed();
- }
- void RunToCompletionWithSSLData(
- SocketDataProvider* data,
- std::unique_ptr<SSLSocketDataProvider> ssl_provider) {
- RunPreTestSetup();
- AddDataWithSSLSocketDataProvider(data, std::move(ssl_provider));
- RunDefaultTest();
- VerifyDataConsumed();
- }
- void AddData(SocketDataProvider* data) {
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider->ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
- AddDataWithSSLSocketDataProvider(data, std::move(ssl_provider));
- }
- void AddDataWithSSLSocketDataProvider(
- SocketDataProvider* data,
- std::unique_ptr<SSLSocketDataProvider> ssl_provider) {
- data_vector_.push_back(data);
- if (ssl_provider->next_proto == kProtoUnknown)
- ssl_provider->next_proto = kProtoHTTP2;
- session_deps_->socket_factory->AddSSLSocketDataProvider(
- ssl_provider.get());
- ssl_vector_.push_back(std::move(ssl_provider));
- session_deps_->socket_factory->AddSocketDataProvider(data);
- }
- size_t GetSpdySessionCount() {
- std::unique_ptr<base::Value> value(
- session_->spdy_session_pool()->SpdySessionPoolInfoToValue());
- CHECK(value && value->is_list());
- return value->GetList().size();
- }
- HttpNetworkTransaction* trans() { return trans_.get(); }
- void ResetTrans() { trans_.reset(); }
- const TransactionHelperResult& output() { return output_; }
- HttpNetworkSession* session() const { return session_.get(); }
- SpdySessionDependencies* session_deps() { return session_deps_.get(); }
- private:
- typedef std::vector<SocketDataProvider*> DataVector;
- typedef std::vector<std::unique_ptr<SSLSocketDataProvider>> SSLVector;
- typedef std::vector<std::unique_ptr<SocketDataProvider>> AlternateVector;
- const HttpRequestInfo request_;
- const RequestPriority priority_;
- std::unique_ptr<SpdySessionDependencies> session_deps_;
- std::unique_ptr<HttpNetworkSession> session_;
- TransactionHelperResult output_;
- SSLVector ssl_vector_;
- TestCompletionCallback callback_;
- std::unique_ptr<HttpNetworkTransaction> trans_;
- DataVector data_vector_;
- const NetLogWithSource log_;
- };
- void ConnectStatusHelperWithExpectedStatus(const MockRead& status,
- int expected_status);
- void ConnectStatusHelper(const MockRead& status);
- [[nodiscard]] HttpRequestInfo CreateGetPushRequest() const {
- HttpRequestInfo request;
- request.method = "GET";
- request.url = GURL(kPushedUrl);
- request.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- return request;
- }
- void UsePostRequest() {
- ASSERT_FALSE(upload_data_stream_);
- std::vector<std::unique_ptr<UploadElementReader>> element_readers;
- element_readers.push_back(std::make_unique<UploadBytesElementReader>(
- kUploadData, kUploadDataSize));
- upload_data_stream_ = std::make_unique<ElementsUploadDataStream>(
- std::move(element_readers), 0);
- request_.method = "POST";
- request_.upload_data_stream = upload_data_stream_.get();
- }
- void UseFilePostRequest() {
- ASSERT_FALSE(upload_data_stream_);
- base::FilePath file_path;
- CHECK(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &file_path));
- CHECK_EQ(static_cast<int>(kUploadDataSize),
- base::WriteFile(file_path, kUploadData, kUploadDataSize));
- std::vector<std::unique_ptr<UploadElementReader>> element_readers;
- element_readers.push_back(std::make_unique<UploadFileElementReader>(
- base::ThreadTaskRunnerHandle::Get().get(), file_path, 0,
- kUploadDataSize, base::Time()));
- upload_data_stream_ = std::make_unique<ElementsUploadDataStream>(
- std::move(element_readers), 0);
- request_.method = "POST";
- request_.upload_data_stream = upload_data_stream_.get();
- request_.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- }
- void UseUnreadableFilePostRequest() {
- ASSERT_FALSE(upload_data_stream_);
- base::FilePath file_path;
- CHECK(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &file_path));
- CHECK_EQ(static_cast<int>(kUploadDataSize),
- base::WriteFile(file_path, kUploadData, kUploadDataSize));
- CHECK(base::MakeFileUnreadable(file_path));
- std::vector<std::unique_ptr<UploadElementReader>> element_readers;
- element_readers.push_back(std::make_unique<UploadFileElementReader>(
- base::ThreadTaskRunnerHandle::Get().get(), file_path, 0,
- kUploadDataSize, base::Time()));
- upload_data_stream_ = std::make_unique<ElementsUploadDataStream>(
- std::move(element_readers), 0);
- request_.method = "POST";
- request_.upload_data_stream = upload_data_stream_.get();
- }
- void UseComplexPostRequest() {
- ASSERT_FALSE(upload_data_stream_);
- const int kFileRangeOffset = 1;
- const int kFileRangeLength = 3;
- CHECK_LT(kFileRangeOffset + kFileRangeLength, kUploadDataSize);
- base::FilePath file_path;
- CHECK(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &file_path));
- CHECK_EQ(static_cast<int>(kUploadDataSize),
- base::WriteFile(file_path, kUploadData, kUploadDataSize));
- std::vector<std::unique_ptr<UploadElementReader>> element_readers;
- element_readers.push_back(std::make_unique<UploadBytesElementReader>(
- kUploadData, kFileRangeOffset));
- element_readers.push_back(std::make_unique<UploadFileElementReader>(
- base::ThreadTaskRunnerHandle::Get().get(), file_path, kFileRangeOffset,
- kFileRangeLength, base::Time()));
- element_readers.push_back(std::make_unique<UploadBytesElementReader>(
- kUploadData + kFileRangeOffset + kFileRangeLength,
- kUploadDataSize - (kFileRangeOffset + kFileRangeLength)));
- upload_data_stream_ = std::make_unique<ElementsUploadDataStream>(
- std::move(element_readers), 0);
- request_.method = "POST";
- request_.upload_data_stream = upload_data_stream_.get();
- }
- void UseChunkedPostRequest() {
- ASSERT_FALSE(upload_chunked_data_stream_);
- upload_chunked_data_stream_ = std::make_unique<ChunkedUploadDataStream>(0);
- request_.method = "POST";
- request_.upload_data_stream = upload_chunked_data_stream_.get();
- }
- // Read the result of a particular transaction, knowing that we've got
- // multiple transactions in the read pipeline; so as we read, we may have
- // to skip over data destined for other transactions while we consume
- // the data for |trans|.
- int ReadResult(HttpNetworkTransaction* trans, std::string* result) {
- const int kSize = 3000;
- int bytes_read = 0;
- scoped_refptr<IOBufferWithSize> buf =
- base::MakeRefCounted<IOBufferWithSize>(kSize);
- TestCompletionCallback callback;
- while (true) {
- int rv = trans->Read(buf.get(), kSize, callback.callback());
- if (rv == ERR_IO_PENDING) {
- rv = callback.WaitForResult();
- } else if (rv <= 0) {
- break;
- }
- result->append(buf->data(), rv);
- bytes_read += rv;
- }
- return bytes_read;
- }
- void VerifyStreamsClosed(const NormalSpdyTransactionHelper& helper) {
- // This lengthy block is reaching into the pool to dig out the active
- // session. Once we have the session, we verify that the streams are
- // all closed and not leaked at this point.
- SpdySessionKey key(HostPortPair::FromURL(request_.url),
- ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- request_.network_isolation_key, SecureDnsPolicy::kAllow);
- HttpNetworkSession* session = helper.session();
- base::WeakPtr<SpdySession> spdy_session =
- session->spdy_session_pool()->FindAvailableSession(
- key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- ASSERT_TRUE(spdy_session);
- EXPECT_EQ(0u, num_active_streams(spdy_session));
- EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session));
- }
- void RunServerPushTest(SequencedSocketData* data,
- HttpResponseInfo* response,
- HttpResponseInfo* push_response,
- const std::string& expected) {
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- nullptr);
- helper.RunPreTestSetup();
- helper.AddData(data);
- HttpNetworkTransaction* trans = helper.trans();
- // Start the transaction with basic parameters.
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- // Finish async network reads/writes.
- base::RunLoop().RunUntilIdle();
- // Request the pushed path.
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request = CreateGetPushRequest();
- rv = trans2.Start(&request, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- base::RunLoop().RunUntilIdle();
- // The data for the pushed path may be coming in more than 1 frame. Compile
- // the results into a single string.
- // Read the server push body.
- std::string result2;
- ReadResult(&trans2, &result2);
- // Read the response body.
- std::string result;
- ReadResult(trans, &result);
- // Verify that we consumed all test data.
- EXPECT_TRUE(data->AllReadDataConsumed());
- EXPECT_TRUE(data->AllWriteDataConsumed());
- LoadTimingInfo load_timing_info;
- EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info));
- EXPECT_TRUE(load_timing_info.push_start.is_null());
- EXPECT_TRUE(load_timing_info.push_end.is_null());
- LoadTimingInfo load_timing_info2;
- EXPECT_TRUE(trans2.GetLoadTimingInfo(&load_timing_info2));
- EXPECT_FALSE(load_timing_info2.push_start.is_null());
- EXPECT_FALSE(load_timing_info2.push_end.is_null());
- // Verify that the received push data is same as the expected push data.
- EXPECT_EQ(result2.compare(expected), 0) << "Received data: "
- << result2
- << "||||| Expected data: "
- << expected;
- // Verify the response HEADERS.
- // Copy the response info, because trans goes away.
- *response = *trans->GetResponseInfo();
- *push_response = *trans2.GetResponseInfo();
- VerifyStreamsClosed(helper);
- }
- void RunBrokenPushTest(SequencedSocketData* data, int expected_rv) {
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- nullptr);
- helper.RunPreTestSetup();
- helper.AddData(data);
- HttpNetworkTransaction* trans = helper.trans();
- // Start the transaction with basic parameters.
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- EXPECT_EQ(expected_rv, rv);
- // Finish async network reads/writes.
- base::RunLoop().RunUntilIdle();
- // Verify that we consumed all test data.
- EXPECT_TRUE(data->AllReadDataConsumed());
- EXPECT_TRUE(data->AllWriteDataConsumed());
- if (expected_rv == OK) {
- // Expected main request to succeed, even if push failed.
- HttpResponseInfo response = *trans->GetResponseInfo();
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- }
- }
- static void DeleteSessionCallback(NormalSpdyTransactionHelper* helper,
- int result) {
- helper->ResetTrans();
- }
- static void StartTransactionCallback(HttpNetworkSession* session,
- GURL url,
- NetLogWithSource log,
- int result) {
- HttpRequestInfo request;
- HttpNetworkTransaction trans(DEFAULT_PRIORITY, session);
- TestCompletionCallback callback;
- request.method = "GET";
- request.url = url;
- request.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- int rv = trans.Start(&request, callback.callback(), log);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- callback.WaitForResult();
- }
- ChunkedUploadDataStream* upload_chunked_data_stream() {
- return upload_chunked_data_stream_.get();
- }
- size_t num_active_streams(base::WeakPtr<SpdySession> session) {
- return session->active_streams_.size();
- }
- static size_t num_unclaimed_pushed_streams(
- base::WeakPtr<SpdySession> session) {
- return session->pool_->push_promise_index()->CountStreamsForSession(
- session.get());
- }
- static bool has_unclaimed_pushed_stream_for_url(
- base::WeakPtr<SpdySession> session,
- const GURL& url) {
- return session->pool_->push_promise_index()->FindStream(
- url, session.get()) != kNoPushedStreamFound;
- }
- static spdy::SpdyStreamId spdy_stream_hi_water_mark(
- base::WeakPtr<SpdySession> session) {
- return session->stream_hi_water_mark_;
- }
- base::RepeatingClosure FastForwardByCallback(base::TimeDelta delta) {
- return base::BindRepeating(&SpdyNetworkTransactionTest::FastForwardBy,
- base::Unretained(this), delta);
- }
- const GURL default_url_;
- const HostPortPair host_port_pair_;
- HttpRequestInfo request_;
- SpdyTestUtil spdy_util_;
- const NetLogWithSource log_;
- private:
- std::unique_ptr<ChunkedUploadDataStream> upload_chunked_data_stream_;
- std::unique_ptr<UploadDataStream> upload_data_stream_;
- base::ScopedTempDir temp_dir_;
- };
- // Verify HttpNetworkTransaction constructor.
- TEST_F(SpdyNetworkTransactionTest, Constructor) {
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- std::unique_ptr<HttpNetworkSession> session(
- SpdySessionDependencies::SpdyCreateSession(session_deps.get()));
- auto trans =
- std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
- }
- TEST_F(SpdyNetworkTransactionTest, Get) {
- // Construct the request.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- TEST_F(SpdyNetworkTransactionTest, SetPriority) {
- for (bool set_priority_before_starting_transaction : {true, false}) {
- SpdyTestUtil spdy_test_util;
- spdy::SpdySerializedFrame req(
- spdy_test_util.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(
- spdy_test_util.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(
- spdy_test_util.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, HIGHEST, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- if (set_priority_before_starting_transaction) {
- helper.trans()->SetPriority(LOWEST);
- EXPECT_TRUE(helper.StartDefaultTest());
- } else {
- EXPECT_TRUE(helper.StartDefaultTest());
- helper.trans()->SetPriority(LOWEST);
- }
- helper.FinishDefaultTest();
- helper.VerifyDataConsumed();
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- }
- // Test that changing the request priority of an existing stream triggers
- // sending PRIORITY frames in case there are multiple open streams and their
- // relative priorities change.
- TEST_F(SpdyNetworkTransactionTest, SetPriorityOnExistingStream) {
- const char* kUrl2 = "https://www.example.org/bar";
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST));
- spdy::SpdySerializedFrame req2(spdy_util_.ConstructSpdyGet(kUrl2, 3, MEDIUM));
- spdy::SpdySerializedFrame priority1(
- spdy_util_.ConstructSpdyPriority(3, 0, MEDIUM, true));
- spdy::SpdySerializedFrame priority2(
- spdy_util_.ConstructSpdyPriority(1, 3, LOWEST, true));
- MockWrite writes[] = {CreateMockWrite(req1, 0), CreateMockWrite(req2, 2),
- CreateMockWrite(priority1, 4),
- CreateMockWrite(priority2, 5)};
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead reads[] = {CreateMockRead(resp1, 1), CreateMockRead(resp2, 3),
- CreateMockRead(body1, 6), CreateMockRead(body2, 7),
- MockRead(ASYNC, 0, 8)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, HIGHEST, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- EXPECT_TRUE(helper.StartDefaultTest());
- // Open HTTP/2 connection and create first stream.
- base::RunLoop().RunUntilIdle();
- HttpNetworkTransaction trans2(MEDIUM, helper.session());
- HttpRequestInfo request2;
- request2.url = GURL(kUrl2);
- request2.method = "GET";
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback2;
- int rv = trans2.Start(&request2, callback2.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // Create second stream.
- base::RunLoop().RunUntilIdle();
- // First request has HIGHEST priority, second request has MEDIUM priority.
- // Changing the priority of the first request to LOWEST changes their order,
- // and therefore triggers sending PRIORITY frames.
- helper.trans()->SetPriority(LOWEST);
- helper.FinishDefaultTest();
- helper.VerifyDataConsumed();
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- rv = callback2.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- const HttpResponseInfo* response2 = trans2.GetResponseInfo();
- ASSERT_TRUE(response2);
- ASSERT_TRUE(response2->headers);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
- response2->connection_info);
- EXPECT_EQ("HTTP/1.1 200", response2->headers->GetStatusLine());
- }
- // Create two requests: a lower priority one first, then a higher priority one.
- // Test that the second request gets sent out first.
- TEST_F(SpdyNetworkTransactionTest, RequestsOrderedByPriority) {
- const char* kUrl2 = "https://www.example.org/foo";
- // First send second request on stream 1, then first request on stream 3.
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(kUrl2, 1, HIGHEST));
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOW));
- MockWrite writes[] = {CreateMockWrite(req2, 0), CreateMockWrite(req1, 1)};
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(
- spdy_util_.ConstructSpdyDataFrame(1, "stream 1", true));
- spdy::SpdySerializedFrame body1(
- spdy_util_.ConstructSpdyDataFrame(3, "stream 3", true));
- MockRead reads[] = {CreateMockRead(resp2, 2), CreateMockRead(body2, 3),
- CreateMockRead(resp1, 4), CreateMockRead(body1, 5),
- MockRead(ASYNC, 0, 6)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, LOW, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- // Create HTTP/2 connection. This is necessary because starting the first
- // transaction does not create the connection yet, so the second request
- // could not use the same connection, whereas running the message loop after
- // starting the first transaction would call Socket::Write() with the first
- // HEADERS frame, so the second transaction could not get ahead of it.
- SpdySessionKey key(HostPortPair("www.example.org", 443),
- ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- auto spdy_session = CreateSpdySession(helper.session(), key, log_);
- EXPECT_TRUE(spdy_session);
- // Start first transaction.
- EXPECT_TRUE(helper.StartDefaultTest());
- // Start second transaction.
- HttpNetworkTransaction trans2(HIGHEST, helper.session());
- HttpRequestInfo request2;
- request2.url = GURL(kUrl2);
- request2.method = "GET";
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback2;
- int rv = trans2.Start(&request2, callback2.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // Complete first transaction and verify results.
- helper.FinishDefaultTest();
- helper.VerifyDataConsumed();
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("stream 3", out.response_data);
- // Complete second transaction and verify results.
- rv = callback2.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- const HttpResponseInfo* response2 = trans2.GetResponseInfo();
- ASSERT_TRUE(response2);
- ASSERT_TRUE(response2->headers);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
- response2->connection_info);
- EXPECT_EQ("HTTP/1.1 200", response2->headers->GetStatusLine());
- std::string response_data;
- ReadTransaction(&trans2, &response_data);
- EXPECT_EQ("stream 1", response_data);
- }
- // Test that already enqueued HEADERS frames are reordered if their relative
- // priority changes.
- TEST_F(SpdyNetworkTransactionTest, QueuedFramesReorderedOnPriorityChange) {
- const char* kUrl2 = "https://www.example.org/foo";
- const char* kUrl3 = "https://www.example.org/bar";
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, DEFAULT_PRIORITY));
- spdy::SpdySerializedFrame req3(spdy_util_.ConstructSpdyGet(kUrl3, 3, MEDIUM));
- spdy::SpdySerializedFrame req2(spdy_util_.ConstructSpdyGet(kUrl2, 5, LOWEST));
- MockWrite writes[] = {MockWrite(ASYNC, ERR_IO_PENDING, 0),
- CreateMockWrite(req1, 1), CreateMockWrite(req3, 2),
- CreateMockWrite(req2, 3)};
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame resp3(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5));
- spdy::SpdySerializedFrame body1(
- spdy_util_.ConstructSpdyDataFrame(1, "stream 1", true));
- spdy::SpdySerializedFrame body3(
- spdy_util_.ConstructSpdyDataFrame(3, "stream 3", true));
- spdy::SpdySerializedFrame body2(
- spdy_util_.ConstructSpdyDataFrame(5, "stream 5", true));
- MockRead reads[] = {CreateMockRead(resp1, 4), CreateMockRead(body1, 5),
- CreateMockRead(resp3, 6), CreateMockRead(body3, 7),
- CreateMockRead(resp2, 8), CreateMockRead(body2, 9),
- MockRead(ASYNC, 0, 10)};
- SequencedSocketData data(reads, writes);
- // Priority of first request does not matter, because Socket::Write() will be
- // called with its HEADERS frame before the other requests start.
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- EXPECT_TRUE(helper.StartDefaultTest());
- // Open HTTP/2 connection, create HEADERS frame for first request, and call
- // Socket::Write() with that frame. After this, no other request can get
- // ahead of the first one.
- base::RunLoop().RunUntilIdle();
- HttpNetworkTransaction trans2(HIGHEST, helper.session());
- HttpRequestInfo request2;
- request2.url = GURL(kUrl2);
- request2.method = "GET";
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback2;
- int rv = trans2.Start(&request2, callback2.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- HttpNetworkTransaction trans3(MEDIUM, helper.session());
- HttpRequestInfo request3;
- request3.url = GURL(kUrl3);
- request3.method = "GET";
- request3.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback3;
- rv = trans3.Start(&request3, callback3.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // Create HEADERS frames for second and third request and enqueue them in
- // SpdyWriteQueue with their original priorities. Writing of the first
- // HEADERS frame to the socked still has not completed.
- base::RunLoop().RunUntilIdle();
- // Second request is of HIGHEST, third of MEDIUM priority. Changing second
- // request to LOWEST changes their relative order. This should result in
- // already enqueued frames being reordered within SpdyWriteQueue.
- trans2.SetPriority(LOWEST);
- // Complete async write of the first HEADERS frame.
- data.Resume();
- helper.FinishDefaultTest();
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("stream 1", out.response_data);
- rv = callback2.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- const HttpResponseInfo* response2 = trans2.GetResponseInfo();
- ASSERT_TRUE(response2);
- ASSERT_TRUE(response2->headers);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
- response2->connection_info);
- EXPECT_EQ("HTTP/1.1 200", response2->headers->GetStatusLine());
- std::string response_data;
- ReadTransaction(&trans2, &response_data);
- EXPECT_EQ("stream 5", response_data);
- rv = callback3.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- const HttpResponseInfo* response3 = trans3.GetResponseInfo();
- ASSERT_TRUE(response3);
- ASSERT_TRUE(response3->headers);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
- response3->connection_info);
- EXPECT_EQ("HTTP/1.1 200", response3->headers->GetStatusLine());
- ReadTransaction(&trans3, &response_data);
- EXPECT_EQ("stream 3", response_data);
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, GetAtEachPriority) {
- for (RequestPriority p = MINIMUM_PRIORITY; p <= MAXIMUM_PRIORITY;
- p = RequestPriority(p + 1)) {
- SpdyTestUtil spdy_test_util;
- // Construct the request.
- spdy::SpdySerializedFrame req(
- spdy_test_util.ConstructSpdyGet(nullptr, 0, 1, p));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdyPriority spdy_prio = 0;
- EXPECT_TRUE(GetSpdyPriority(req, &spdy_prio));
- // this repeats the RequestPriority-->spdy::SpdyPriority mapping from
- // spdy::SpdyFramer::ConvertRequestPriorityToSpdyPriority to make
- // sure it's being done right.
- switch (p) {
- case HIGHEST:
- EXPECT_EQ(0, spdy_prio);
- break;
- case MEDIUM:
- EXPECT_EQ(1, spdy_prio);
- break;
- case LOW:
- EXPECT_EQ(2, spdy_prio);
- break;
- case LOWEST:
- EXPECT_EQ(3, spdy_prio);
- break;
- case IDLE:
- EXPECT_EQ(4, spdy_prio);
- break;
- case THROTTLED:
- EXPECT_EQ(5, spdy_prio);
- break;
- default:
- FAIL();
- }
- spdy::SpdySerializedFrame resp(
- spdy_test_util.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(
- spdy_test_util.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, p, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- }
- // Start three gets simultaniously; making sure that multiplexed
- // streams work properly.
- // This can't use the TransactionHelper method, since it only
- // handles a single transaction, and finishes them as soon
- // as it launches them.
- // TODO(gavinp): create a working generalized TransactionHelper that
- // can allow multiple streams in flight.
- TEST_F(SpdyNetworkTransactionTest, ThreeGets) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
- spdy::SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false));
- spdy::SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
- spdy::SpdySerializedFrame req3(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 5, LOWEST));
- spdy::SpdySerializedFrame resp3(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5));
- spdy::SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, false));
- spdy::SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(5, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(req2, 3),
- CreateMockWrite(req3, 6),
- };
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
- CreateMockRead(resp3, 7), CreateMockRead(body3, 8),
- CreateMockRead(fbody, 9), CreateMockRead(fbody2, 10),
- CreateMockRead(fbody3, 11),
- MockRead(ASYNC, 0, 12), // EOF
- };
- SequencedSocketData data(reads, writes);
- SequencedSocketData data_placeholder1;
- SequencedSocketData data_placeholder2;
- TransactionHelperResult out;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- // We require placeholder data because three get requests are sent out at
- // the same time which results in three sockets being connected. The first
- // on will negotiate SPDY and will be used for all requests.
- helper.AddData(&data_placeholder1);
- helper.AddData(&data_placeholder2);
- TestCompletionCallback callback1;
- TestCompletionCallback callback2;
- TestCompletionCallback callback3;
- HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- HttpNetworkTransaction trans3(DEFAULT_PRIORITY, helper.session());
- out.rv = trans1.Start(&request_, callback1.callback(), log_);
- ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING));
- out.rv = trans2.Start(&request_, callback2.callback(), log_);
- ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING));
- out.rv = trans3.Start(&request_, callback3.callback(), log_);
- ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING));
- out.rv = callback1.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- out.rv = callback3.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- const HttpResponseInfo* response1 = trans1.GetResponseInfo();
- EXPECT_TRUE(response1->headers);
- EXPECT_TRUE(response1->was_fetched_via_spdy);
- out.status_line = response1->headers->GetStatusLine();
- out.response_info = *response1;
- trans2.GetResponseInfo();
- out.rv = ReadTransaction(&trans1, &out.response_data);
- helper.VerifyDataConsumed();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- }
- TEST_F(SpdyNetworkTransactionTest, TwoGetsLateBinding) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
- spdy::SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false));
- spdy::SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(req2, 3),
- };
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
- CreateMockRead(fbody, 6), CreateMockRead(fbody2, 7),
- MockRead(ASYNC, 0, 8), // EOF
- };
- SequencedSocketData data(reads, writes);
- MockConnect never_finishing_connect(SYNCHRONOUS, ERR_IO_PENDING);
- SequencedSocketData data_placeholder;
- data_placeholder.set_connect_data(never_finishing_connect);
- TransactionHelperResult out;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- // We require placeholder data because two requests are sent out at
- // the same time which results in two sockets being connected. The first
- // on will negotiate SPDY and will be used for all requests.
- helper.AddData(&data_placeholder);
- HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- TestCompletionCallback callback1;
- TestCompletionCallback callback2;
- out.rv = trans1.Start(&request_, callback1.callback(), log_);
- ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING));
- out.rv = trans2.Start(&request_, callback2.callback(), log_);
- ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING));
- out.rv = callback1.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- out.rv = callback2.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- const HttpResponseInfo* response1 = trans1.GetResponseInfo();
- EXPECT_TRUE(response1->headers);
- EXPECT_TRUE(response1->was_fetched_via_spdy);
- out.status_line = response1->headers->GetStatusLine();
- out.response_info = *response1;
- out.rv = ReadTransaction(&trans1, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- const HttpResponseInfo* response2 = trans2.GetResponseInfo();
- EXPECT_TRUE(response2->headers);
- EXPECT_TRUE(response2->was_fetched_via_spdy);
- out.status_line = response2->headers->GetStatusLine();
- out.response_info = *response2;
- out.rv = ReadTransaction(&trans2, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, TwoGetsLateBindingFromPreconnect) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
- spdy::SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false));
- spdy::SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(req2, 3),
- };
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
- CreateMockRead(fbody, 6), CreateMockRead(fbody2, 7),
- MockRead(ASYNC, 0, 8), // EOF
- };
- SequencedSocketData preconnect_data(reads, writes);
- MockConnect never_finishing_connect(ASYNC, ERR_IO_PENDING);
- SequencedSocketData data_placeholder;
- data_placeholder.set_connect_data(never_finishing_connect);
- TransactionHelperResult out;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&preconnect_data);
- // We require placeholder data because 3 connections are attempted (first is
- // the preconnect, 2nd and 3rd are the never finished connections.
- helper.AddData(&data_placeholder);
- helper.AddData(&data_placeholder);
- HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- TestCompletionCallback callback1;
- TestCompletionCallback callback2;
- // Preconnect the first.
- HttpStreamFactory* http_stream_factory =
- helper.session()->http_stream_factory();
- http_stream_factory->PreconnectStreams(1, request_);
- out.rv = trans1.Start(&request_, callback1.callback(), log_);
- ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING));
- out.rv = trans2.Start(&request_, callback2.callback(), log_);
- ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING));
- out.rv = callback1.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- out.rv = callback2.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- const HttpResponseInfo* response1 = trans1.GetResponseInfo();
- EXPECT_TRUE(response1->headers);
- EXPECT_TRUE(response1->was_fetched_via_spdy);
- out.status_line = response1->headers->GetStatusLine();
- out.response_info = *response1;
- out.rv = ReadTransaction(&trans1, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- const HttpResponseInfo* response2 = trans2.GetResponseInfo();
- EXPECT_TRUE(response2->headers);
- EXPECT_TRUE(response2->was_fetched_via_spdy);
- out.status_line = response2->headers->GetStatusLine();
- out.response_info = *response2;
- out.rv = ReadTransaction(&trans2, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- helper.VerifyDataConsumed();
- }
- // Similar to ThreeGets above, however this test adds a SETTINGS
- // frame. The SETTINGS frame is read during the IO loop waiting on
- // the first transaction completion, and sets a maximum concurrent
- // stream limit of 1. This means that our IO loop exists after the
- // second transaction completes, so we can assert on read_index().
- TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrent) {
- // Construct the request.
- // Each request fully completes before the next starts.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
- spdy::SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false));
- spdy::SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
- spdy_util_.UpdateWithStreamDestruction(3);
- spdy::SpdySerializedFrame req3(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 5, LOWEST));
- spdy::SpdySerializedFrame resp3(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5));
- spdy::SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, false));
- spdy::SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(5, true));
- spdy::SettingsMap settings;
- const uint32_t max_concurrent_streams = 1;
- settings[spdy::SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams;
- spdy::SpdySerializedFrame settings_frame(
- spdy_util_.ConstructSpdySettings(settings));
- spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5),
- CreateMockWrite(req2, 6), CreateMockWrite(req3, 10),
- };
- MockRead reads[] = {
- CreateMockRead(settings_frame, 1),
- CreateMockRead(resp, 2),
- CreateMockRead(body, 3),
- CreateMockRead(fbody, 4),
- CreateMockRead(resp2, 7),
- CreateMockRead(body2, 8),
- CreateMockRead(fbody2, 9),
- CreateMockRead(resp3, 11),
- CreateMockRead(body3, 12),
- CreateMockRead(fbody3, 13),
- MockRead(ASYNC, 0, 14), // EOF
- };
- SequencedSocketData data(reads, writes);
- TransactionHelperResult out;
- {
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- HttpNetworkTransaction trans3(DEFAULT_PRIORITY, helper.session());
- TestCompletionCallback callback1;
- TestCompletionCallback callback2;
- TestCompletionCallback callback3;
- out.rv = trans1.Start(&request_, callback1.callback(), log_);
- ASSERT_EQ(out.rv, ERR_IO_PENDING);
- // Run transaction 1 through quickly to force a read of our SETTINGS
- // frame.
- out.rv = callback1.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- out.rv = trans2.Start(&request_, callback2.callback(), log_);
- ASSERT_EQ(out.rv, ERR_IO_PENDING);
- out.rv = trans3.Start(&request_, callback3.callback(), log_);
- ASSERT_EQ(out.rv, ERR_IO_PENDING);
- out.rv = callback2.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- out.rv = callback3.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- const HttpResponseInfo* response1 = trans1.GetResponseInfo();
- ASSERT_TRUE(response1);
- EXPECT_TRUE(response1->headers);
- EXPECT_TRUE(response1->was_fetched_via_spdy);
- out.status_line = response1->headers->GetStatusLine();
- out.response_info = *response1;
- out.rv = ReadTransaction(&trans1, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- const HttpResponseInfo* response2 = trans2.GetResponseInfo();
- out.status_line = response2->headers->GetStatusLine();
- out.response_info = *response2;
- out.rv = ReadTransaction(&trans2, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- const HttpResponseInfo* response3 = trans3.GetResponseInfo();
- out.status_line = response3->headers->GetStatusLine();
- out.response_info = *response3;
- out.rv = ReadTransaction(&trans3, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- helper.VerifyDataConsumed();
- }
- EXPECT_THAT(out.rv, IsOk());
- }
- // Similar to ThreeGetsWithMaxConcurrent above, however this test adds
- // a fourth transaction. The third and fourth transactions have
- // different data ("hello!" vs "hello!hello!") and because of the
- // user specified priority, we expect to see them inverted in
- // the response from the server.
- TEST_F(SpdyNetworkTransactionTest, FourGetsWithMaxConcurrentPriority) {
- // Construct the request.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
- spdy::SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false));
- spdy::SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
- spdy_util_.UpdateWithStreamDestruction(3);
- spdy::SpdySerializedFrame req4(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 5, HIGHEST));
- spdy::SpdySerializedFrame resp4(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5));
- spdy::SpdySerializedFrame fbody4(spdy_util_.ConstructSpdyDataFrame(5, true));
- spdy_util_.UpdateWithStreamDestruction(5);
- spdy::SpdySerializedFrame req3(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 7, LOWEST));
- spdy::SpdySerializedFrame resp3(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 7));
- spdy::SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(7, false));
- spdy::SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(7, true));
- spdy::SettingsMap settings;
- const uint32_t max_concurrent_streams = 1;
- settings[spdy::SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams;
- spdy::SpdySerializedFrame settings_frame(
- spdy_util_.ConstructSpdySettings(settings));
- spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5),
- // By making these synchronous, it guarantees that they are not *started*
- // before their sequence number, which in turn verifies that only a single
- // request is in-flight at a time.
- CreateMockWrite(req2, 6, SYNCHRONOUS),
- CreateMockWrite(req4, 10, SYNCHRONOUS),
- CreateMockWrite(req3, 13, SYNCHRONOUS),
- };
- MockRead reads[] = {
- CreateMockRead(settings_frame, 1),
- CreateMockRead(resp, 2),
- CreateMockRead(body, 3),
- CreateMockRead(fbody, 4),
- CreateMockRead(resp2, 7),
- CreateMockRead(body2, 8),
- CreateMockRead(fbody2, 9),
- CreateMockRead(resp4, 11),
- CreateMockRead(fbody4, 12),
- CreateMockRead(resp3, 14),
- CreateMockRead(body3, 15),
- CreateMockRead(fbody3, 16),
- MockRead(ASYNC, 0, 17), // EOF
- };
- SequencedSocketData data(reads, writes);
- TransactionHelperResult out;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- HttpNetworkTransaction trans3(DEFAULT_PRIORITY, helper.session());
- HttpNetworkTransaction trans4(HIGHEST, helper.session());
- TestCompletionCallback callback1;
- TestCompletionCallback callback2;
- TestCompletionCallback callback3;
- TestCompletionCallback callback4;
- out.rv = trans1.Start(&request_, callback1.callback(), log_);
- ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING));
- // Run transaction 1 through quickly to force a read of our SETTINGS frame.
- out.rv = callback1.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- // Finish async network reads and writes associated with |trans1|.
- base::RunLoop().RunUntilIdle();
- out.rv = trans2.Start(&request_, callback2.callback(), log_);
- ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING));
- out.rv = trans3.Start(&request_, callback3.callback(), log_);
- ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING));
- out.rv = trans4.Start(&request_, callback4.callback(), log_);
- ASSERT_THAT(out.rv, IsError(ERR_IO_PENDING));
- out.rv = callback2.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- out.rv = callback3.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- const HttpResponseInfo* response1 = trans1.GetResponseInfo();
- EXPECT_TRUE(response1->headers);
- EXPECT_TRUE(response1->was_fetched_via_spdy);
- out.status_line = response1->headers->GetStatusLine();
- out.response_info = *response1;
- out.rv = ReadTransaction(&trans1, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- const HttpResponseInfo* response2 = trans2.GetResponseInfo();
- out.status_line = response2->headers->GetStatusLine();
- out.response_info = *response2;
- out.rv = ReadTransaction(&trans2, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- // notice: response3 gets two hellos, response4 gets one
- // hello, so we know dequeuing priority was respected.
- const HttpResponseInfo* response3 = trans3.GetResponseInfo();
- out.status_line = response3->headers->GetStatusLine();
- out.response_info = *response3;
- out.rv = ReadTransaction(&trans3, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- out.rv = callback4.WaitForResult();
- EXPECT_THAT(out.rv, IsOk());
- const HttpResponseInfo* response4 = trans4.GetResponseInfo();
- out.status_line = response4->headers->GetStatusLine();
- out.response_info = *response4;
- out.rv = ReadTransaction(&trans4, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- helper.VerifyDataConsumed();
- EXPECT_THAT(out.rv, IsOk());
- }
- // Similar to ThreeGetsMaxConcurrrent above, however, this test
- // deletes a session in the middle of the transaction to ensure
- // that we properly remove pendingcreatestream objects from
- // the spdy_session
- TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentDelete) {
- // Construct the request.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
- spdy::SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false));
- spdy::SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
- spdy::SettingsMap settings;
- const uint32_t max_concurrent_streams = 1;
- settings[spdy::SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams;
- spdy::SpdySerializedFrame settings_frame(
- spdy_util_.ConstructSpdySettings(settings));
- spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5),
- CreateMockWrite(req2, 6),
- };
- MockRead reads[] = {
- CreateMockRead(settings_frame, 1), CreateMockRead(resp, 2),
- CreateMockRead(body, 3), CreateMockRead(fbody, 4),
- CreateMockRead(resp2, 7), CreateMockRead(body2, 8),
- CreateMockRead(fbody2, 9), MockRead(ASYNC, 0, 10), // EOF
- };
- SequencedSocketData data(reads, writes);
- TransactionHelperResult out;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- auto trans1 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
- helper.session());
- auto trans2 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
- helper.session());
- auto trans3 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
- helper.session());
- TestCompletionCallback callback1;
- TestCompletionCallback callback2;
- TestCompletionCallback callback3;
- out.rv = trans1->Start(&request_, callback1.callback(), log_);
- ASSERT_EQ(out.rv, ERR_IO_PENDING);
- // Run transaction 1 through quickly to force a read of our SETTINGS frame.
- out.rv = callback1.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- out.rv = trans2->Start(&request_, callback2.callback(), log_);
- ASSERT_EQ(out.rv, ERR_IO_PENDING);
- out.rv = trans3->Start(&request_, callback3.callback(), log_);
- trans3.reset();
- ASSERT_EQ(out.rv, ERR_IO_PENDING);
- out.rv = callback2.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- const HttpResponseInfo* response1 = trans1->GetResponseInfo();
- ASSERT_TRUE(response1);
- EXPECT_TRUE(response1->headers);
- EXPECT_TRUE(response1->was_fetched_via_spdy);
- out.status_line = response1->headers->GetStatusLine();
- out.response_info = *response1;
- out.rv = ReadTransaction(trans1.get(), &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- const HttpResponseInfo* response2 = trans2->GetResponseInfo();
- ASSERT_TRUE(response2);
- out.status_line = response2->headers->GetStatusLine();
- out.response_info = *response2;
- out.rv = ReadTransaction(trans2.get(), &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!hello!", out.response_data);
- helper.VerifyDataConsumed();
- EXPECT_THAT(out.rv, IsOk());
- }
- namespace {
- // A helper class that will delete |transaction| on error when the callback is
- // invoked.
- class KillerCallback : public TestCompletionCallbackBase {
- public:
- explicit KillerCallback(std::unique_ptr<HttpNetworkTransaction> transaction)
- : transaction_(std::move(transaction)) {}
- ~KillerCallback() override = default;
- CompletionOnceCallback callback() {
- return base::BindOnce(&KillerCallback::OnComplete, base::Unretained(this));
- }
- private:
- void OnComplete(int result) {
- if (result < 0)
- transaction_.reset();
- SetResult(result);
- }
- std::unique_ptr<HttpNetworkTransaction> transaction_;
- };
- } // namespace
- // Similar to ThreeGetsMaxConcurrrentDelete above, however, this test
- // closes the socket while we have a pending transaction waiting for
- // a pending stream creation. http://crbug.com/52901
- TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentSocketClose) {
- // Construct the request. Each stream uses a different priority to provide
- // more useful failure information if the requests are made in an unexpected
- // order.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
- spdy::SpdySerializedFrame fin_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, MEDIUM));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SettingsMap settings;
- const uint32_t max_concurrent_streams = 1;
- settings[spdy::SETTINGS_MAX_CONCURRENT_STREAMS] = max_concurrent_streams;
- spdy::SpdySerializedFrame settings_frame(
- spdy_util_.ConstructSpdySettings(settings));
- spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
- MockWrite writes[] = {CreateMockWrite(req, 0),
- CreateMockWrite(settings_ack, 6),
- CreateMockWrite(req2, 7)};
- MockRead reads[] = {
- CreateMockRead(settings_frame, 1), CreateMockRead(resp, 2),
- CreateMockRead(body, 3),
- // Delay the request here. For this test to pass, the three HTTP streams
- // have to be created in order, but SpdySession doesn't actually guarantee
- // that (See note in SpdySession::ProcessPendingStreamRequests). As a
- // workaround, delay finishing up the first stream until the second and
- // third streams are waiting in the SPDY stream request queue.
- MockRead(ASYNC, ERR_IO_PENDING, 4), CreateMockRead(fin_body, 5),
- CreateMockRead(resp2, 8),
- // The exact error does not matter, but some errors, such as
- // ERR_CONNECTION_RESET, may trigger a retry, which this test does not
- // account for.
- MockRead(ASYNC, ERR_SSL_BAD_RECORD_MAC_ALERT, 9), // Abort!
- };
- SequencedSocketData data(reads, writes);
- SequencedSocketData data_placeholder;
- TransactionHelperResult out;
- NormalSpdyTransactionHelper helper(request_, HIGHEST, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- // We require placeholder data because three get requests are sent out, so
- // there needs to be three sets of SSL connection data.
- helper.AddData(&data_placeholder);
- helper.AddData(&data_placeholder);
- HttpNetworkTransaction trans1(HIGHEST, helper.session());
- HttpNetworkTransaction trans2(MEDIUM, helper.session());
- auto trans3 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
- helper.session());
- auto* trans3_ptr = trans3.get();
- TestCompletionCallback callback1;
- TestCompletionCallback callback2;
- KillerCallback callback3(std::move(trans3));
- out.rv = trans1.Start(&request_, callback1.callback(), log_);
- ASSERT_EQ(out.rv, ERR_IO_PENDING);
- // Run transaction 1 through quickly to force a read of our SETTINGS frame.
- out.rv = callback1.WaitForResult();
- ASSERT_THAT(out.rv, IsOk());
- out.rv = trans2.Start(&request_, callback2.callback(), log_);
- ASSERT_EQ(out.rv, ERR_IO_PENDING);
- out.rv = trans3_ptr->Start(&request_, callback3.callback(), log_);
- ASSERT_EQ(out.rv, ERR_IO_PENDING);
- // Run until both transactions are in the SpdySession's queue, waiting for the
- // final request to complete.
- base::RunLoop().RunUntilIdle();
- data.Resume();
- out.rv = callback3.WaitForResult();
- EXPECT_THAT(out.rv, IsError(ERR_SSL_BAD_RECORD_MAC_ALERT));
- const HttpResponseInfo* response1 = trans1.GetResponseInfo();
- ASSERT_TRUE(response1);
- EXPECT_TRUE(response1->headers);
- EXPECT_TRUE(response1->was_fetched_via_spdy);
- out.status_line = response1->headers->GetStatusLine();
- out.response_info = *response1;
- out.rv = ReadTransaction(&trans1, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- const HttpResponseInfo* response2 = trans2.GetResponseInfo();
- ASSERT_TRUE(response2);
- out.status_line = response2->headers->GetStatusLine();
- out.response_info = *response2;
- out.rv = ReadTransaction(&trans2, &out.response_data);
- EXPECT_THAT(out.rv, IsError(ERR_SSL_BAD_RECORD_MAC_ALERT));
- helper.VerifyDataConsumed();
- }
- // Test that a simple PUT request works.
- TEST_F(SpdyNetworkTransactionTest, Put) {
- // Setup the request.
- request_.method = "PUT";
- spdy::Http2HeaderBlock put_headers(
- spdy_util_.ConstructPutHeaderBlock(kDefaultUrl, 0));
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyHeaders(1, std::move(put_headers), LOWEST, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0),
- };
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- }
- // Test that a simple HEAD request works.
- TEST_F(SpdyNetworkTransactionTest, Head) {
- // Setup the request.
- request_.method = "HEAD";
- spdy::Http2HeaderBlock head_headers(
- spdy_util_.ConstructHeadHeaderBlock(kDefaultUrl, 0));
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyHeaders(
- 1, std::move(head_headers), LOWEST, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0),
- };
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- }
- // Test that a simple POST works.
- TEST_F(SpdyNetworkTransactionTest, Post) {
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 2), CreateMockRead(body, 3),
- MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- UsePostRequest();
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- // Test that a POST with a file works.
- TEST_F(SpdyNetworkTransactionTest, FilePost) {
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 2), CreateMockRead(body, 3),
- MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- UseFilePostRequest();
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- // Test that a POST with a unreadable file fails.
- TEST_F(SpdyNetworkTransactionTest, UnreadableFilePost) {
- MockWrite writes[] = {
- MockWrite(ASYNC, 0, 0) // EOF
- };
- MockRead reads[] = {
- MockRead(ASYNC, 0, 1) // EOF
- };
- SequencedSocketData data(reads, writes);
- UseUnreadableFilePostRequest();
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- helper.RunDefaultTest();
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataNotConsumed();
- EXPECT_THAT(helper.output().rv, IsError(ERR_ACCESS_DENIED));
- }
- // Test that a complex POST works.
- TEST_F(SpdyNetworkTransactionTest, ComplexPost) {
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 2), CreateMockRead(body, 3),
- MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- UseComplexPostRequest();
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- // Test that a chunked POST works.
- TEST_F(SpdyNetworkTransactionTest, ChunkedPost) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructChunkedSpdyPost(nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(body, 1),
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 2), CreateMockRead(body, 3),
- MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- UseChunkedPostRequest();
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- // These chunks get merged into a single frame when being sent.
- const int kFirstChunkSize = kUploadDataSize/2;
- upload_chunked_data_stream()->AppendData(kUploadData, kFirstChunkSize, false);
- upload_chunked_data_stream()->AppendData(
- kUploadData + kFirstChunkSize, kUploadDataSize - kFirstChunkSize, true);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ(kUploadData, out.response_data);
- }
- // Test that a chunked POST works with chunks appended after transaction starts.
- TEST_F(SpdyNetworkTransactionTest, DelayedChunkedPost) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructChunkedSpdyPost(nullptr, 0));
- spdy::SpdySerializedFrame chunk1(spdy_util_.ConstructSpdyDataFrame(1, false));
- spdy::SpdySerializedFrame chunk2(spdy_util_.ConstructSpdyDataFrame(1, false));
- spdy::SpdySerializedFrame chunk3(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(chunk1, 1),
- CreateMockWrite(chunk2, 2), CreateMockWrite(chunk3, 3),
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 4), CreateMockRead(chunk1, 5),
- CreateMockRead(chunk2, 6), CreateMockRead(chunk3, 7),
- MockRead(ASYNC, 0, 8) // EOF
- };
- SequencedSocketData data(reads, writes);
- UseChunkedPostRequest();
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- upload_chunked_data_stream()->AppendData(kUploadData, kUploadDataSize, false);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- ASSERT_TRUE(helper.StartDefaultTest());
- base::RunLoop().RunUntilIdle();
- upload_chunked_data_stream()->AppendData(kUploadData, kUploadDataSize, false);
- base::RunLoop().RunUntilIdle();
- upload_chunked_data_stream()->AppendData(kUploadData, kUploadDataSize, true);
- helper.FinishDefaultTest();
- helper.VerifyDataConsumed();
- std::string expected_response;
- expected_response += kUploadData;
- expected_response += kUploadData;
- expected_response += kUploadData;
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ(expected_response, out.response_data);
- }
- // Test that a POST without any post data works.
- TEST_F(SpdyNetworkTransactionTest, NullPost) {
- // Setup the request.
- request_.method = "POST";
- // Create an empty UploadData.
- request_.upload_data_stream = nullptr;
- // When request.upload_data_stream is NULL for post, content-length is
- // expected to be 0.
- spdy::Http2HeaderBlock req_block(
- spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyHeaders(1, std::move(req_block), LOWEST, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0),
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- // Test that a simple POST works.
- TEST_F(SpdyNetworkTransactionTest, EmptyPost) {
- // Create an empty UploadDataStream.
- std::vector<std::unique_ptr<UploadElementReader>> element_readers;
- ElementsUploadDataStream stream(std::move(element_readers), 0);
- // Setup the request.
- request_.method = "POST";
- request_.upload_data_stream = &stream;
- const uint64_t kContentLength = 0;
- spdy::Http2HeaderBlock req_block(
- spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, kContentLength));
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyHeaders(1, std::move(req_block), LOWEST, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0),
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- // While we're doing a post, the server sends the reply before upload completes.
- TEST_F(SpdyNetworkTransactionTest, ResponseBeforePostCompletes) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructChunkedSpdyPost(nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(body, 3),
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 4) // EOF
- };
- // Write the request headers, and read the complete response
- // while still waiting for chunked request data.
- SequencedSocketData data(reads, writes);
- UseChunkedPostRequest();
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- ASSERT_TRUE(helper.StartDefaultTest());
- base::RunLoop().RunUntilIdle();
- // Process the request headers, response headers, and response body.
- // The request body is still in flight.
- const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- // Finish sending the request body.
- upload_chunked_data_stream()->AppendData(kUploadData, kUploadDataSize, true);
- helper.WaitForCallbackToComplete();
- EXPECT_THAT(helper.output().rv, IsOk());
- std::string response_body;
- EXPECT_THAT(ReadTransaction(helper.trans(), &response_body), IsOk());
- EXPECT_EQ(kUploadData, response_body);
- // Finish async network reads/writes.
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- // The client upon cancellation tries to send a RST_STREAM frame. The mock
- // socket causes the TCP write to return zero. This test checks that the client
- // tries to queue up the RST_STREAM frame again.
- TEST_F(SpdyNetworkTransactionTest, SocketWriteReturnsZero) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
- MockWrite writes[] = {
- CreateMockWrite(req, 0, SYNCHRONOUS),
- MockWrite(SYNCHRONOUS, nullptr, 0, 2),
- CreateMockWrite(rst, 3, SYNCHRONOUS),
- };
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- MockRead reads[] = {
- CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, nullptr, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- helper.StartDefaultTest();
- EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING));
- helper.WaitForCallbackToComplete();
- EXPECT_THAT(helper.output().rv, IsOk());
- helper.ResetTrans();
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- // Test that the transaction doesn't crash when we don't have a reply.
- TEST_F(SpdyNetworkTransactionTest, ResponseWithoutHeaders) {
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(body, 1), MockRead(ASYNC, 0, 3) // EOF
- };
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_PROTOCOL_ERROR));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(rst, 2),
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_PROTOCOL_ERROR));
- }
- // Test that the transaction doesn't crash when we get two replies on the same
- // stream ID. See http://crbug.com/45639.
- TEST_F(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_PROTOCOL_ERROR));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(rst, 4),
- };
- spdy::SpdySerializedFrame resp0(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp0, 1), CreateMockRead(resp1, 2),
- CreateMockRead(body, 3), MockRead(ASYNC, 0, 5) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- const HttpResponseInfo* response = trans->GetResponseInfo();
- ASSERT_TRUE(response);
- EXPECT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- std::string response_data;
- rv = ReadTransaction(trans, &response_data);
- EXPECT_THAT(rv, IsError(ERR_HTTP2_PROTOCOL_ERROR));
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, ResetReplyWithTransferEncoding) {
- // Construct the request.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_PROTOCOL_ERROR));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(rst, 2),
- };
- const char* const headers[] = {
- "transfer-encoding", "chunked"
- };
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(headers, 1, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 3),
- MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_PROTOCOL_ERROR));
- helper.session()->spdy_session_pool()->CloseAllSessions();
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, ResetPushWithTransferEncoding) {
- // Construct the request.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_PROTOCOL_ERROR));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(priority, 3),
- CreateMockWrite(rst, 5),
- };
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- const char* const headers[] = {
- "transfer-encoding", "chunked"
- };
- spdy::SpdySerializedFrame push(spdy_util_.ConstructSpdyPush(
- headers, std::size(headers) / 2, 2, 1, "https://www.example.org/1"));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(push, 2), CreateMockRead(body, 4),
- MockRead(ASYNC, 0, 6) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- helper.session()->spdy_session_pool()->CloseAllSessions();
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, CancelledTransaction) {
- // Construct the request.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {
- CreateMockWrite(req),
- };
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- MockRead reads[] = {
- CreateMockRead(resp),
- // This following read isn't used by the test, except during the
- // RunUntilIdle() call at the end since the SpdySession survives the
- // HttpNetworkTransaction and still tries to continue Read()'ing. Any
- // MockRead will do here.
- MockRead(ASYNC, 0, 0) // EOF
- };
- StaticSocketDataProvider data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- helper.ResetTrans(); // Cancel the transaction.
- // Flush the MessageLoop while the SpdySessionDependencies (in particular, the
- // MockClientSocketFactory) are still alive.
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataNotConsumed();
- }
- // Verify that the client sends a Rst Frame upon cancelling the stream.
- TEST_F(SpdyNetworkTransactionTest, CancelledTransactionSendRst) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
- MockWrite writes[] = {
- CreateMockWrite(req, 0, SYNCHRONOUS),
- CreateMockWrite(rst, 2, SYNCHRONOUS),
- };
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- MockRead reads[] = {
- CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, nullptr, 0, 3) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(callback.GetResult(rv), IsOk());
- helper.ResetTrans();
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- // Verify that the client can correctly deal with the user callback attempting
- // to start another transaction on a session that is closing down. See
- // http://crbug.com/47455
- TEST_F(SpdyNetworkTransactionTest, StartTransactionOnReadCallback) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req)};
- MockWrite writes2[] = {CreateMockWrite(req, 0),
- MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 3)};
- // The indicated length of this frame is longer than its actual length. When
- // the session receives an empty frame after this one, it shuts down the
- // session, and calls the read callback with the incomplete data.
- const uint8_t kGetBodyFrame2[] = {
- 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
- 0x07, 'h', 'e', 'l', 'l', 'o', '!',
- };
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- MockRead reads[] = {
- CreateMockRead(resp, 1),
- MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause
- MockRead(ASYNC, reinterpret_cast<const char*>(kGetBodyFrame2),
- std::size(kGetBodyFrame2), 3),
- MockRead(ASYNC, ERR_IO_PENDING, 4), // Force a pause
- MockRead(ASYNC, nullptr, 0, 5), // EOF
- };
- MockRead reads2[] = {
- CreateMockRead(resp, 1), MockRead(ASYNC, nullptr, 0, 2), // EOF
- };
- SequencedSocketData data(reads, writes);
- SequencedSocketData data2(reads2, writes2);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- helper.AddData(&data2);
- HttpNetworkTransaction* trans = helper.trans();
- // Start the transaction with basic parameters.
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- const int kSize = 3000;
- scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBuffer>(kSize);
- rv = trans->Read(
- buf.get(), kSize,
- base::BindOnce(&SpdyNetworkTransactionTest::StartTransactionCallback,
- helper.session(), default_url_, log_));
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- // This forces an err_IO_pending, which sets the callback.
- data.Resume();
- data.RunUntilPaused();
- // This finishes the read.
- data.Resume();
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- // Verify that the client can correctly deal with the user callback deleting
- // the transaction. Failures will usually be flagged by thread and/or memory
- // checking tools. See http://crbug.com/46925
- TEST_F(SpdyNetworkTransactionTest, DeleteSessionOnReadCallback) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1),
- MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause
- CreateMockRead(body, 3), MockRead(ASYNC, nullptr, 0, 4), // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- // Start the transaction with basic parameters.
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- // Setup a user callback which will delete the session, and clear out the
- // memory holding the stream object. Note that the callback deletes trans.
- const int kSize = 3000;
- scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBuffer>(kSize);
- rv = trans->Read(
- buf.get(), kSize,
- base::BindOnce(&SpdyNetworkTransactionTest::DeleteSessionCallback,
- base::Unretained(&helper)));
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- data.Resume();
- // Finish running rest of tasks.
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, RedirectGetRequest) {
- MockClientSocketFactory socket_factory;
- auto context_builder =
- CreateSpdyTestURLRequestContextBuilder(&socket_factory);
- auto spdy_url_request_context = context_builder->Build();
- SpdySessionPoolPeer pool_peer(
- spdy_url_request_context->http_transaction_factory()
- ->GetSession()
- ->spdy_session_pool());
- pool_peer.SetEnableSendingInitialData(false);
- // Use a different port to avoid trying to reuse the initial H2 session.
- const char kRedirectUrl[] = "https://www.foo.com:8080/index.php";
- SSLSocketDataProvider ssl_provider0(ASYNC, OK);
- ssl_provider0.next_proto = kProtoHTTP2;
- socket_factory.AddSSLSocketDataProvider(&ssl_provider0);
- spdy::Http2HeaderBlock headers0(
- spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
- headers0["user-agent"] = "";
- headers0["accept-encoding"] = "gzip, deflate";
- spdy::SpdySerializedFrame req0(
- spdy_util_.ConstructSpdyHeaders(1, std::move(headers0), LOWEST, true));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
- MockWrite writes0[] = {CreateMockWrite(req0, 0), CreateMockWrite(rst, 2)};
- const char* const kExtraHeaders[] = {"location", kRedirectUrl};
- spdy::SpdySerializedFrame resp0(spdy_util_.ConstructSpdyReplyError(
- "301", kExtraHeaders, std::size(kExtraHeaders) / 2, 1));
- MockRead reads0[] = {CreateMockRead(resp0, 1), MockRead(ASYNC, 0, 3)};
- SequencedSocketData data0(reads0, writes0);
- socket_factory.AddSocketDataProvider(&data0);
- SSLSocketDataProvider ssl_provider1(ASYNC, OK);
- ssl_provider1.next_proto = kProtoHTTP2;
- socket_factory.AddSSLSocketDataProvider(&ssl_provider1);
- SpdyTestUtil spdy_util1;
- spdy::Http2HeaderBlock headers1(
- spdy_util1.ConstructGetHeaderBlock(kRedirectUrl));
- headers1["user-agent"] = "";
- headers1["accept-encoding"] = "gzip, deflate";
- spdy::SpdySerializedFrame req1(
- spdy_util1.ConstructSpdyHeaders(1, std::move(headers1), LOWEST, true));
- MockWrite writes1[] = {CreateMockWrite(req1, 0)};
- spdy::SpdySerializedFrame resp1(
- spdy_util1.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util1.ConstructSpdyDataFrame(1, true));
- MockRead reads1[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
- MockRead(ASYNC, 0, 3)};
- SequencedSocketData data1(reads1, writes1);
- socket_factory.AddSocketDataProvider(&data1);
- TestDelegate delegate;
- std::unique_ptr<URLRequest> request = spdy_url_request_context->CreateRequest(
- default_url_, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
- request->Start();
- delegate.RunUntilRedirect();
- EXPECT_EQ(1, delegate.received_redirect_count());
- request->FollowDeferredRedirect(absl::nullopt /* removed_headers */,
- absl::nullopt /* modified_headers */);
- delegate.RunUntilComplete();
- EXPECT_EQ(1, delegate.response_started_count());
- EXPECT_FALSE(delegate.received_data_before_response());
- EXPECT_THAT(delegate.request_status(), IsOk());
- EXPECT_EQ("hello!", delegate.data_received());
- // Pump the message loop to allow read data to be consumed.
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(data0.AllReadDataConsumed());
- EXPECT_TRUE(data0.AllWriteDataConsumed());
- EXPECT_TRUE(data1.AllReadDataConsumed());
- EXPECT_TRUE(data1.AllWriteDataConsumed());
- }
- TEST_F(SpdyNetworkTransactionTest, RedirectServerPush) {
- const char redirected_url[] = "https://www.foo.com/index.php";
- MockClientSocketFactory socket_factory;
- auto context_builder =
- CreateSpdyTestURLRequestContextBuilder(&socket_factory);
- auto spdy_url_request_context = context_builder->Build();
- SpdySessionPoolPeer pool_peer(
- spdy_url_request_context->http_transaction_factory()
- ->GetSession()
- ->spdy_session_pool());
- pool_peer.SetEnableSendingInitialData(false);
- SSLSocketDataProvider ssl_provider0(ASYNC, OK);
- ssl_provider0.next_proto = kProtoHTTP2;
- ssl_provider0.ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
- ASSERT_TRUE(ssl_provider0.ssl_info.cert);
- socket_factory.AddSSLSocketDataProvider(&ssl_provider0);
- spdy::Http2HeaderBlock headers0(
- spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
- headers0["user-agent"] = "";
- headers0["accept-encoding"] = "gzip, deflate";
- spdy::SpdySerializedFrame req0(
- spdy_util_.ConstructSpdyHeaders(1, std::move(headers0), LOWEST, true));
- spdy::SpdySerializedFrame priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_CANCEL));
- MockWrite writes[] = {CreateMockWrite(req0, 0), CreateMockWrite(priority, 3),
- CreateMockWrite(rst, 5)};
- spdy::SpdySerializedFrame resp0(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame push(spdy_util_.ConstructSpdyPush(
- nullptr, 0, 2, 1, kPushedUrl, "301", redirected_url));
- spdy::SpdySerializedFrame body0(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(resp0, 1), CreateMockRead(push, 2),
- CreateMockRead(body0, 4), MockRead(ASYNC, 0, 6)};
- SequencedSocketData data0(reads, writes);
- socket_factory.AddSocketDataProvider(&data0);
- SSLSocketDataProvider ssl_provider1(ASYNC, OK);
- ssl_provider1.next_proto = kProtoHTTP2;
- socket_factory.AddSSLSocketDataProvider(&ssl_provider1);
- SpdyTestUtil spdy_util1;
- spdy::Http2HeaderBlock headers1(
- spdy_util1.ConstructGetHeaderBlock(redirected_url));
- headers1["user-agent"] = "";
- headers1["accept-encoding"] = "gzip, deflate";
- spdy::SpdySerializedFrame req1(
- spdy_util1.ConstructSpdyHeaders(1, std::move(headers1), LOWEST, true));
- MockWrite writes1[] = {CreateMockWrite(req1, 0)};
- spdy::SpdySerializedFrame resp1(
- spdy_util1.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util1.ConstructSpdyDataFrame(1, true));
- MockRead reads1[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
- MockRead(ASYNC, 0, 3)};
- SequencedSocketData data1(reads1, writes1);
- socket_factory.AddSocketDataProvider(&data1);
- TestDelegate delegate0;
- std::unique_ptr<URLRequest> request = spdy_url_request_context->CreateRequest(
- default_url_, DEFAULT_PRIORITY, &delegate0, TRAFFIC_ANNOTATION_FOR_TESTS);
- request->Start();
- delegate0.RunUntilComplete();
- EXPECT_EQ(0, delegate0.received_redirect_count());
- EXPECT_EQ("hello!", delegate0.data_received());
- TestDelegate delegate1;
- std::unique_ptr<URLRequest> request1 =
- spdy_url_request_context->CreateRequest(GURL(kPushedUrl),
- DEFAULT_PRIORITY, &delegate1,
- TRAFFIC_ANNOTATION_FOR_TESTS);
- request1->Start();
- delegate1.RunUntilRedirect();
- EXPECT_EQ(1, delegate1.received_redirect_count());
- request1->FollowDeferredRedirect(absl::nullopt /* removed_headers */,
- absl::nullopt /* modified_headers */);
- delegate1.RunUntilComplete();
- EXPECT_EQ(1, delegate1.response_started_count());
- EXPECT_FALSE(delegate1.received_data_before_response());
- EXPECT_EQ(OK, delegate1.request_status());
- EXPECT_EQ("hello!", delegate1.data_received());
- // Pump the message loop to allow read data to be consumed.
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(data0.AllReadDataConsumed());
- EXPECT_TRUE(data0.AllWriteDataConsumed());
- EXPECT_TRUE(data1.AllReadDataConsumed());
- EXPECT_TRUE(data1.AllWriteDataConsumed());
- }
- TEST_F(SpdyNetworkTransactionTest, RedirectMultipleLocations) {
- const spdy::SpdyStreamId kStreamId = 1;
- // Construct the request and the RST frame.
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyGet(
- /*extra_headers=*/nullptr, /*extra_header_count=*/0, kStreamId, LOWEST));
- spdy::SpdySerializedFrame rst(spdy_util_.ConstructSpdyRstStream(
- kStreamId, spdy::ERROR_CODE_PROTOCOL_ERROR));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(rst, 4)};
- // Construct the response.
- const char* const kExtraResponseHeaders[] = {
- "location",
- "https://example1.test",
- "location",
- "https://example2.test",
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError(
- "301", kExtraResponseHeaders, std::size(kExtraResponseHeaders) / 2,
- kStreamId));
- spdy::SpdySerializedFrame body(
- spdy_util_.ConstructSpdyDataFrame(kStreamId, /*fin=*/true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_RESPONSE_HEADERS_MULTIPLE_LOCATION));
- }
- TEST_F(SpdyNetworkTransactionTest, ServerPushSingleDataFrame) {
- spdy::SpdySerializedFrame stream1_syn(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame stream2_priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- MockWrite writes[] = {
- CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream2_syn(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- const char kPushedData[] = "pushed";
- spdy::SpdySerializedFrame stream2_body(
- spdy_util_.ConstructSpdyDataFrame(2, kPushedData, true));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2),
- CreateMockRead(stream1_body, 4), CreateMockRead(stream2_body, 5),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause
- };
- HttpResponseInfo response;
- HttpResponseInfo response2;
- std::string expected_push_result("pushed");
- SequencedSocketData data(reads, writes);
- RunServerPushTest(&data,
- &response,
- &response2,
- expected_push_result);
- // Verify the response headers.
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- // Verify the pushed stream.
- EXPECT_TRUE(response2.headers);
- EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine());
- }
- // When server push is disabled by
- // HttpNetworkSession::initial_settings[SETTINGS_ENABLE_PUSH] = 0, verify that
- // such a setting is sent out in the initial SETTINGS frame, and if the server
- // creates a pushed stream despite of this, it is immediately reset.
- TEST_F(SpdyNetworkTransactionTest, ServerPushDisabled) {
- base::HistogramTester histogram_tester;
- spdy::SpdySerializedFrame preface(
- const_cast<char*>(spdy::kHttp2ConnectionHeaderPrefix),
- spdy::kHttp2ConnectionHeaderPrefixSize,
- /* owns_buffer = */ false);
- spdy::SettingsMap initial_settings;
- initial_settings[spdy::SETTINGS_HEADER_TABLE_SIZE] = kSpdyMaxHeaderTableSize;
- initial_settings[spdy::SETTINGS_ENABLE_PUSH] = 0;
- initial_settings[spdy::SETTINGS_MAX_CONCURRENT_STREAMS] =
- kSpdyMaxConcurrentPushedStreams;
- initial_settings[spdy::SETTINGS_MAX_HEADER_LIST_SIZE] =
- kSpdyMaxHeaderListSize;
- spdy::SpdySerializedFrame initial_settings_frame(
- spdy_util_.ConstructSpdySettings(initial_settings));
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_REFUSED_STREAM));
- MockWrite writes[] = {CreateMockWrite(preface, 0),
- CreateMockWrite(initial_settings_frame, 1),
- CreateMockWrite(req, 2), CreateMockWrite(rst, 5)};
- spdy::SpdySerializedFrame reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame push(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(reply, 3), CreateMockRead(push, 4),
- CreateMockRead(body, 6), MockRead(ASYNC, OK, 7)};
- SequencedSocketData data(reads, writes);
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->http2_settings[spdy::SETTINGS_ENABLE_PUSH] = 0;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool();
- SpdySessionPoolPeer pool_peer(spdy_session_pool);
- pool_peer.SetEnableSendingInitialData(true);
- helper.RunToCompletion(&data);
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kPushDisabled), 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1);
- }
- TEST_F(SpdyNetworkTransactionTest, ServerPushHeadMethod) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(priority, 2)};
- spdy::Http2HeaderBlock push_promise_header_block;
- push_promise_header_block[spdy::kHttp2MethodHeader] = "HEAD";
- spdy_util_.AddUrlToHeaderBlock(kPushedUrl, &push_promise_header_block);
- spdy::SpdySerializedFrame push_promise(spdy_util_.ConstructSpdyPushPromise(
- 1, 2, std::move(push_promise_header_block)));
- spdy::Http2HeaderBlock push_response_headers;
- push_response_headers[spdy::kHttp2StatusHeader] = "200";
- push_response_headers["foo"] = "bar";
- spdy::SpdyHeadersIR headers_ir(2, std::move(push_response_headers));
- spdy::SpdySerializedFrame push_headers(spdy_util_.SerializeFrame(headers_ir));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(push_promise, 1), CreateMockRead(push_headers, 3),
- CreateMockRead(resp, 4), CreateMockRead(body, 5),
- // Do not close the connection after first request is done.
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- // Run first request. This reads PUSH_PROMISE.
- helper.RunDefaultTest();
- // Request the pushed resource.
- HttpNetworkTransaction trans(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request = CreateGetPushRequest();
- request.method = "HEAD";
- request.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback;
- int rv = trans.Start(&request, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- const HttpResponseInfo* response = trans.GetResponseInfo();
- ASSERT_TRUE(response);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_TRUE(response->was_alpn_negotiated);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string value;
- EXPECT_TRUE(response->headers->GetNormalizedHeader("foo", &value));
- EXPECT_EQ("bar", value);
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, ServerPushHeadDoesNotMatchGetRequest) {
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(kPushedUrl, 3, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req1, 0), CreateMockWrite(priority, 2),
- CreateMockWrite(req2, 6)};
- spdy::Http2HeaderBlock push_promise_header_block;
- push_promise_header_block[spdy::kHttp2MethodHeader] = "HEAD";
- spdy_util_.AddUrlToHeaderBlock(kPushedUrl, &push_promise_header_block);
- spdy::SpdySerializedFrame push_promise(spdy_util_.ConstructSpdyPushPromise(
- 1, 2, std::move(push_promise_header_block)));
- spdy::Http2HeaderBlock push_response_headers;
- push_response_headers[spdy::kHttp2StatusHeader] = "200";
- push_response_headers["foo"] = "bar";
- spdy::SpdyHeadersIR headers_ir(2, std::move(push_response_headers));
- spdy::SpdySerializedFrame push_headers(spdy_util_.SerializeFrame(headers_ir));
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead reads[] = {CreateMockRead(push_promise, 1),
- CreateMockRead(push_headers, 3),
- CreateMockRead(resp1, 4),
- CreateMockRead(body1, 5),
- CreateMockRead(resp2, 7),
- CreateMockRead(body2, 8),
- MockRead(ASYNC, 0, 9)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- // Run first request. This reads PUSH_PROMISE.
- helper.RunDefaultTest();
- // Request the pushed resource.
- HttpNetworkTransaction trans(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request = CreateGetPushRequest();
- TestCompletionCallback callback;
- int rv = trans.Start(&request, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- const HttpResponseInfo* response = trans.GetResponseInfo();
- ASSERT_TRUE(response);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_TRUE(response->was_alpn_negotiated);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string value;
- EXPECT_FALSE(response->headers->GetNormalizedHeader("foo", &value));
- std::string result;
- ReadResult(&trans, &result);
- EXPECT_EQ("hello!", result);
- // Read EOF.
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, ServerPushSingleDataFrame2) {
- spdy::SpdySerializedFrame stream1_syn(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame stream2_priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- MockWrite writes[] = {
- CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream2_syn(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- const char kPushedData[] = "pushed";
- spdy::SpdySerializedFrame stream2_body(
- spdy_util_.ConstructSpdyDataFrame(2, kPushedData, true));
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1),
- CreateMockRead(stream2_syn, 2),
- CreateMockRead(stream2_body, 4),
- CreateMockRead(stream1_body, 5, SYNCHRONOUS),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause
- };
- HttpResponseInfo response;
- HttpResponseInfo response2;
- std::string expected_push_result("pushed");
- SequencedSocketData data(reads, writes);
- RunServerPushTest(&data,
- &response,
- &response2,
- expected_push_result);
- // Verify the response headers.
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- // Verify the pushed stream.
- EXPECT_TRUE(response2.headers);
- EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine());
- }
- TEST_F(SpdyNetworkTransactionTest, ServerPushUpdatesPriority) {
- spdy::SpdySerializedFrame stream1_headers(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST));
- spdy::SpdySerializedFrame stream3_headers(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, MEDIUM));
- spdy::SpdySerializedFrame stream5_headers(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 5, MEDIUM));
- // Stream 1 pushes two streams that are initially prioritized below stream 5.
- // Stream 2 is later prioritized below stream 1 after it matches a request.
- spdy::SpdySerializedFrame stream2_priority(
- spdy_util_.ConstructSpdyPriority(2, 5, IDLE, true));
- spdy::SpdySerializedFrame stream4_priority(
- spdy_util_.ConstructSpdyPriority(4, 2, IDLE, true));
- spdy::SpdySerializedFrame stream4_priority_update(
- spdy_util_.ConstructSpdyPriority(4, 5, IDLE, true));
- spdy::SpdySerializedFrame stream2_priority_update(
- spdy_util_.ConstructSpdyPriority(2, 1, HIGHEST, true));
- MockWrite writes[] = {
- CreateMockWrite(stream1_headers, 0),
- CreateMockWrite(stream3_headers, 1),
- CreateMockWrite(stream5_headers, 2),
- CreateMockWrite(stream2_priority, 7),
- CreateMockWrite(stream4_priority, 9),
- CreateMockWrite(stream4_priority_update, 11),
- CreateMockWrite(stream2_priority_update, 12),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream3_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame stream5_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5));
- spdy::SpdySerializedFrame stream2_push(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- spdy::SpdySerializedFrame stream4_push(spdy_util_.ConstructSpdyPush(
- nullptr, 0, 4, 1, "https://www.example.org/bar.dat"));
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame stream2_body(
- spdy_util_.ConstructSpdyDataFrame(2, true));
- spdy::SpdySerializedFrame stream3_body(
- spdy_util_.ConstructSpdyDataFrame(3, true));
- spdy::SpdySerializedFrame stream5_body(
- spdy_util_.ConstructSpdyDataFrame(5, true));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 3),
- CreateMockRead(stream3_reply, 4),
- CreateMockRead(stream5_reply, 5),
- CreateMockRead(stream2_push, 6),
- CreateMockRead(stream4_push, 8),
- MockRead(ASYNC, ERR_IO_PENDING, 10),
- CreateMockRead(stream1_body, 13),
- CreateMockRead(stream2_body, 14),
- CreateMockRead(stream3_body, 15),
- CreateMockRead(stream5_body, 16),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 17), // Force a pause
- };
- SequencedSocketData data(reads, writes);
- SequencedSocketData data_placeholder1;
- SequencedSocketData data_placeholder2;
- SequencedSocketData data_placeholder3;
- NormalSpdyTransactionHelper helper(request_, LOWEST, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- helper.AddData(&data_placeholder1); // other requests reuse the same socket
- helper.AddData(&data_placeholder2);
- helper.AddData(&data_placeholder3);
- HttpNetworkTransaction trans1(HIGHEST, helper.session());
- HttpNetworkTransaction trans3(MEDIUM, helper.session());
- HttpNetworkTransaction trans5(MEDIUM, helper.session());
- TestCompletionCallback callback1;
- TestCompletionCallback callback3;
- TestCompletionCallback callback5;
- // Start the ordinary requests.
- ASSERT_THAT(trans1.Start(&request_, callback1.callback(), log_),
- IsError(ERR_IO_PENDING));
- ASSERT_THAT(trans3.Start(&request_, callback3.callback(), log_),
- IsError(ERR_IO_PENDING));
- ASSERT_THAT(trans5.Start(&request_, callback5.callback(), log_),
- IsError(ERR_IO_PENDING));
- data.RunUntilPaused();
- // Start a request that matches the push.
- HttpRequestInfo push_req = CreateGetPushRequest();
- HttpNetworkTransaction trans2(HIGHEST, helper.session());
- TestCompletionCallback callback2;
- ASSERT_THAT(trans2.Start(&push_req, callback2.callback(), log_),
- IsError(ERR_IO_PENDING));
- data.Resume();
- base::RunLoop().RunUntilIdle();
- ASSERT_THAT(callback1.WaitForResult(), IsOk());
- ASSERT_THAT(callback2.WaitForResult(), IsOk());
- ASSERT_THAT(callback3.WaitForResult(), IsOk());
- ASSERT_THAT(callback5.WaitForResult(), IsOk());
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, ServerPushServerAborted) {
- spdy::SpdySerializedFrame stream1_syn(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame stream2_priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- MockWrite writes[] = {
- CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream2_syn(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- spdy::SpdySerializedFrame stream2_rst(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_PROTOCOL_ERROR));
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1),
- CreateMockRead(stream2_syn, 2, SYNCHRONOUS),
- CreateMockRead(stream2_rst, 4),
- CreateMockRead(stream1_body, 5, SYNCHRONOUS),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- // Start the transaction with basic parameters.
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- // Verify that we consumed all test data.
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(data.AllReadDataConsumed());
- EXPECT_TRUE(data.AllWriteDataConsumed());
- // Verify the response headers.
- HttpResponseInfo response = *trans->GetResponseInfo();
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- }
- // Verify that we don't leak streams and that we properly send a reset
- // if the server pushes the same stream twice.
- TEST_F(SpdyNetworkTransactionTest, ServerPushDuplicate) {
- base::HistogramTester histogram_tester;
- spdy::SpdySerializedFrame stream1_syn(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame stream2_priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- spdy::SpdySerializedFrame stream3_rst(
- spdy_util_.ConstructSpdyRstStream(4, spdy::ERROR_CODE_REFUSED_STREAM));
- MockWrite writes[] = {
- CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3),
- CreateMockWrite(stream3_rst, 5),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream2_syn(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- spdy::SpdySerializedFrame stream3_syn(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 4, 1, kPushedUrl));
- const char kPushedData[] = "pushed";
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame stream2_body(
- spdy_util_.ConstructSpdyDataFrame(2, kPushedData, true));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1),
- CreateMockRead(stream2_syn, 2),
- CreateMockRead(stream3_syn, 4),
- CreateMockRead(stream1_body, 6),
- CreateMockRead(stream2_body, 7),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 8), // Force a pause
- };
- HttpResponseInfo response;
- HttpResponseInfo response2;
- std::string expected_push_result("pushed");
- SequencedSocketData data(reads, writes);
- RunServerPushTest(&data,
- &response,
- &response2,
- expected_push_result);
- // Verify the response headers.
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- // Verify the pushed stream.
- EXPECT_TRUE(response2.headers);
- EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine());
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kAcceptedNoVary), 1);
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kDuplicateUrl), 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 2);
- }
- TEST_F(SpdyNetworkTransactionTest, ServerPushMultipleDataFrame) {
- spdy::SpdySerializedFrame stream1_syn(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame stream2_priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- MockWrite writes[] = {
- CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream2_syn(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- static const char kPushedData[] = "pushed payload for chunked test";
- spdy::SpdySerializedFrame stream2_body_base(
- spdy_util_.ConstructSpdyDataFrame(2, kPushedData, true));
- const size_t kChunkSize = strlen(kPushedData) / 4;
- spdy::SpdySerializedFrame stream2_body1(stream2_body_base.data(), kChunkSize,
- false);
- spdy::SpdySerializedFrame stream2_body2(stream2_body_base.data() + kChunkSize,
- kChunkSize, false);
- spdy::SpdySerializedFrame stream2_body3(
- stream2_body_base.data() + 2 * kChunkSize, kChunkSize, false);
- spdy::SpdySerializedFrame stream2_body4(
- stream2_body_base.data() + 3 * kChunkSize,
- stream2_body_base.size() - 3 * kChunkSize, false);
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1),
- CreateMockRead(stream2_syn, 2),
- CreateMockRead(stream2_body1, 4),
- CreateMockRead(stream2_body2, 5),
- CreateMockRead(stream2_body3, 6),
- CreateMockRead(stream2_body4, 7),
- CreateMockRead(stream1_body, 8, SYNCHRONOUS),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 9), // Force a pause
- };
- HttpResponseInfo response;
- HttpResponseInfo response2;
- std::string expected_push_result(kPushedData);
- SequencedSocketData data(reads, writes);
- RunServerPushTest(&data, &response, &response2, kPushedData);
- // Verify the response headers.
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- // Verify the pushed stream.
- EXPECT_TRUE(response2.headers);
- EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine());
- }
- TEST_F(SpdyNetworkTransactionTest, ServerPushMultipleDataFrameInterrupted) {
- spdy::SpdySerializedFrame stream1_syn(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame stream2_priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- MockWrite writes[] = {
- CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream2_syn(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- static const char kPushedData[] = "pushed payload for chunked test";
- spdy::SpdySerializedFrame stream2_body_base(
- spdy_util_.ConstructSpdyDataFrame(2, kPushedData, true));
- const size_t kChunkSize = strlen(kPushedData) / 4;
- spdy::SpdySerializedFrame stream2_body1(stream2_body_base.data(), kChunkSize,
- false);
- spdy::SpdySerializedFrame stream2_body2(stream2_body_base.data() + kChunkSize,
- kChunkSize, false);
- spdy::SpdySerializedFrame stream2_body3(
- stream2_body_base.data() + 2 * kChunkSize, kChunkSize, false);
- spdy::SpdySerializedFrame stream2_body4(
- stream2_body_base.data() + 3 * kChunkSize,
- stream2_body_base.size() - 3 * kChunkSize, false);
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1),
- CreateMockRead(stream2_syn, 2),
- CreateMockRead(stream2_body1, 4),
- CreateMockRead(stream2_body2, 5),
- CreateMockRead(stream2_body3, 6),
- CreateMockRead(stream2_body4, 7),
- CreateMockRead(stream1_body, 8, SYNCHRONOUS),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 9) // Force a pause.
- };
- HttpResponseInfo response;
- HttpResponseInfo response2;
- SequencedSocketData data(reads, writes);
- RunServerPushTest(&data, &response, &response2, kPushedData);
- // Verify the response headers.
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- // Verify the pushed stream.
- EXPECT_TRUE(response2.headers);
- EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine());
- }
- TEST_F(SpdyNetworkTransactionTest, ServerPushInvalidUrl) {
- base::HistogramTester histogram_tester;
- // Coverage on how a non-empty invalid GURL in a PUSH_PROMISE is handled.
- spdy::Http2HeaderBlock headers(
- spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
- // Can't use ConstructSpdyPush here since it wants to parse a URL and
- // split it into the appropriate :header pieces. So we have to hand-fill
- // those pieces in.
- spdy::Http2HeaderBlock push_promise_header_block;
- push_promise_header_block[spdy::kHttp2AuthorityHeader] = "";
- push_promise_header_block[spdy::kHttp2SchemeHeader] = "";
- push_promise_header_block[spdy::kHttp2PathHeader] = "/index.html";
- spdy::SpdySerializedFrame push_promise(spdy_util_.ConstructSpdyPushPromise(
- 1, 2, std::move(push_promise_header_block)));
- spdy::SpdySerializedFrame stream2_rst(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_REFUSED_STREAM));
- MockWrite writes[] = {CreateMockWrite(req, 0),
- CreateMockWrite(stream2_rst, 2)};
- MockRead reads[] = {
- CreateMockRead(push_promise, 1), MockRead(ASYNC, 0, 3) /* EOF */
- };
- SequencedSocketData data(reads, writes);
- RunBrokenPushTest(&data, ERR_CONNECTION_CLOSED);
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kInvalidUrl), 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1);
- }
- TEST_F(SpdyNetworkTransactionTest, ServerPushInvalidAssociatedStreamID0) {
- spdy::SpdySerializedFrame stream1_syn(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame goaway(
- spdy_util_.ConstructSpdyGoAway(0, spdy::ERROR_CODE_PROTOCOL_ERROR,
- "Framer error: 1 (INVALID_STREAM_ID)."));
- MockWrite writes[] = {
- CreateMockWrite(stream1_syn, 0), CreateMockWrite(goaway, 3),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream2_syn(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 0, kPushedUrl));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2),
- };
- SequencedSocketData data(reads, writes);
- RunBrokenPushTest(&data, OK);
- }
- TEST_F(SpdyNetworkTransactionTest, ServerPushInvalidAssociatedStreamID9) {
- base::HistogramTester histogram_tester;
- spdy::SpdySerializedFrame stream1_syn(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame stream2_rst(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_STREAM_CLOSED));
- MockWrite writes[] = {
- CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_rst, 3),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream2_syn(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 9, kPushedUrl));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2),
- CreateMockRead(stream1_body, 4),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5), // Force a pause
- };
- SequencedSocketData data(reads, writes);
- RunBrokenPushTest(&data, OK);
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kInactiveAssociatedStream), 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1);
- }
- TEST_F(SpdyNetworkTransactionTest, ServerPushNoURL) {
- base::HistogramTester histogram_tester;
- spdy::SpdySerializedFrame stream1_syn(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame stream2_rst(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_REFUSED_STREAM));
- MockWrite writes[] = {
- CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_rst, 3),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::Http2HeaderBlock incomplete_headers;
- incomplete_headers[spdy::kHttp2StatusHeader] = "200 OK";
- incomplete_headers["hello"] = "bye";
- spdy::SpdySerializedFrame stream2_syn(
- spdy_util_.ConstructSpdyPushPromise(1, 2, std::move(incomplete_headers)));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2),
- CreateMockRead(stream1_body, 4),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5) // Force a pause
- };
- SequencedSocketData data(reads, writes);
- RunBrokenPushTest(&data, OK);
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kInvalidUrl), 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1);
- }
- // PUSH_PROMISE on a server-initiated stream should trigger GOAWAY.
- TEST_F(SpdyNetworkTransactionTest, ServerPushOnPushedStream) {
- base::HistogramTester histogram_tester;
- spdy::SpdySerializedFrame stream1_syn(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame stream2_priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- spdy::SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(
- 2, spdy::ERROR_CODE_PROTOCOL_ERROR,
- "Received pushed stream id 4 on invalid stream id 2 (must be odd)."));
- MockWrite writes[] = {
- CreateMockWrite(stream1_syn, 0), CreateMockWrite(stream2_priority, 3),
- CreateMockWrite(goaway, 5),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream2_syn(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- spdy::SpdySerializedFrame stream3_syn(spdy_util_.ConstructSpdyPush(
- nullptr, 0, 4, 2, "https://www.example.org/bar.dat"));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1), CreateMockRead(stream2_syn, 2),
- CreateMockRead(stream3_syn, 4),
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kAssociatedStreamIdParityError),
- 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1);
- }
- // PUSH_PROMISE on a closed client-initiated stream should trigger RST_STREAM.
- TEST_F(SpdyNetworkTransactionTest, ServerPushOnClosedStream) {
- base::HistogramTester histogram_tester;
- spdy::SpdySerializedFrame stream1_syn(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_STREAM_CLOSED));
- MockWrite writes[] = {
- CreateMockWrite(stream1_syn, 0), CreateMockWrite(rst, 5),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame stream2_syn(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1), CreateMockRead(stream1_body, 2),
- CreateMockRead(stream2_syn, 3), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 4),
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- rv = callback.GetResult(rv);
- EXPECT_THAT(rv, IsOk());
- // Finish async network reads/writes.
- base::RunLoop().RunUntilIdle();
- HttpResponseInfo response = *trans->GetResponseInfo();
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- EXPECT_TRUE(data.AllReadDataConsumed());
- EXPECT_TRUE(data.AllWriteDataConsumed());
- VerifyStreamsClosed(helper);
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kInactiveAssociatedStream), 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1);
- }
- // PUSH_PROMISE on a server-initiated stream should trigger GOAWAY even if
- // stream is closed.
- TEST_F(SpdyNetworkTransactionTest, ServerPushOnClosedPushedStream) {
- base::HistogramTester histogram_tester;
- spdy::SpdySerializedFrame stream1_syn(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame stream2_priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- spdy::SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(
- 2, spdy::ERROR_CODE_PROTOCOL_ERROR,
- "Received pushed stream id 4 on invalid stream id 2 (must be odd)."));
- MockWrite writes[] = {CreateMockWrite(stream1_syn, 0),
- CreateMockWrite(stream2_priority, 3),
- CreateMockWrite(goaway, 8)};
- spdy::SpdySerializedFrame stream2_syn(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- const char kPushedData[] = "pushed";
- spdy::SpdySerializedFrame stream2_body(
- spdy_util_.ConstructSpdyDataFrame(2, kPushedData, true));
- spdy::SpdySerializedFrame stream3_syn(spdy_util_.ConstructSpdyPush(
- nullptr, 0, 4, 2, "https://www.example.org/bar.dat"));
- MockRead reads[] = {
- CreateMockRead(stream2_syn, 1), CreateMockRead(stream1_reply, 2),
- CreateMockRead(stream1_body, 4), CreateMockRead(stream2_body, 5),
- MockRead(ASYNC, ERR_IO_PENDING, 6), CreateMockRead(stream3_syn, 7)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans1 = helper.trans();
- TestCompletionCallback callback1;
- int rv = trans1->Start(&request_, callback1.callback(), log_);
- rv = callback1.GetResult(rv);
- EXPECT_THAT(rv, IsOk());
- HttpResponseInfo response = *trans1->GetResponseInfo();
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- TestCompletionCallback callback2;
- HttpRequestInfo request = CreateGetPushRequest();
- rv = trans2.Start(&request, callback2.callback(), log_);
- rv = callback2.GetResult(rv);
- EXPECT_THAT(rv, IsOk());
- response = *trans2.GetResponseInfo();
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- std::string result;
- ReadResult(&trans2, &result);
- EXPECT_EQ(kPushedData, result);
- data.Resume();
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(data.AllReadDataConsumed());
- EXPECT_TRUE(data.AllWriteDataConsumed());
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kAssociatedStreamIdParityError),
- 1);
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kAcceptedNoVary), 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 2);
- }
- TEST_F(SpdyNetworkTransactionTest, ServerCancelsPush) {
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(kPushedUrl, 3, LOWEST));
- MockWrite writes1[] = {CreateMockWrite(req1, 0), CreateMockWrite(priority, 3),
- CreateMockWrite(req2, 6)};
- spdy::SpdySerializedFrame reply1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame push(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_INTERNAL_ERROR));
- spdy::SpdySerializedFrame reply2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead reads1[] = {CreateMockRead(reply1, 1), CreateMockRead(push, 2),
- CreateMockRead(body1, 4), CreateMockRead(rst, 5),
- CreateMockRead(reply2, 7), CreateMockRead(body2, 8),
- MockRead(ASYNC, 0, 9)};
- SequencedSocketData data(reads1, writes1);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- // First request opens up connection.
- HttpNetworkTransaction* trans1 = helper.trans();
- TestCompletionCallback callback1;
- int rv = trans1->Start(&request_, callback1.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // Read until response body arrives. PUSH_PROMISE comes earlier.
- rv = callback1.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- const HttpResponseInfo* response = trans1->GetResponseInfo();
- EXPECT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string result1;
- ReadResult(trans1, &result1);
- EXPECT_EQ("hello!", result1);
- SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool();
- SpdySessionKey key(host_port_pair_, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session =
- spdy_session_pool->FindAvailableSession(
- key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- EXPECT_EQ(1u, num_unclaimed_pushed_streams(spdy_session));
- // Create request matching pushed stream.
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request2 = CreateGetPushRequest();
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // Pushed stream is now claimed by second request.
- EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session));
- // Second request receives RST_STREAM and is retried on the same connection.
- rv = callback2.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- response = trans2.GetResponseInfo();
- EXPECT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string result2;
- ReadResult(&trans2, &result2);
- EXPECT_EQ("hello!", result2);
- // Read EOF.
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- // Regression test for https://crbug.com/776415.
- // A client-initiated request can only pool to an existing HTTP/2 connection if
- // the IP address matches. However, a resource can be pushed by the server on a
- // connection even if the IP address does not match. This test verifies that if
- // the request binds to such a pushed stream, and after that the server resets
- // the stream before SpdySession::GetPushedStream() is called, then the retry
- // (using a client-initiated stream) does not pool to this connection.
- TEST_F(SpdyNetworkTransactionTest, ServerCancelsCrossOriginPush) {
- const char* kUrl1 = "https://www.example.org";
- const char* kUrl2 = "https://mail.example.org";
- auto resolver = std::make_unique<MockHostResolver>();
- resolver->rules()->ClearRules();
- resolver->rules()->AddRule("www.example.org", "127.0.0.1");
- resolver->rules()->AddRule("mail.example.org", "127.0.0.2");
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->host_resolver = std::move(resolver);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- spdy::SpdySerializedFrame req1(spdy_util_.ConstructSpdyGet(kUrl1, 1, LOWEST));
- spdy::SpdySerializedFrame priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- MockWrite writes1[] = {CreateMockWrite(req1, 0),
- CreateMockWrite(priority, 3)};
- spdy::SpdySerializedFrame reply1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame push(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kUrl2));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_INTERNAL_ERROR));
- MockRead reads1[] = {
- CreateMockRead(reply1, 1), CreateMockRead(push, 2),
- CreateMockRead(body1, 4), CreateMockRead(rst, 5),
- MockRead(ASYNC, ERR_IO_PENDING, 6), MockRead(ASYNC, 0, 7)};
- SequencedSocketData data1(reads1, writes1);
- SpdyTestUtil spdy_util2;
- spdy::SpdySerializedFrame req2(spdy_util2.ConstructSpdyGet(kUrl2, 1, LOWEST));
- MockWrite writes2[] = {CreateMockWrite(req2, 0)};
- spdy::SpdySerializedFrame reply2(
- spdy_util2.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body2(spdy_util2.ConstructSpdyDataFrame(
- 1, "Response on the second connection.", true));
- MockRead reads2[] = {CreateMockRead(reply2, 1), CreateMockRead(body2, 2),
- MockRead(ASYNC, 0, 3)};
- SequencedSocketData data2(reads2, writes2);
- helper.RunPreTestSetup();
- helper.AddData(&data1);
- helper.AddData(&data2);
- // First request opens up connection to www.example.org.
- HttpNetworkTransaction* trans1 = helper.trans();
- HttpRequestInfo request1;
- request1.method = "GET";
- request1.url = GURL(kUrl1);
- request1.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback1;
- int rv = trans1->Start(&request1, callback1.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // Read until response body arrives. PUSH_PROMISE comes earlier.
- rv = callback1.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- const HttpResponseInfo* response = trans1->GetResponseInfo();
- EXPECT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string result1;
- ReadResult(trans1, &result1);
- EXPECT_EQ("hello!", result1);
- SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool();
- SpdySessionKey key1(HostPortPair::FromURL(GURL(kUrl1)), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session1 =
- spdy_session_pool->FindAvailableSession(
- key1, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- EXPECT_EQ(1u, num_unclaimed_pushed_streams(spdy_session1));
- // While cross-origin push for kUrl2 is allowed on spdy_session1,
- // a client-initiated request would not pool to this connection,
- // because the IP address does not match.
- SpdySessionKey key2(HostPortPair::FromURL(GURL(kUrl2)), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- EXPECT_FALSE(spdy_session_pool->FindAvailableSession(
- key2, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_));
- // Create request matching pushed stream.
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL(kUrl2);
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // Pushed stream is now claimed by second request.
- EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session1));
- // Second request receives RST_STREAM and is retried on a new connection.
- rv = callback2.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- response = trans2.GetResponseInfo();
- EXPECT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string result2;
- ReadResult(&trans2, &result2);
- EXPECT_EQ("Response on the second connection.", result2);
- // Make sure that the first connection is still open. This is important in
- // order to test that the retry created its own connection (because the IP
- // address does not match), instead of using the connection of the cancelled
- // pushed stream.
- EXPECT_TRUE(spdy_session1);
- // Read EOF.
- data1.Resume();
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, NoConnectionPoolingOverTunnel) {
- // Use port 443 for two reasons: This makes the endpoint is port 443 check in
- // NormalSpdyTransactionHelper pass, and this means that the tunnel uses the
- // same port as the servers, to further confuse things.
- const char kPacString[] = "PROXY myproxy:443";
- auto session_deps = std::make_unique<SpdySessionDependencies>(
- ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
- kPacString, TRAFFIC_ANNOTATION_FOR_TESTS));
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- // Only one request uses the first connection.
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet("https://www.example.org", 1, LOWEST));
- MockWrite writes1[] = {
- MockWrite(ASYNC, 0,
- "CONNECT www.example.org:443 HTTP/1.1\r\n"
- "Host: www.example.org:443\r\n"
- "Proxy-Connection: keep-alive\r\n\r\n"),
- CreateMockWrite(req1, 2),
- };
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads1[] = {MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n\r\n"),
- CreateMockRead(resp1, 3), CreateMockRead(body1, 4),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5)};
- MockConnect connect1(ASYNC, OK);
- SequencedSocketData data1(connect1, reads1, writes1);
- // Run a transaction to completion to set up a SPDY session.
- helper.RunToCompletion(&data1);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- // A new SPDY session should have been created.
- SpdySessionKey key1(HostPortPair("www.example.org", 443),
- PacResultElementToProxyServer(kPacString),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> session1 =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key1, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource());
- ASSERT_TRUE(session1);
- // The second request uses a second connection.
- SpdyTestUtil spdy_util2;
- spdy::SpdySerializedFrame req2(
- spdy_util2.ConstructSpdyGet("https://example.test", 1, LOWEST));
- MockWrite writes2[] = {
- MockWrite(ASYNC, 0,
- "CONNECT example.test:443 HTTP/1.1\r\n"
- "Host: example.test:443\r\n"
- "Proxy-Connection: keep-alive\r\n\r\n"),
- CreateMockWrite(req2, 2),
- };
- spdy::SpdySerializedFrame resp2(
- spdy_util2.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body2(spdy_util2.ConstructSpdyDataFrame(1, true));
- MockRead reads2[] = {MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n\r\n"),
- CreateMockRead(resp2, 3), CreateMockRead(body2, 4),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5)};
- MockConnect connect2(ASYNC, OK);
- SequencedSocketData data2(connect2, reads2, writes2);
- helper.AddData(&data2);
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL("https://example.test/");
- request2.load_flags = 0;
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- auto trans2 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
- helper.session());
- TestCompletionCallback callback;
- EXPECT_THAT(trans2->Start(&request2, callback.callback(), NetLogWithSource()),
- IsError(ERR_IO_PENDING));
- // Wait for the second request to get headers. It should create a new H2
- // session to do so.
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- const HttpResponseInfo* response = trans2->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_TRUE(response->was_alpn_negotiated);
- std::string response_data;
- ASSERT_THAT(ReadTransaction(trans2.get(), &response_data), IsOk());
- EXPECT_EQ("hello!", response_data);
- // Inspect the new session.
- SpdySessionKey key2(HostPortPair("example.test", 443),
- PacResultElementToProxyServer(kPacString),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> session2 =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key2, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource());
- ASSERT_TRUE(session2);
- ASSERT_TRUE(session1);
- EXPECT_NE(session1.get(), session2.get());
- }
- // Check that if a session is found after host resolution, but is closed before
- // the task to try to use it executes, the request will continue to create a new
- // socket and use it.
- TEST_F(SpdyNetworkTransactionTest, ConnectionPoolingSessionClosedBeforeUse) {
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- // Only one request uses the first connection.
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet("https://www.example.org", 1, LOWEST));
- MockWrite writes1[] = {
- CreateMockWrite(req1, 0),
- };
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads1[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3)};
- MockConnect connect1(ASYNC, OK);
- SequencedSocketData data1(connect1, reads1, writes1);
- // Run a transaction to completion to set up a SPDY session.
- helper.RunToCompletion(&data1);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- // A new SPDY session should have been created.
- SpdySessionKey key1(HostPortPair("www.example.org", 443),
- ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
- key1, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource()));
- // The second request uses a second connection.
- SpdyTestUtil spdy_util2;
- spdy::SpdySerializedFrame req2(
- spdy_util2.ConstructSpdyGet("https://example.test", 1, LOWEST));
- MockWrite writes2[] = {
- CreateMockWrite(req2, 0),
- };
- spdy::SpdySerializedFrame resp2(
- spdy_util2.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body2(spdy_util2.ConstructSpdyDataFrame(1, true));
- MockRead reads2[] = {CreateMockRead(resp2, 1), CreateMockRead(body2, 2),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3)};
- MockConnect connect2(ASYNC, OK);
- SequencedSocketData data2(connect2, reads2, writes2);
- helper.AddData(&data2);
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL("https://example.test/");
- request2.load_flags = 0;
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- auto trans2 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
- helper.session());
- // Set on-demand mode and run the second request to the DNS lookup.
- helper.session_deps()->host_resolver->set_ondemand_mode(true);
- TestCompletionCallback callback;
- EXPECT_THAT(trans2->Start(&request2, callback.callback(), NetLogWithSource()),
- IsError(ERR_IO_PENDING));
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(helper.session_deps()->host_resolver->has_pending_requests());
- // Resolve the request now, which should create an alias for the SpdySession
- // immediately, but the task to use the session for the second request should
- // run asynchronously, so it hasn't run yet.
- helper.session_deps()->host_resolver->ResolveOnlyRequestNow();
- SpdySessionKey key2(HostPortPair("example.test", 443), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> session1 =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key2, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource());
- ASSERT_TRUE(session1);
- EXPECT_EQ(key1, session1->spdy_session_key());
- // Remove the session before the second request can try to use it.
- helper.session()->spdy_session_pool()->CloseAllSessions();
- // Wait for the second request to get headers. It should create a new H2
- // session to do so.
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- const HttpResponseInfo* response = trans2->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_TRUE(response->was_alpn_negotiated);
- std::string response_data;
- ASSERT_THAT(ReadTransaction(trans2.get(), &response_data), IsOk());
- EXPECT_EQ("hello!", response_data);
- // Inspect the new session.
- base::WeakPtr<SpdySession> session2 =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key2, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource());
- ASSERT_TRUE(session2);
- EXPECT_EQ(key2, session2->spdy_session_key());
- helper.VerifyDataConsumed();
- }
- #if BUILDFLAG(IS_ANDROID)
- // Test this if two HttpNetworkTransactions try to repurpose the same
- // SpdySession with two different SocketTags, only one request gets the session,
- // while the other makes a new SPDY session.
- TEST_F(SpdyNetworkTransactionTest, ConnectionPoolingMultipleSocketTags) {
- const SocketTag kSocketTag1(SocketTag::UNSET_UID, 1);
- const SocketTag kSocketTag2(SocketTag::UNSET_UID, 2);
- const SocketTag kSocketTag3(SocketTag::UNSET_UID, 3);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- // The first and third requests use the first connection.
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet("https://www.example.org", 1, LOWEST));
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req3(
- spdy_util_.ConstructSpdyGet("https://example.test/request3", 3, LOWEST));
- MockWrite writes1[] = {
- CreateMockWrite(req1, 0),
- CreateMockWrite(req3, 3),
- };
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame resp3(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead reads1[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
- CreateMockRead(resp3, 4), CreateMockRead(body3, 5),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6)};
- SequencedSocketData data1(MockConnect(ASYNC, OK), reads1, writes1);
- helper.AddData(&data1);
- // Due to the vagaries of how the socket pools work, in this particular case,
- // the second ConnectJob will be cancelled, but only after it tries to start
- // connecting. This does not happen in the general case of a bunch of requests
- // using the same socket tag.
- SequencedSocketData data2(MockConnect(SYNCHRONOUS, ERR_IO_PENDING),
- base::span<const MockRead>(),
- base::span<const MockWrite>());
- helper.AddData(&data2);
- // The second request uses a second connection.
- SpdyTestUtil spdy_util2;
- spdy::SpdySerializedFrame req2(
- spdy_util2.ConstructSpdyGet("https://example.test/request2", 1, LOWEST));
- MockWrite writes2[] = {
- CreateMockWrite(req2, 0),
- };
- spdy::SpdySerializedFrame resp2(
- spdy_util2.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body2(spdy_util2.ConstructSpdyDataFrame(1, true));
- MockRead reads2[] = {CreateMockRead(resp2, 1), CreateMockRead(body2, 2),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3)};
- SequencedSocketData data3(MockConnect(ASYNC, OK), reads2, writes2);
- helper.AddData(&data3);
- // Run a transaction to completion to set up a SPDY session. This can't use
- // RunToCompletion(), since it can't call VerifyDataConsumed() yet.
- helper.RunPreTestSetup();
- helper.RunDefaultTest();
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- // A new SPDY session should have been created.
- SpdySessionKey key1(HostPortPair("www.example.org", 443),
- ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
- key1, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource()));
- // Set on-demand mode for the next two requests.
- helper.session_deps()->host_resolver->set_ondemand_mode(true);
- HttpRequestInfo request2;
- request2.socket_tag = kSocketTag2;
- request2.method = "GET";
- request2.url = GURL("https://example.test/request2");
- request2.load_flags = 0;
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- auto trans2 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
- helper.session());
- TestCompletionCallback callback2;
- EXPECT_THAT(
- trans2->Start(&request2, callback2.callback(), NetLogWithSource()),
- IsError(ERR_IO_PENDING));
- HttpRequestInfo request3;
- request3.socket_tag = kSocketTag3;
- request3.method = "GET";
- request3.url = GURL("https://example.test/request3");
- request3.load_flags = 0;
- request3.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- auto trans3 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
- helper.session());
- TestCompletionCallback callback3;
- EXPECT_THAT(
- trans3->Start(&request3, callback3.callback(), NetLogWithSource()),
- IsError(ERR_IO_PENDING));
- // Run the message loop until both requests are waiting on the host resolver.
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(helper.session_deps()->host_resolver->has_pending_requests());
- // Complete the second requests's DNS lookup now, which should create an alias
- // for the SpdySession immediately, but the task to use the session for the
- // second request should run asynchronously, so it hasn't run yet.
- helper.session_deps()->host_resolver->ResolveNow(2);
- SpdySessionKey key2(HostPortPair("example.test", 443), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, kSocketTag2,
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- // Complete the third requests's DNS lookup now, which should hijack the
- // SpdySession from the second request.
- helper.session_deps()->host_resolver->ResolveNow(3);
- SpdySessionKey key3(HostPortPair("example.test", 443), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, kSocketTag3,
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- // Wait for the second request to get headers. It should create a new H2
- // session to do so.
- EXPECT_THAT(callback2.WaitForResult(), IsOk());
- const HttpResponseInfo* response = trans2->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_TRUE(response->was_alpn_negotiated);
- std::string response_data;
- ASSERT_THAT(ReadTransaction(trans2.get(), &response_data), IsOk());
- EXPECT_EQ("hello!", response_data);
- // Wait for the third request to get headers. It should have reused the first
- // session.
- EXPECT_THAT(callback3.WaitForResult(), IsOk());
- response = trans3->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_TRUE(response->was_alpn_negotiated);
- ASSERT_THAT(ReadTransaction(trans3.get(), &response_data), IsOk());
- EXPECT_EQ("hello!", response_data);
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, SocketTagChangeSessionTagWithDnsAliases) {
- SocketTag socket_tag_1(SocketTag::UNSET_UID, 1);
- SocketTag socket_tag_2(SocketTag::UNSET_UID, 2);
- request_.socket_tag = socket_tag_1;
- std::unique_ptr<SpdySessionDependencies> session_deps =
- std::make_unique<SpdySessionDependencies>();
- std::unique_ptr<MockCachingHostResolver> host_resolver =
- std::make_unique<MockCachingHostResolver>(2 /* cache_invalidation_num */);
- session_deps->host_resolver = std::move(host_resolver);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- GURL url = request_.url;
- std::set<std::string> dns_aliases({"alias1", "alias2", "alias3"});
- helper.session_deps()->host_resolver->rules()->AddIPLiteralRuleWithDnsAliases(
- url.host(), "127.0.0.1", dns_aliases);
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet(url.spec().c_str(), 1, DEFAULT_PRIORITY));
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(url.spec().c_str(), 3, DEFAULT_PRIORITY));
- MockWrite writes[] = {
- CreateMockWrite(req1, 0),
- CreateMockWrite(req2, 3),
- };
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead reads[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
- CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6)};
- SequencedSocketData data(MockConnect(ASYNC, OK), reads, writes);
- helper.AddData(&data);
- // Run a transaction to completion to set up a SPDY session. This can't use
- // RunToCompletion(), since it can't call VerifyDataConsumed() yet because
- // there are still further requests expected.
- helper.RunPreTestSetup();
- helper.RunDefaultTest();
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- // A new SPDY session should have been created.
- EXPECT_EQ(1u, helper.GetSpdySessionCount());
- SpdySessionKey key1(HostPortPair(url.host(), 443), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, socket_tag_1,
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
- key1, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource()));
- EXPECT_EQ(
- dns_aliases,
- helper.session()->spdy_session_pool()->GetDnsAliasesForSessionKey(key1));
- // Clear host resolver rules to ensure that cached values for DNS aliases
- // are used.
- helper.session_deps()->host_resolver->rules()->ClearRules();
- HttpRequestInfo request2;
- request2.socket_tag = socket_tag_2;
- request2.method = "GET";
- request2.url = url;
- request2.load_flags = 0;
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- SpdySessionKey key2(HostPortPair(url.host(), 443), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, socket_tag_2,
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- auto trans2 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
- helper.session());
- TestCompletionCallback callback2;
- EXPECT_THAT(
- trans2->Start(&request2, callback2.callback(), NetLogWithSource()),
- IsError(ERR_IO_PENDING));
- // Wait for the second request to get headers. It should have reused the
- // first session but changed the tag.
- EXPECT_THAT(callback2.WaitForResult(), IsOk());
- EXPECT_EQ(1u, helper.GetSpdySessionCount());
- EXPECT_FALSE(helper.session()->spdy_session_pool()->FindAvailableSession(
- key1, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource()));
- EXPECT_TRUE(helper.session()
- ->spdy_session_pool()
- ->GetDnsAliasesForSessionKey(key1)
- .empty());
- EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
- key2, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource()));
- EXPECT_EQ(
- dns_aliases,
- helper.session()->spdy_session_pool()->GetDnsAliasesForSessionKey(key2));
- const HttpResponseInfo* response = trans2->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_TRUE(response->was_alpn_negotiated);
- std::string response_data;
- ASSERT_THAT(ReadTransaction(trans2.get(), &response_data), IsOk());
- EXPECT_EQ("hello!", response_data);
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest,
- SocketTagChangeFromIPAliasedSessionWithDnsAliases) {
- SocketTag socket_tag_1(SocketTag::UNSET_UID, 1);
- SocketTag socket_tag_2(SocketTag::UNSET_UID, 2);
- request_.socket_tag = socket_tag_1;
- std::unique_ptr<SpdySessionDependencies> session_deps =
- std::make_unique<SpdySessionDependencies>();
- std::unique_ptr<MockCachingHostResolver> host_resolver =
- std::make_unique<MockCachingHostResolver>(2 /* cache_invalidation_num */);
- session_deps->host_resolver = std::move(host_resolver);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- GURL url1 = request_.url;
- std::set<std::string> dns_aliases1({"alias1", "alias2", "alias3"});
- GURL url2("https://example.test/");
- std::set<std::string> dns_aliases2({"example.net", "example.com"});
- helper.session_deps()->host_resolver->rules()->AddIPLiteralRuleWithDnsAliases(
- url1.host(), "127.0.0.1", dns_aliases1);
- helper.session_deps()->host_resolver->rules()->AddIPLiteralRuleWithDnsAliases(
- url2.host(), "127.0.0.1", dns_aliases2);
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet(url1.spec().c_str(), 1, DEFAULT_PRIORITY));
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(url2.spec().c_str(), 3, DEFAULT_PRIORITY));
- spdy_util_.UpdateWithStreamDestruction(3);
- spdy::SpdySerializedFrame req3(
- spdy_util_.ConstructSpdyGet(url2.spec().c_str(), 5, DEFAULT_PRIORITY));
- spdy_util_.UpdateWithStreamDestruction(5);
- spdy::SpdySerializedFrame req4(
- spdy_util_.ConstructSpdyGet(url1.spec().c_str(), 7, DEFAULT_PRIORITY));
- MockWrite writes[] = {
- CreateMockWrite(req1, 0),
- CreateMockWrite(req2, 3),
- CreateMockWrite(req3, 6),
- CreateMockWrite(req4, 9),
- };
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
- spdy::SpdySerializedFrame resp3(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5));
- spdy::SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, true));
- spdy::SpdySerializedFrame resp4(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 7));
- spdy::SpdySerializedFrame body4(spdy_util_.ConstructSpdyDataFrame(7, true));
- MockRead reads[] = {CreateMockRead(resp1, 1),
- CreateMockRead(body1, 2),
- CreateMockRead(resp2, 4),
- CreateMockRead(body2, 5),
- CreateMockRead(resp3, 7),
- CreateMockRead(body3, 8),
- CreateMockRead(resp4, 10),
- CreateMockRead(body4, 11),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 12)};
- SequencedSocketData data(MockConnect(ASYNC, OK), reads, writes);
- helper.AddData(&data);
- // Run a transaction to completion to set up a SPDY session. This can't use
- // RunToCompletion(), since it can't call VerifyDataConsumed() yet.
- helper.RunPreTestSetup();
- helper.RunDefaultTest();
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- // A new SPDY session should have been created.
- EXPECT_EQ(1u, helper.GetSpdySessionCount());
- SpdySessionKey key1(HostPortPair(url1.host(), 443), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, socket_tag_1,
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
- key1, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource()));
- EXPECT_EQ(
- dns_aliases1,
- helper.session()->spdy_session_pool()->GetDnsAliasesForSessionKey(key1));
- HttpRequestInfo request2;
- request2.socket_tag = socket_tag_1;
- request2.method = "GET";
- request2.url = url2;
- request2.load_flags = 0;
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- SpdySessionKey key2(HostPortPair(url2.host(), 443), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, socket_tag_1,
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- auto trans2 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
- helper.session());
- TestCompletionCallback callback2;
- EXPECT_THAT(
- trans2->Start(&request2, callback2.callback(), NetLogWithSource()),
- IsError(ERR_IO_PENDING));
- // Wait for the second request to get headers. It should have reused the
- // first session.
- EXPECT_THAT(callback2.WaitForResult(), IsOk());
- EXPECT_EQ(1u, helper.GetSpdySessionCount());
- EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
- key2, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource()));
- EXPECT_EQ(
- dns_aliases2,
- helper.session()->spdy_session_pool()->GetDnsAliasesForSessionKey(key2));
- const HttpResponseInfo* response = trans2->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_TRUE(response->was_alpn_negotiated);
- std::string response_data;
- ASSERT_THAT(ReadTransaction(trans2.get(), &response_data), IsOk());
- EXPECT_EQ("hello!", response_data);
- // Clear host resolver rules to ensure that cached values for DNS aliases
- // are used.
- helper.session_deps()->host_resolver->rules()->ClearRules();
- trans2.reset();
- HttpRequestInfo request3;
- request3.socket_tag = socket_tag_2;
- request3.method = "GET";
- request3.url = url2;
- request3.load_flags = 0;
- request3.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- SpdySessionKey key3(HostPortPair(url2.host(), 443), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, socket_tag_2,
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- auto trans3 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
- helper.session());
- TestCompletionCallback callback3;
- EXPECT_THAT(
- trans3->Start(&request3, callback3.callback(), NetLogWithSource()),
- IsError(ERR_IO_PENDING));
- // Wait for the third request to get headers. It should have reused the
- // first session but changed the socket tag.
- EXPECT_THAT(callback3.WaitForResult(), IsOk());
- EXPECT_EQ(1u, helper.GetSpdySessionCount());
- EXPECT_FALSE(helper.session()->spdy_session_pool()->FindAvailableSession(
- key2, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource()));
- EXPECT_TRUE(helper.session()
- ->spdy_session_pool()
- ->GetDnsAliasesForSessionKey(key2)
- .empty());
- EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
- key3, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource()));
- EXPECT_EQ(
- dns_aliases2,
- helper.session()->spdy_session_pool()->GetDnsAliasesForSessionKey(key3));
- response = trans3->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_TRUE(response->was_alpn_negotiated);
- ASSERT_THAT(ReadTransaction(trans3.get(), &response_data), IsOk());
- EXPECT_EQ("hello!", response_data);
- trans3.reset();
- HttpRequestInfo request4;
- request4.socket_tag = socket_tag_2;
- request4.method = "GET";
- request4.url = url1;
- request4.load_flags = 0;
- request4.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- SpdySessionKey key4(HostPortPair(url1.host(), 443), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, socket_tag_2,
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- auto trans4 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
- helper.session());
- TestCompletionCallback callback4;
- EXPECT_THAT(
- trans4->Start(&request4, callback4.callback(), NetLogWithSource()),
- IsError(ERR_IO_PENDING));
- // Wait for the third request to get headers. It should have reused the
- // first session but changed the socket tag.
- EXPECT_THAT(callback4.WaitForResult(), IsOk());
- EXPECT_EQ(1u, helper.GetSpdySessionCount());
- EXPECT_FALSE(helper.session()->spdy_session_pool()->FindAvailableSession(
- key1, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource()));
- EXPECT_TRUE(helper.session()
- ->spdy_session_pool()
- ->GetDnsAliasesForSessionKey(key1)
- .empty());
- EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession(
- key4, true /* enable_ip_based_pooling */, false /* is_websocket */,
- NetLogWithSource()));
- EXPECT_EQ(
- dns_aliases1,
- helper.session()->spdy_session_pool()->GetDnsAliasesForSessionKey(key4));
- response = trans4->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_TRUE(response->was_alpn_negotiated);
- ASSERT_THAT(ReadTransaction(trans4.get(), &response_data), IsOk());
- EXPECT_EQ("hello!", response_data);
- helper.VerifyDataConsumed();
- }
- #endif // BUILDFLAG(IS_ANDROID)
- // Regression test for https://crbug.com/727653.
- TEST_F(SpdyNetworkTransactionTest, RejectServerPushWithNoMethod) {
- base::HistogramTester histogram_tester;
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_REFUSED_STREAM));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(rst, 3)};
- spdy::SpdySerializedFrame reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::Http2HeaderBlock push_promise_header_block;
- spdy_util_.AddUrlToHeaderBlock(kPushedUrl, &push_promise_header_block);
- spdy::SpdySerializedFrame push_promise(spdy_util_.ConstructSpdyPushPromise(
- 1, 2, std::move(push_promise_header_block)));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(reply, 1), CreateMockRead(push_promise, 2),
- CreateMockRead(body, 4),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kInvalidUrl), 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1);
- }
- // Regression test for https://crbug.com/727653.
- TEST_F(SpdyNetworkTransactionTest, RejectServerPushWithInvalidMethod) {
- base::HistogramTester histogram_tester;
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_REFUSED_STREAM));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(rst, 3)};
- spdy::SpdySerializedFrame reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::Http2HeaderBlock push_promise_header_block;
- push_promise_header_block[":method"] = "POST";
- spdy_util_.AddUrlToHeaderBlock(kPushedUrl, &push_promise_header_block);
- spdy::SpdySerializedFrame push_promise(spdy_util_.ConstructSpdyPushPromise(
- 1, 2, std::move(push_promise_header_block)));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(reply, 1), CreateMockRead(push_promise, 2),
- CreateMockRead(body, 4),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kInvalidUrl), 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1);
- }
- // Verify that various response headers parse correctly through the HTTP layer.
- TEST_F(SpdyNetworkTransactionTest, ResponseHeaders) {
- struct ResponseHeadersTests {
- int extra_header_count;
- const char* extra_headers[4];
- size_t expected_header_count;
- base::StringPiece expected_headers[8];
- } test_cases[] = {
- // No extra headers.
- {0, {}, 1, {"hello", "bye"}},
- // Comma-separated header value.
- {1,
- {"cookie", "val1, val2"},
- 2,
- {"hello", "bye", "cookie", "val1, val2"}},
- // Multiple headers are preserved: they are joined with \0 separator in
- // spdy::Http2HeaderBlock.AppendValueOrAddHeader(), then split up in
- // HpackEncoder, then joined with \0 separator when
- // spdy::HpackDecoderAdapter::ListenerAdapter::OnHeader() calls
- // spdy::Http2HeaderBlock.AppendValueOrAddHeader(), then split up again in
- // HttpResponseHeaders.
- {2,
- {"content-encoding", "val1", "content-encoding", "val2"},
- 3,
- {"hello", "bye", "content-encoding", "val1", "content-encoding",
- "val2"}},
- // Cookie header is not split up by HttpResponseHeaders.
- {2,
- {"cookie", "val1", "cookie", "val2"},
- 2,
- {"hello", "bye", "cookie", "val1; val2"}}};
- for (size_t i = 0; i < std::size(test_cases); ++i) {
- SCOPED_TRACE(i);
- SpdyTestUtil spdy_test_util;
- spdy::SpdySerializedFrame req(
- spdy_test_util.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(spdy_test_util.ConstructSpdyGetReply(
- test_cases[i].extra_headers, test_cases[i].extra_header_count, 1));
- spdy::SpdySerializedFrame body(
- spdy_test_util.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- scoped_refptr<HttpResponseHeaders> headers = out.response_info.headers;
- ASSERT_TRUE(headers);
- EXPECT_EQ("HTTP/1.1 200", headers->GetStatusLine());
- size_t iter = 0;
- std::string name, value;
- size_t expected_header_index = 0;
- while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
- ASSERT_LT(expected_header_index, test_cases[i].expected_header_count);
- EXPECT_EQ(name,
- test_cases[i].expected_headers[2 * expected_header_index]);
- EXPECT_EQ(value,
- test_cases[i].expected_headers[2 * expected_header_index + 1]);
- ++expected_header_index;
- }
- EXPECT_EQ(expected_header_index, test_cases[i].expected_header_count);
- }
- }
- // Verify that we don't crash on invalid response headers.
- TEST_F(SpdyNetworkTransactionTest, InvalidResponseHeaders) {
- struct InvalidResponseHeadersTests {
- int num_headers;
- const char* headers[10];
- } test_cases[] = {// Response headers missing status header
- {2, {"cookie", "val1", "cookie", "val2", nullptr}},
- // Response headers with no headers
- {0, {nullptr}}};
- for (size_t i = 0; i < std::size(test_cases); ++i) {
- SCOPED_TRACE(i);
- SpdyTestUtil spdy_test_util;
- spdy::SpdySerializedFrame req(
- spdy_test_util.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(spdy_test_util.ConstructSpdyRstStream(
- 1, spdy::ERROR_CODE_PROTOCOL_ERROR));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(rst, 2),
- };
- // Construct the reply.
- spdy::Http2HeaderBlock reply_headers;
- AppendToHeaderBlock(
- test_cases[i].headers, test_cases[i].num_headers, &reply_headers);
- spdy::SpdySerializedFrame resp(
- spdy_test_util.ConstructSpdyReply(1, std::move(reply_headers)));
- MockRead reads[] = {
- CreateMockRead(resp, 1), MockRead(ASYNC, 0, 3) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_PROTOCOL_ERROR));
- }
- }
- TEST_F(SpdyNetworkTransactionTest, CorruptFrameSessionError) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(
- 0, spdy::ERROR_CODE_COMPRESSION_ERROR,
- "Framer error: 24 (HPACK_TRUNCATED_BLOCK)."));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(goaway, 2)};
- // This is the length field that's too short.
- spdy::SpdySerializedFrame reply_wrong_length(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- size_t right_size = reply_wrong_length.size() - spdy::kFrameHeaderSize;
- size_t wrong_size = right_size - 4;
- spdy::test::SetFrameLength(&reply_wrong_length, wrong_size);
- MockRead reads[] = {
- MockRead(ASYNC, reply_wrong_length.data(), reply_wrong_length.size() - 4,
- 1),
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_COMPRESSION_ERROR));
- }
- TEST_F(SpdyNetworkTransactionTest, GoAwayOnDecompressionFailure) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(
- 0, spdy::ERROR_CODE_COMPRESSION_ERROR,
- "Framer error: 24 (HPACK_TRUNCATED_BLOCK)."));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(goaway, 2)};
- // Read HEADERS with corrupted payload.
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- memset(resp.data() + 12, 0xcf, resp.size() - 12);
- MockRead reads[] = {CreateMockRead(resp, 1)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_COMPRESSION_ERROR));
- }
- TEST_F(SpdyNetworkTransactionTest, GoAwayOnFrameSizeError) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(
- 0, spdy::ERROR_CODE_FRAME_SIZE_ERROR,
- "Framer error: 9 (INVALID_CONTROL_FRAME_SIZE)."));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(goaway, 2)};
- // Read WINDOW_UPDATE with incorrectly-sized payload.
- spdy::SpdySerializedFrame bad_window_update(
- spdy_util_.ConstructSpdyWindowUpdate(1, 1));
- spdy::test::SetFrameLength(&bad_window_update, bad_window_update.size() - 1);
- MockRead reads[] = {CreateMockRead(bad_window_update, 1)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_FRAME_SIZE_ERROR));
- }
- // Test that we shutdown correctly on write errors.
- TEST_F(SpdyNetworkTransactionTest, WriteError) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {
- // We'll write 10 bytes successfully
- MockWrite(ASYNC, req.data(), 10, 1),
- // Followed by ERROR!
- MockWrite(ASYNC, ERR_FAILED, 2),
- // Session drains and attempts to write a GOAWAY: Another ERROR!
- MockWrite(ASYNC, ERR_FAILED, 3),
- };
- MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- EXPECT_TRUE(helper.StartDefaultTest());
- helper.FinishDefaultTest();
- EXPECT_TRUE(data.AllWriteDataConsumed());
- EXPECT_TRUE(data.AllReadDataConsumed());
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_FAILED));
- }
- // Test that partial writes work.
- TEST_F(SpdyNetworkTransactionTest, PartialWrite) {
- // Chop the HEADERS frame into 5 chunks.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- const int kChunks = 5;
- std::unique_ptr<MockWrite[]> writes = ChopWriteFrame(req, kChunks);
- for (int i = 0; i < kChunks; ++i) {
- writes[i].sequence_number = i;
- }
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, kChunks), CreateMockRead(body, kChunks + 1),
- MockRead(ASYNC, 0, kChunks + 2) // EOF
- };
- SequencedSocketData data(reads, base::make_span(writes.get(), kChunks));
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- // Test that the NetLog contains good data for a simple GET request.
- TEST_F(SpdyNetworkTransactionTest, NetLog) {
- static const char* const kExtraHeaders[] = {
- "user-agent", "Chrome",
- };
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(kExtraHeaders, 1, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- RecordingNetLogObserver net_log_observer;
- SequencedSocketData data(reads, writes);
- request_.extra_headers.SetHeader("User-Agent", "Chrome");
- NormalSpdyTransactionHelper helper(
- request_, DEFAULT_PRIORITY,
- NetLogWithSource::Make(NetLogSourceType::NONE), nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- // Check that the NetLog was filled reasonably.
- // This test is intentionally non-specific about the exact ordering of the
- // log; instead we just check to make sure that certain events exist, and that
- // they are in the right order.
- auto entries = net_log_observer.GetEntries();
- EXPECT_LT(0u, entries.size());
- int pos = 0;
- pos = ExpectLogContainsSomewhere(
- entries, 0, NetLogEventType::HTTP_TRANSACTION_SEND_REQUEST,
- NetLogEventPhase::BEGIN);
- pos = ExpectLogContainsSomewhere(
- entries, pos + 1, NetLogEventType::HTTP_TRANSACTION_SEND_REQUEST,
- NetLogEventPhase::END);
- pos = ExpectLogContainsSomewhere(
- entries, pos + 1, NetLogEventType::HTTP_TRANSACTION_READ_HEADERS,
- NetLogEventPhase::BEGIN);
- pos = ExpectLogContainsSomewhere(
- entries, pos + 1, NetLogEventType::HTTP_TRANSACTION_READ_HEADERS,
- NetLogEventPhase::END);
- pos = ExpectLogContainsSomewhere(entries, pos + 1,
- NetLogEventType::HTTP_TRANSACTION_READ_BODY,
- NetLogEventPhase::BEGIN);
- pos = ExpectLogContainsSomewhere(entries, pos + 1,
- NetLogEventType::HTTP_TRANSACTION_READ_BODY,
- NetLogEventPhase::END);
- // Check that we logged all the headers correctly
- pos = ExpectLogContainsSomewhere(entries, 0,
- NetLogEventType::HTTP2_SESSION_SEND_HEADERS,
- NetLogEventPhase::NONE);
- ASSERT_TRUE(entries[pos].HasParams());
- auto* header_list = entries[pos].params.GetDict().FindList("headers");
- ASSERT_TRUE(header_list);
- ASSERT_EQ(5u, header_list->size());
- ASSERT_TRUE((*header_list)[0].is_string());
- EXPECT_EQ(":method: GET", (*header_list)[0].GetString());
- ASSERT_TRUE((*header_list)[1].is_string());
- EXPECT_EQ(":authority: www.example.org", (*header_list)[1].GetString());
- ASSERT_TRUE((*header_list)[2].is_string());
- EXPECT_EQ(":scheme: https", (*header_list)[2].GetString());
- ASSERT_TRUE((*header_list)[3].is_string());
- EXPECT_EQ(":path: /", (*header_list)[3].GetString());
- ASSERT_TRUE((*header_list)[4].is_string());
- EXPECT_EQ("user-agent: Chrome", (*header_list)[4].GetString());
- }
- // Since we buffer the IO from the stream to the renderer, this test verifies
- // that when we read out the maximum amount of data (e.g. we received 50 bytes
- // on the network, but issued a Read for only 5 of those bytes) that the data
- // flow still works correctly.
- TEST_F(SpdyNetworkTransactionTest, BufferFull) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- // 2 data frames in a single read.
- spdy::SpdySerializedFrame data_frame_1(
- spdy_util_.ConstructSpdyDataFrame(1, "goodby", /*fin=*/false));
- spdy::SpdySerializedFrame data_frame_2(
- spdy_util_.ConstructSpdyDataFrame(1, "e worl", /*fin=*/false));
- spdy::SpdySerializedFrame combined_data_frames =
- CombineFrames({&data_frame_1, &data_frame_2});
- spdy::SpdySerializedFrame last_frame(
- spdy_util_.ConstructSpdyDataFrame(1, "d", /*fin=*/true));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- MockRead reads[] = {
- CreateMockRead(resp, 1),
- MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause
- CreateMockRead(combined_data_frames, 3),
- MockRead(ASYNC, ERR_IO_PENDING, 4), // Force a pause
- CreateMockRead(last_frame, 5),
- MockRead(ASYNC, 0, 6) // EOF
- };
- SequencedSocketData data(reads, writes);
- TestCompletionCallback callback;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- TransactionHelperResult out = helper.output();
- out.rv = callback.WaitForResult();
- EXPECT_EQ(out.rv, OK);
- const HttpResponseInfo* response = trans->GetResponseInfo();
- EXPECT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- out.status_line = response->headers->GetStatusLine();
- out.response_info = *response; // Make a copy so we can verify.
- // Read Data
- TestCompletionCallback read_callback;
- std::string content;
- do {
- // Read small chunks at a time.
- const int kSmallReadSize = 3;
- scoped_refptr<IOBuffer> buf =
- base::MakeRefCounted<IOBuffer>(kSmallReadSize);
- rv = trans->Read(buf.get(), kSmallReadSize, read_callback.callback());
- if (rv == ERR_IO_PENDING) {
- data.Resume();
- rv = read_callback.WaitForResult();
- }
- if (rv > 0) {
- content.append(buf->data(), rv);
- } else if (rv < 0) {
- NOTREACHED();
- }
- } while (rv > 0);
- out.response_data.swap(content);
- // Flush the MessageLoop while the SpdySessionDependencies (in particular, the
- // MockClientSocketFactory) are still alive.
- base::RunLoop().RunUntilIdle();
- // Verify that we consumed all test data.
- helper.VerifyDataConsumed();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("goodbye world", out.response_data);
- }
- // Verify that basic buffering works; when multiple data frames arrive
- // at the same time, ensure that we don't notify a read completion for
- // each data frame individually.
- TEST_F(SpdyNetworkTransactionTest, Buffering) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- // 4 data frames in a single read.
- spdy::SpdySerializedFrame data_frame(
- spdy_util_.ConstructSpdyDataFrame(1, "message", /*fin=*/false));
- spdy::SpdySerializedFrame data_frame_fin(
- spdy_util_.ConstructSpdyDataFrame(1, "message", /*fin=*/true));
- spdy::SpdySerializedFrame combined_data_frames =
- CombineFrames({&data_frame, &data_frame, &data_frame, &data_frame_fin});
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- MockRead reads[] = {
- CreateMockRead(resp, 1),
- MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause
- CreateMockRead(combined_data_frames, 3), MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- TransactionHelperResult out = helper.output();
- out.rv = callback.WaitForResult();
- EXPECT_EQ(out.rv, OK);
- const HttpResponseInfo* response = trans->GetResponseInfo();
- EXPECT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- out.status_line = response->headers->GetStatusLine();
- out.response_info = *response; // Make a copy so we can verify.
- // Read Data
- TestCompletionCallback read_callback;
- std::string content;
- int reads_completed = 0;
- do {
- // Read small chunks at a time.
- const int kSmallReadSize = 14;
- scoped_refptr<IOBuffer> buf =
- base::MakeRefCounted<IOBuffer>(kSmallReadSize);
- rv = trans->Read(buf.get(), kSmallReadSize, read_callback.callback());
- if (rv == ERR_IO_PENDING) {
- data.Resume();
- rv = read_callback.WaitForResult();
- }
- if (rv > 0) {
- EXPECT_EQ(kSmallReadSize, rv);
- content.append(buf->data(), rv);
- } else if (rv < 0) {
- FAIL() << "Unexpected read error: " << rv;
- }
- reads_completed++;
- } while (rv > 0);
- EXPECT_EQ(3, reads_completed); // Reads are: 14 bytes, 14 bytes, 0 bytes.
- out.response_data.swap(content);
- // Flush the MessageLoop while the SpdySessionDependencies (in particular, the
- // MockClientSocketFactory) are still alive.
- base::RunLoop().RunUntilIdle();
- // Verify that we consumed all test data.
- helper.VerifyDataConsumed();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("messagemessagemessagemessage", out.response_data);
- }
- // Verify the case where we buffer data but read it after it has been buffered.
- TEST_F(SpdyNetworkTransactionTest, BufferedAll) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- // 5 data frames in a single read.
- spdy::SpdySerializedFrame reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame data_frame(
- spdy_util_.ConstructSpdyDataFrame(1, "message", /*fin=*/false));
- spdy::SpdySerializedFrame data_frame_fin(
- spdy_util_.ConstructSpdyDataFrame(1, "message", /*fin=*/true));
- spdy::SpdySerializedFrame combined_frames = CombineFrames(
- {&reply, &data_frame, &data_frame, &data_frame, &data_frame_fin});
- MockRead reads[] = {
- CreateMockRead(combined_frames, 1), MockRead(ASYNC, 0, 2) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- TransactionHelperResult out = helper.output();
- out.rv = callback.WaitForResult();
- EXPECT_EQ(out.rv, OK);
- const HttpResponseInfo* response = trans->GetResponseInfo();
- EXPECT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- out.status_line = response->headers->GetStatusLine();
- out.response_info = *response; // Make a copy so we can verify.
- // Read Data
- TestCompletionCallback read_callback;
- std::string content;
- int reads_completed = 0;
- do {
- // Read small chunks at a time.
- const int kSmallReadSize = 14;
- scoped_refptr<IOBuffer> buf =
- base::MakeRefCounted<IOBuffer>(kSmallReadSize);
- rv = trans->Read(buf.get(), kSmallReadSize, read_callback.callback());
- if (rv > 0) {
- EXPECT_EQ(kSmallReadSize, rv);
- content.append(buf->data(), rv);
- } else if (rv < 0) {
- FAIL() << "Unexpected read error: " << rv;
- }
- reads_completed++;
- } while (rv > 0);
- EXPECT_EQ(3, reads_completed);
- out.response_data.swap(content);
- // Flush the MessageLoop while the SpdySessionDependencies (in particular, the
- // MockClientSocketFactory) are still alive.
- base::RunLoop().RunUntilIdle();
- // Verify that we consumed all test data.
- helper.VerifyDataConsumed();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("messagemessagemessagemessage", out.response_data);
- }
- // Verify the case where we buffer data and close the connection.
- TEST_F(SpdyNetworkTransactionTest, BufferedClosed) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- // All data frames in a single read.
- // NOTE: We don't FIN the stream.
- spdy::SpdySerializedFrame data_frame(
- spdy_util_.ConstructSpdyDataFrame(1, "message", /*fin=*/false));
- spdy::SpdySerializedFrame combined_data_frames =
- CombineFrames({&data_frame, &data_frame, &data_frame, &data_frame});
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- MockRead reads[] = {
- CreateMockRead(resp, 1),
- MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a wait
- CreateMockRead(combined_data_frames, 3), MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- TransactionHelperResult out = helper.output();
- rv = callback.WaitForResult();
- EXPECT_EQ(rv, OK);
- const HttpResponseInfo* response = trans->GetResponseInfo();
- EXPECT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- // Read Data
- TestCompletionCallback read_callback;
- std::string content;
- int reads_completed = 0;
- do {
- // Allocate a large buffer to allow buffering. If a single read fills the
- // buffer, no buffering happens.
- const int kLargeReadSize = 1000;
- scoped_refptr<IOBuffer> buf =
- base::MakeRefCounted<IOBuffer>(kLargeReadSize);
- rv = trans->Read(buf.get(), kLargeReadSize, read_callback.callback());
- if (rv == ERR_IO_PENDING) {
- data.Resume();
- rv = read_callback.WaitForResult();
- }
- if (rv < 0) {
- // This test intentionally closes the connection, and will get an error.
- EXPECT_THAT(rv, IsError(ERR_CONNECTION_CLOSED));
- break;
- }
- reads_completed++;
- } while (rv > 0);
- EXPECT_EQ(0, reads_completed);
- // Flush the MessageLoop while the SpdySessionDependencies (in particular, the
- // MockClientSocketFactory) are still alive.
- base::RunLoop().RunUntilIdle();
- // Verify that we consumed all test data.
- helper.VerifyDataConsumed();
- }
- // Verify the case where we buffer data and cancel the transaction.
- TEST_F(SpdyNetworkTransactionTest, BufferedCancelled) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(rst, 4)};
- // NOTE: We don't FIN the stream.
- spdy::SpdySerializedFrame data_frame(
- spdy_util_.ConstructSpdyDataFrame(1, "message", /*fin=*/false));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- MockRead reads[] = {
- CreateMockRead(resp, 1),
- MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a wait
- CreateMockRead(data_frame, 3), MockRead(ASYNC, 0, 5) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- TransactionHelperResult out = helper.output();
- out.rv = callback.WaitForResult();
- EXPECT_EQ(out.rv, OK);
- const HttpResponseInfo* response = trans->GetResponseInfo();
- EXPECT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- out.status_line = response->headers->GetStatusLine();
- out.response_info = *response; // Make a copy so we can verify.
- // Read Data
- TestCompletionCallback read_callback;
- const int kReadSize = 256;
- scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBuffer>(kReadSize);
- rv = trans->Read(buf.get(), kReadSize, read_callback.callback());
- ASSERT_EQ(ERR_IO_PENDING, rv) << "Unexpected read: " << rv;
- // Complete the read now, which causes buffering to start.
- data.Resume();
- base::RunLoop().RunUntilIdle();
- // Destroy the transaction, causing the stream to get cancelled
- // and orphaning the buffered IO task.
- helper.ResetTrans();
- // Flush the MessageLoop; this will cause the buffered IO task
- // to run for the final time.
- base::RunLoop().RunUntilIdle();
- // Verify that we consumed all test data.
- helper.VerifyDataConsumed();
- }
- // Request should fail upon receiving a GOAWAY frame
- // with Last-Stream-ID lower than the stream id corresponding to the request
- // and with error code other than NO_ERROR.
- TEST_F(SpdyNetworkTransactionTest, FailOnGoAway) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame go_away(
- spdy_util_.ConstructSpdyGoAway(0, spdy::ERROR_CODE_INTERNAL_ERROR, ""));
- MockRead reads[] = {
- CreateMockRead(go_away, 1),
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_PROTOCOL_ERROR));
- }
- // Request should be retried on a new connection upon receiving a GOAWAY frame
- // with Last-Stream-ID lower than the stream id corresponding to the request
- // and with error code NO_ERROR.
- TEST_F(SpdyNetworkTransactionTest, RetryOnGoAway) {
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- // First connection.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes1[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame go_away(
- spdy_util_.ConstructSpdyGoAway(0, spdy::ERROR_CODE_NO_ERROR, ""));
- MockRead reads1[] = {CreateMockRead(go_away, 1)};
- SequencedSocketData data1(reads1, writes1);
- helper.AddData(&data1);
- // Second connection.
- MockWrite writes2[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads2[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3)};
- SequencedSocketData data2(reads2, writes2);
- helper.AddData(&data2);
- helper.RunPreTestSetup();
- helper.RunDefaultTest();
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- helper.VerifyDataConsumed();
- }
- // A server can gracefully shut down by sending a GOAWAY frame
- // with maximum last-stream-id value.
- // Transactions started before receiving such a GOAWAY frame should succeed,
- // but SpdySession should be unavailable for new streams.
- TEST_F(SpdyNetworkTransactionTest, GracefulGoaway) {
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet("https://www.example.org/foo", 3, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req1, 0), CreateMockWrite(req2, 3)};
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(
- 0x7fffffff, spdy::ERROR_CODE_NO_ERROR, "Graceful shutdown."));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead reads[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
- CreateMockRead(goaway, 4), CreateMockRead(resp2, 5),
- CreateMockRead(body2, 6), MockRead(ASYNC, 0, 7)};
- // Run first transaction.
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- helper.RunDefaultTest();
- // Verify first response.
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- // GOAWAY frame has not yet been received, SpdySession should be available.
- SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool();
- SpdySessionKey key(host_port_pair_, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- EXPECT_TRUE(
- spdy_session_pool->HasAvailableSession(key, /* is_websocket = */ false));
- base::WeakPtr<SpdySession> spdy_session =
- spdy_session_pool->FindAvailableSession(
- key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- EXPECT_TRUE(spdy_session);
- // Start second transaction.
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- TestCompletionCallback callback;
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL("https://www.example.org/foo");
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- int rv = trans2.Start(&request2, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- // Verify second response.
- const HttpResponseInfo* response = trans2.GetResponseInfo();
- ASSERT_TRUE(response);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2, response->connection_info);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_TRUE(response->was_alpn_negotiated);
- EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
- EXPECT_EQ(443, response->remote_endpoint.port());
- std::string response_data;
- rv = ReadTransaction(&trans2, &response_data);
- EXPECT_THAT(rv, IsOk());
- EXPECT_EQ("hello!", response_data);
- // Graceful GOAWAY was received, SpdySession should be unavailable.
- EXPECT_FALSE(
- spdy_session_pool->HasAvailableSession(key, /* is_websocket = */ false));
- spdy_session = spdy_session_pool->FindAvailableSession(
- key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- EXPECT_FALSE(spdy_session);
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, CloseWithActiveStream) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- MockRead reads[] = {
- CreateMockRead(resp, 1), MockRead(SYNCHRONOUS, 0, 2) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- helper.StartDefaultTest();
- EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING));
- helper.WaitForCallbackToComplete();
- EXPECT_THAT(helper.output().rv, IsError(ERR_CONNECTION_CLOSED));
- const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
- EXPECT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- // Verify that we consumed all test data.
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, GoAwayImmediately) {
- spdy::SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(1));
- MockRead reads[] = {CreateMockRead(goaway, 0, SYNCHRONOUS)};
- SequencedSocketData data(reads, base::span<MockWrite>());
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- helper.StartDefaultTest();
- EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING));
- helper.WaitForCallbackToComplete();
- EXPECT_THAT(helper.output().rv, IsError(ERR_CONNECTION_CLOSED));
- const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
- EXPECT_FALSE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- // Verify that we consumed all test data.
- helper.VerifyDataConsumed();
- }
- // Retry with HTTP/1.1 when receiving HTTP_1_1_REQUIRED. Note that no actual
- // protocol negotiation happens, instead this test forces protocols for both
- // sockets.
- TEST_F(SpdyNetworkTransactionTest, HTTP11RequiredRetry) {
- request_.method = "GET";
- // Do not force SPDY so that second socket can negotiate HTTP/1.1.
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- // First socket: HTTP/2 request rejected with HTTP_1_1_REQUIRED.
- spdy::Http2HeaderBlock headers(
- spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
- MockWrite writes0[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_HTTP_1_1_REQUIRED));
- MockRead reads0[] = {CreateMockRead(rst, 1)};
- SequencedSocketData data0(reads0, writes0);
- auto ssl_provider0 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Expect HTTP/2 protocols too in SSLConfig.
- ssl_provider0->next_protos_expected_in_ssl_config =
- NextProtoVector{kProtoHTTP2, kProtoHTTP11};
- // Force SPDY.
- ssl_provider0->next_proto = kProtoHTTP2;
- helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0));
- // Second socket: falling back to HTTP/1.1.
- MockWrite writes1[] = {MockWrite(ASYNC, 0,
- "GET / HTTP/1.1\r\n"
- "Host: www.example.org\r\n"
- "Connection: keep-alive\r\n\r\n")};
- MockRead reads1[] = {MockRead(ASYNC, 1,
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 5\r\n\r\n"
- "hello")};
- SequencedSocketData data1(reads1, writes1);
- auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Expect only HTTP/1.1 protocol in SSLConfig.
- ssl_provider1->next_protos_expected_in_ssl_config =
- NextProtoVector{kProtoHTTP11};
- // Force HTTP/1.1.
- ssl_provider1->next_proto = kProtoHTTP11;
- helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
- HttpServerProperties* http_server_properties =
- helper.session()->spdy_session_pool()->http_server_properties();
- EXPECT_FALSE(http_server_properties->RequiresHTTP11(
- url::SchemeHostPort(request_.url), NetworkIsolationKey()));
- helper.RunPreTestSetup();
- helper.StartDefaultTest();
- helper.FinishDefaultTestWithoutVerification();
- helper.VerifyDataConsumed();
- EXPECT_TRUE(http_server_properties->RequiresHTTP11(
- url::SchemeHostPort(request_.url), NetworkIsolationKey()));
- const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
- EXPECT_FALSE(response->was_fetched_via_spdy);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1_1,
- response->connection_info);
- EXPECT_TRUE(response->was_alpn_negotiated);
- EXPECT_TRUE(request_.url.SchemeIs("https"));
- EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
- EXPECT_EQ(443, response->remote_endpoint.port());
- std::string response_data;
- ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk());
- EXPECT_EQ("hello", response_data);
- }
- // Same as above test, but checks that NetworkIsolationKeys are respected.
- TEST_F(SpdyNetworkTransactionTest, HTTP11RequiredRetryWithNetworkIsolationKey) {
- const SchemefulSite kSite1(GURL("https://foo.test/"));
- const SchemefulSite kSite2(GURL("https://bar.test/"));
- const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
- const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
- const NetworkIsolationKey kNetworkIsolationKeys[] = {
- kNetworkIsolationKey1, kNetworkIsolationKey2, NetworkIsolationKey()};
- base::test::ScopedFeatureList feature_list;
- feature_list.InitWithFeatures(
- // enabled_features
- {features::kPartitionHttpServerPropertiesByNetworkIsolationKey,
- // Need to partition connections by NetworkIsolationKey for
- // SpdySessionKeys to include NetworkIsolationKeys.
- features::kPartitionConnectionsByNetworkIsolationKey},
- // disabled_features
- {});
- // Do not force SPDY so that sockets can negotiate HTTP/1.1.
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- // For each server, set up and tear down a QUIC session cleanly, and check
- // that stats have been added to HttpServerProperties using the correct
- // NetworkIsolationKey.
- for (size_t i = 0; i < std::size(kNetworkIsolationKeys); ++i) {
- SCOPED_TRACE(i);
- request_.method = "GET";
- request_.network_isolation_key = kNetworkIsolationKeys[i];
- // First socket: HTTP/2 request rejected with HTTP_1_1_REQUIRED.
- SpdyTestUtil spdy_util;
- spdy::Http2HeaderBlock headers(
- spdy_util.ConstructGetHeaderBlock(kDefaultUrl));
- spdy::SpdySerializedFrame req(
- spdy_util.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
- MockWrite writes0[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame rst(spdy_util.ConstructSpdyRstStream(
- 1, spdy::ERROR_CODE_HTTP_1_1_REQUIRED));
- MockRead reads0[] = {CreateMockRead(rst, 1)};
- SequencedSocketData data0(reads0, writes0);
- auto ssl_provider0 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Expect HTTP/2 protocols too in SSLConfig.
- ssl_provider0->next_protos_expected_in_ssl_config =
- NextProtoVector{kProtoHTTP2, kProtoHTTP11};
- // Force SPDY.
- ssl_provider0->next_proto = kProtoHTTP2;
- helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0));
- // Second socket: falling back to HTTP/1.1.
- MockWrite writes1[] = {MockWrite(ASYNC, 0,
- "GET / HTTP/1.1\r\n"
- "Host: www.example.org\r\n"
- "Connection: keep-alive\r\n\r\n")};
- MockRead reads1[] = {MockRead(ASYNC, 1,
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 5\r\n\r\n"
- "hello")};
- SequencedSocketData data1(reads1, writes1);
- auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Expect only HTTP/1.1 protocol in SSLConfig.
- ssl_provider1->next_protos_expected_in_ssl_config =
- NextProtoVector{kProtoHTTP11};
- // Force HTTP/1.1.
- ssl_provider1->next_proto = kProtoHTTP11;
- helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
- HttpServerProperties* http_server_properties =
- helper.session()->spdy_session_pool()->http_server_properties();
- EXPECT_FALSE(http_server_properties->RequiresHTTP11(
- url::SchemeHostPort(request_.url), kNetworkIsolationKeys[i]));
- HttpNetworkTransaction trans(DEFAULT_PRIORITY, helper.session());
- TestCompletionCallback callback;
- int rv = trans.Start(&request_, callback.callback(), log_);
- EXPECT_THAT(callback.GetResult(rv), IsOk());
- const HttpResponseInfo* response = trans.GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
- EXPECT_FALSE(response->was_fetched_via_spdy);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1_1,
- response->connection_info);
- EXPECT_TRUE(response->was_alpn_negotiated);
- EXPECT_TRUE(request_.url.SchemeIs("https"));
- EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
- EXPECT_EQ(443, response->remote_endpoint.port());
- std::string response_data;
- ASSERT_THAT(ReadTransaction(&trans, &response_data), IsOk());
- EXPECT_EQ("hello", response_data);
- for (size_t j = 0; j < std::size(kNetworkIsolationKeys); ++j) {
- // NetworkIsolationKeys up to kNetworkIsolationKeys[j] are known to
- // require HTTP/1.1, others are not.
- if (j <= i) {
- EXPECT_TRUE(http_server_properties->RequiresHTTP11(
- url::SchemeHostPort(request_.url), kNetworkIsolationKeys[j]));
- } else {
- EXPECT_FALSE(http_server_properties->RequiresHTTP11(
- url::SchemeHostPort(request_.url), kNetworkIsolationKeys[j]));
- }
- }
- }
- }
- // Retry with HTTP/1.1 to the proxy when receiving HTTP_1_1_REQUIRED from the
- // proxy. Note that no actual protocol negotiation happens, instead this test
- // forces protocols for both sockets.
- TEST_F(SpdyNetworkTransactionTest, HTTP11RequiredProxyRetry) {
- request_.method = "GET";
- auto session_deps = std::make_unique<SpdySessionDependencies>(
- ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
- "HTTPS myproxy:70", TRAFFIC_ANNOTATION_FOR_TESTS));
- // Do not force SPDY so that second socket can negotiate HTTP/1.1.
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- // First socket: HTTP/2 CONNECT rejected with HTTP_1_1_REQUIRED.
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect(
- nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
- HostPortPair("www.example.org", 443)));
- MockWrite writes0[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_HTTP_1_1_REQUIRED));
- MockRead reads0[] = {CreateMockRead(rst, 1)};
- SequencedSocketData data0(reads0, writes0);
- auto ssl_provider0 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Expect HTTP/2 protocols too in SSLConfig.
- ssl_provider0->next_protos_expected_in_ssl_config =
- NextProtoVector{kProtoHTTP2, kProtoHTTP11};
- // Force SPDY.
- ssl_provider0->next_proto = kProtoHTTP2;
- helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0));
- // Second socket: retry using HTTP/1.1.
- MockWrite writes1[] = {
- MockWrite(ASYNC, 0,
- "CONNECT www.example.org:443 HTTP/1.1\r\n"
- "Host: www.example.org:443\r\n"
- "Proxy-Connection: keep-alive\r\n\r\n"),
- MockWrite(ASYNC, 2,
- "GET / HTTP/1.1\r\n"
- "Host: www.example.org\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads1[] = {
- MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n\r\n"),
- MockRead(ASYNC, 3,
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 5\r\n\r\n"
- "hello"),
- };
- SequencedSocketData data1(reads1, writes1);
- auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Expect only HTTP/1.1 protocol in SSLConfig.
- ssl_provider1->next_protos_expected_in_ssl_config =
- NextProtoVector{kProtoHTTP11};
- // Force HTTP/1.1.
- ssl_provider1->next_proto = kProtoHTTP11;
- helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
- // A third socket is needed for the tunnelled connection.
- auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- helper.session_deps()->socket_factory->AddSSLSocketDataProvider(
- ssl_provider2.get());
- HttpServerProperties* http_server_properties =
- helper.session()->spdy_session_pool()->http_server_properties();
- url::SchemeHostPort proxy_scheme_host_port(url::kHttpsScheme, "myproxy", 70);
- EXPECT_FALSE(http_server_properties->RequiresHTTP11(proxy_scheme_host_port,
- NetworkIsolationKey()));
- helper.RunPreTestSetup();
- helper.StartDefaultTest();
- helper.FinishDefaultTestWithoutVerification();
- helper.VerifyDataConsumed();
- EXPECT_TRUE(http_server_properties->RequiresHTTP11(proxy_scheme_host_port,
- NetworkIsolationKey()));
- const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
- EXPECT_FALSE(response->was_fetched_via_spdy);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1_1,
- response->connection_info);
- EXPECT_FALSE(response->was_alpn_negotiated);
- EXPECT_TRUE(request_.url.SchemeIs("https"));
- EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
- EXPECT_EQ(70, response->remote_endpoint.port());
- std::string response_data;
- ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk());
- EXPECT_EQ("hello", response_data);
- }
- // Same as above, but also test that NetworkIsolationKeys are respected.
- TEST_F(SpdyNetworkTransactionTest,
- HTTP11RequiredProxyRetryWithNetworkIsolationKey) {
- const SchemefulSite kSite1(GURL("https://foo.test/"));
- const SchemefulSite kSite2(GURL("https://bar.test/"));
- const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
- const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
- const NetworkIsolationKey kNetworkIsolationKeys[] = {
- kNetworkIsolationKey1, kNetworkIsolationKey2, NetworkIsolationKey()};
- base::test::ScopedFeatureList feature_list;
- feature_list.InitWithFeatures(
- // enabled_features
- {features::kPartitionHttpServerPropertiesByNetworkIsolationKey,
- // Need to partition connections by NetworkIsolationKey for
- // SpdySessionKeys to include NetworkIsolationKeys.
- features::kPartitionConnectionsByNetworkIsolationKey},
- // disabled_features
- {});
- request_.method = "GET";
- auto session_deps = std::make_unique<SpdySessionDependencies>(
- ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
- "HTTPS myproxy:70", TRAFFIC_ANNOTATION_FOR_TESTS));
- // Do not force SPDY so that second socket can negotiate HTTP/1.1.
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- helper.RunPreTestSetup();
- for (size_t i = 0; i < std::size(kNetworkIsolationKeys); ++i) {
- // First socket: HTTP/2 CONNECT rejected with HTTP_1_1_REQUIRED.
- SpdyTestUtil spdy_util;
- spdy::SpdySerializedFrame req(spdy_util.ConstructSpdyConnect(
- nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
- HostPortPair("www.example.org", 443)));
- MockWrite writes0[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame rst(spdy_util.ConstructSpdyRstStream(
- 1, spdy::ERROR_CODE_HTTP_1_1_REQUIRED));
- MockRead reads0[] = {CreateMockRead(rst, 1)};
- SequencedSocketData data0(reads0, writes0);
- auto ssl_provider0 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Expect HTTP/2 protocols too in SSLConfig.
- ssl_provider0->next_protos_expected_in_ssl_config =
- NextProtoVector{kProtoHTTP2, kProtoHTTP11};
- // Force SPDY.
- ssl_provider0->next_proto = kProtoHTTP2;
- helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0));
- // Second socket: retry using HTTP/1.1.
- MockWrite writes1[] = {
- MockWrite(ASYNC, 0,
- "CONNECT www.example.org:443 HTTP/1.1\r\n"
- "Host: www.example.org:443\r\n"
- "Proxy-Connection: keep-alive\r\n\r\n"),
- MockWrite(ASYNC, 2,
- "GET / HTTP/1.1\r\n"
- "Host: www.example.org\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead reads1[] = {
- MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n\r\n"),
- MockRead(ASYNC, 3,
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 5\r\n\r\n"
- "hello"),
- };
- SequencedSocketData data1(reads1, writes1);
- auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Expect only HTTP/1.1 protocol in SSLConfig.
- ssl_provider1->next_protos_expected_in_ssl_config =
- NextProtoVector{kProtoHTTP11};
- // Force HTTP/1.1.
- ssl_provider1->next_proto = kProtoHTTP11;
- helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
- // A third socket is needed for the tunnelled connection.
- auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- helper.session_deps()->socket_factory->AddSSLSocketDataProvider(
- ssl_provider2.get());
- HttpServerProperties* http_server_properties =
- helper.session()->spdy_session_pool()->http_server_properties();
- url::SchemeHostPort proxy_scheme_host_port(url::kHttpsScheme, "myproxy",
- 70);
- EXPECT_FALSE(http_server_properties->RequiresHTTP11(
- proxy_scheme_host_port, kNetworkIsolationKeys[i]));
- request_.network_isolation_key = kNetworkIsolationKeys[i];
- HttpNetworkTransaction trans(DEFAULT_PRIORITY, helper.session());
- TestCompletionCallback callback;
- int rv = trans.Start(&request_, callback.callback(), log_);
- EXPECT_THAT(callback.GetResult(rv), IsOk());
- helper.VerifyDataConsumed();
- const HttpResponseInfo* response = trans.GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
- EXPECT_FALSE(response->was_fetched_via_spdy);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1_1,
- response->connection_info);
- EXPECT_FALSE(response->was_alpn_negotiated);
- EXPECT_TRUE(request_.url.SchemeIs("https"));
- EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
- EXPECT_EQ(70, response->remote_endpoint.port());
- std::string response_data;
- ASSERT_THAT(ReadTransaction(&trans, &response_data), IsOk());
- EXPECT_EQ("hello", response_data);
- for (size_t j = 0; j < std::size(kNetworkIsolationKeys); ++j) {
- // The proxy SchemeHostPort URL should not be marked as requiring HTTP/1.1
- // using the current NetworkIsolationKey, and the state of others should
- // be unchanged since the last loop iteration..
- if (j <= i) {
- EXPECT_TRUE(http_server_properties->RequiresHTTP11(
- proxy_scheme_host_port, kNetworkIsolationKeys[j]));
- } else {
- EXPECT_FALSE(http_server_properties->RequiresHTTP11(
- proxy_scheme_host_port, kNetworkIsolationKeys[j]));
- }
- }
- // The destination SchemeHostPort should not be marked as requiring
- // HTTP/1.1.
- EXPECT_FALSE(http_server_properties->RequiresHTTP11(
- url::SchemeHostPort(request_.url), kNetworkIsolationKeys[i]));
- }
- }
- // Test to make sure we can correctly connect through a proxy.
- TEST_F(SpdyNetworkTransactionTest, ProxyConnect) {
- auto session_deps = std::make_unique<SpdySessionDependencies>(
- ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
- "PROXY myproxy:70", TRAFFIC_ANNOTATION_FOR_TESTS));
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- helper.RunPreTestSetup();
- HttpNetworkTransaction* trans = helper.trans();
- const char kConnect443[] = {
- "CONNECT www.example.org:443 HTTP/1.1\r\n"
- "Host: www.example.org:443\r\n"
- "Proxy-Connection: keep-alive\r\n\r\n"};
- const char kHTTP200[] = {"HTTP/1.1 200 OK\r\n\r\n"};
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- MockWrite(SYNCHRONOUS, kConnect443, std::size(kConnect443) - 1, 0),
- CreateMockWrite(req, 2),
- };
- MockRead reads[] = {
- MockRead(SYNCHRONOUS, kHTTP200, std::size(kHTTP200) - 1, 1),
- CreateMockRead(resp, 3),
- CreateMockRead(body, 4),
- MockRead(ASYNC, nullptr, 0, 5),
- };
- SequencedSocketData data(reads, writes);
- helper.AddData(&data);
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- EXPECT_EQ(0, rv);
- // Verify the response headers.
- HttpResponseInfo response = *trans->GetResponseInfo();
- ASSERT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- std::string response_data;
- ASSERT_THAT(ReadTransaction(trans, &response_data), IsOk());
- EXPECT_EQ("hello!", response_data);
- helper.VerifyDataConsumed();
- }
- // Test to make sure we can correctly connect through a proxy to
- // www.example.org, if there already exists a direct spdy connection to
- // www.example.org. See https://crbug.com/49874.
- TEST_F(SpdyNetworkTransactionTest, DirectConnectProxyReconnect) {
- // Use a proxy service which returns a proxy fallback list from DIRECT to
- // myproxy:70. For this test there will be no fallback, so it is equivalent
- // to simply DIRECT. The reason for appending the second proxy is to verify
- // that the session pool key used does is just "DIRECT".
- auto session_deps = std::make_unique<SpdySessionDependencies>(
- ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
- "DIRECT; PROXY myproxy:70", TRAFFIC_ANNOTATION_FOR_TESTS));
- // When setting up the first transaction, we store the SpdySessionPool so that
- // we can use the same pool in the second transaction.
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool();
- helper.RunPreTestSetup();
- // Construct and send a simple GET request.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {
- CreateMockWrite(req, 0),
- };
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3), // Force a pause
- };
- SequencedSocketData data(reads, writes);
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- TransactionHelperResult out;
- out.rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_EQ(out.rv, ERR_IO_PENDING);
- out.rv = callback.WaitForResult();
- EXPECT_EQ(out.rv, OK);
- const HttpResponseInfo* response = trans->GetResponseInfo();
- EXPECT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- out.rv = ReadTransaction(trans, &out.response_data);
- EXPECT_THAT(out.rv, IsOk());
- out.status_line = response->headers->GetStatusLine();
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- // Check that the SpdySession is still in the SpdySessionPool.
- SpdySessionKey session_pool_key_direct(
- host_port_pair_, ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- EXPECT_TRUE(HasSpdySession(spdy_session_pool, session_pool_key_direct));
- SpdySessionKey session_pool_key_proxy(
- host_port_pair_,
- ProxyUriToProxyServer("www.foo.com", ProxyServer::SCHEME_HTTP),
- PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse,
- SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- EXPECT_FALSE(HasSpdySession(spdy_session_pool, session_pool_key_proxy));
- // New SpdyTestUtil instance for the session that will be used for the
- // proxy connection.
- SpdyTestUtil spdy_util_2;
- // Set up data for the proxy connection.
- const char kConnect443[] = {
- "CONNECT www.example.org:443 HTTP/1.1\r\n"
- "Host: www.example.org:443\r\n"
- "Proxy-Connection: keep-alive\r\n\r\n"};
- const char kHTTP200[] = {"HTTP/1.1 200 OK\r\n\r\n"};
- spdy::SpdySerializedFrame req2(
- spdy_util_2.ConstructSpdyGet(kPushedUrl, 1, LOWEST));
- spdy::SpdySerializedFrame resp2(
- spdy_util_2.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body2(spdy_util_2.ConstructSpdyDataFrame(1, true));
- MockWrite writes2[] = {
- MockWrite(SYNCHRONOUS, kConnect443, std::size(kConnect443) - 1, 0),
- CreateMockWrite(req2, 2),
- };
- MockRead reads2[] = {
- MockRead(SYNCHRONOUS, kHTTP200, std::size(kHTTP200) - 1, 1),
- CreateMockRead(resp2, 3), CreateMockRead(body2, 4),
- MockRead(ASYNC, 0, 5) // EOF
- };
- SequencedSocketData data_proxy(reads2, writes2);
- // Create another request to www.example.org, but this time through a proxy.
- request_.method = "GET";
- request_.url = GURL(kPushedUrl);
- auto session_deps_proxy = std::make_unique<SpdySessionDependencies>(
- ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
- "PROXY myproxy:70", TRAFFIC_ANNOTATION_FOR_TESTS));
- NormalSpdyTransactionHelper helper_proxy(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps_proxy));
- helper_proxy.RunPreTestSetup();
- helper_proxy.AddData(&data_proxy);
- HttpNetworkTransaction* trans_proxy = helper_proxy.trans();
- TestCompletionCallback callback_proxy;
- int rv = trans_proxy->Start(&request_, callback_proxy.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback_proxy.WaitForResult();
- EXPECT_EQ(0, rv);
- HttpResponseInfo response_proxy = *trans_proxy->GetResponseInfo();
- ASSERT_TRUE(response_proxy.headers);
- EXPECT_EQ("HTTP/1.1 200", response_proxy.headers->GetStatusLine());
- std::string response_data;
- ASSERT_THAT(ReadTransaction(trans_proxy, &response_data), IsOk());
- EXPECT_EQ("hello!", response_data);
- helper_proxy.VerifyDataConsumed();
- }
- // When we get a TCP-level RST, we need to retry a HttpNetworkTransaction
- // on a new connection, if the connection was previously known to be good.
- // This can happen when a server reboots without saying goodbye, or when
- // we're behind a NAT that masked the RST.
- TEST_F(SpdyNetworkTransactionTest, VerifyRetryOnConnectionReset) {
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, ERR_IO_PENDING, 3),
- MockRead(ASYNC, ERR_CONNECTION_RESET, 4),
- };
- MockRead reads2[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- // In all cases the connection will be reset before req3 can be
- // dispatched, destroying both streams.
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req3(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST));
- MockWrite writes1[] = {CreateMockWrite(req, 0), CreateMockWrite(req3, 5)};
- MockWrite writes2[] = {CreateMockWrite(req, 0)};
- // This test has a couple of variants.
- enum {
- // Induce the RST while waiting for our transaction to send.
- VARIANT_RST_DURING_SEND_COMPLETION = 0,
- // Induce the RST while waiting for our transaction to read.
- // In this case, the send completed - everything copied into the SNDBUF.
- VARIANT_RST_DURING_READ_COMPLETION = 1
- };
- for (int variant = VARIANT_RST_DURING_SEND_COMPLETION;
- variant <= VARIANT_RST_DURING_READ_COMPLETION;
- ++variant) {
- SequencedSocketData data1(reads,
- base::make_span(writes1).first(1 + variant));
- SequencedSocketData data2(reads2, writes2);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- nullptr);
- helper.AddData(&data1);
- helper.AddData(&data2);
- helper.RunPreTestSetup();
- for (int i = 0; i < 2; ++i) {
- HttpNetworkTransaction trans(DEFAULT_PRIORITY, helper.session());
- TestCompletionCallback callback;
- int rv = trans.Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // On the second transaction, we trigger the RST.
- if (i == 1) {
- if (variant == VARIANT_RST_DURING_READ_COMPLETION) {
- // Writes to the socket complete asynchronously on SPDY by running
- // through the message loop. Complete the write here.
- base::RunLoop().RunUntilIdle();
- }
- // Now schedule the ERR_CONNECTION_RESET.
- data1.Resume();
- }
- rv = callback.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- const HttpResponseInfo* response = trans.GetResponseInfo();
- ASSERT_TRUE(response);
- EXPECT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- std::string response_data;
- rv = ReadTransaction(&trans, &response_data);
- EXPECT_THAT(rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- EXPECT_EQ("hello!", response_data);
- base::RunLoop().RunUntilIdle();
- }
- helper.VerifyDataConsumed();
- base::RunLoop().RunUntilIdle();
- }
- }
- // Tests that Basic authentication works over SPDY
- TEST_F(SpdyNetworkTransactionTest, SpdyBasicAuth) {
- // The first request will be a bare GET, the second request will be a
- // GET with an Authorization header.
- spdy::SpdySerializedFrame req_get(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- // Will be refused for lack of auth.
- spdy_util_.UpdateWithStreamDestruction(1);
- const char* const kExtraAuthorizationHeaders[] = {
- "authorization", "Basic Zm9vOmJhcg=="
- };
- spdy::SpdySerializedFrame req_get_authorization(spdy_util_.ConstructSpdyGet(
- kExtraAuthorizationHeaders, std::size(kExtraAuthorizationHeaders) / 2, 3,
- LOWEST));
- MockWrite spdy_writes[] = {
- CreateMockWrite(req_get, 0), CreateMockWrite(req_get_authorization, 3),
- };
- // The first response is a 401 authentication challenge, and the second
- // response will be a 200 response since the second request includes a valid
- // Authorization header.
- const char* const kExtraAuthenticationHeaders[] = {
- "www-authenticate",
- "Basic realm=\"MyRealm\""
- };
- spdy::SpdySerializedFrame resp_authentication(
- spdy_util_.ConstructSpdyReplyError(
- "401", kExtraAuthenticationHeaders,
- std::size(kExtraAuthenticationHeaders) / 2, 1));
- spdy::SpdySerializedFrame body_authentication(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame resp_data(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body_data(
- spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead spdy_reads[] = {
- CreateMockRead(resp_authentication, 1),
- CreateMockRead(body_authentication, 2, SYNCHRONOUS),
- CreateMockRead(resp_data, 4),
- CreateMockRead(body_data, 5),
- MockRead(ASYNC, 0, 6),
- };
- SequencedSocketData data(spdy_reads, spdy_writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- helper.StartDefaultTest();
- EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING));
- helper.WaitForCallbackToComplete();
- EXPECT_THAT(helper.output().rv, IsOk());
- // Make sure the response has an auth challenge.
- HttpNetworkTransaction* trans = helper.trans();
- const HttpResponseInfo* const response_start = trans->GetResponseInfo();
- ASSERT_TRUE(response_start);
- ASSERT_TRUE(response_start->headers);
- EXPECT_EQ(401, response_start->headers->response_code());
- EXPECT_TRUE(response_start->was_fetched_via_spdy);
- const absl::optional<AuthChallengeInfo>& auth_challenge =
- response_start->auth_challenge;
- ASSERT_TRUE(auth_challenge);
- EXPECT_FALSE(auth_challenge->is_proxy);
- EXPECT_EQ(kBasicAuthScheme, auth_challenge->scheme);
- EXPECT_EQ("MyRealm", auth_challenge->realm);
- // Restart with a username/password.
- AuthCredentials credentials(u"foo", u"bar");
- TestCompletionCallback callback_restart;
- const int rv_restart = trans->RestartWithAuth(
- credentials, callback_restart.callback());
- EXPECT_THAT(rv_restart, IsError(ERR_IO_PENDING));
- const int rv_restart_complete = callback_restart.WaitForResult();
- EXPECT_THAT(rv_restart_complete, IsOk());
- // TODO(cbentzel): This is actually the same response object as before, but
- // data has changed.
- const HttpResponseInfo* const response_restart = trans->GetResponseInfo();
- ASSERT_TRUE(response_restart);
- ASSERT_TRUE(response_restart->headers);
- EXPECT_EQ(200, response_restart->headers->response_code());
- EXPECT_FALSE(response_restart->auth_challenge);
- }
- struct PushHeaderTestParams {
- std::vector<std::pair<std::string, std::string>> extra_request_headers;
- std::vector<std::pair<std::string, std::string>> extra_pushed_request_headers;
- std::vector<std::pair<std::string, std::string>>
- extra_pushed_response_headers;
- std::string pushed_status_code;
- bool push_accepted;
- SpdyPushedStreamFate expected_fate;
- } push_header_test_cases[] = {
- // Base case: no extra headers.
- {{}, {}, {}, "200", true, SpdyPushedStreamFate::kAcceptedNoVary},
- // Cookie headers match.
- {{{"cookie", "value=foo"}},
- {{"cookie", "value=foo"}},
- {{"vary", "Cookie"}},
- "200",
- true,
- SpdyPushedStreamFate::kAcceptedMatchingVary},
- // Cookie headers mismatch.
- {{{"cookie", "value=foo"}},
- {{"cookie", "value=bar"}},
- {{"vary", "Cookie"}},
- "200",
- false,
- SpdyPushedStreamFate::kVaryMismatch},
- // Partial Content response, no Range headers.
- {{}, {}, {}, "206", false, SpdyPushedStreamFate::kClientRequestNotRange},
- // Partial Content response, no Range headers in pushed request.
- {{{"range", "0-42"}},
- {},
- {},
- "206",
- false,
- SpdyPushedStreamFate::kPushedRequestNotRange},
- // Partial Content response, no Range headers in client request.
- {{},
- {{"range", "0-42"}},
- {},
- "206",
- false,
- SpdyPushedStreamFate::kClientRequestNotRange},
- // Partial Content response, mismatching Range headers.
- {{{"range", "0-42"}},
- {{"range", "10-42"}},
- {},
- "206",
- false,
- SpdyPushedStreamFate::kRangeMismatch},
- // Partial Content response, matching Range headers.
- {{{"range", "0-42"}},
- {{"range", "0-42"}},
- {},
- "206",
- true,
- SpdyPushedStreamFate::kAcceptedNoVary},
- };
- class SpdyNetworkTransactionPushHeaderTest
- : public SpdyNetworkTransactionTest,
- public ::testing::WithParamInterface<PushHeaderTestParams> {
- protected:
- void RunTest(bool pushed_response_headers_received_before_request) {
- base::HistogramTester histogram_tester;
- int seq = 0;
- std::vector<MockWrite> writes;
- std::vector<MockRead> reads;
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- writes.push_back(CreateMockWrite(req1, seq++));
- spdy::Http2HeaderBlock pushed_request_headers;
- pushed_request_headers[spdy::kHttp2MethodHeader] = "GET";
- for (const auto& header : GetParam().extra_pushed_request_headers) {
- pushed_request_headers.insert(header);
- }
- spdy_util_.AddUrlToHeaderBlock(kPushedUrl, &pushed_request_headers);
- spdy::SpdySerializedFrame pushed_request(
- spdy_util_.ConstructSpdyPushPromise(1, 2,
- std::move(pushed_request_headers)));
- reads.push_back(CreateMockRead(pushed_request, seq++));
- spdy::SpdySerializedFrame priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- writes.push_back(CreateMockWrite(priority, seq++));
- reads.emplace_back(ASYNC, ERR_IO_PENDING, seq++);
- spdy::Http2HeaderBlock pushed_response_headers;
- pushed_response_headers[spdy::kHttp2StatusHeader] =
- GetParam().pushed_status_code;
- for (const auto& header : GetParam().extra_pushed_response_headers) {
- pushed_response_headers.insert(header);
- }
- spdy::SpdySerializedFrame pushed_response(
- spdy_util_.ConstructSpdyReply(2, std::move(pushed_response_headers)));
- reads.push_back(CreateMockRead(pushed_response, seq++));
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- reads.push_back(CreateMockRead(resp1, seq++));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- reads.push_back(CreateMockRead(body1, seq++));
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame pushed_body(
- spdy_util_.ConstructSpdyDataFrame(2, "This is pushed.", true));
- reads.push_back(CreateMockRead(pushed_body, seq++));
- // If push is not accepted, a new request is sent on the wire.
- spdy::SpdySerializedFrame rst;
- spdy::SpdySerializedFrame req2;
- spdy::SpdySerializedFrame resp2;
- spdy::SpdySerializedFrame body2;
- if (!GetParam().push_accepted) {
- rst = spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_CANCEL);
- writes.push_back(CreateMockWrite(rst, seq++));
- spdy::Http2HeaderBlock request_headers2(
- spdy_util_.ConstructGetHeaderBlock(kPushedUrl));
- for (const auto& header : GetParam().extra_request_headers) {
- request_headers2.insert(header);
- }
- req2 = spdy_util_.ConstructSpdyHeaders(3, std::move(request_headers2),
- LOWEST, true);
- writes.push_back(CreateMockWrite(req2, seq++));
- resp2 = spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3);
- reads.push_back(CreateMockRead(resp2, seq++));
- body2 = spdy_util_.ConstructSpdyDataFrame(3, "This is not pushed.", true);
- reads.push_back(CreateMockRead(body2, seq++));
- }
- reads.emplace_back(ASYNC, ERR_IO_PENDING, seq++);
- reads.emplace_back(ASYNC, 0, seq++);
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback1;
- int rv = trans->Start(&request_, callback1.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // Open connection.
- base::RunLoop().RunUntilIdle();
- if (pushed_response_headers_received_before_request) {
- // Read pushed response headers.
- data.Resume();
- base::RunLoop().RunUntilIdle();
- }
- HttpRequestInfo request2 = CreateGetPushRequest();
- for (const auto& header : GetParam().extra_request_headers) {
- request2.extra_headers.SetHeader(header.first, header.second);
- }
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- base::RunLoop().RunUntilIdle();
- if (!pushed_response_headers_received_before_request) {
- // Read pushed response headers.
- data.Resume();
- base::RunLoop().RunUntilIdle();
- }
- rv = callback1.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- const HttpResponseInfo* const response1 = trans->GetResponseInfo();
- EXPECT_TRUE(response1->headers);
- EXPECT_EQ("HTTP/1.1 200", response1->headers->GetStatusLine());
- std::string result1;
- ReadResult(trans, &result1);
- EXPECT_EQ(result1, "hello!");
- rv = callback2.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- std::string result2;
- ReadResult(&trans2, &result2);
- EXPECT_EQ(result2, GetParam().push_accepted ? "This is pushed."
- : "This is not pushed.");
- data.Resume();
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate", static_cast<int>(GetParam().expected_fate),
- 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1);
- }
- };
- INSTANTIATE_TEST_SUITE_P(All,
- SpdyNetworkTransactionPushHeaderTest,
- ::testing::ValuesIn(push_header_test_cases));
- TEST_P(SpdyNetworkTransactionPushHeaderTest,
- PushedResponseHeadersReceivedBeforeRequest) {
- RunTest(/* pushed_response_headers_received_before_request = */ true);
- }
- TEST_P(SpdyNetworkTransactionPushHeaderTest,
- PushedResponseHeadersReceivedAfterRequest) {
- RunTest(/* pushed_response_headers_received_before_request = */ false);
- }
- TEST_F(SpdyNetworkTransactionTest, ResponseHeadersTwice) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_PROTOCOL_ERROR));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(rst, 4),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::Http2HeaderBlock late_headers;
- late_headers["hello"] = "bye";
- spdy::SpdySerializedFrame stream1_headers(
- spdy_util_.ConstructSpdyResponseHeaders(1, std::move(late_headers),
- false));
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1), CreateMockRead(stream1_headers, 2),
- CreateMockRead(stream1_body, 3), MockRead(ASYNC, 0, 5) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_PROTOCOL_ERROR));
- }
- // Tests that receiving HEADERS, DATA, HEADERS, and DATA in that sequence will
- // trigger a ERR_HTTP2_PROTOCOL_ERROR because trailing HEADERS must not be
- // followed by any DATA frames.
- TEST_F(SpdyNetworkTransactionTest, SyncReplyDataAfterTrailers) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_PROTOCOL_ERROR));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(rst, 5),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream1_body(
- spdy_util_.ConstructSpdyDataFrame(1, false));
- spdy::Http2HeaderBlock late_headers;
- late_headers["hello"] = "bye";
- spdy::SpdySerializedFrame stream1_headers(
- spdy_util_.ConstructSpdyResponseHeaders(1, std::move(late_headers),
- false));
- spdy::SpdySerializedFrame stream1_body2(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1), CreateMockRead(stream1_body, 2),
- CreateMockRead(stream1_headers, 3), CreateMockRead(stream1_body2, 4),
- MockRead(ASYNC, 0, 6) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_PROTOCOL_ERROR));
- }
- struct PushUrlTestParams {
- const char* url_to_fetch;
- const char* url_to_push;
- bool client_cert_sent;
- bool expect_ct_error;
- SpdyPushedStreamFate expected_fate;
- } push_url_test_cases[] = {
- // http scheme cannot be pushed (except by trusted proxy).
- {"https://www.example.org/foo.html", "http://www.example.org/foo.js",
- false /* client_cert_sent */, false /* expect_ct_error */,
- SpdyPushedStreamFate::kNonHttpsPushedScheme},
- // ftp scheme cannot be pushed.
- {"https://www.example.org/foo.html", "ftp://www.example.org/foo.js",
- false /* client_cert_sent */, false /* expect_ct_error */,
- SpdyPushedStreamFate::kInvalidUrl},
- // Cross subdomain, certificate not valid.
- {"https://www.example.org/foo.html", "https://blat.www.example.org/foo.js",
- false /* client_cert_sent */, false /* expect_ct_error */,
- SpdyPushedStreamFate::kCertificateMismatch},
- // Cross domain, certificate not valid.
- {"https://www.example.org/foo.html", "https://www.foo.com/foo.js",
- false /* client_cert_sent */, false /* expect_ct_error */,
- SpdyPushedStreamFate::kCertificateMismatch},
- // Cross domain, certificate valid, but cross-origin push is rejected on a
- // connection with client certificate.
- {"https://www.example.org/foo.html", "https://mail.example.org/foo.js",
- true /* client_cert_sent */, false /* expect_ct_error */,
- SpdyPushedStreamFate::kCertificateMismatch},
- // Cross domain, certificate valid, but cross-origin push is rejected on a
- // connection with an Expect-CT error.
- {"https://www.example.org/foo.html", "https://mail.example.org/foo.js",
- false /* client_cert_sent */, true /* expect_ct_error */,
- SpdyPushedStreamFate::kCertificateMismatch}};
- class SpdyNetworkTransactionPushUrlTest
- : public SpdyNetworkTransactionTest,
- public ::testing::WithParamInterface<PushUrlTestParams> {
- public:
- SpdyNetworkTransactionPushUrlTest() {
- // Set features needed for the |expect_ct_error| case, where it's important
- // to check that NetworkIsolationKeys are respected.
- feature_list_.InitWithFeatures(
- /* enabled_features */
- {TransportSecurityState::kDynamicExpectCTFeature,
- features::kPartitionExpectCTStateByNetworkIsolationKey,
- features::kPartitionConnectionsByNetworkIsolationKey,
- features::kPartitionSSLSessionsByNetworkIsolationKey},
- /* disabled_features */
- {});
- }
- protected:
- // In this test we want to verify that we can't accidentally push content
- // which can't be pushed by this content server.
- // This test assumes that:
- // - if we're requesting http://www.foo.com/barbaz
- // - the browser has made a connection to "www.foo.com".
- void RunTest() {
- base::HistogramTester histogram_tester;
- SpdyTestUtil spdy_test_util;
- spdy::SpdySerializedFrame stream1_syn(
- spdy_test_util.ConstructSpdyGet(GetParam().url_to_fetch, 1, LOWEST));
- spdy::SpdySerializedFrame stream1_body(
- spdy_test_util.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame push_rst(spdy_test_util.ConstructSpdyRstStream(
- 2, spdy::ERROR_CODE_REFUSED_STREAM));
- MockWrite writes[] = {
- CreateMockWrite(stream1_syn, 0), CreateMockWrite(push_rst, 3),
- };
- spdy::SpdySerializedFrame stream1_reply(
- spdy_test_util.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame stream2_syn(spdy_test_util.ConstructSpdyPush(
- nullptr, 0, 2, 1, GetParam().url_to_push));
- const char kPushedData[] = "pushed";
- spdy::SpdySerializedFrame stream2_body(
- spdy_test_util.ConstructSpdyDataFrame(2, kPushedData, true));
- spdy::SpdySerializedFrame rst(
- spdy_test_util.ConstructSpdyRstStream(2, spdy::ERROR_CODE_CANCEL));
- MockRead reads[] = {
- CreateMockRead(stream1_reply, 1),
- CreateMockRead(stream2_syn, 2),
- CreateMockRead(stream1_body, 4),
- CreateMockRead(stream2_body, 5),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), // Force a pause
- };
- HttpResponseInfo response;
- SequencedSocketData data(reads, writes);
- request_.url = GURL(GetParam().url_to_fetch);
- // Set a NetworkIsolationKey for the |expect_ct_error| case, to make sure
- // NetworkIsolationKeys are respected.
- request_.network_isolation_key = NetworkIsolationKey::CreateTransient();
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider->ssl_info.client_cert_sent = GetParam().client_cert_sent;
- ssl_provider->ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- if (GetParam().expect_ct_error) {
- ssl_provider->ssl_info.ct_policy_compliance =
- ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
- ssl_provider->ssl_info.is_issued_by_known_root = true;
- session_deps->transport_security_state->AddExpectCT(
- "mail.example.org", base::Time::Now() + base::Days(1) /* expiry */,
- true, GURL(), request_.network_isolation_key);
- }
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- helper.RunPreTestSetup();
- if (GetParam().expect_ct_error) {
- ssl_provider->ssl_info.ct_policy_compliance =
- ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
- ssl_provider->ssl_info.is_issued_by_known_root = true;
- }
- helper.AddDataWithSSLSocketDataProvider(&data, std::move(ssl_provider));
- HttpNetworkTransaction* trans = helper.trans();
- // Start the transaction with basic parameters.
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- // Finish async network reads/writes.
- base::RunLoop().RunUntilIdle();
- // Read the response body.
- std::string result;
- ReadResult(trans, &result);
- // Verify that we consumed all test data.
- EXPECT_TRUE(data.AllReadDataConsumed());
- EXPECT_TRUE(data.AllWriteDataConsumed());
- // Verify the response headers.
- // Copy the response info, because trans goes away.
- response = *trans->GetResponseInfo();
- VerifyStreamsClosed(helper);
- // Verify the response headers.
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate", static_cast<int>(GetParam().expected_fate),
- 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1);
- }
- base::test::ScopedFeatureList feature_list_;
- };
- INSTANTIATE_TEST_SUITE_P(All,
- SpdyNetworkTransactionPushUrlTest,
- ::testing::ValuesIn(push_url_test_cases));
- TEST_P(SpdyNetworkTransactionPushUrlTest, PushUrlTest) {
- RunTest();
- }
- // Verify that push works cross origin as long as the certificate is valid for
- // the pushed authority.
- TEST_F(SpdyNetworkTransactionTest, ServerPushValidCrossOrigin) {
- // "spdy_pooling.pem" is valid for both www.example.org and mail.example.org.
- const char* url_to_fetch = "https://www.example.org";
- const char* url_to_push = "https://mail.example.org";
- spdy::SpdySerializedFrame headers(
- spdy_util_.ConstructSpdyGet(url_to_fetch, 1, LOWEST));
- spdy::SpdySerializedFrame push_priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- MockWrite writes[] = {
- CreateMockWrite(headers, 0), CreateMockWrite(push_priority, 3),
- };
- spdy::SpdySerializedFrame reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame push(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- const char kPushedData[] = "pushed";
- spdy::SpdySerializedFrame pushed_body(
- spdy_util_.ConstructSpdyDataFrame(2, kPushedData, true));
- MockRead reads[] = {
- CreateMockRead(reply, 1),
- CreateMockRead(push, 2, SYNCHRONOUS),
- CreateMockRead(body, 4),
- CreateMockRead(pushed_body, 5, SYNCHRONOUS),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6),
- };
- SequencedSocketData data(reads, writes);
- request_.url = GURL(url_to_fetch);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans0 = helper.trans();
- TestCompletionCallback callback0;
- int rv = trans0->Start(&request_, callback0.callback(), log_);
- rv = callback0.GetResult(rv);
- EXPECT_THAT(rv, IsOk());
- SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool();
- SpdySessionKey key(host_port_pair_, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session =
- spdy_session_pool->FindAvailableSession(
- key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- EXPECT_EQ(1u, num_unclaimed_pushed_streams(spdy_session));
- EXPECT_TRUE(
- has_unclaimed_pushed_stream_for_url(spdy_session, GURL(url_to_push)));
- HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo push_request;
- push_request.method = "GET";
- push_request.url = GURL(url_to_push);
- push_request.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback1;
- rv = trans1.Start(&push_request, callback1.callback(), log_);
- rv = callback1.GetResult(rv);
- EXPECT_THAT(rv, IsOk());
- EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session));
- HttpResponseInfo response = *trans0->GetResponseInfo();
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- std::string result0;
- ReadResult(trans0, &result0);
- EXPECT_EQ("hello!", result0);
- HttpResponseInfo push_response = *trans1.GetResponseInfo();
- EXPECT_TRUE(push_response.headers);
- EXPECT_EQ("HTTP/1.1 200", push_response.headers->GetStatusLine());
- std::string result1;
- ReadResult(&trans1, &result1);
- EXPECT_EQ(kPushedData, result1);
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- VerifyStreamsClosed(helper);
- }
- // Verify that push does not work cross origin when NetworkIsolationKeys don't
- // match.
- TEST_F(SpdyNetworkTransactionTest,
- ServerPushCrossOriginNetworkIsolationKeyMistmatch) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitWithFeatures(
- // enabled_features
- {features::kPartitionHttpServerPropertiesByNetworkIsolationKey,
- // Need to partition connections by NetworkIsolationKey for
- // SpdySessionKeys to include NetworkIsolationKeys.
- features::kPartitionConnectionsByNetworkIsolationKey},
- // disabled_features
- {});
- // "spdy_pooling.pem" is valid for both www.example.org and mail.example.org.
- const char* url_to_fetch = "https://www.example.org";
- const char* url_to_push = "https://mail.example.org";
- spdy::SpdySerializedFrame headers(
- spdy_util_.ConstructSpdyGet(url_to_fetch, 1, LOWEST));
- spdy::SpdySerializedFrame push_priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- MockWrite writes[] = {
- CreateMockWrite(headers, 0),
- CreateMockWrite(push_priority, 3),
- };
- spdy::SpdySerializedFrame reply(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame push(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- const char kPushedData[] = "pushed";
- spdy::SpdySerializedFrame pushed_body(
- spdy_util_.ConstructSpdyDataFrame(2, kPushedData, true));
- MockRead reads[] = {
- CreateMockRead(reply, 1),
- CreateMockRead(push, 2, SYNCHRONOUS),
- CreateMockRead(body, 4),
- CreateMockRead(pushed_body, 5, SYNCHRONOUS),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6),
- };
- SequencedSocketData data(reads, writes);
- request_.url = GURL(url_to_fetch);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- SequencedSocketData data2(net::MockConnect(ASYNC, net::ERR_FAILED),
- base::span<MockRead>(), base::span<MockWrite>());
- helper.AddData(&data2);
- HttpNetworkTransaction* trans0 = helper.trans();
- TestCompletionCallback callback0;
- int rv = trans0->Start(&request_, callback0.callback(), log_);
- rv = callback0.GetResult(rv);
- EXPECT_THAT(rv, IsOk());
- SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool();
- SpdySessionKey key(host_port_pair_, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session =
- spdy_session_pool->FindAvailableSession(
- key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- EXPECT_EQ(1u, num_unclaimed_pushed_streams(spdy_session));
- EXPECT_TRUE(
- has_unclaimed_pushed_stream_for_url(spdy_session, GURL(url_to_push)));
- HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo push_request;
- // Use a different NetworkIsolationKey than |spdy_session| (which uses an
- // empty one).
- push_request.network_isolation_key = NetworkIsolationKey::CreateTransient();
- push_request.method = "GET";
- push_request.url = GURL(url_to_push);
- push_request.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback1;
- rv = trans1.Start(&push_request, callback1.callback(), log_);
- // This transaction should try and use a new socket, which fails.
- EXPECT_THAT(callback1.GetResult(rv), IsError(net::ERR_FAILED));
- // The pushed stream should still be pending.
- EXPECT_EQ(1u, num_unclaimed_pushed_streams(spdy_session));
- // Try again, this time with an empty NetworkIsolationKey, matching the
- // SpdySession's. This request should successfully get the pushed stream.
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- push_request.network_isolation_key = NetworkIsolationKey();
- TestCompletionCallback callback2;
- rv = trans1.Start(&push_request, callback2.callback(), log_);
- EXPECT_THAT(callback2.GetResult(rv), IsOk());
- HttpResponseInfo response = *trans0->GetResponseInfo();
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- std::string result0;
- ReadResult(trans0, &result0);
- EXPECT_EQ("hello!", result0);
- HttpResponseInfo push_response = *trans1.GetResponseInfo();
- EXPECT_TRUE(push_response.headers);
- EXPECT_EQ("HTTP/1.1 200", push_response.headers->GetStatusLine());
- std::string result1;
- ReadResult(&trans1, &result1);
- EXPECT_EQ(kPushedData, result1);
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- VerifyStreamsClosed(helper);
- }
- // Regression test for https://crbug.com/832859: Server push is accepted on a
- // connection with client certificate, as long as SpdySessionKey matches.
- TEST_F(SpdyNetworkTransactionTest, ServerPushWithClientCert) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, DEFAULT_PRIORITY));
- spdy::SpdySerializedFrame priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(priority, 3)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame push(
- spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, kPushedUrl));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame body2(
- spdy_util_.ConstructSpdyDataFrame(2, "pushed", true));
- MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(push, 2),
- CreateMockRead(body1, 4), CreateMockRead(body2, 5),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6)};
- SequencedSocketData data(reads, writes);
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider->ssl_info.client_cert_sent = true;
- ssl_provider->ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddDataWithSSLSocketDataProvider(&data, std::move(ssl_provider));
- EXPECT_TRUE(helper.StartDefaultTest());
- helper.FinishDefaultTest();
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request = CreateGetPushRequest();
- TestCompletionCallback callback;
- int rv = trans2.Start(&request, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- const HttpResponseInfo* const response = trans2.GetResponseInfo();
- EXPECT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string result;
- ReadResult(&trans2, &result);
- EXPECT_EQ("pushed", result);
- EXPECT_TRUE(data.AllReadDataConsumed());
- EXPECT_TRUE(data.AllWriteDataConsumed());
- VerifyStreamsClosed(helper);
- }
- // Verify that push works cross origin, even if there is already a connection
- // open to origin of pushed resource.
- TEST_F(SpdyNetworkTransactionTest, ServerPushValidCrossOriginWithOpenSession) {
- const char* url_to_fetch0 = "https://mail.example.org/foo";
- const char* url_to_fetch1 = "https://docs.example.org";
- const char* url_to_push = "https://mail.example.org/bar";
- SpdyTestUtil spdy_util_0;
- spdy::SpdySerializedFrame headers0(
- spdy_util_0.ConstructSpdyGet(url_to_fetch0, 1, LOWEST));
- MockWrite writes0[] = {
- CreateMockWrite(headers0, 0),
- };
- spdy::SpdySerializedFrame reply0(
- spdy_util_0.ConstructSpdyGetReply(nullptr, 0, 1));
- const char kData0[] = "first";
- spdy::SpdySerializedFrame body0(
- spdy_util_0.ConstructSpdyDataFrame(1, kData0, true));
- MockRead reads0[] = {CreateMockRead(reply0, 1), CreateMockRead(body0, 2),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3)};
- SequencedSocketData data0(reads0, writes0);
- SpdyTestUtil spdy_util_1;
- spdy::SpdySerializedFrame headers1(
- spdy_util_1.ConstructSpdyGet(url_to_fetch1, 1, LOWEST));
- spdy::SpdySerializedFrame push_priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- MockWrite writes1[] = {
- CreateMockWrite(headers1, 0),
- CreateMockWrite(push_priority, 3, SYNCHRONOUS),
- };
- spdy::SpdySerializedFrame reply1(
- spdy_util_1.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame push(
- spdy_util_1.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push));
- const char kData1[] = "second";
- spdy::SpdySerializedFrame body1(
- spdy_util_1.ConstructSpdyDataFrame(1, kData1, true));
- const char kPushedData[] = "pushed";
- spdy::SpdySerializedFrame pushed_body(
- spdy_util_1.ConstructSpdyDataFrame(2, kPushedData, true));
- MockRead reads1[] = {
- CreateMockRead(reply1, 1),
- CreateMockRead(push, 2, SYNCHRONOUS),
- CreateMockRead(body1, 4),
- CreateMockRead(pushed_body, 5, SYNCHRONOUS),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6),
- };
- SequencedSocketData data1(reads1, writes1);
- // Request |url_to_fetch0| to open connection to mail.example.org.
- request_.url = GURL(url_to_fetch0);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- // "spdy_pooling.pem" is valid for www.example.org, but not for
- // docs.example.org.
- auto ssl_provider0 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider0->ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
- ASSERT_TRUE(ssl_provider0->ssl_info.cert);
- helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0));
- // "wildcard.pem" is valid for both www.example.org and docs.example.org.
- auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider1->ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem");
- ASSERT_TRUE(ssl_provider1->ssl_info.cert);
- helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
- HttpNetworkTransaction* trans0 = helper.trans();
- TestCompletionCallback callback0;
- int rv = trans0->Start(&request_, callback0.callback(), log_);
- rv = callback0.GetResult(rv);
- EXPECT_THAT(rv, IsOk());
- // Request |url_to_fetch1|, during which docs.example.org pushes
- // |url_to_push|, which happens to be for www.example.org, to which there is
- // already an open connection.
- HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request1;
- request1.method = "GET";
- request1.url = GURL(url_to_fetch1);
- request1.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback1;
- rv = trans1.Start(&request1, callback1.callback(), log_);
- rv = callback1.GetResult(rv);
- EXPECT_THAT(rv, IsOk());
- SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool();
- HostPortPair host_port_pair0("mail.example.org", 443);
- SpdySessionKey key0(host_port_pair0, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session0 =
- spdy_session_pool->FindAvailableSession(
- key0, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session0));
- HostPortPair host_port_pair1("docs.example.org", 443);
- SpdySessionKey key1(host_port_pair1, ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session1 =
- spdy_session_pool->FindAvailableSession(
- key1, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- EXPECT_EQ(1u, num_unclaimed_pushed_streams(spdy_session1));
- EXPECT_TRUE(
- has_unclaimed_pushed_stream_for_url(spdy_session1, GURL(url_to_push)));
- // Request |url_to_push|, which should be served from the pushed resource.
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo push_request;
- push_request.method = "GET";
- push_request.url = GURL(url_to_push);
- push_request.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback2;
- rv = trans2.Start(&push_request, callback2.callback(), log_);
- rv = callback2.GetResult(rv);
- EXPECT_THAT(rv, IsOk());
- EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session0));
- EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session1));
- HttpResponseInfo response0 = *trans0->GetResponseInfo();
- EXPECT_TRUE(response0.headers);
- EXPECT_EQ("HTTP/1.1 200", response0.headers->GetStatusLine());
- std::string result0;
- ReadResult(trans0, &result0);
- EXPECT_EQ(kData0, result0);
- HttpResponseInfo response1 = *trans1.GetResponseInfo();
- EXPECT_TRUE(response1.headers);
- EXPECT_EQ("HTTP/1.1 200", response1.headers->GetStatusLine());
- std::string result1;
- ReadResult(&trans1, &result1);
- EXPECT_EQ(kData1, result1);
- HttpResponseInfo push_response = *trans2.GetResponseInfo();
- EXPECT_TRUE(push_response.headers);
- EXPECT_EQ("HTTP/1.1 200", push_response.headers->GetStatusLine());
- std::string result2;
- ReadResult(&trans2, &result2);
- EXPECT_EQ(kPushedData, result2);
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- VerifyStreamsClosed(helper);
- }
- TEST_F(SpdyNetworkTransactionTest, RetryAfterRefused) {
- // Construct the request.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- // Will be destroyed by the RST before stream 3 starts.
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(req2, 2),
- };
- spdy::SpdySerializedFrame refused(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_REFUSED_STREAM));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead reads[] = {
- CreateMockRead(refused, 1), CreateMockRead(resp, 3),
- CreateMockRead(body, 4), MockRead(ASYNC, 0, 5) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- // Start the transaction with basic parameters.
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- // Finish async network reads.
- base::RunLoop().RunUntilIdle();
- // Verify that we consumed all test data.
- EXPECT_TRUE(data.AllReadDataConsumed());
- EXPECT_TRUE(data.AllWriteDataConsumed());
- // Verify the response headers.
- HttpResponseInfo response = *trans->GetResponseInfo();
- EXPECT_TRUE(response.headers);
- EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
- }
- TEST_F(SpdyNetworkTransactionTest, OutOfOrderHeaders) {
- // This first request will start to establish the SpdySession.
- // Then we will start the second (MEDIUM priority) and then third
- // (HIGHEST priority) request in such a way that the third will actually
- // start before the second, causing the second to be numbered differently
- // than the order they were created.
- //
- // Note that the requests and responses created below are expectations
- // of what the above will produce on the wire, and hence are in the
- // initial->HIGHEST->LOWEST priority.
- //
- // Frames are created by SpdySession just before the write associated
- // with the frame is attempted, so stream dependencies will be based
- // on the streams alive at the point of the request write attempt. Thus
- // req1 is alive when req2 is attempted (during but not after the
- // |data.RunFor(2);| statement below) but not when req3 is attempted.
- // The call to spdy_util_.UpdateWithStreamDestruction() reflects this.
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, HIGHEST));
- spdy_util_.UpdateWithStreamDestruction(1);
- spdy::SpdySerializedFrame req3(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 5, MEDIUM));
- MockWrite writes[] = {
- MockWrite(ASYNC, ERR_IO_PENDING, 0), CreateMockWrite(req1, 1),
- CreateMockWrite(req2, 5), CreateMockWrite(req3, 6),
- };
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
- spdy::SpdySerializedFrame resp3(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5));
- spdy::SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, true));
- MockRead reads[] = {
- CreateMockRead(resp1, 2), MockRead(ASYNC, ERR_IO_PENDING, 3),
- CreateMockRead(body1, 4), CreateMockRead(resp2, 7),
- CreateMockRead(body2, 8), CreateMockRead(resp3, 9),
- CreateMockRead(body3, 10), MockRead(ASYNC, 0, 11) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, LOWEST, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- // Start the first transaction to set up the SpdySession
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // Run the message loop, but do not allow the write to complete.
- // This leaves the SpdySession with a write pending, which prevents
- // SpdySession from attempting subsequent writes until this write completes.
- base::RunLoop().RunUntilIdle();
- // Now, start both new transactions
- TestCompletionCallback callback2;
- HttpNetworkTransaction trans2(MEDIUM, helper.session());
- rv = trans2.Start(&request_, callback2.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- base::RunLoop().RunUntilIdle();
- TestCompletionCallback callback3;
- HttpNetworkTransaction trans3(HIGHEST, helper.session());
- rv = trans3.Start(&request_, callback3.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- base::RunLoop().RunUntilIdle();
- // We now have two HEADERS frames queued up which will be
- // dequeued only once the first write completes, which we
- // now allow to happen.
- ASSERT_TRUE(data.IsPaused());
- data.Resume();
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- // And now we can allow everything else to run to completion.
- data.Resume();
- base::RunLoop().RunUntilIdle();
- EXPECT_THAT(callback2.WaitForResult(), IsOk());
- EXPECT_THAT(callback3.WaitForResult(), IsOk());
- helper.VerifyDataConsumed();
- // At this point the test is completed and we need to safely destroy
- // all allocated structures. Helper stores a transaction that has a
- // reference to a stack allocated request, which has a short lifetime,
- // and is accessed during the transaction destruction. We need to delete
- // the transaction while the request is still a valid object.
- helper.ResetTrans();
- }
- // Test that sent data frames and received WINDOW_UPDATE frames change
- // the send_window_size_ correctly.
- // WINDOW_UPDATE is different than most other frames in that it can arrive
- // while the client is still sending the request body. In order to enforce
- // this scenario, we feed a couple of dummy frames and give a delay of 0 to
- // socket data provider, so that initial read that is done as soon as the
- // stream is created, succeeds and schedules another read. This way reads
- // and writes are interleaved; after doing a full frame write, SpdyStream
- // will break out of DoLoop and will read and process a WINDOW_UPDATE.
- // Once our WINDOW_UPDATE is read, we cannot send HEADERS right away
- // since request has not been completely written, therefore we feed
- // enough number of WINDOW_UPDATEs to finish the first read and cause a
- // write, leading to a complete write of request body; after that we send
- // a reply with a body, to cause a graceful shutdown.
- // TODO(agayev): develop a socket data provider where both, reads and
- // writes are ordered so that writing tests like these are easy and rewrite
- // all these tests using it. Right now we are working around the
- // limitations as described above and it's not deterministic, tests may
- // fail under specific circumstances.
- TEST_F(SpdyNetworkTransactionTest, WindowUpdateReceived) {
- static int kFrameCount = 2;
- std::string content(kMaxSpdyFrameChunkSize, 'a');
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kMaxSpdyFrameChunkSize * kFrameCount, LOWEST, nullptr,
- 0));
- spdy::SpdySerializedFrame body(
- spdy_util_.ConstructSpdyDataFrame(1, content, false));
- spdy::SpdySerializedFrame body_end(
- spdy_util_.ConstructSpdyDataFrame(1, content, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(body, 1),
- CreateMockWrite(body_end, 2),
- };
- static const int32_t kDeltaWindowSize = 0xff;
- static const int kDeltaCount = 4;
- spdy::SpdySerializedFrame window_update(
- spdy_util_.ConstructSpdyWindowUpdate(1, kDeltaWindowSize));
- spdy::SpdySerializedFrame window_update_dummy(
- spdy_util_.ConstructSpdyWindowUpdate(2, kDeltaWindowSize));
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(window_update_dummy, 3),
- CreateMockRead(window_update_dummy, 4),
- CreateMockRead(window_update_dummy, 5),
- CreateMockRead(window_update, 6), // Four updates, therefore window
- CreateMockRead(window_update, 7), // size should increase by
- CreateMockRead(window_update, 8), // kDeltaWindowSize * 4
- CreateMockRead(window_update, 9),
- CreateMockRead(resp, 10),
- MockRead(ASYNC, ERR_IO_PENDING, 11),
- CreateMockRead(body_end, 12),
- MockRead(ASYNC, 0, 13) // EOF
- };
- SequencedSocketData data(reads, writes);
- std::vector<std::unique_ptr<UploadElementReader>> element_readers;
- for (int i = 0; i < kFrameCount; ++i) {
- element_readers.push_back(std::make_unique<UploadBytesElementReader>(
- content.data(), content.size()));
- }
- ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0);
- // Setup the request.
- request_.method = "POST";
- request_.upload_data_stream = &upload_data_stream;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.AddData(&data);
- helper.RunPreTestSetup();
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- data.RunUntilPaused();
- base::RunLoop().RunUntilIdle();
- SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get());
- ASSERT_TRUE(stream);
- ASSERT_TRUE(stream->stream());
- EXPECT_EQ(static_cast<int>(kDefaultInitialWindowSize) +
- kDeltaWindowSize * kDeltaCount -
- kMaxSpdyFrameChunkSize * kFrameCount,
- stream->stream()->send_window_size());
- data.Resume();
- base::RunLoop().RunUntilIdle();
- rv = callback.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- helper.VerifyDataConsumed();
- }
- // Test that received data frames and sent WINDOW_UPDATE frames change
- // the recv_window_size_ correctly.
- TEST_F(SpdyNetworkTransactionTest, WindowUpdateSent) {
- // Session level maximum window size that is more than twice the default
- // initial window size so that an initial window update is sent.
- const int32_t session_max_recv_window_size = 5 * 64 * 1024;
- ASSERT_LT(2 * kDefaultInitialWindowSize, session_max_recv_window_size);
- // Stream level maximum window size that is less than the session level
- // maximum window size so that we test for confusion between the two.
- const int32_t stream_max_recv_window_size = 4 * 64 * 1024;
- ASSERT_GT(session_max_recv_window_size, stream_max_recv_window_size);
- // Size of body to be sent. Has to be less than or equal to both window sizes
- // so that we do not run out of receiving window. Also has to be greater than
- // half of them so that it triggers both a session level and a stream level
- // window update frame.
- const int32_t kTargetSize = 3 * 64 * 1024;
- ASSERT_GE(session_max_recv_window_size, kTargetSize);
- ASSERT_GE(stream_max_recv_window_size, kTargetSize);
- ASSERT_LT(session_max_recv_window_size / 2, kTargetSize);
- ASSERT_LT(stream_max_recv_window_size / 2, kTargetSize);
- // Size of each DATA frame.
- const int32_t kChunkSize = 4096;
- // Size of window updates.
- ASSERT_EQ(0, session_max_recv_window_size / 2 % kChunkSize);
- const int32_t session_window_update_delta =
- session_max_recv_window_size / 2 + kChunkSize;
- ASSERT_EQ(0, stream_max_recv_window_size / 2 % kChunkSize);
- const int32_t stream_window_update_delta =
- stream_max_recv_window_size / 2 + kChunkSize;
- spdy::SpdySerializedFrame preface(
- const_cast<char*>(spdy::kHttp2ConnectionHeaderPrefix),
- spdy::kHttp2ConnectionHeaderPrefixSize,
- /* owns_buffer = */ false);
- spdy::SettingsMap initial_settings;
- initial_settings[spdy::SETTINGS_HEADER_TABLE_SIZE] = kSpdyMaxHeaderTableSize;
- initial_settings[spdy::SETTINGS_MAX_CONCURRENT_STREAMS] =
- kSpdyMaxConcurrentPushedStreams;
- initial_settings[spdy::SETTINGS_INITIAL_WINDOW_SIZE] =
- stream_max_recv_window_size;
- initial_settings[spdy::SETTINGS_MAX_HEADER_LIST_SIZE] =
- kSpdyMaxHeaderListSize;
- spdy::SpdySerializedFrame initial_settings_frame(
- spdy_util_.ConstructSpdySettings(initial_settings));
- spdy::SpdySerializedFrame initial_window_update(
- spdy_util_.ConstructSpdyWindowUpdate(
- spdy::kSessionFlowControlStreamId,
- session_max_recv_window_size - kDefaultInitialWindowSize));
- spdy::SpdySerializedFrame combined_frames = CombineFrames(
- {&preface, &initial_settings_frame, &initial_window_update});
- std::vector<MockWrite> writes;
- writes.push_back(CreateMockWrite(combined_frames));
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- writes.push_back(CreateMockWrite(req, writes.size()));
- std::vector<MockRead> reads;
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- reads.push_back(CreateMockRead(resp, writes.size() + reads.size()));
- std::vector<spdy::SpdySerializedFrame> body_frames;
- const std::string body_data(kChunkSize, 'x');
- for (size_t remaining = kTargetSize; remaining != 0;) {
- size_t frame_size = std::min(remaining, body_data.size());
- body_frames.push_back(spdy_util_.ConstructSpdyDataFrame(
- 1, base::StringPiece(body_data.data(), frame_size), false));
- reads.push_back(
- CreateMockRead(body_frames.back(), writes.size() + reads.size()));
- remaining -= frame_size;
- }
- // Yield.
- reads.emplace_back(SYNCHRONOUS, ERR_IO_PENDING, writes.size() + reads.size());
- spdy::SpdySerializedFrame session_window_update(
- spdy_util_.ConstructSpdyWindowUpdate(0, session_window_update_delta));
- writes.push_back(
- CreateMockWrite(session_window_update, writes.size() + reads.size()));
- spdy::SpdySerializedFrame stream_window_update(
- spdy_util_.ConstructSpdyWindowUpdate(1, stream_window_update_delta));
- writes.push_back(
- CreateMockWrite(stream_window_update, writes.size() + reads.size()));
- SequencedSocketData data(reads, writes);
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->session_max_recv_window_size = session_max_recv_window_size;
- session_deps->http2_settings[spdy::SETTINGS_INITIAL_WINDOW_SIZE] =
- stream_max_recv_window_size;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- helper.AddData(&data);
- helper.RunPreTestSetup();
- SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool();
- SpdySessionPoolPeer pool_peer(spdy_session_pool);
- pool_peer.SetEnableSendingInitialData(true);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- // Finish async network reads.
- base::RunLoop().RunUntilIdle();
- SpdyHttpStream* stream =
- static_cast<SpdyHttpStream*>(trans->stream_.get());
- ASSERT_TRUE(stream);
- ASSERT_TRUE(stream->stream());
- // All data has been read, but not consumed. The window reflects this.
- EXPECT_EQ(static_cast<int>(stream_max_recv_window_size - kTargetSize),
- stream->stream()->recv_window_size());
- const HttpResponseInfo* response = trans->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- EXPECT_TRUE(response->was_fetched_via_spdy);
- // Issue a read which will cause a WINDOW_UPDATE to be sent and window
- // size increased to default.
- scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBuffer>(kTargetSize);
- EXPECT_EQ(static_cast<int>(kTargetSize),
- trans->Read(buf.get(), kTargetSize, CompletionOnceCallback()));
- EXPECT_EQ(static_cast<int>(stream_max_recv_window_size),
- stream->stream()->recv_window_size());
- EXPECT_THAT(base::StringPiece(buf->data(), kTargetSize), Each(Eq('x')));
- // Allow scheduled WINDOW_UPDATE frames to write.
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- // Test that WINDOW_UPDATE frame causing overflow is handled correctly.
- TEST_F(SpdyNetworkTransactionTest, WindowUpdateOverflow) {
- // Number of full frames we hope to write (but will not, used to
- // set content-length header correctly)
- static int kFrameCount = 3;
- std::string content(kMaxSpdyFrameChunkSize, 'a');
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kMaxSpdyFrameChunkSize * kFrameCount, LOWEST, nullptr,
- 0));
- spdy::SpdySerializedFrame body(
- spdy_util_.ConstructSpdyDataFrame(1, content, false));
- spdy::SpdySerializedFrame rst(spdy_util_.ConstructSpdyRstStream(
- 1, spdy::ERROR_CODE_FLOW_CONTROL_ERROR));
- // We're not going to write a data frame with FIN, we'll receive a bad
- // WINDOW_UPDATE while sending a request and will send a RST_STREAM frame.
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(body, 2),
- CreateMockWrite(rst, 3),
- };
- static const int32_t kDeltaWindowSize = 0x7fffffff; // cause an overflow
- spdy::SpdySerializedFrame window_update(
- spdy_util_.ConstructSpdyWindowUpdate(1, kDeltaWindowSize));
- MockRead reads[] = {
- CreateMockRead(window_update, 1), MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- std::vector<std::unique_ptr<UploadElementReader>> element_readers;
- for (int i = 0; i < kFrameCount; ++i) {
- element_readers.push_back(std::make_unique<UploadBytesElementReader>(
- content.data(), content.size()));
- }
- ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0);
- // Setup the request.
- request_.method = "POST";
- request_.upload_data_stream = &upload_data_stream;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(callback.have_result());
- EXPECT_THAT(callback.WaitForResult(), IsError(ERR_HTTP2_FLOW_CONTROL_ERROR));
- helper.VerifyDataConsumed();
- }
- // Regression test for https://crbug.com/732019.
- // RFC7540 Section 6.9.2: A spdy::SETTINGS_INITIAL_WINDOW_SIZE change that
- // causes any stream flow control window to overflow MUST be treated as a
- // connection error.
- TEST_F(SpdyNetworkTransactionTest, InitialWindowSizeOverflow) {
- spdy::SpdySerializedFrame window_update(
- spdy_util_.ConstructSpdyWindowUpdate(1, 0x60000000));
- spdy::SettingsMap settings;
- settings[spdy::SETTINGS_INITIAL_WINDOW_SIZE] = 0x60000000;
- spdy::SpdySerializedFrame settings_frame(
- spdy_util_.ConstructSpdySettings(settings));
- MockRead reads[] = {CreateMockRead(window_update, 1),
- CreateMockRead(settings_frame, 2)};
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
- spdy::SpdySerializedFrame goaway(
- spdy_util_.ConstructSpdyGoAway(0, spdy::ERROR_CODE_FLOW_CONTROL_ERROR,
- "New spdy::SETTINGS_INITIAL_WINDOW_SIZE "
- "value overflows flow control window of "
- "stream 1."));
- MockWrite writes[] = {CreateMockWrite(req, 0),
- CreateMockWrite(settings_ack, 3),
- CreateMockWrite(goaway, 4)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_FLOW_CONTROL_ERROR));
- }
- // Tests that we close the connection if we try to enqueue more frames than
- // the cap allows.
- TEST_F(SpdyNetworkTransactionTest, SessionMaxQueuedCappedFramesExceeded) {
- const int kTestSessionMaxQueuedCappedFrames = 5;
- const int kTestNumPings = kTestSessionMaxQueuedCappedFrames + 1;
- spdy::SettingsMap settings;
- settings[spdy::SETTINGS_INITIAL_WINDOW_SIZE] = 0xffff;
- spdy::SpdySerializedFrame settings_frame(
- spdy_util_.ConstructSpdySettings(settings));
- std::vector<spdy::SpdySerializedFrame> ping_frames;
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
- std::vector<MockWrite> writes;
- std::vector<MockRead> reads;
- // Send request, receive SETTINGS and send a SETTINGS ACK.
- writes.push_back(CreateMockWrite(req, writes.size() + reads.size()));
- reads.push_back(CreateMockRead(settings_frame, writes.size() + reads.size()));
- writes.push_back(CreateMockWrite(settings_ack, writes.size() + reads.size()));
- // Receive more pings than our limit allows.
- for (int i = 1; i <= kTestNumPings; ++i) {
- ping_frames.push_back(
- spdy_util_.ConstructSpdyPing(/*ping_id=*/i, /*is_ack=*/false));
- reads.push_back(
- CreateMockRead(ping_frames.back(), writes.size() + reads.size()));
- }
- // Only write PING ACKs after receiving all of them to ensure they are all in
- // the write queue.
- for (int i = 1; i <= kTestNumPings; ++i) {
- ping_frames.push_back(
- spdy_util_.ConstructSpdyPing(/*ping_id=*/i, /*is_ack=*/true));
- writes.push_back(
- CreateMockWrite(ping_frames.back(), writes.size() + reads.size()));
- }
- // Stop reading.
- reads.emplace_back(ASYNC, 0, writes.size() + reads.size());
- SequencedSocketData data(reads, writes);
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->session_max_queued_capped_frames =
- kTestSessionMaxQueuedCappedFrames;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_CONNECTION_CLOSED));
- }
- // Test that after hitting a send window size of 0, the write process
- // stalls and upon receiving WINDOW_UPDATE frame write resumes.
- // This test constructs a POST request followed by enough data frames
- // containing 'a' that would make the window size 0, followed by another
- // data frame containing default content (which is "hello!") and this frame
- // also contains a FIN flag. SequencedSocketData is used to enforce all
- // writes, save the last, go through before a read could happen. The last frame
- // ("hello!") is not permitted to go through since by the time its turn
- // arrives, window size is 0. At this point MessageLoop::Run() called via
- // callback would block. Therefore we call MessageLoop::RunUntilIdle()
- // which returns after performing all possible writes. We use DCHECKS to
- // ensure that last data frame is still there and stream has stalled.
- // After that, next read is artifically enforced, which causes a
- // WINDOW_UPDATE to be read and I/O process resumes.
- TEST_F(SpdyNetworkTransactionTest, FlowControlStallResume) {
- const int32_t initial_window_size = kDefaultInitialWindowSize;
- // Number of upload data buffers we need to send to zero out the window size
- // is the minimal number of upload buffers takes to be bigger than
- // |initial_window_size|.
- size_t num_upload_buffers =
- ceil(static_cast<double>(initial_window_size) / kBufferSize);
- // Each upload data buffer consists of |num_frames_in_one_upload_buffer|
- // frames, each with |kMaxSpdyFrameChunkSize| bytes except the last frame,
- // which has kBufferSize % kMaxSpdyChunkSize bytes.
- size_t num_frames_in_one_upload_buffer =
- ceil(static_cast<double>(kBufferSize) / kMaxSpdyFrameChunkSize);
- // Construct content for a data frame of maximum size.
- std::string content(kMaxSpdyFrameChunkSize, 'a');
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1,
- /*content_length=*/kBufferSize * num_upload_buffers + kUploadDataSize,
- LOWEST, nullptr, 0));
- // Full frames.
- spdy::SpdySerializedFrame body1(
- spdy_util_.ConstructSpdyDataFrame(1, content, false));
- // Last frame in each upload data buffer.
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(
- 1,
- base::StringPiece(content.data(), kBufferSize % kMaxSpdyFrameChunkSize),
- false));
- // The very last frame before the stalled frames.
- spdy::SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(
- 1,
- base::StringPiece(content.data(), initial_window_size % kBufferSize %
- kMaxSpdyFrameChunkSize),
- false));
- // Data frames to be sent once WINDOW_UPDATE frame is received.
- // If kBufferSize * num_upload_buffers > initial_window_size,
- // we need one additional frame to send the rest of 'a'.
- std::string last_body(kBufferSize * num_upload_buffers - initial_window_size,
- 'a');
- spdy::SpdySerializedFrame body4(
- spdy_util_.ConstructSpdyDataFrame(1, last_body, false));
- // Also send a "hello!" after WINDOW_UPDATE.
- spdy::SpdySerializedFrame body5(spdy_util_.ConstructSpdyDataFrame(1, true));
- // Fill in mock writes.
- size_t i = 0;
- std::vector<MockWrite> writes;
- writes.push_back(CreateMockWrite(req, i++));
- for (size_t j = 0; j < num_upload_buffers; j++) {
- for (size_t k = 0; k < num_frames_in_one_upload_buffer; k++) {
- if (j == num_upload_buffers - 1 &&
- (initial_window_size % kBufferSize != 0)) {
- writes.push_back(CreateMockWrite(body3, i++));
- } else if (k == num_frames_in_one_upload_buffer - 1 &&
- kBufferSize % kMaxSpdyFrameChunkSize != 0) {
- writes.push_back(CreateMockWrite(body2, i++));
- } else {
- writes.push_back(CreateMockWrite(body1, i++));
- }
- }
- }
- // Fill in mock reads.
- std::vector<MockRead> reads;
- // Force a pause.
- reads.emplace_back(ASYNC, ERR_IO_PENDING, i++);
- // Construct read frame for window updates that gives enough space to upload
- // the rest of the data.
- spdy::SpdySerializedFrame session_window_update(
- spdy_util_.ConstructSpdyWindowUpdate(0,
- kUploadDataSize + last_body.size()));
- spdy::SpdySerializedFrame window_update(spdy_util_.ConstructSpdyWindowUpdate(
- 1, kUploadDataSize + last_body.size()));
- reads.push_back(CreateMockRead(session_window_update, i++));
- reads.push_back(CreateMockRead(window_update, i++));
- // Stalled frames which can be sent after receiving window updates.
- if (last_body.size() > 0)
- writes.push_back(CreateMockWrite(body4, i++));
- writes.push_back(CreateMockWrite(body5, i++));
- spdy::SpdySerializedFrame reply(
- spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- reads.push_back(CreateMockRead(reply, i++));
- reads.push_back(CreateMockRead(body2, i++));
- reads.push_back(CreateMockRead(body5, i++));
- reads.emplace_back(ASYNC, 0, i++); // EOF
- SequencedSocketData data(reads, writes);
- std::vector<std::unique_ptr<UploadElementReader>> element_readers;
- std::string upload_data_string(kBufferSize * num_upload_buffers, 'a');
- upload_data_string.append(kUploadData, kUploadDataSize);
- element_readers.push_back(std::make_unique<UploadBytesElementReader>(
- upload_data_string.c_str(), upload_data_string.size()));
- ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0);
- request_.method = "POST";
- request_.upload_data_stream = &upload_data_stream;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.AddData(&data);
- helper.RunPreTestSetup();
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- base::RunLoop().RunUntilIdle(); // Write as much as we can.
- SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get());
- ASSERT_TRUE(stream);
- ASSERT_TRUE(stream->stream());
- EXPECT_EQ(0, stream->stream()->send_window_size());
- if (initial_window_size % kBufferSize != 0) {
- // If it does not take whole number of full upload buffer to zero out
- // initial window size, then the upload data is not at EOF, because the
- // last read must be stalled.
- EXPECT_FALSE(upload_data_stream.IsEOF());
- } else {
- // All the body data should have been read.
- // TODO(satorux): This is because of the weirdness in reading the request
- // body in OnSendBodyComplete(). See crbug.com/113107.
- EXPECT_TRUE(upload_data_stream.IsEOF());
- }
- // But the body is not yet fully sent (kUploadData is not yet sent)
- // since we're send-stalled.
- EXPECT_TRUE(stream->stream()->send_stalled_by_flow_control());
- data.Resume(); // Read in WINDOW_UPDATE frame.
- rv = callback.WaitForResult();
- EXPECT_THAT(rv, IsOk());
- // Finish async network reads.
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- // Test we correctly handle the case where the SETTINGS frame results in
- // unstalling the send window.
- TEST_F(SpdyNetworkTransactionTest, FlowControlStallResumeAfterSettings) {
- const int32_t initial_window_size = kDefaultInitialWindowSize;
- // Number of upload data buffers we need to send to zero out the window size
- // is the minimal number of upload buffers takes to be bigger than
- // |initial_window_size|.
- size_t num_upload_buffers =
- ceil(static_cast<double>(initial_window_size) / kBufferSize);
- // Each upload data buffer consists of |num_frames_in_one_upload_buffer|
- // frames, each with |kMaxSpdyFrameChunkSize| bytes except the last frame,
- // which has kBufferSize % kMaxSpdyChunkSize bytes.
- size_t num_frames_in_one_upload_buffer =
- ceil(static_cast<double>(kBufferSize) / kMaxSpdyFrameChunkSize);
- // Construct content for a data frame of maximum size.
- std::string content(kMaxSpdyFrameChunkSize, 'a');
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1,
- /*content_length=*/kBufferSize * num_upload_buffers + kUploadDataSize,
- LOWEST, nullptr, 0));
- // Full frames.
- spdy::SpdySerializedFrame body1(
- spdy_util_.ConstructSpdyDataFrame(1, content, false));
- // Last frame in each upload data buffer.
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(
- 1,
- base::StringPiece(content.data(), kBufferSize % kMaxSpdyFrameChunkSize),
- false));
- // The very last frame before the stalled frames.
- spdy::SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(
- 1,
- base::StringPiece(content.data(), initial_window_size % kBufferSize %
- kMaxSpdyFrameChunkSize),
- false));
- // Data frames to be sent once WINDOW_UPDATE frame is received.
- // If kBufferSize * num_upload_buffers > initial_window_size,
- // we need one additional frame to send the rest of 'a'.
- std::string last_body(kBufferSize * num_upload_buffers - initial_window_size,
- 'a');
- spdy::SpdySerializedFrame body4(
- spdy_util_.ConstructSpdyDataFrame(1, last_body, false));
- // Also send a "hello!" after WINDOW_UPDATE.
- spdy::SpdySerializedFrame body5(spdy_util_.ConstructSpdyDataFrame(1, true));
- // Fill in mock writes.
- size_t i = 0;
- std::vector<MockWrite> writes;
- writes.push_back(CreateMockWrite(req, i++));
- for (size_t j = 0; j < num_upload_buffers; j++) {
- for (size_t k = 0; k < num_frames_in_one_upload_buffer; k++) {
- if (j == num_upload_buffers - 1 &&
- (initial_window_size % kBufferSize != 0)) {
- writes.push_back(CreateMockWrite(body3, i++));
- } else if (k == num_frames_in_one_upload_buffer - 1 &&
- kBufferSize % kMaxSpdyFrameChunkSize != 0) {
- writes.push_back(CreateMockWrite(body2, i++));
- } else {
- writes.push_back(CreateMockWrite(body1, i++));
- }
- }
- }
- // Fill in mock reads.
- std::vector<MockRead> reads;
- // Force a pause.
- reads.emplace_back(ASYNC, ERR_IO_PENDING, i++);
- // Construct read frame for SETTINGS that gives enough space to upload the
- // rest of the data.
- spdy::SettingsMap settings;
- settings[spdy::SETTINGS_INITIAL_WINDOW_SIZE] = initial_window_size * 2;
- spdy::SpdySerializedFrame settings_frame_large(
- spdy_util_.ConstructSpdySettings(settings));
- reads.push_back(CreateMockRead(settings_frame_large, i++));
- spdy::SpdySerializedFrame session_window_update(
- spdy_util_.ConstructSpdyWindowUpdate(0,
- last_body.size() + kUploadDataSize));
- reads.push_back(CreateMockRead(session_window_update, i++));
- spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
- writes.push_back(CreateMockWrite(settings_ack, i++));
- // Stalled frames which can be sent after |settings_ack|.
- if (last_body.size() > 0)
- writes.push_back(CreateMockWrite(body4, i++));
- writes.push_back(CreateMockWrite(body5, i++));
- spdy::SpdySerializedFrame reply(
- spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- reads.push_back(CreateMockRead(reply, i++));
- reads.push_back(CreateMockRead(body2, i++));
- reads.push_back(CreateMockRead(body5, i++));
- reads.emplace_back(ASYNC, 0, i++); // EOF
- // Force all writes to happen before any read, last write will not
- // actually queue a frame, due to window size being 0.
- SequencedSocketData data(reads, writes);
- std::vector<std::unique_ptr<UploadElementReader>> element_readers;
- std::string upload_data_string(kBufferSize * num_upload_buffers, 'a');
- upload_data_string.append(kUploadData, kUploadDataSize);
- element_readers.push_back(std::make_unique<UploadBytesElementReader>(
- upload_data_string.c_str(), upload_data_string.size()));
- ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0);
- request_.method = "POST";
- request_.upload_data_stream = &upload_data_stream;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- data.RunUntilPaused(); // Write as much as we can.
- base::RunLoop().RunUntilIdle();
- SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get());
- ASSERT_TRUE(stream);
- ASSERT_TRUE(stream->stream());
- EXPECT_EQ(0, stream->stream()->send_window_size());
- if (initial_window_size % kBufferSize != 0) {
- // If it does not take whole number of full upload buffer to zero out
- // initial window size, then the upload data is not at EOF, because the
- // last read must be stalled.
- EXPECT_FALSE(upload_data_stream.IsEOF());
- } else {
- // All the body data should have been read.
- // TODO(satorux): This is because of the weirdness in reading the request
- // body in OnSendBodyComplete(). See crbug.com/113107.
- EXPECT_TRUE(upload_data_stream.IsEOF());
- }
- // But the body is not yet fully sent (kUploadData is not yet sent)
- // since we're send-stalled.
- EXPECT_TRUE(stream->stream()->send_stalled_by_flow_control());
- // Read in SETTINGS frame to unstall.
- data.Resume();
- base::RunLoop().RunUntilIdle();
- rv = callback.WaitForResult();
- helper.VerifyDataConsumed();
- // If stream is nullptr, that means it was unstalled and closed.
- EXPECT_TRUE(stream->stream() == nullptr);
- }
- // Test we correctly handle the case where the SETTINGS frame results in a
- // negative send window size.
- TEST_F(SpdyNetworkTransactionTest, FlowControlNegativeSendWindowSize) {
- const int32_t initial_window_size = kDefaultInitialWindowSize;
- // Number of upload data buffers we need to send to zero out the window size
- // is the minimal number of upload buffers takes to be bigger than
- // |initial_window_size|.
- size_t num_upload_buffers =
- ceil(static_cast<double>(initial_window_size) / kBufferSize);
- // Each upload data buffer consists of |num_frames_in_one_upload_buffer|
- // frames, each with |kMaxSpdyFrameChunkSize| bytes except the last frame,
- // which has kBufferSize % kMaxSpdyChunkSize bytes.
- size_t num_frames_in_one_upload_buffer =
- ceil(static_cast<double>(kBufferSize) / kMaxSpdyFrameChunkSize);
- // Construct content for a data frame of maximum size.
- std::string content(kMaxSpdyFrameChunkSize, 'a');
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1,
- /*content_length=*/kBufferSize * num_upload_buffers + kUploadDataSize,
- LOWEST, nullptr, 0));
- // Full frames.
- spdy::SpdySerializedFrame body1(
- spdy_util_.ConstructSpdyDataFrame(1, content, false));
- // Last frame in each upload data buffer.
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(
- 1,
- base::StringPiece(content.data(), kBufferSize % kMaxSpdyFrameChunkSize),
- false));
- // The very last frame before the stalled frames.
- spdy::SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(
- 1,
- base::StringPiece(content.data(), initial_window_size % kBufferSize %
- kMaxSpdyFrameChunkSize),
- false));
- // Data frames to be sent once WINDOW_UPDATE frame is received.
- // If kBufferSize * num_upload_buffers > initial_window_size,
- // we need one additional frame to send the rest of 'a'.
- std::string last_body(kBufferSize * num_upload_buffers - initial_window_size,
- 'a');
- spdy::SpdySerializedFrame body4(
- spdy_util_.ConstructSpdyDataFrame(1, last_body, false));
- // Also send a "hello!" after WINDOW_UPDATE.
- spdy::SpdySerializedFrame body5(spdy_util_.ConstructSpdyDataFrame(1, true));
- // Fill in mock writes.
- size_t i = 0;
- std::vector<MockWrite> writes;
- writes.push_back(CreateMockWrite(req, i++));
- for (size_t j = 0; j < num_upload_buffers; j++) {
- for (size_t k = 0; k < num_frames_in_one_upload_buffer; k++) {
- if (j == num_upload_buffers - 1 &&
- (initial_window_size % kBufferSize != 0)) {
- writes.push_back(CreateMockWrite(body3, i++));
- } else if (k == num_frames_in_one_upload_buffer - 1 &&
- kBufferSize % kMaxSpdyFrameChunkSize != 0) {
- writes.push_back(CreateMockWrite(body2, i++));
- } else {
- writes.push_back(CreateMockWrite(body1, i++));
- }
- }
- }
- // Fill in mock reads.
- std::vector<MockRead> reads;
- // Force a pause.
- reads.emplace_back(ASYNC, ERR_IO_PENDING, i++);
- // Construct read frame for SETTINGS that makes the send_window_size
- // negative.
- spdy::SettingsMap new_settings;
- new_settings[spdy::SETTINGS_INITIAL_WINDOW_SIZE] = initial_window_size / 2;
- spdy::SpdySerializedFrame settings_frame_small(
- spdy_util_.ConstructSpdySettings(new_settings));
- // Construct read frames for WINDOW_UPDATE that makes the send_window_size
- // positive.
- spdy::SpdySerializedFrame session_window_update_init_size(
- spdy_util_.ConstructSpdyWindowUpdate(0, initial_window_size));
- spdy::SpdySerializedFrame window_update_init_size(
- spdy_util_.ConstructSpdyWindowUpdate(1, initial_window_size));
- reads.push_back(CreateMockRead(settings_frame_small, i++));
- reads.push_back(CreateMockRead(session_window_update_init_size, i++));
- reads.push_back(CreateMockRead(window_update_init_size, i++));
- spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
- writes.push_back(CreateMockWrite(settings_ack, i++));
- // Stalled frames which can be sent after |settings_ack|.
- if (last_body.size() > 0)
- writes.push_back(CreateMockWrite(body4, i++));
- writes.push_back(CreateMockWrite(body5, i++));
- spdy::SpdySerializedFrame reply(
- spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- reads.push_back(CreateMockRead(reply, i++));
- reads.push_back(CreateMockRead(body2, i++));
- reads.push_back(CreateMockRead(body5, i++));
- reads.emplace_back(ASYNC, 0, i++); // EOF
- // Force all writes to happen before any read, last write will not
- // actually queue a frame, due to window size being 0.
- SequencedSocketData data(reads, writes);
- std::vector<std::unique_ptr<UploadElementReader>> element_readers;
- std::string upload_data_string(kBufferSize * num_upload_buffers, 'a');
- upload_data_string.append(kUploadData, kUploadDataSize);
- element_readers.push_back(std::make_unique<UploadBytesElementReader>(
- upload_data_string.c_str(), upload_data_string.size()));
- ElementsUploadDataStream upload_data_stream(std::move(element_readers), 0);
- request_.method = "POST";
- request_.upload_data_stream = &upload_data_stream;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestCompletionCallback callback;
- int rv = trans->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- data.RunUntilPaused(); // Write as much as we can.
- base::RunLoop().RunUntilIdle();
- SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get());
- ASSERT_TRUE(stream);
- ASSERT_TRUE(stream->stream());
- EXPECT_EQ(0, stream->stream()->send_window_size());
- if (initial_window_size % kBufferSize != 0) {
- // If it does not take whole number of full upload buffer to zero out
- // initial window size, then the upload data is not at EOF, because the
- // last read must be stalled.
- EXPECT_FALSE(upload_data_stream.IsEOF());
- } else {
- // All the body data should have been read.
- // TODO(satorux): This is because of the weirdness in reading the request
- // body in OnSendBodyComplete(). See crbug.com/113107.
- EXPECT_TRUE(upload_data_stream.IsEOF());
- }
- // Read in WINDOW_UPDATE or SETTINGS frame.
- data.Resume();
- base::RunLoop().RunUntilIdle();
- rv = callback.WaitForResult();
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, GoAwayOnOddPushStreamId) {
- base::HistogramTester histogram_tester;
- spdy::Http2HeaderBlock push_headers;
- spdy_util_.AddUrlToHeaderBlock("http://www.example.org/a.dat", &push_headers);
- spdy::SpdySerializedFrame push(
- spdy_util_.ConstructSpdyPushPromise(1, 3, std::move(push_headers)));
- MockRead reads[] = {CreateMockRead(push, 1)};
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(
- 0, spdy::ERROR_CODE_PROTOCOL_ERROR,
- "Received invalid pushed stream id 3 (must be even) on stream id 1."));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(goaway, 2),
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_PROTOCOL_ERROR));
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kPromisedStreamIdParityError), 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1);
- }
- TEST_F(SpdyNetworkTransactionTest,
- GoAwayOnPushStreamIdLesserOrEqualThanLastAccepted) {
- base::HistogramTester histogram_tester;
- spdy::SpdySerializedFrame push_a(spdy_util_.ConstructSpdyPush(
- nullptr, 0, 4, 1, "https://www.example.org/a.dat"));
- spdy::Http2HeaderBlock push_b_headers;
- spdy_util_.AddUrlToHeaderBlock("https://www.example.org/b.dat",
- &push_b_headers);
- spdy::SpdySerializedFrame push_b(
- spdy_util_.ConstructSpdyPushPromise(1, 2, std::move(push_b_headers)));
- MockRead reads[] = {
- CreateMockRead(push_a, 1), CreateMockRead(push_b, 3),
- };
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame priority_a(
- spdy_util_.ConstructSpdyPriority(4, 1, IDLE, true));
- spdy::SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(
- 4, spdy::ERROR_CODE_PROTOCOL_ERROR,
- "Received pushed stream id 2 must be larger than last accepted id 4."));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(priority_a, 2),
- CreateMockWrite(goaway, 4),
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_PROTOCOL_ERROR));
- histogram_tester.ExpectBucketCount(
- "Net.SpdyPushedStreamFate",
- static_cast<int>(SpdyPushedStreamFate::kStreamIdOutOfOrder), 1);
- histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1);
- }
- // Regression test for https://crbug.com/493348: request header exceeds 16 kB
- // and thus sent in multiple frames when using HTTP/2.
- TEST_F(SpdyNetworkTransactionTest, LargeRequest) {
- const std::string kKey("foo");
- const std::string kValue(1 << 15, 'z');
- request_.extra_headers.SetHeader(kKey, kValue);
- spdy::Http2HeaderBlock headers(
- spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
- headers[kKey] = kValue;
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0),
- };
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- // Regression test for https://crbug.com/535629: response header exceeds 16 kB.
- TEST_F(SpdyNetworkTransactionTest, LargeResponseHeader) {
- spdy::Http2HeaderBlock headers(
- spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0),
- };
- // HPACK decoder implementation limits string literal length to 16 kB.
- const char* response_headers[2];
- const std::string kKey(16 * 1024, 'a');
- response_headers[0] = kKey.data();
- const std::string kValue(16 * 1024, 'b');
- response_headers[1] = kValue.data();
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(response_headers, 1, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- SequencedSocketData data(reads, writes);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- ASSERT_TRUE(out.response_info.headers->HasHeaderValue(kKey, kValue));
- }
- // End of line delimiter is forbidden according to RFC 7230 Section 3.2.
- TEST_F(SpdyNetworkTransactionTest, CRLFInHeaderValue) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_PROTOCOL_ERROR));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(rst, 2)};
- const char* response_headers[] = {"folded", "foo\r\nbar"};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(response_headers, 1, 1));
- MockRead reads[] = {CreateMockRead(resp, 1), MockRead(ASYNC, 0, 3)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_PROTOCOL_ERROR));
- }
- // Regression test for https://crbug.com/603182.
- // No response headers received before RST_STREAM: error.
- TEST_F(SpdyNetworkTransactionTest, RstStreamNoError) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructChunkedSpdyPost(nullptr, 0));
- MockWrite writes[] = {CreateMockWrite(req, 0, ASYNC)};
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_NO_ERROR));
- MockRead reads[] = {CreateMockRead(rst, 1), MockRead(ASYNC, 0, 2)};
- SequencedSocketData data(reads, writes);
- UseChunkedPostRequest();
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_PROTOCOL_ERROR));
- }
- // Regression test for https://crbug.com/603182.
- // Response headers and data, then RST_STREAM received,
- // before request body is sent: success.
- TEST_F(SpdyNetworkTransactionTest, RstStreamNoErrorAfterResponse) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructChunkedSpdyPost(nullptr, 0));
- MockWrite writes[] = {CreateMockWrite(req, 0, ASYNC)};
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_NO_ERROR));
- MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2),
- CreateMockRead(rst, 3), MockRead(ASYNC, 0, 4)};
- SequencedSocketData data(reads, writes);
- UseChunkedPostRequest();
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- TEST_F(SpdyNetworkTransactionTest, 100Continue) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::Http2HeaderBlock informational_headers;
- informational_headers[spdy::kHttp2StatusHeader] = "100";
- spdy::SpdySerializedFrame informational_response(
- spdy_util_.ConstructSpdyReply(1, std::move(informational_headers)));
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(informational_response, 1), CreateMockRead(resp, 2),
- CreateMockRead(body, 3), MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- // "A server can send a complete response prior to the client sending an entire
- // request if the response does not depend on any portion of the request that
- // has not been sent and received." (RFC7540 Section 8.1)
- // Regression test for https://crbug.com/606990. Server responds before POST
- // data are sent and closes connection: this must result in
- // ERR_CONNECTION_CLOSED (as opposed to ERR_HTTP2_PROTOCOL_ERROR).
- TEST_F(SpdyNetworkTransactionTest, ResponseBeforePostDataSent) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructChunkedSpdyPost(nullptr, 0));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3)};
- SequencedSocketData data(reads, writes);
- UseChunkedPostRequest();
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- helper.StartDefaultTest();
- EXPECT_THAT(helper.output().rv, IsError(ERR_IO_PENDING));
- helper.WaitForCallbackToComplete();
- EXPECT_THAT(helper.output().rv, IsError(ERR_CONNECTION_CLOSED));
- }
- // Regression test for https://crbug.com/606990.
- // Server responds before POST data are sent and resets stream with NO_ERROR.
- TEST_F(SpdyNetworkTransactionTest, ResponseAndRstStreamBeforePostDataSent) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructChunkedSpdyPost(nullptr, 0));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame rst(
- spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_NO_ERROR));
- MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2),
- CreateMockRead(rst, 3), MockRead(ASYNC, 0, 4)};
- SequencedSocketData data(reads, writes);
- UseChunkedPostRequest();
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- // Unsupported frames must be ignored. This is especially important for frame
- // type 0xb, which used to be the BLOCKED frame in previous versions of SPDY,
- // but is going to be used for the ORIGIN frame.
- // TODO(bnc): Implement ORIGIN frame support. https://crbug.com/697333
- TEST_F(SpdyNetworkTransactionTest, IgnoreUnsupportedOriginFrame) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- const char origin_frame_on_stream_zero[] = {
- 0x00, 0x00, 0x05, // Length
- 0x0b, // Type
- 0x00, // Flags
- 0x00, 0x00, 0x00, 0x00, // Stream ID
- 0x00, 0x03, // Origin-Len
- 'f', 'o', 'o' // ASCII-Origin
- };
- const char origin_frame_on_stream_one[] = {
- 0x00, 0x00, 0x05, // Length
- 0x0b, // Type
- 0x00, // Flags
- 0x00, 0x00, 0x00, 0x01, // Stream ID
- 0x00, 0x03, // Origin-Len
- 'b', 'a', 'r' // ASCII-Origin
- };
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {MockRead(ASYNC, origin_frame_on_stream_zero,
- std::size(origin_frame_on_stream_zero), 1),
- CreateMockRead(resp, 2),
- MockRead(ASYNC, origin_frame_on_stream_one,
- std::size(origin_frame_on_stream_one), 3),
- CreateMockRead(body, 4), MockRead(ASYNC, 0, 5)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- class SpdyNetworkTransactionTLSUsageCheckTest
- : public SpdyNetworkTransactionTest {
- protected:
- void RunTLSUsageCheckTest(
- std::unique_ptr<SSLSocketDataProvider> ssl_provider) {
- spdy::SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(
- 0, spdy::ERROR_CODE_INADEQUATE_SECURITY, ""));
- MockWrite writes[] = {CreateMockWrite(goaway)};
- StaticSocketDataProvider data(base::span<MockRead>(), writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- nullptr);
- helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY));
- }
- };
- TEST_F(SpdyNetworkTransactionTLSUsageCheckTest, TLSVersionTooOld) {
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- SSLConnectionStatusSetVersion(SSL_CONNECTION_VERSION_SSL3,
- &ssl_provider->ssl_info.connection_status);
- RunTLSUsageCheckTest(std::move(ssl_provider));
- }
- TEST_F(SpdyNetworkTransactionTLSUsageCheckTest, TLSCipherSuiteSucky) {
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Set to TLS_RSA_WITH_NULL_MD5
- SSLConnectionStatusSetCipherSuite(0x1,
- &ssl_provider->ssl_info.connection_status);
- RunTLSUsageCheckTest(std::move(ssl_provider));
- }
- // Regression test for https://crbug.com/737143.
- // This test sets up an old TLS version just like in TLSVersionTooOld,
- // and makes sure that it results in an spdy::ERROR_CODE_INADEQUATE_SECURITY
- // even for a non-secure request URL.
- TEST_F(SpdyNetworkTransactionTest, InsecureUrlCreatesSecureSpdySession) {
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- SSLConnectionStatusSetVersion(SSL_CONNECTION_VERSION_SSL3,
- &ssl_provider->ssl_info.connection_status);
- spdy::SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(
- 0, spdy::ERROR_CODE_INADEQUATE_SECURITY, ""));
- MockWrite writes[] = {CreateMockWrite(goaway)};
- StaticSocketDataProvider data(base::span<MockRead>(), writes);
- request_.url = GURL("http://www.example.org/");
- // Need secure proxy so that insecure URL can use HTTP/2.
- auto session_deps = std::make_unique<SpdySessionDependencies>(
- ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
- "HTTPS myproxy:70", TRAFFIC_ANNOTATION_FOR_TESTS));
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY));
- }
- TEST_F(SpdyNetworkTransactionTest, RequestHeadersCallback) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, DEFAULT_PRIORITY));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- HttpRawRequestHeaders raw_headers;
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- helper.trans()->SetRequestHeadersCallback(base::BindRepeating(
- &HttpRawRequestHeaders::Assign, base::Unretained(&raw_headers)));
- helper.StartDefaultTest();
- helper.FinishDefaultTestWithoutVerification();
- EXPECT_FALSE(raw_headers.headers().empty());
- std::string value;
- EXPECT_TRUE(raw_headers.FindHeaderForTest(":path", &value));
- EXPECT_EQ("/", value);
- EXPECT_TRUE(raw_headers.FindHeaderForTest(":method", &value));
- EXPECT_EQ("GET", value);
- EXPECT_TRUE(raw_headers.request_line().empty());
- }
- // A request that has adopted a push promise and later got reset by the server
- // should be retried on a new stream.
- // Regression test for https://crbug.com/798508.
- TEST_F(SpdyNetworkTransactionTest, PushCanceledByServerAfterClaimed) {
- const char pushed_url[] = "https://www.example.org/a.dat";
- // Construct a request to the default URL on stream 1.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(pushed_url, 3, LOWEST));
- // Construct a priority frame for stream 2.
- spdy::SpdySerializedFrame priority(
- spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(priority, 3),
- CreateMockWrite(req2, 6)};
- // Construct a Push Promise frame, with no response.
- spdy::SpdySerializedFrame push_promise(spdy_util_.ConstructSpdyPushPromise(
- 1, 2, spdy_util_.ConstructGetHeaderBlock(pushed_url)));
- // Construct a RST frame, canceling stream 2.
- spdy::SpdySerializedFrame rst_server(
- spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_CANCEL));
- // Construct response headers and bodies.
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead reads[] = {
- CreateMockRead(push_promise, 1), MockRead(ASYNC, ERR_IO_PENDING, 2),
- CreateMockRead(rst_server, 4), MockRead(ASYNC, ERR_IO_PENDING, 5),
- CreateMockRead(resp1, 7), CreateMockRead(body1, 8),
- CreateMockRead(resp2, 9), CreateMockRead(body2, 10),
- MockRead(ASYNC, 0, 11)};
- SequencedSocketData data(reads, writes);
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- // First request to start the connection.
- TestCompletionCallback callback1;
- int rv = trans->Start(&request_, callback1.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- data.RunUntilPaused();
- // Get a SpdySession.
- SpdySessionKey key(HostPortPair::FromURL(request_.url), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- HttpNetworkSession* session = helper.session();
- base::WeakPtr<SpdySession> spdy_session =
- session->spdy_session_pool()->FindAvailableSession(
- key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- // Verify that there is one unclaimed push stream.
- EXPECT_EQ(1u, num_unclaimed_pushed_streams(spdy_session));
- // Claim the pushed stream.
- HttpNetworkTransaction transaction2(DEFAULT_PRIORITY, session);
- TestCompletionCallback callback2;
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL(pushed_url);
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- transaction2.Start(&request2, callback2.callback(), log_);
- base::RunLoop().RunUntilIdle();
- EXPECT_EQ(3u, spdy_stream_hi_water_mark(spdy_session));
- EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session));
- // Continue reading and get the RST.
- data.Resume();
- base::RunLoop().RunUntilIdle();
- // Make sure we got the RST and retried the request.
- EXPECT_EQ(2u, num_active_streams(spdy_session));
- EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session));
- EXPECT_EQ(5u, spdy_stream_hi_water_mark(spdy_session));
- data.Resume();
- // Test that transactions succeeded.
- rv = callback1.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- rv = callback2.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- // Read EOF.
- base::RunLoop().RunUntilIdle();
- // Verify that all data was read and written.
- helper.VerifyDataConsumed();
- }
- #if BUILDFLAG(ENABLE_WEBSOCKETS)
- TEST_F(SpdyNetworkTransactionTest, WebSocketOpensNewConnection) {
- base::HistogramTester histogram_tester;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- // First request opens up an HTTP/2 connection.
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, DEFAULT_PRIORITY));
- MockWrite writes1[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads1[] = {CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, ERR_IO_PENDING, 3),
- MockRead(ASYNC, 0, 4)};
- SequencedSocketData data1(reads1, writes1);
- helper.AddData(&data1);
- // WebSocket request opens a new connection with HTTP/2 disabled.
- MockWrite writes2[] = {
- MockWrite("GET / HTTP/1.1\r\n"
- "Host: www.example.org\r\n"
- "Connection: Upgrade\r\n"
- "Upgrade: websocket\r\n"
- "Origin: http://www.example.org\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Sec-WebSocket-Extensions: permessage-deflate; "
- "client_max_window_bits\r\n\r\n")};
- MockRead reads2[] = {
- MockRead("HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n")};
- StaticSocketDataProvider data2(reads2, writes2);
- auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Test that the request has HTTP/2 disabled.
- ssl_provider2->next_protos_expected_in_ssl_config = {kProtoHTTP11};
- // Force socket to use HTTP/1.1, the default protocol without ALPN.
- ssl_provider2->next_proto = kProtoHTTP11;
- ssl_provider2->ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
- helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
- TestCompletionCallback callback1;
- HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
- int rv = trans1.Start(&request_, callback1.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback1.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- const HttpResponseInfo* response = trans1.GetResponseInfo();
- ASSERT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string response_data;
- rv = ReadTransaction(&trans1, &response_data);
- EXPECT_THAT(rv, IsOk());
- EXPECT_EQ("hello!", response_data);
- SpdySessionKey key(HostPortPair::FromURL(request_.url), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- ASSERT_TRUE(spdy_session);
- EXPECT_FALSE(spdy_session->support_websocket());
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL("wss://www.example.org/");
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- EXPECT_TRUE(HostPortPair::FromURL(request_.url)
- .Equals(HostPortPair::FromURL(request2.url)));
- request2.extra_headers.SetHeader("Connection", "Upgrade");
- request2.extra_headers.SetHeader("Upgrade", "websocket");
- request2.extra_headers.SetHeader("Origin", "http://www.example.org");
- request2.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
- TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- trans2.SetWebSocketHandshakeStreamCreateHelper(
- &websocket_stream_create_helper);
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback2.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- // HTTP/2 connection is still open, but WebSocket request did not pool to it.
- ASSERT_TRUE(spdy_session);
- data1.Resume();
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- // Server did not advertise WebSocket support.
- histogram_tester.ExpectUniqueSample("Net.SpdySession.ServerSupportsWebSocket",
- /* support_websocket = false */ 0,
- /* expected_count = */ 1);
- }
- // Make sure that a WebSocket job doesn't pick up a newly created SpdySession
- // that doesn't support WebSockets through
- // HttpStreamFactory::Job::OnSpdySessionAvailable().
- TEST_F(SpdyNetworkTransactionTest,
- WebSocketDoesUseNewH2SessionWithoutWebSocketSupport) {
- base::HistogramTester histogram_tester;
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- NormalSpdyTransactionHelper helper(request_, HIGHEST, log_,
- std::move(session_deps));
- helper.RunPreTestSetup();
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3)};
- SequencedSocketData data(
- // Just as with other operations, this means to pause during connection
- // establishment.
- MockConnect(ASYNC, ERR_IO_PENDING), reads, writes);
- helper.AddData(&data);
- MockWrite writes2[] = {
- MockWrite(SYNCHRONOUS, 0,
- "GET / HTTP/1.1\r\n"
- "Host: www.example.org\r\n"
- "Connection: Upgrade\r\n"
- "Upgrade: websocket\r\n"
- "Origin: http://www.example.org\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Sec-WebSocket-Extensions: permessage-deflate; "
- "client_max_window_bits\r\n\r\n")};
- MockRead reads2[] = {
- MockRead(SYNCHRONOUS, 1,
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n")};
- SequencedSocketData data2(MockConnect(ASYNC, ERR_IO_PENDING), reads2,
- writes2);
- auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Test that the request has HTTP/2 disabled.
- ssl_provider2->next_protos_expected_in_ssl_config = {kProtoHTTP11};
- // Force socket to use HTTP/1.1, the default protocol without ALPN.
- ssl_provider2->next_proto = kProtoHTTP11;
- helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
- TestCompletionCallback callback1;
- int rv = helper.trans()->Start(&request_, callback1.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- // Create HTTP/2 connection.
- base::RunLoop().RunUntilIdle();
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL("wss://www.example.org/");
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- EXPECT_TRUE(HostPortPair::FromURL(request_.url)
- .Equals(HostPortPair::FromURL(request2.url)));
- request2.extra_headers.SetHeader("Connection", "Upgrade");
- request2.extra_headers.SetHeader("Upgrade", "websocket");
- request2.extra_headers.SetHeader("Origin", "http://www.example.org");
- request2.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
- TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
- HttpNetworkTransaction trans2(MEDIUM, helper.session());
- trans2.SetWebSocketHandshakeStreamCreateHelper(
- &websocket_stream_create_helper);
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- // Run until waiting on both connections.
- base::RunLoop().RunUntilIdle();
- // The H2 connection completes.
- data.socket()->OnConnectComplete(MockConnect(SYNCHRONOUS, OK));
- EXPECT_EQ(OK, callback1.WaitForResult());
- const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
- ASSERT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string response_data;
- rv = ReadTransaction(helper.trans(), &response_data);
- EXPECT_THAT(rv, IsOk());
- EXPECT_EQ("hello!", response_data);
- SpdySessionKey key(HostPortPair::FromURL(request_.url), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- ASSERT_TRUE(spdy_session);
- EXPECT_FALSE(spdy_session->support_websocket());
- EXPECT_FALSE(callback2.have_result());
- // Create WebSocket stream.
- data2.socket()->OnConnectComplete(MockConnect(SYNCHRONOUS, OK));
- rv = callback2.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, WebSocketOverHTTP2) {
- base::HistogramTester histogram_tester;
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- NormalSpdyTransactionHelper helper(request_, HIGHEST, log_,
- std::move(session_deps));
- helper.RunPreTestSetup();
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST));
- spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
- spdy::Http2HeaderBlock websocket_request_headers;
- websocket_request_headers[spdy::kHttp2MethodHeader] = "CONNECT";
- websocket_request_headers[spdy::kHttp2AuthorityHeader] = "www.example.org";
- websocket_request_headers[spdy::kHttp2SchemeHeader] = "https";
- websocket_request_headers[spdy::kHttp2PathHeader] = "/";
- websocket_request_headers[spdy::kHttp2ProtocolHeader] = "websocket";
- websocket_request_headers["origin"] = "http://www.example.org";
- websocket_request_headers["sec-websocket-version"] = "13";
- websocket_request_headers["sec-websocket-extensions"] =
- "permessage-deflate; client_max_window_bits";
- spdy::SpdySerializedFrame websocket_request(spdy_util_.ConstructSpdyHeaders(
- 3, std::move(websocket_request_headers), MEDIUM, false));
- spdy::SpdySerializedFrame priority1(
- spdy_util_.ConstructSpdyPriority(3, 0, MEDIUM, true));
- spdy::SpdySerializedFrame priority2(
- spdy_util_.ConstructSpdyPriority(1, 3, LOWEST, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 2),
- CreateMockWrite(websocket_request, 4), CreateMockWrite(priority1, 5),
- CreateMockWrite(priority2, 6)};
- spdy::SettingsMap settings;
- settings[spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1;
- spdy::SpdySerializedFrame settings_frame(
- spdy_util_.ConstructSpdySettings(settings));
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame websocket_response(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- MockRead reads[] = {CreateMockRead(settings_frame, 1),
- CreateMockRead(resp1, 3), CreateMockRead(body1, 7),
- CreateMockRead(websocket_response, 8),
- MockRead(ASYNC, 0, 9)};
- SequencedSocketData data(reads, writes);
- helper.AddData(&data);
- TestCompletionCallback callback1;
- int rv = helper.trans()->Start(&request_, callback1.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- // Create HTTP/2 connection.
- base::RunLoop().RunUntilIdle();
- SpdySessionKey key(HostPortPair::FromURL(request_.url), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ true, log_);
- ASSERT_TRUE(spdy_session);
- EXPECT_TRUE(spdy_session->support_websocket());
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL("wss://www.example.org/");
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- EXPECT_TRUE(HostPortPair::FromURL(request_.url)
- .Equals(HostPortPair::FromURL(request2.url)));
- request2.extra_headers.SetHeader("Origin", "http://www.example.org");
- request2.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
- // The following two headers must be removed by WebSocketHttp2HandshakeStream.
- request2.extra_headers.SetHeader("Connection", "Upgrade");
- request2.extra_headers.SetHeader("Upgrade", "websocket");
- TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
- HttpNetworkTransaction trans2(MEDIUM, helper.session());
- trans2.SetWebSocketHandshakeStreamCreateHelper(
- &websocket_stream_create_helper);
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- // Create WebSocket stream.
- base::RunLoop().RunUntilIdle();
- ASSERT_TRUE(spdy_session);
- // First request has HIGHEST priority, WebSocket request has MEDIUM priority.
- // Changing the priority of the first request to LOWEST changes their order,
- // and therefore triggers sending PRIORITY frames.
- helper.trans()->SetPriority(LOWEST);
- rv = callback1.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
- ASSERT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string response_data;
- rv = ReadTransaction(helper.trans(), &response_data);
- EXPECT_THAT(rv, IsOk());
- EXPECT_EQ("hello!", response_data);
- rv = callback2.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- helper.VerifyDataConsumed();
- // Server advertised WebSocket support.
- histogram_tester.ExpectUniqueSample("Net.SpdySession.ServerSupportsWebSocket",
- /* support_websocket = true */ 1,
- /* expected_count = */ 1);
- }
- // Make sure that a WebSocket job doesn't pick up a newly created SpdySession
- // that supports WebSockets through an HTTPS proxy when an H2 server doesn't
- // support websockets. See https://crbug.com/1010491.
- TEST_F(SpdyNetworkTransactionTest,
- WebSocketDoesNotUseNewH2SessionWithoutWebSocketSupportOverHttpsProxy) {
- auto session_deps = std::make_unique<SpdySessionDependencies>(
- ConfiguredProxyResolutionService::CreateFixedForTest(
- "https://proxy:70", TRAFFIC_ANNOTATION_FOR_TESTS));
- NormalSpdyTransactionHelper helper(request_, HIGHEST, log_,
- std::move(session_deps));
- helper.RunPreTestSetup();
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST));
- MockWrite writes[] = {MockWrite(SYNCHRONOUS, 0,
- "CONNECT www.example.org:443 HTTP/1.1\r\n"
- "Host: www.example.org:443\r\n"
- "Proxy-Connection: keep-alive\r\n\r\n"),
- CreateMockWrite(req, 2)};
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n\r\n"),
- CreateMockRead(resp1, 3), CreateMockRead(body1, 4),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5)};
- // SSL data for the proxy.
- SSLSocketDataProvider tunnel_ssl_data(ASYNC, OK);
- helper.session_deps()->socket_factory->AddSSLSocketDataProvider(
- &tunnel_ssl_data);
- SequencedSocketData data(
- // Just as with other operations, this means to pause during connection
- // establishment.
- MockConnect(ASYNC, ERR_IO_PENDING), reads, writes);
- helper.AddData(&data);
- MockWrite writes2[] = {
- MockWrite(SYNCHRONOUS, 0,
- "CONNECT www.example.org:443 HTTP/1.1\r\n"
- "Host: www.example.org:443\r\n"
- "Proxy-Connection: keep-alive\r\n\r\n"),
- MockWrite(SYNCHRONOUS, 2,
- "GET / HTTP/1.1\r\n"
- "Host: www.example.org\r\n"
- "Connection: Upgrade\r\n"
- "Upgrade: websocket\r\n"
- "Origin: http://www.example.org\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Sec-WebSocket-Extensions: permessage-deflate; "
- "client_max_window_bits\r\n\r\n")};
- MockRead reads2[] = {
- MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n\r\n"),
- MockRead(SYNCHRONOUS, 3,
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n")};
- SequencedSocketData data2(MockConnect(ASYNC, ERR_IO_PENDING), reads2,
- writes2);
- // SSL data for the proxy.
- SSLSocketDataProvider tunnel_ssl_data2(ASYNC, OK);
- helper.session_deps()->socket_factory->AddSSLSocketDataProvider(
- &tunnel_ssl_data2);
- auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Test that the request has HTTP/2 disabled.
- ssl_provider2->next_protos_expected_in_ssl_config = {kProtoHTTP11};
- // Force socket to use HTTP/1.1, the default protocol without ALPN.
- ssl_provider2->next_proto = kProtoHTTP11;
- helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
- TestCompletionCallback callback1;
- int rv = helper.trans()->Start(&request_, callback1.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- // Create HTTP/2 connection.
- base::RunLoop().RunUntilIdle();
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL("wss://www.example.org/");
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- EXPECT_TRUE(HostPortPair::FromURL(request_.url)
- .Equals(HostPortPair::FromURL(request2.url)));
- request2.extra_headers.SetHeader("Connection", "Upgrade");
- request2.extra_headers.SetHeader("Upgrade", "websocket");
- request2.extra_headers.SetHeader("Origin", "http://www.example.org");
- request2.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
- TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
- HttpNetworkTransaction trans2(MEDIUM, helper.session());
- trans2.SetWebSocketHandshakeStreamCreateHelper(
- &websocket_stream_create_helper);
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- // Run until waiting on both connections.
- base::RunLoop().RunUntilIdle();
- // The H2 connection completes.
- data.socket()->OnConnectComplete(MockConnect(SYNCHRONOUS, OK));
- EXPECT_EQ(OK, callback1.WaitForResult());
- const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
- ASSERT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string response_data;
- rv = ReadTransaction(helper.trans(), &response_data);
- EXPECT_THAT(rv, IsOk());
- EXPECT_EQ("hello!", response_data);
- SpdySessionKey key(
- HostPortPair::FromURL(request_.url),
- ProxyUriToProxyServer("https://proxy:70", ProxyServer::SCHEME_HTTPS),
- PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse,
- SocketTag(), NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- ASSERT_TRUE(spdy_session);
- EXPECT_FALSE(spdy_session->support_websocket());
- EXPECT_FALSE(callback2.have_result());
- // Create WebSocket stream.
- data2.socket()->OnConnectComplete(MockConnect(SYNCHRONOUS, OK));
- rv = callback2.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- helper.VerifyDataConsumed();
- }
- // Same as above, but checks that a WebSocket connection avoids creating a new
- // socket if it detects an H2 session when host resolution completes, and
- // requests also use different hostnames.
- TEST_F(SpdyNetworkTransactionTest,
- WebSocketOverHTTP2DetectsNewSessionWithAliasing) {
- base::HistogramTester histogram_tester;
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->host_resolver->set_ondemand_mode(true);
- NormalSpdyTransactionHelper helper(request_, HIGHEST, log_,
- std::move(session_deps));
- helper.RunPreTestSetup();
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST));
- spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
- spdy::Http2HeaderBlock websocket_request_headers;
- websocket_request_headers[spdy::kHttp2MethodHeader] = "CONNECT";
- websocket_request_headers[spdy::kHttp2AuthorityHeader] = "example.test";
- websocket_request_headers[spdy::kHttp2SchemeHeader] = "https";
- websocket_request_headers[spdy::kHttp2PathHeader] = "/";
- websocket_request_headers[spdy::kHttp2ProtocolHeader] = "websocket";
- websocket_request_headers["origin"] = "http://example.test";
- websocket_request_headers["sec-websocket-version"] = "13";
- websocket_request_headers["sec-websocket-extensions"] =
- "permessage-deflate; client_max_window_bits";
- spdy::SpdySerializedFrame websocket_request(spdy_util_.ConstructSpdyHeaders(
- 3, std::move(websocket_request_headers), MEDIUM, false));
- spdy::SpdySerializedFrame priority1(
- spdy_util_.ConstructSpdyPriority(3, 0, MEDIUM, true));
- spdy::SpdySerializedFrame priority2(
- spdy_util_.ConstructSpdyPriority(1, 3, LOWEST, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 2),
- CreateMockWrite(websocket_request, 4), CreateMockWrite(priority1, 5),
- CreateMockWrite(priority2, 6)};
- spdy::SettingsMap settings;
- settings[spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1;
- spdy::SpdySerializedFrame settings_frame(
- spdy_util_.ConstructSpdySettings(settings));
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame websocket_response(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- MockRead reads[] = {CreateMockRead(settings_frame, 1),
- CreateMockRead(resp1, 3), CreateMockRead(body1, 7),
- CreateMockRead(websocket_response, 8),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 9)};
- SequencedSocketData data(reads, writes);
- helper.AddData(&data);
- TestCompletionCallback callback1;
- int rv = helper.trans()->Start(&request_, callback1.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL("wss://example.test/");
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- request2.extra_headers.SetHeader("Origin", "http://example.test");
- request2.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
- // The following two headers must be removed by WebSocketHttp2HandshakeStream.
- request2.extra_headers.SetHeader("Connection", "Upgrade");
- request2.extra_headers.SetHeader("Upgrade", "websocket");
- TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
- HttpNetworkTransaction trans2(MEDIUM, helper.session());
- trans2.SetWebSocketHandshakeStreamCreateHelper(
- &websocket_stream_create_helper);
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- // Make sure both requests are blocked on host resolution.
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(helper.session_deps()->host_resolver->has_pending_requests());
- // Complete the first DNS lookup, which should result in the first transaction
- // creating an H2 session (And completing successfully).
- helper.session_deps()->host_resolver->ResolveNow(1);
- base::RunLoop().RunUntilIdle();
- SpdySessionKey key1(HostPortPair::FromURL(request_.url),
- ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- EXPECT_TRUE(helper.session()->spdy_session_pool()->HasAvailableSession(
- key1, /* is_websocket = */ false));
- base::WeakPtr<SpdySession> spdy_session1 =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key1, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- ASSERT_TRUE(spdy_session1);
- EXPECT_TRUE(spdy_session1->support_websocket());
- // Second DNS lookup completes, which results in creating a WebSocket stream.
- helper.session_deps()->host_resolver->ResolveNow(2);
- ASSERT_TRUE(spdy_session1);
- SpdySessionKey key2(HostPortPair::FromURL(request2.url),
- ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- EXPECT_TRUE(helper.session()->spdy_session_pool()->HasAvailableSession(
- key2, /* is_websocket = */ true));
- base::WeakPtr<SpdySession> spdy_session2 =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key1, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ true, log_);
- ASSERT_TRUE(spdy_session2);
- EXPECT_EQ(spdy_session1.get(), spdy_session2.get());
- base::RunLoop().RunUntilIdle();
- // First request has HIGHEST priority, WebSocket request has MEDIUM priority.
- // Changing the priority of the first request to LOWEST changes their order,
- // and therefore triggers sending PRIORITY frames.
- helper.trans()->SetPriority(LOWEST);
- rv = callback1.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
- ASSERT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string response_data;
- rv = ReadTransaction(helper.trans(), &response_data);
- EXPECT_THAT(rv, IsOk());
- EXPECT_EQ("hello!", response_data);
- rv = callback2.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- helper.VerifyDataConsumed();
- }
- // Same as above, but the SpdySession is closed just before use, so the
- // WebSocket is sent over a new HTTP/1.x connection instead.
- TEST_F(SpdyNetworkTransactionTest,
- WebSocketOverDetectsNewSessionWithAliasingButClosedBeforeUse) {
- base::HistogramTester histogram_tester;
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->host_resolver->set_ondemand_mode(true);
- NormalSpdyTransactionHelper helper(request_, HIGHEST, log_,
- std::move(session_deps));
- helper.RunPreTestSetup();
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST));
- spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
- MockWrite writes[] = {CreateMockWrite(req, 0),
- CreateMockWrite(settings_ack, 2)};
- spdy::SettingsMap settings;
- settings[spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1;
- spdy::SpdySerializedFrame settings_frame(
- spdy_util_.ConstructSpdySettings(settings));
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(settings_frame, 1),
- CreateMockRead(resp1, 3), CreateMockRead(body1, 4),
- MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5)};
- SequencedSocketData data(reads, writes);
- helper.AddData(&data);
- MockWrite writes2[] = {
- MockWrite("GET / HTTP/1.1\r\n"
- "Host: example.test\r\n"
- "Connection: Upgrade\r\n"
- "Upgrade: websocket\r\n"
- "Origin: http://example.test\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Sec-WebSocket-Extensions: permessage-deflate; "
- "client_max_window_bits\r\n\r\n")};
- MockRead reads2[] = {
- MockRead("HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n")};
- StaticSocketDataProvider data2(reads2, writes2);
- auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Test that the request has HTTP/2 disabled.
- ssl_provider2->next_protos_expected_in_ssl_config = {kProtoHTTP11};
- // Force socket to use HTTP/1.1, the default protocol without ALPN.
- ssl_provider2->next_proto = kProtoHTTP11;
- ssl_provider2->ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
- helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
- TestCompletionCallback callback1;
- int rv = helper.trans()->Start(&request_, callback1.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL("wss://example.test/");
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- request2.extra_headers.SetHeader("Connection", "Upgrade");
- request2.extra_headers.SetHeader("Upgrade", "websocket");
- request2.extra_headers.SetHeader("Origin", "http://example.test");
- request2.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
- TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
- HttpNetworkTransaction trans2(MEDIUM, helper.session());
- trans2.SetWebSocketHandshakeStreamCreateHelper(
- &websocket_stream_create_helper);
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- // Make sure both requests are blocked on host resolution.
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(helper.session_deps()->host_resolver->has_pending_requests());
- // Complete the first DNS lookup, which should result in the first transaction
- // creating an H2 session (And completing successfully).
- helper.session_deps()->host_resolver->ResolveNow(1);
- // Complete first request.
- rv = callback1.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
- ASSERT_TRUE(response->headers);
- EXPECT_TRUE(response->was_fetched_via_spdy);
- EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
- std::string response_data;
- rv = ReadTransaction(helper.trans(), &response_data);
- EXPECT_THAT(rv, IsOk());
- EXPECT_EQ("hello!", response_data);
- SpdySessionKey key1(HostPortPair::FromURL(request_.url),
- ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session1 =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key1, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ false, log_);
- ASSERT_TRUE(spdy_session1);
- EXPECT_TRUE(spdy_session1->support_websocket());
- // Second DNS lookup completes, which results in creating an alias for the
- // SpdySession immediately, and a task is posted asynchronously to use the
- // alias..
- helper.session_deps()->host_resolver->ResolveNow(2);
- SpdySessionKey key2(HostPortPair::FromURL(request2.url),
- ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session2 =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key1, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ true, log_);
- ASSERT_TRUE(spdy_session2);
- EXPECT_EQ(spdy_session1.get(), spdy_session2.get());
- // But the session is closed before it can be used.
- helper.session()->spdy_session_pool()->CloseAllSessions();
- // The second request establishes another connection (without even doing
- // another DNS lookup) instead, and uses HTTP/1.x.
- rv = callback2.WaitForResult();
- ASSERT_THAT(rv, IsOk());
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, WebSocketNegotiatesHttp2) {
- HttpRequestInfo request;
- request.method = "GET";
- request.url = GURL("wss://www.example.org/");
- request.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- EXPECT_TRUE(HostPortPair::FromURL(request_.url)
- .Equals(HostPortPair::FromURL(request.url)));
- request.extra_headers.SetHeader("Connection", "Upgrade");
- request.extra_headers.SetHeader("Upgrade", "websocket");
- request.extra_headers.SetHeader("Origin", "http://www.example.org");
- request.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunPreTestSetup();
- StaticSocketDataProvider data;
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Test that the request has HTTP/2 disabled.
- ssl_provider->next_protos_expected_in_ssl_config = {kProtoHTTP11};
- // Force socket to use HTTP/2, which should never happen (TLS implementation
- // should fail TLS handshake if server chooses HTTP/2 without client
- // advertising support).
- ssl_provider->next_proto = kProtoHTTP2;
- ssl_provider->ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
- helper.AddDataWithSSLSocketDataProvider(&data, std::move(ssl_provider));
- HttpNetworkTransaction* trans = helper.trans();
- TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
- trans->SetWebSocketHandshakeStreamCreateHelper(
- &websocket_stream_create_helper);
- TestCompletionCallback callback;
- int rv = trans->Start(&request, callback.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- rv = callback.WaitForResult();
- ASSERT_THAT(rv, IsError(ERR_NOT_IMPLEMENTED));
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, WebSocketHttp11Required) {
- base::HistogramTester histogram_tester;
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- NormalSpdyTransactionHelper helper(request_, HIGHEST, log_,
- std::move(session_deps));
- helper.RunPreTestSetup();
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST));
- spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
- spdy::Http2HeaderBlock websocket_request_headers;
- websocket_request_headers[spdy::kHttp2MethodHeader] = "CONNECT";
- websocket_request_headers[spdy::kHttp2AuthorityHeader] = "www.example.org";
- websocket_request_headers[spdy::kHttp2SchemeHeader] = "https";
- websocket_request_headers[spdy::kHttp2PathHeader] = "/";
- websocket_request_headers[spdy::kHttp2ProtocolHeader] = "websocket";
- websocket_request_headers["origin"] = "http://www.example.org";
- websocket_request_headers["sec-websocket-version"] = "13";
- websocket_request_headers["sec-websocket-extensions"] =
- "permessage-deflate; client_max_window_bits";
- spdy::SpdySerializedFrame websocket_request(spdy_util_.ConstructSpdyHeaders(
- 3, std::move(websocket_request_headers), MEDIUM, false));
- spdy::SpdySerializedFrame priority1(
- spdy_util_.ConstructSpdyPriority(3, 0, MEDIUM, true));
- spdy::SpdySerializedFrame priority2(
- spdy_util_.ConstructSpdyPriority(1, 3, LOWEST, true));
- MockWrite writes1[] = {CreateMockWrite(req, 0),
- CreateMockWrite(settings_ack, 2),
- CreateMockWrite(websocket_request, 4)};
- spdy::SettingsMap settings;
- settings[spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1;
- spdy::SpdySerializedFrame settings_frame(
- spdy_util_.ConstructSpdySettings(settings));
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame websocket_response_http11_required(
- spdy_util_.ConstructSpdyRstStream(3, spdy::ERROR_CODE_HTTP_1_1_REQUIRED));
- MockRead reads1[] = {CreateMockRead(settings_frame, 1),
- CreateMockRead(resp1, 3),
- CreateMockRead(websocket_response_http11_required, 5)};
- SequencedSocketData data1(reads1, writes1);
- helper.AddData(&data1);
- MockWrite writes2[] = {
- MockWrite("GET / HTTP/1.1\r\n"
- "Host: www.example.org\r\n"
- "Connection: Upgrade\r\n"
- "Origin: http://www.example.org\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Upgrade: websocket\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Sec-WebSocket-Extensions: permessage-deflate; "
- "client_max_window_bits\r\n\r\n")};
- MockRead reads2[] = {
- MockRead("HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n")};
- StaticSocketDataProvider data2(reads2, writes2);
- auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Test that the request has HTTP/2 disabled.
- ssl_provider2->next_protos_expected_in_ssl_config = {kProtoHTTP11};
- // Force socket to use HTTP/1.1, the default protocol without ALPN.
- ssl_provider2->next_proto = kProtoHTTP11;
- ssl_provider2->ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
- helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
- // Create HTTP/2 connection.
- TestCompletionCallback callback1;
- int rv = helper.trans()->Start(&request_, callback1.callback(), log_);
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- // Create HTTP/2 connection.
- base::RunLoop().RunUntilIdle();
- SpdySessionKey key(HostPortPair::FromURL(request_.url), ProxyServer::Direct(),
- PRIVACY_MODE_DISABLED,
- SpdySessionKey::IsProxySession::kFalse, SocketTag(),
- NetworkIsolationKey(), SecureDnsPolicy::kAllow);
- base::WeakPtr<SpdySession> spdy_session =
- helper.session()->spdy_session_pool()->FindAvailableSession(
- key, /* enable_ip_based_pooling = */ true,
- /* is_websocket = */ true, log_);
- ASSERT_TRUE(spdy_session);
- EXPECT_TRUE(spdy_session->support_websocket());
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL("wss://www.example.org/");
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- EXPECT_TRUE(HostPortPair::FromURL(request_.url)
- .Equals(HostPortPair::FromURL(request2.url)));
- request2.extra_headers.SetHeader("Origin", "http://www.example.org");
- request2.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
- // The following two headers must be removed by WebSocketHttp2HandshakeStream.
- request2.extra_headers.SetHeader("Connection", "Upgrade");
- request2.extra_headers.SetHeader("Upgrade", "websocket");
- TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
- HttpNetworkTransaction trans2(MEDIUM, helper.session());
- trans2.SetWebSocketHandshakeStreamCreateHelper(
- &websocket_stream_create_helper);
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- EXPECT_THAT(callback2.GetResult(rv), IsOk());
- helper.VerifyDataConsumed();
- // Server advertised WebSocket support.
- histogram_tester.ExpectUniqueSample("Net.SpdySession.ServerSupportsWebSocket",
- /* support_websocket = true */ 1,
- /* expected_count = */ 1);
- }
- // When using an HTTP(S) proxy, plaintext WebSockets use CONNECT tunnels. This
- // should work for HTTP/2 proxies.
- TEST_F(SpdyNetworkTransactionTest, PlaintextWebSocketOverHttp2Proxy) {
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect(
- nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
- HostPortPair("www.example.org", 80)));
- const char kWebSocketRequest[] =
- "GET / HTTP/1.1\r\n"
- "Host: www.example.org\r\n"
- "Connection: Upgrade\r\n"
- "Upgrade: websocket\r\n"
- "Origin: http://www.example.org\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Sec-WebSocket-Extensions: permessage-deflate; "
- "client_max_window_bits\r\n\r\n";
- spdy::SpdySerializedFrame websocket_request(spdy_util_.ConstructSpdyDataFrame(
- /*stream_id=*/1, kWebSocketRequest, /*fin=*/false));
- MockWrite writes[] = {CreateMockWrite(req, 0),
- CreateMockWrite(websocket_request, 2)};
- spdy::SpdySerializedFrame connect_response(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- const char kWebSocketResponse[] =
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n";
- spdy::SpdySerializedFrame websocket_response(
- spdy_util_.ConstructSpdyDataFrame(/*stream_id=*/1, kWebSocketResponse,
- /*fin=*/false));
- MockRead reads[] = {CreateMockRead(connect_response, 1),
- CreateMockRead(websocket_response, 3),
- MockRead(ASYNC, 0, 4)};
- SequencedSocketData data(reads, writes);
- request_.url = GURL("ws://www.example.org/");
- request_.extra_headers.SetHeader("Connection", "Upgrade");
- request_.extra_headers.SetHeader("Upgrade", "websocket");
- request_.extra_headers.SetHeader("Origin", "http://www.example.org");
- request_.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
- auto session_deps = std::make_unique<SpdySessionDependencies>(
- ConfiguredProxyResolutionService::CreateFixedForTest(
- "https://proxy:70", TRAFFIC_ANNOTATION_FOR_TESTS));
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- helper.RunPreTestSetup();
- helper.AddData(&data);
- HttpNetworkTransaction* trans = helper.trans();
- TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
- trans->SetWebSocketHandshakeStreamCreateHelper(
- &websocket_stream_create_helper);
- EXPECT_TRUE(helper.StartDefaultTest());
- helper.WaitForCallbackToComplete();
- EXPECT_THAT(helper.output().rv, IsOk());
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, SecureWebSocketOverHttp2Proxy) {
- spdy::SpdySerializedFrame connect_request(spdy_util_.ConstructSpdyConnect(
- nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
- HostPortPair("www.example.org", 443)));
- const char kWebSocketRequest[] =
- "GET / HTTP/1.1\r\n"
- "Host: www.example.org\r\n"
- "Connection: Upgrade\r\n"
- "Upgrade: websocket\r\n"
- "Origin: http://www.example.org\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
- "Sec-WebSocket-Extensions: permessage-deflate; "
- "client_max_window_bits\r\n\r\n";
- spdy::SpdySerializedFrame websocket_request(
- spdy_util_.ConstructSpdyDataFrame(1, kWebSocketRequest, false));
- MockWrite writes[] = {CreateMockWrite(connect_request, 0),
- CreateMockWrite(websocket_request, 2)};
- spdy::SpdySerializedFrame connect_response(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- const char kWebSocketResponse[] =
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n";
- spdy::SpdySerializedFrame websocket_response(
- spdy_util_.ConstructSpdyDataFrame(1, kWebSocketResponse, false));
- MockRead reads[] = {CreateMockRead(connect_response, 1),
- CreateMockRead(websocket_response, 3),
- MockRead(ASYNC, 0, 4)};
- SequencedSocketData data(reads, writes);
- request_.url = GURL("wss://www.example.org/");
- request_.extra_headers.SetHeader("Connection", "Upgrade");
- request_.extra_headers.SetHeader("Upgrade", "websocket");
- request_.extra_headers.SetHeader("Origin", "http://www.example.org");
- request_.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
- auto session_deps = std::make_unique<SpdySessionDependencies>(
- ConfiguredProxyResolutionService::CreateFixedForTest(
- "https://proxy:70", TRAFFIC_ANNOTATION_FOR_TESTS));
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- helper.RunPreTestSetup();
- helper.AddData(&data);
- // Add SSL data for the tunneled connection.
- SSLSocketDataProvider ssl_provider(ASYNC, OK);
- ssl_provider.ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem");
- // A WebSocket request should not advertise HTTP/2 support.
- ssl_provider.next_protos_expected_in_ssl_config = {kProtoHTTP11};
- // This test uses WebSocket over HTTP/1.1.
- ssl_provider.next_proto = kProtoHTTP11;
- helper.session_deps()->socket_factory->AddSSLSocketDataProvider(
- &ssl_provider);
- HttpNetworkTransaction* trans = helper.trans();
- TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
- trans->SetWebSocketHandshakeStreamCreateHelper(
- &websocket_stream_create_helper);
- EXPECT_TRUE(helper.StartDefaultTest());
- helper.WaitForCallbackToComplete();
- EXPECT_THAT(helper.output().rv, IsOk());
- const HttpResponseInfo* response = trans->GetResponseInfo();
- ASSERT_TRUE(response);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1_1,
- response->connection_info);
- EXPECT_TRUE(response->was_alpn_negotiated);
- EXPECT_FALSE(response->was_fetched_via_spdy);
- EXPECT_EQ(70, response->remote_endpoint.port());
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 101 Switching Protocols",
- response->headers->GetStatusLine());
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- // Regression test for https://crbug.com/828865.
- TEST_F(SpdyNetworkTransactionTest,
- SecureWebSocketOverHttp2ProxyNegotiatesHttp2) {
- spdy::SpdySerializedFrame connect_request(spdy_util_.ConstructSpdyConnect(
- nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
- HostPortPair("www.example.org", 443)));
- MockWrite writes[] = {CreateMockWrite(connect_request, 0)};
- spdy::SpdySerializedFrame connect_response(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- MockRead reads[] = {CreateMockRead(connect_response, 1),
- MockRead(ASYNC, 0, 2)};
- SequencedSocketData data(reads, writes);
- request_.url = GURL("wss://www.example.org/");
- request_.extra_headers.SetHeader("Connection", "Upgrade");
- request_.extra_headers.SetHeader("Upgrade", "websocket");
- request_.extra_headers.SetHeader("Origin", "http://www.example.org");
- request_.extra_headers.SetHeader("Sec-WebSocket-Version", "13");
- auto session_deps = std::make_unique<SpdySessionDependencies>(
- ConfiguredProxyResolutionService::CreateFixedForTest(
- "https://proxy:70", TRAFFIC_ANNOTATION_FOR_TESTS));
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- helper.RunPreTestSetup();
- helper.AddData(&data);
- // Add SSL data for the tunneled connection.
- SSLSocketDataProvider ssl_provider(ASYNC, OK);
- ssl_provider.ssl_info.cert =
- ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem");
- // A WebSocket request should not advertise HTTP/2 support.
- ssl_provider.next_protos_expected_in_ssl_config = {kProtoHTTP11};
- // The server should not negotiate HTTP/2 over the tunnelled connection,
- // but it must be handled gracefully if it does.
- ssl_provider.next_proto = kProtoHTTP2;
- helper.session_deps()->socket_factory->AddSSLSocketDataProvider(
- &ssl_provider);
- HttpNetworkTransaction* trans = helper.trans();
- TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper;
- trans->SetWebSocketHandshakeStreamCreateHelper(
- &websocket_stream_create_helper);
- EXPECT_TRUE(helper.StartDefaultTest());
- helper.WaitForCallbackToComplete();
- EXPECT_THAT(helper.output().rv, IsError(ERR_NOT_IMPLEMENTED));
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- #endif // BUILDFLAG(ENABLE_WEBSOCKETS)
- TEST_F(SpdyNetworkTransactionTest, ZeroRTTDoesntConfirm) {
- static const base::TimeDelta kDelay = base::Milliseconds(10);
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {
- CreateMockRead(resp, 1), CreateMockRead(body, 2),
- MockRead(ASYNC, 0, 3) // EOF
- };
- SequencedSocketData data(reads, writes);
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->enable_early_data = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider->connect_callback = FastForwardByCallback(kDelay);
- // Configure |ssl_provider| to fail if ConfirmHandshake is called. The request
- // should still succeed.
- ssl_provider->confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
- ssl_provider->confirm_callback = FastForwardByCallback(kDelay);
- base::TimeTicks start_time = base::TimeTicks::Now();
- helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- // The handshake time should include the time it took to run Connect(), but
- // not ConfirmHandshake().
- LoadTimingInfo load_timing_info;
- EXPECT_TRUE(helper.trans()->GetLoadTimingInfo(&load_timing_info));
- EXPECT_EQ(load_timing_info.connect_timing.connect_start, start_time);
- EXPECT_EQ(load_timing_info.connect_timing.ssl_start, start_time);
- EXPECT_EQ(load_timing_info.connect_timing.ssl_end, start_time + kDelay);
- EXPECT_EQ(load_timing_info.connect_timing.connect_end, start_time + kDelay);
- }
- // Run multiple concurrent streams that don't require handshake confirmation.
- TEST_F(SpdyNetworkTransactionTest, ZeroRTTNoConfirmMultipleStreams) {
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST));
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST));
- MockWrite writes1[] = {CreateMockWrite(req1, 0), CreateMockWrite(req2, 3)};
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead reads1[] = {
- CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
- CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
- MockRead(ASYNC, 0, 6) // EOF
- };
- SequencedSocketData data1(reads1, writes1);
- SequencedSocketData data2({}, {});
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->enable_early_data = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider1->confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
- auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider2->confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
- helper.RunPreTestSetup();
- helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
- helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
- EXPECT_TRUE(helper.StartDefaultTest());
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL(kDefaultUrl);
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback2;
- int rv = trans2.Start(&request2, callback2.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- helper.FinishDefaultTest();
- EXPECT_THAT(callback2.GetResult(ERR_IO_PENDING), IsOk());
- helper.VerifyDataConsumed();
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- // Run multiple concurrent streams that require handshake confirmation.
- TEST_F(SpdyNetworkTransactionTest, ZeroRTTConfirmMultipleStreams) {
- spdy::Http2HeaderBlock req_block1(
- spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyHeaders(1, std::move(req_block1), LOWEST, true));
- spdy::Http2HeaderBlock req_block2(
- spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyHeaders(3, std::move(req_block2), LOWEST, true));
- MockWrite writes[] = {
- CreateMockWrite(req1, 0),
- CreateMockWrite(req2, 3),
- };
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead reads[] = {
- CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
- CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
- MockRead(ASYNC, 0, 6) // EOF
- };
- SequencedSocketData data1(reads, writes);
- SequencedSocketData data2({}, {});
- UsePostRequest();
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->enable_early_data = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider1->confirm = MockConfirm(ASYNC, OK);
- auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider2->confirm = MockConfirm(ASYNC, OK);
- helper.RunPreTestSetup();
- helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
- helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
- HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request1;
- request1.method = "POST";
- request1.url = GURL(kDefaultUrl);
- request1.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback1;
- int rv = trans1.Start(&request1, callback1.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request2;
- request2.method = "POST";
- request2.url = GURL(kDefaultUrl);
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback1.GetResult(ERR_IO_PENDING), IsOk());
- EXPECT_THAT(callback2.GetResult(ERR_IO_PENDING), IsOk());
- const HttpResponseInfo* response1 = trans1.GetResponseInfo();
- ASSERT_TRUE(response1);
- ASSERT_TRUE(response1->headers);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
- response1->connection_info);
- EXPECT_EQ("HTTP/1.1 200", response1->headers->GetStatusLine());
- std::string response_data;
- ReadTransaction(&trans1, &response_data);
- EXPECT_EQ("hello!", response_data);
- const HttpResponseInfo* response2 = trans2.GetResponseInfo();
- ASSERT_TRUE(response2);
- ASSERT_TRUE(response2->headers);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
- response2->connection_info);
- EXPECT_EQ("HTTP/1.1 200", response2->headers->GetStatusLine());
- ReadTransaction(&trans2, &response_data);
- EXPECT_EQ("hello!", response_data);
- helper.VerifyDataConsumed();
- }
- // Run multiple concurrent streams, the first require a confirmation and the
- // second not requiring confirmation.
- TEST_F(SpdyNetworkTransactionTest, ZeroRTTConfirmNoConfirmStreams) {
- // This test orders the writes such that the GET (no confirmation) is written
- // before the POST (confirmation required).
- spdy::Http2HeaderBlock req_block1(
- spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyHeaders(1, std::move(req_block1), LOWEST, true));
- spdy::Http2HeaderBlock req_block2(
- spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyHeaders(3, std::move(req_block2), LOWEST, true));
- MockWrite writes[] = {
- CreateMockWrite(req1, 0),
- CreateMockWrite(req2, 3),
- };
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead reads[] = {
- CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
- CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
- MockRead(ASYNC, 0, 6) // EOF
- };
- SequencedSocketData data1(reads, writes);
- SequencedSocketData data2({}, {});
- UsePostRequest();
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->enable_early_data = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider1->confirm = MockConfirm(ASYNC, OK);
- auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider2->confirm = MockConfirm(ASYNC, OK);
- helper.RunPreTestSetup();
- helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
- helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
- // TODO(https://crbug.com/949724): Explicitly verify the ordering of
- // ConfirmHandshake and the second stream.
- HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request1;
- request1.method = "POST";
- request1.url = GURL(kDefaultUrl);
- request1.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback1;
- int rv = trans1.Start(&request1, callback1.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request2;
- request2.method = "GET";
- request2.url = GURL(kDefaultUrl);
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback1.GetResult(ERR_IO_PENDING), IsOk());
- EXPECT_THAT(callback2.GetResult(ERR_IO_PENDING), IsOk());
- const HttpResponseInfo* response1 = trans1.GetResponseInfo();
- ASSERT_TRUE(response1);
- ASSERT_TRUE(response1->headers);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
- response1->connection_info);
- EXPECT_EQ("HTTP/1.1 200", response1->headers->GetStatusLine());
- std::string response_data;
- ReadTransaction(&trans1, &response_data);
- EXPECT_EQ("hello!", response_data);
- const HttpResponseInfo* response2 = trans2.GetResponseInfo();
- ASSERT_TRUE(response2);
- ASSERT_TRUE(response2->headers);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
- response2->connection_info);
- EXPECT_EQ("HTTP/1.1 200", response2->headers->GetStatusLine());
- ReadTransaction(&trans2, &response_data);
- EXPECT_EQ("hello!", response_data);
- helper.VerifyDataConsumed();
- }
- // Run multiple concurrent streams, the first not requiring confirmation and the
- // second requiring confirmation.
- TEST_F(SpdyNetworkTransactionTest, ZeroRTTNoConfirmConfirmStreams) {
- // This test orders the writes such that the GET (no confirmation) is written
- // before the POST (confirmation required).
- spdy::Http2HeaderBlock req_block1(
- spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
- spdy::SpdySerializedFrame req1(
- spdy_util_.ConstructSpdyHeaders(1, std::move(req_block1), LOWEST, true));
- spdy::Http2HeaderBlock req_block2(
- spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
- spdy::SpdySerializedFrame req2(
- spdy_util_.ConstructSpdyHeaders(3, std::move(req_block2), LOWEST, true));
- MockWrite writes[] = {
- CreateMockWrite(req1, 0),
- CreateMockWrite(req2, 3),
- };
- spdy::SpdySerializedFrame resp1(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
- spdy::SpdySerializedFrame resp2(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
- spdy::SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
- MockRead reads[] = {
- CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
- CreateMockRead(resp2, 4), CreateMockRead(body2, 5),
- MockRead(ASYNC, 0, 6) // EOF
- };
- SequencedSocketData data1(reads, writes);
- SequencedSocketData data2({}, {});
- UsePostRequest();
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->enable_early_data = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- auto ssl_provider1 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider1->confirm = MockConfirm(ASYNC, OK);
- auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider2->confirm = MockConfirm(ASYNC, OK);
- helper.RunPreTestSetup();
- helper.AddDataWithSSLSocketDataProvider(&data1, std::move(ssl_provider1));
- helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2));
- // TODO(https://crbug.com/949724): Explicitly verify the ordering of
- // ConfirmHandshake and the second stream.
- HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request1;
- request1.method = "GET";
- request1.url = GURL(kDefaultUrl);
- request1.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback1;
- int rv = trans1.Start(&request1, callback1.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
- HttpRequestInfo request2;
- request2.method = "POST";
- request2.url = GURL(kDefaultUrl);
- request2.traffic_annotation =
- net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
- TestCompletionCallback callback2;
- rv = trans2.Start(&request2, callback2.callback(), log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback1.GetResult(ERR_IO_PENDING), IsOk());
- EXPECT_THAT(callback2.GetResult(ERR_IO_PENDING), IsOk());
- const HttpResponseInfo* response1 = trans1.GetResponseInfo();
- ASSERT_TRUE(response1);
- ASSERT_TRUE(response1->headers);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
- response1->connection_info);
- EXPECT_EQ("HTTP/1.1 200", response1->headers->GetStatusLine());
- std::string response_data;
- ReadTransaction(&trans1, &response_data);
- EXPECT_EQ("hello!", response_data);
- const HttpResponseInfo* response2 = trans2.GetResponseInfo();
- ASSERT_TRUE(response2);
- ASSERT_TRUE(response2->headers);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP2,
- response2->connection_info);
- EXPECT_EQ("HTTP/1.1 200", response2->headers->GetStatusLine());
- ReadTransaction(&trans2, &response_data);
- EXPECT_EQ("hello!", response_data);
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, ZeroRTTSyncConfirmSyncWrite) {
- static const base::TimeDelta kDelay = base::Milliseconds(10);
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0, SYNCHRONOUS),
- CreateMockWrite(body, 1), // POST upload frame
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 2), CreateMockRead(body, 3),
- MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- UsePostRequest();
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->enable_early_data = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider->connect_callback = FastForwardByCallback(kDelay);
- ssl_provider->confirm = MockConfirm(SYNCHRONOUS, OK);
- ssl_provider->confirm_callback = FastForwardByCallback(kDelay);
- base::TimeTicks start_time = base::TimeTicks::Now();
- helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- // The handshake time should include the time it took to run Connect(), but
- // not ConfirmHandshake(). If ConfirmHandshake() returns synchronously, we
- // assume the connection did not negotiate 0-RTT or the handshake was already
- // confirmed.
- LoadTimingInfo load_timing_info;
- EXPECT_TRUE(helper.trans()->GetLoadTimingInfo(&load_timing_info));
- EXPECT_EQ(load_timing_info.connect_timing.connect_start, start_time);
- EXPECT_EQ(load_timing_info.connect_timing.ssl_start, start_time);
- EXPECT_EQ(load_timing_info.connect_timing.ssl_end, start_time + kDelay);
- EXPECT_EQ(load_timing_info.connect_timing.connect_end, start_time + kDelay);
- }
- TEST_F(SpdyNetworkTransactionTest, ZeroRTTSyncConfirmAsyncWrite) {
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0, ASYNC),
- CreateMockWrite(body, 1), // POST upload frame
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 2), CreateMockRead(body, 3),
- MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- UsePostRequest();
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->enable_early_data = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider->confirm = MockConfirm(SYNCHRONOUS, OK);
- helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- TEST_F(SpdyNetworkTransactionTest, ZeroRTTAsyncConfirmSyncWrite) {
- static const base::TimeDelta kDelay = base::Milliseconds(10);
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0, SYNCHRONOUS),
- CreateMockWrite(body, 1), // POST upload frame
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 2), CreateMockRead(body, 3),
- MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- UsePostRequest();
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->enable_early_data = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider->connect_callback = FastForwardByCallback(kDelay);
- ssl_provider->confirm = MockConfirm(ASYNC, OK);
- ssl_provider->confirm_callback = FastForwardByCallback(kDelay);
- base::TimeTicks start_time = base::TimeTicks::Now();
- helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- // The handshake time should include the time it took to run Connect() and
- // ConfirmHandshake().
- LoadTimingInfo load_timing_info;
- EXPECT_TRUE(helper.trans()->GetLoadTimingInfo(&load_timing_info));
- EXPECT_EQ(load_timing_info.connect_timing.connect_start, start_time);
- EXPECT_EQ(load_timing_info.connect_timing.ssl_start, start_time);
- EXPECT_EQ(load_timing_info.connect_timing.ssl_end, start_time + 2 * kDelay);
- EXPECT_EQ(load_timing_info.connect_timing.connect_end,
- start_time + 2 * kDelay);
- }
- TEST_F(SpdyNetworkTransactionTest, ZeroRTTAsyncConfirmAsyncWrite) {
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0, ASYNC),
- CreateMockWrite(body, 1), // POST upload frame
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 2), CreateMockRead(body, 3),
- MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- UsePostRequest();
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->enable_early_data = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider->confirm = MockConfirm(ASYNC, OK);
- helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- TEST_F(SpdyNetworkTransactionTest, ZeroRTTConfirmErrorSync) {
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 2), CreateMockRead(body, 3),
- MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- UsePostRequest();
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->enable_early_data = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider->confirm = MockConfirm(SYNCHRONOUS, ERR_SSL_PROTOCOL_ERROR);
- helper.RunPreTestSetup();
- helper.AddDataWithSSLSocketDataProvider(&data, std::move(ssl_provider));
- helper.RunDefaultTest();
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_SSL_PROTOCOL_ERROR));
- }
- TEST_F(SpdyNetworkTransactionTest, ZeroRTTConfirmErrorAsync) {
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 2), CreateMockRead(body, 3),
- MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- UsePostRequest();
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->enable_early_data = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider->confirm = MockConfirm(ASYNC, ERR_SSL_PROTOCOL_ERROR);
- helper.RunPreTestSetup();
- helper.AddDataWithSSLSocketDataProvider(&data, std::move(ssl_provider));
- helper.RunDefaultTest();
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_SSL_PROTOCOL_ERROR));
- }
- TEST_F(SpdyNetworkTransactionTest, GreaseSettings) {
- RecordingNetLogObserver net_log_observer;
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- session_deps->enable_http2_settings_grease = true;
- NormalSpdyTransactionHelper helper(
- request_, DEFAULT_PRIORITY,
- NetLogWithSource::Make(NetLogSourceType::NONE), std::move(session_deps));
- SpdySessionPool* spdy_session_pool = helper.session()->spdy_session_pool();
- SpdySessionPoolPeer pool_peer(spdy_session_pool);
- pool_peer.SetEnableSendingInitialData(true);
- // Greased setting parameter is random. Hang writes instead of trying to
- // construct matching mock data. Extra write and read is needed because mock
- // data cannot end on ERR_IO_PENDING. Writes or reads will not actually be
- // resumed.
- MockWrite writes[] = {MockWrite(ASYNC, ERR_IO_PENDING, 0),
- MockWrite(ASYNC, OK, 1)};
- MockRead reads[] = {MockRead(ASYNC, ERR_IO_PENDING, 2),
- MockRead(ASYNC, OK, 3)};
- SequencedSocketData data(reads, writes);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- int rv = helper.trans()->Start(&request_, CompletionOnceCallback{}, log_);
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- base::RunLoop().RunUntilIdle();
- helper.ResetTrans();
- EXPECT_FALSE(data.AllReadDataConsumed());
- EXPECT_FALSE(data.AllWriteDataConsumed());
- const auto entries = net_log_observer.GetEntries();
- size_t pos = ExpectLogContainsSomewhere(
- entries, 0, NetLogEventType::HTTP2_SESSION_SEND_SETTINGS,
- NetLogEventPhase::NONE);
- ASSERT_LT(pos, entries.size());
- const base::Value& params = entries[pos].params;
- const base::Value::List* const settings =
- params.GetDict().FindList("settings");
- ASSERT_TRUE(settings);
- ASSERT_FALSE(settings->empty());
- // Get last setting parameter.
- const base::Value& greased_setting = (*settings)[settings->size() - 1];
- ASSERT_TRUE(greased_setting.is_string());
- base::StringPiece greased_setting_string(greased_setting.GetString());
- const std::string kExpectedPrefix = "[id:";
- EXPECT_EQ(kExpectedPrefix,
- greased_setting_string.substr(0, kExpectedPrefix.size()));
- int setting_identifier = 0;
- base::StringToInt(greased_setting_string.substr(kExpectedPrefix.size()),
- &setting_identifier);
- // The setting identifier must be of format 0x?a?a.
- EXPECT_EQ(0xa, setting_identifier % 16);
- EXPECT_EQ(0xa, (setting_identifier / 256) % 16);
- }
- // If |http2_end_stream_with_data_frame| is false, then the HEADERS frame of a
- // GET request will close the stream using the END_STREAM flag. Test that
- // |greased_http2_frame| is ignored and no reserved frames are sent on a closed
- // stream.
- TEST_F(SpdyNetworkTransactionTest,
- DoNotGreaseFrameTypeWithGetRequestIfHeadersFrameClosesStream) {
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- const uint8_t type = 0x0b;
- const uint8_t flags = 0xcc;
- const std::string payload("foo");
- session_deps->greased_http2_frame =
- absl::optional<net::SpdySessionPool::GreasedHttp2Frame>(
- {type, flags, payload});
- session_deps->http2_end_stream_with_data_frame = false;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyGet(nullptr, 0, 1, DEFAULT_PRIORITY));
- MockWrite writes[] = {CreateMockWrite(req, 0)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame response_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(response_body, 2),
- MockRead(ASYNC, 0, 3)};
- SequencedSocketData data(reads, writes);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- TestCompletionCallback callback;
- int rv = helper.trans()->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(callback.GetResult(rv), IsOk());
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- // Test that if |http2_end_stream_with_data_frame| and |greased_http2_frame| are
- // both set, then the HEADERS frame does not have the END_STREAM flag set, it is
- // followed by a greased frame, and then by an empty DATA frame with END_STREAM
- // set.
- TEST_F(SpdyNetworkTransactionTest, GreaseFrameTypeWithGetRequest) {
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- const uint8_t type = 0x0b;
- const uint8_t flags = 0xcc;
- const std::string payload("foo");
- session_deps->greased_http2_frame =
- absl::optional<net::SpdySessionPool::GreasedHttp2Frame>(
- {type, flags, payload});
- session_deps->http2_end_stream_with_data_frame = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- spdy::Http2HeaderBlock headers(
- spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyHeaders(1, std::move(headers), DEFAULT_PRIORITY,
- /* fin = */ false));
- uint8_t kRawFrameData[] = {
- 0x00, 0x00, 0x03, // length
- 0x0b, // type
- 0xcc, // flags
- 0x00, 0x00, 0x00, 0x01, // stream ID
- 'f', 'o', 'o' // payload
- };
- spdy::SpdySerializedFrame grease(reinterpret_cast<char*>(kRawFrameData),
- std::size(kRawFrameData),
- /* owns_buffer = */ false);
- spdy::SpdySerializedFrame empty_body(
- spdy_util_.ConstructSpdyDataFrame(1, "", true));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(grease, 1),
- CreateMockWrite(empty_body, 2)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame response_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(resp, 3), CreateMockRead(response_body, 4),
- MockRead(ASYNC, 0, 5)};
- SequencedSocketData data(reads, writes);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- TestCompletionCallback callback;
- int rv = helper.trans()->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(callback.GetResult(rv), IsOk());
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- // Test sending a greased frame before DATA frame that closes the stream when
- // |http2_end_stream_with_data_frame| is false.
- TEST_F(SpdyNetworkTransactionTest,
- GreaseFrameTypeWithPostRequestWhenHeadersFrameClosesStream) {
- UsePostRequest();
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- const uint8_t type = 0x0b;
- const uint8_t flags = 0xcc;
- const std::string payload("foo");
- session_deps->greased_http2_frame =
- absl::optional<net::SpdySessionPool::GreasedHttp2Frame>(
- {type, flags, payload});
- session_deps->http2_end_stream_with_data_frame = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
- uint8_t kRawFrameData[] = {
- 0x00, 0x00, 0x03, // length
- 0x0b, // type
- 0xcc, // flags
- 0x00, 0x00, 0x00, 0x01, // stream ID
- 'f', 'o', 'o' // payload
- };
- spdy::SpdySerializedFrame grease(reinterpret_cast<char*>(kRawFrameData),
- std::size(kRawFrameData),
- /* owns_buffer = */ false);
- spdy::SpdySerializedFrame request_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(grease, 1),
- CreateMockWrite(request_body, 2)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame response_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(resp, 3), CreateMockRead(response_body, 4),
- MockRead(ASYNC, 0, 5)};
- SequencedSocketData data(reads, writes);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- TestCompletionCallback callback;
- int rv = helper.trans()->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(callback.GetResult(rv), IsOk());
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- // Test sending a greased frame before DATA frame that closes the stream.
- // |http2_end_stream_with_data_frame| is true but should make no difference,
- // because the stream is already closed by a DATA frame.
- TEST_F(SpdyNetworkTransactionTest,
- GreaseFrameTypeWithPostRequestWhenEmptyDataFrameClosesStream) {
- UsePostRequest();
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- const uint8_t type = 0x0b;
- const uint8_t flags = 0xcc;
- const std::string payload("foo");
- session_deps->greased_http2_frame =
- absl::optional<net::SpdySessionPool::GreasedHttp2Frame>(
- {type, flags, payload});
- session_deps->http2_end_stream_with_data_frame = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
- kDefaultUrl, 1, kUploadDataSize, LOWEST, nullptr, 0));
- uint8_t kRawFrameData[] = {
- 0x00, 0x00, 0x03, // length
- 0x0b, // type
- 0xcc, // flags
- 0x00, 0x00, 0x00, 0x01, // stream ID
- 'f', 'o', 'o' // payload
- };
- spdy::SpdySerializedFrame grease(reinterpret_cast<char*>(kRawFrameData),
- std::size(kRawFrameData),
- /* owns_buffer = */ false);
- spdy::SpdySerializedFrame request_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(grease, 1),
- CreateMockWrite(request_body, 2)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame response_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(resp, 3), CreateMockRead(response_body, 4),
- MockRead(ASYNC, 0, 5)};
- SequencedSocketData data(reads, writes);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- TestCompletionCallback callback;
- int rv = helper.trans()->Start(&request_, callback.callback(), log_);
- EXPECT_THAT(callback.GetResult(rv), IsOk());
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- // According to https://httpwg.org/specs/rfc7540.html#CONNECT, "frame types
- // other than DATA or stream management frames (RST_STREAM, WINDOW_UPDATE, and
- // PRIORITY) MUST NOT be sent on a connected stream".
- // Also test that |http2_end_stream_with_data_frame| has no effect on proxy
- // streams.
- TEST_F(SpdyNetworkTransactionTest, DoNotGreaseFrameTypeWithConnect) {
- auto session_deps = std::make_unique<SpdySessionDependencies>(
- ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
- "HTTPS myproxy:70", TRAFFIC_ANNOTATION_FOR_TESTS));
- const uint8_t type = 0x0b;
- const uint8_t flags = 0xcc;
- const std::string payload("foo");
- session_deps->greased_http2_frame =
- absl::optional<net::SpdySessionPool::GreasedHttp2Frame>(
- {type, flags, payload});
- session_deps->http2_end_stream_with_data_frame = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- // CONNECT to proxy.
- spdy::SpdySerializedFrame connect_req(spdy_util_.ConstructSpdyConnect(
- nullptr, 0, 1, HttpProxyConnectJob::kH2QuicTunnelPriority,
- HostPortPair("www.example.org", 443)));
- spdy::SpdySerializedFrame connect_response(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- // Tunneled transaction wrapped in DATA frames.
- const char req[] =
- "GET / HTTP/1.1\r\n"
- "Host: www.example.org\r\n"
- "Connection: keep-alive\r\n\r\n";
- spdy::SpdySerializedFrame tunneled_req(
- spdy_util_.ConstructSpdyDataFrame(1, req, false));
- const char resp[] =
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 5\r\n\r\n"
- "hello";
- spdy::SpdySerializedFrame tunneled_response(
- spdy_util_.ConstructSpdyDataFrame(1, resp, false));
- MockWrite writes[] = {CreateMockWrite(connect_req, 0),
- CreateMockWrite(tunneled_req, 2)};
- MockRead reads[] = {CreateMockRead(connect_response, 1),
- CreateMockRead(tunneled_response, 3),
- MockRead(ASYNC, 0, 4)};
- SequencedSocketData data0(reads, writes);
- // HTTP/2 connection to proxy.
- auto ssl_provider0 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- ssl_provider0->next_proto = kProtoHTTP2;
- helper.AddDataWithSSLSocketDataProvider(&data0, std::move(ssl_provider0));
- // HTTP/1.1 to destination.
- SSLSocketDataProvider ssl_provider1(ASYNC, OK);
- ssl_provider1.next_proto = kProtoHTTP11;
- helper.session_deps()->socket_factory->AddSSLSocketDataProvider(
- &ssl_provider1);
- helper.RunPreTestSetup();
- helper.StartDefaultTest();
- helper.FinishDefaultTestWithoutVerification();
- helper.VerifyDataConsumed();
- const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
- ASSERT_TRUE(response);
- ASSERT_TRUE(response->headers);
- EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
- EXPECT_FALSE(response->was_fetched_via_spdy);
- EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1_1,
- response->connection_info);
- EXPECT_TRUE(response->was_alpn_negotiated);
- EXPECT_TRUE(request_.url.SchemeIs("https"));
- EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort());
- EXPECT_EQ(70, response->remote_endpoint.port());
- std::string response_data;
- ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk());
- EXPECT_EQ("hello", response_data);
- }
- // Regression test for https://crbug.com/1081955.
- // Greasing frame types is enabled, the outgoing HEADERS frame is followed by a
- // frame of reserved type, then an empty DATA frame to close the stream.
- // Response arrives before reserved frame and DATA frame can be sent.
- // SpdyHttpStream::OnDataSent() must not crash.
- TEST_F(SpdyNetworkTransactionTest, OnDataSentDoesNotCrashWithGreasedFrameType) {
- auto session_deps = std::make_unique<SpdySessionDependencies>();
- const uint8_t type = 0x0b;
- const uint8_t flags = 0xcc;
- const std::string payload("foo");
- session_deps->greased_http2_frame =
- absl::optional<net::SpdySessionPool::GreasedHttp2Frame>(
- {type, flags, payload});
- session_deps->http2_end_stream_with_data_frame = true;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_,
- std::move(session_deps));
- spdy::Http2HeaderBlock headers(
- spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructSpdyHeaders(1, std::move(headers), DEFAULT_PRIORITY,
- /* fin = */ false));
- uint8_t kRawFrameData[] = {
- 0x00, 0x00, 0x03, // length
- 0x0b, // type
- 0xcc, // flags
- 0x00, 0x00, 0x00, 0x01, // stream ID
- 'f', 'o', 'o' // payload
- };
- spdy::SpdySerializedFrame grease(reinterpret_cast<char*>(kRawFrameData),
- std::size(kRawFrameData),
- /* owns_buffer = */ false);
- spdy::SpdySerializedFrame empty_body(
- spdy_util_.ConstructSpdyDataFrame(1, "", true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), MockWrite(ASYNC, ERR_IO_PENDING, 2),
- CreateMockWrite(grease, 3), CreateMockWrite(empty_body, 4)};
- spdy::SpdySerializedFrame resp(
- spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
- spdy::SpdySerializedFrame response_body(
- spdy_util_.ConstructSpdyDataFrame(1, true));
- MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(response_body, 5),
- MockRead(ASYNC, 0, 6)};
- SequencedSocketData data(reads, writes);
- helper.RunPreTestSetup();
- helper.AddData(&data);
- TestCompletionCallback callback;
- int rv = helper.trans()->Start(&request_, callback.callback(), log_);
- base::RunLoop().RunUntilIdle();
- // Response headers received. Resume sending |grease| and |empty_body|.
- data.Resume();
- EXPECT_THAT(callback.GetResult(rv), IsOk());
- base::RunLoop().RunUntilIdle();
- helper.VerifyDataConsumed();
- }
- TEST_F(SpdyNetworkTransactionTest, NotAllowHTTP1NotBlockH2Post) {
- spdy::SpdySerializedFrame req(
- spdy_util_.ConstructChunkedSpdyPost(nullptr, 0));
- spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
- MockWrite writes[] = {
- CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame
- };
- spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
- MockRead reads[] = {
- CreateMockRead(resp, 2), CreateMockRead(body, 3),
- MockRead(ASYNC, 0, 4) // EOF
- };
- SequencedSocketData data(reads, writes);
- request_.method = "POST";
- UploadDataStreamNotAllowHTTP1 upload_data(kUploadData);
- request_.upload_data_stream = &upload_data;
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletion(&data);
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsOk());
- EXPECT_EQ("HTTP/1.1 200", out.status_line);
- EXPECT_EQ("hello!", out.response_data);
- }
- TEST_F(SpdyNetworkTransactionTest, AlpsFramingError) {
- base::HistogramTester histogram_tester;
- spdy::SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(
- 0, spdy::ERROR_CODE_PROTOCOL_ERROR, "Error parsing ALPS: 3"));
- MockWrite writes[] = {CreateMockWrite(goaway, 0)};
- SequencedSocketData data(base::span<MockRead>(), writes);
- auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
- // Not a complete HTTP/2 frame.
- ssl_provider->peer_application_settings = "boo";
- NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
- helper.RunToCompletionWithSSLData(&data, std::move(ssl_provider));
- TransactionHelperResult out = helper.output();
- EXPECT_THAT(out.rv, IsError(ERR_HTTP2_PROTOCOL_ERROR));
- histogram_tester.ExpectUniqueSample(
- "Net.SpdySession.AlpsDecoderStatus",
- static_cast<int>(AlpsDecoder::Error::kNotOnFrameBoundary), 1);
- histogram_tester.ExpectTotalCount("Net.SpdySession.AlpsAcceptChEntries", 0);
- histogram_tester.ExpectTotalCount("Net.SpdySession.AlpsSettingParameterCount",
- 0);
- }
- } // namespace net
|