rfc7540_SUITE.erl 161 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735
  1. %% Copyright (c) 2016-2017, Loïc Hoguin <essen@ninenines.eu>
  2. %%
  3. %% Permission to use, copy, modify, and/or distribute this software for any
  4. %% purpose with or without fee is hereby granted, provided that the above
  5. %% copyright notice and this permission notice appear in all copies.
  6. %%
  7. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. -module(rfc7540_SUITE).
  15. -compile(export_all).
  16. -compile(nowarn_export_all).
  17. -import(ct_helper, [config/2]).
  18. -import(ct_helper, [doc/1]).
  19. -import(ct_helper, [name/0]).
  20. -import(cowboy_test, [gun_open/1]).
  21. -import(cowboy_test, [raw_open/1]).
  22. -import(cowboy_test, [raw_send/2]).
  23. -import(cowboy_test, [raw_recv_head/1]).
  24. -import(cowboy_test, [raw_recv/3]).
  25. all() -> [{group, clear}, {group, tls}].
  26. groups() ->
  27. Modules = ct_helper:all(?MODULE),
  28. Clear = [M || M <- Modules, lists:sublist(atom_to_list(M), 4) =/= "alpn"] -- [prior_knowledge_reject_tls],
  29. TLS = [M || M <- Modules, lists:sublist(atom_to_list(M), 4) =:= "alpn"] ++ [prior_knowledge_reject_tls],
  30. [{clear, [parallel], Clear}, {tls, [parallel], TLS}].
  31. init_per_group(Name = clear, Config) ->
  32. cowboy_test:init_http(Name = clear, #{
  33. env => #{dispatch => cowboy_router:compile(init_routes(Config))}
  34. }, Config);
  35. init_per_group(Name = tls, Config) ->
  36. cowboy_test:init_http2(Name = tls, #{
  37. env => #{dispatch => cowboy_router:compile(init_routes(Config))}
  38. }, Config).
  39. end_per_group(Name, _) ->
  40. ok = cowboy:stop_listener(Name).
  41. init_routes(_) -> [
  42. {"localhost", [
  43. {"/", hello_h, []},
  44. {"/echo/:key", echo_h, []},
  45. {"/long_polling", long_polling_h, []},
  46. {"/resp/:key[/:arg]", resp_h, []}
  47. ]}
  48. ].
  49. %% Starting HTTP/2 for "http" URIs.
  50. http_upgrade_ignore_h2(Config) ->
  51. doc("An h2 token in an Upgrade field must be ignored. (RFC7540 3.2)"),
  52. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  53. ok = gen_tcp:send(Socket, [
  54. "GET / HTTP/1.1\r\n"
  55. "Host: localhost\r\n"
  56. "Connection: Upgrade, HTTP2-Settings\r\n"
  57. "Upgrade: h2\r\n"
  58. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  59. "\r\n"]),
  60. {ok, <<"HTTP/1.1 200">>} = gen_tcp:recv(Socket, 12, 1000),
  61. ok.
  62. http_upgrade_ignore_if_http_10(Config) ->
  63. doc("The Upgrade header must be ignored if part of an HTTP/1.0 request. (RFC7230 6.7)"),
  64. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  65. ok = gen_tcp:send(Socket, [
  66. "GET / HTTP/1.0\r\n"
  67. "Host: localhost\r\n"
  68. "Connection: Upgrade, HTTP2-Settings\r\n"
  69. "Upgrade: h2c\r\n"
  70. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  71. "\r\n"]),
  72. {ok, <<"HTTP/1.1 200">>} = gen_tcp:recv(Socket, 12, 1000),
  73. ok.
  74. http_upgrade_ignore_missing_upgrade_in_connection(Config) ->
  75. doc("The Upgrade header must be listed in the "
  76. "Connection header field. (RFC7230 6.7)"),
  77. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  78. ok = gen_tcp:send(Socket, [
  79. "GET / HTTP/1.1\r\n"
  80. "Host: localhost\r\n"
  81. "Connection: HTTP2-Settings\r\n"
  82. "Upgrade: h2c\r\n"
  83. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  84. "\r\n"]),
  85. {ok, <<"HTTP/1.1 200">>} = gen_tcp:recv(Socket, 12, 1000),
  86. ok.
  87. http_upgrade_ignore_missing_http2_settings_in_connection(Config) ->
  88. doc("The HTTP2-Settings header must be listed in the "
  89. "Connection header field. (RFC7540 3.2.1, RFC7230 6.7)"),
  90. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  91. ok = gen_tcp:send(Socket, [
  92. "GET / HTTP/1.1\r\n"
  93. "Host: localhost\r\n"
  94. "Connection: Upgrade\r\n"
  95. "Upgrade: h2c\r\n"
  96. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  97. "\r\n"]),
  98. {ok, <<"HTTP/1.1 200">>} = gen_tcp:recv(Socket, 12, 1000),
  99. ok.
  100. http_upgrade_ignore_zero_http2_settings_header(Config) ->
  101. doc("The HTTP Upgrade request must include "
  102. "exactly one HTTP2-Settings header field (RFC7540 3.2, RFC7540 3.2.1)"),
  103. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  104. ok = gen_tcp:send(Socket, [
  105. "GET / HTTP/1.1\r\n"
  106. "Host: localhost\r\n"
  107. "Connection: Upgrade, HTTP2-Settings\r\n"
  108. "Upgrade: h2c\r\n"
  109. "\r\n"]),
  110. {ok, <<"HTTP/1.1 200">>} = gen_tcp:recv(Socket, 12, 1000),
  111. ok.
  112. http_upgrade_reject_two_http2_settings_header(Config) ->
  113. doc("The HTTP Upgrade request must include "
  114. "exactly one HTTP2-Settings header field (RFC7540 3.2, RFC7540 3.2.1)"),
  115. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  116. ok = gen_tcp:send(Socket, [
  117. "GET / HTTP/1.1\r\n"
  118. "Host: localhost\r\n"
  119. "Connection: Upgrade, HTTP2-Settings\r\n"
  120. "Upgrade: h2c\r\n"
  121. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  122. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  123. "\r\n"]),
  124. {ok, <<"HTTP/1.1 400">>} = gen_tcp:recv(Socket, 12, 1000),
  125. ok.
  126. http_upgrade_reject_bad_http2_settings_header(Config) ->
  127. doc("The HTTP Upgrade request must include "
  128. "a valid HTTP2-Settings header field (RFC7540 3.2, RFC7540 3.2.1)"),
  129. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  130. ok = gen_tcp:send(Socket, [
  131. "GET / HTTP/1.1\r\n"
  132. "Host: localhost\r\n"
  133. "Connection: Upgrade, HTTP2-Settings\r\n"
  134. "Upgrade: h2c\r\n"
  135. %% We send a full SETTINGS frame on purpose.
  136. "HTTP2-Settings: ", base64:encode(iolist_to_binary(cow_http2:settings(#{}))), "\r\n",
  137. "\r\n"]),
  138. {ok, <<"HTTP/1.1 400">>} = gen_tcp:recv(Socket, 12, 1000),
  139. ok.
  140. %% Match directly for now.
  141. do_recv_101(Socket) ->
  142. {ok, <<
  143. "HTTP/1.1 101 Switching Protocols\r\n"
  144. "connection: Upgrade\r\n"
  145. "upgrade: h2c\r\n"
  146. "\r\n"
  147. >>} = gen_tcp:recv(Socket, 71, 1000),
  148. ok.
  149. http_upgrade_101(Config) ->
  150. doc("A 101 response must be sent on successful upgrade "
  151. "to HTTP/2 when using the HTTP Upgrade mechanism. (RFC7540 3.2, RFC7230 6.7)"),
  152. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  153. ok = gen_tcp:send(Socket, [
  154. "GET / HTTP/1.1\r\n"
  155. "Host: localhost\r\n"
  156. "Connection: Upgrade, HTTP2-Settings\r\n"
  157. "Upgrade: h2c\r\n"
  158. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  159. "\r\n"]),
  160. ok = do_recv_101(Socket),
  161. ok.
  162. http_upgrade_server_preface(Config) ->
  163. doc("The first frame after the upgrade must be a "
  164. "SETTINGS frame for the server connection preface. (RFC7540 3.2, RFC7540 3.5, RFC7540 6.5)"),
  165. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  166. ok = gen_tcp:send(Socket, [
  167. "GET / HTTP/1.1\r\n"
  168. "Host: localhost\r\n"
  169. "Connection: Upgrade, HTTP2-Settings\r\n"
  170. "Upgrade: h2c\r\n"
  171. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  172. "\r\n"]),
  173. ok = do_recv_101(Socket),
  174. %% Receive the server preface.
  175. {ok, << _:24, 4:8, 0:40 >>} = gen_tcp:recv(Socket, 9, 1000),
  176. ok.
  177. http_upgrade_client_preface_timeout(Config) ->
  178. doc("Clients negotiating HTTP/2 and not sending a preface in "
  179. "a timely manner must be disconnected."),
  180. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  181. ok = gen_tcp:send(Socket, [
  182. "GET / HTTP/1.1\r\n"
  183. "Host: localhost\r\n"
  184. "Connection: Upgrade, HTTP2-Settings\r\n"
  185. "Upgrade: h2c\r\n"
  186. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  187. "\r\n"]),
  188. ok = do_recv_101(Socket),
  189. %% Receive the server preface.
  190. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  191. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  192. %% Receive the response to the initial HTTP/1.1 request.
  193. {ok, << HeadersSkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  194. {ok, _} = gen_tcp:recv(Socket, HeadersSkipLen, 1000),
  195. {ok, << DataSkipLen:24, 0:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  196. {ok, _} = gen_tcp:recv(Socket, DataSkipLen, 1000),
  197. %% Do not send the preface. Wait for the server to disconnect us.
  198. {error, closed} = gen_tcp:recv(Socket, 9, 6000),
  199. ok.
  200. http_upgrade_reject_missing_client_preface(Config) ->
  201. doc("Servers must treat an invalid connection preface as a "
  202. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  203. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  204. ok = gen_tcp:send(Socket, [
  205. "GET / HTTP/1.1\r\n"
  206. "Host: localhost\r\n"
  207. "Connection: Upgrade, HTTP2-Settings\r\n"
  208. "Upgrade: h2c\r\n"
  209. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  210. "\r\n"]),
  211. ok = do_recv_101(Socket),
  212. %% Send a SETTINGS frame directly instead of the proper preface.
  213. ok = gen_tcp:send(Socket, cow_http2:settings(#{})),
  214. %% Receive the server preface.
  215. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  216. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  217. %% We expect the server to close the connection when it receives a bad preface.
  218. %% The server may however have already started sending the response to the
  219. %% initial HTTP/1.1 request.
  220. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  221. case gen_tcp:recv(Socket, 9, 1000) of
  222. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  223. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  224. [headers|Acc];
  225. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  226. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  227. [data|Acc];
  228. {error, _} ->
  229. [closed|Acc]
  230. end
  231. end, [], [1, 2, 3])),
  232. case Received of
  233. [closed|_] -> ok;
  234. [headers, closed|_] -> ok;
  235. [headers, data, closed] -> ok
  236. end.
  237. http_upgrade_reject_invalid_client_preface(Config) ->
  238. doc("Servers must treat an invalid connection preface as a "
  239. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  240. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  241. ok = gen_tcp:send(Socket, [
  242. "GET / HTTP/1.1\r\n"
  243. "Host: localhost\r\n"
  244. "Connection: Upgrade, HTTP2-Settings\r\n"
  245. "Upgrade: h2c\r\n"
  246. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  247. "\r\n"]),
  248. ok = do_recv_101(Socket),
  249. %% Send a slightly incorrect preface.
  250. ok = gen_tcp:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  251. %% Receive the server preface.
  252. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  253. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  254. %% We expect the server to close the connection when it receives a bad preface.
  255. %% The server may however have already started sending the response to the
  256. %% initial HTTP/1.1 request.
  257. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  258. case gen_tcp:recv(Socket, 9, 1000) of
  259. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  260. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  261. [headers|Acc];
  262. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  263. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  264. [data|Acc];
  265. {error, _} ->
  266. [closed|Acc]
  267. end
  268. end, [], [1, 2, 3])),
  269. case Received of
  270. [closed|_] -> ok;
  271. [headers, closed|_] -> ok;
  272. [headers, data, closed] -> ok
  273. end.
  274. http_upgrade_reject_missing_client_preface_settings(Config) ->
  275. doc("Servers must treat an invalid connection preface as a "
  276. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  277. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  278. ok = gen_tcp:send(Socket, [
  279. "GET / HTTP/1.1\r\n"
  280. "Host: localhost\r\n"
  281. "Connection: Upgrade, HTTP2-Settings\r\n"
  282. "Upgrade: h2c\r\n"
  283. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  284. "\r\n"]),
  285. ok = do_recv_101(Socket),
  286. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  287. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  288. %% Receive the server preface.
  289. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  290. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  291. %% We expect the server to close the connection when it receives a bad preface.
  292. %% The server may however have already started sending the response to the
  293. %% initial HTTP/1.1 request.
  294. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  295. case gen_tcp:recv(Socket, 9, 1000) of
  296. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  297. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  298. [headers|Acc];
  299. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  300. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  301. [data|Acc];
  302. {error, _} ->
  303. [closed|Acc]
  304. end
  305. end, [], [1, 2, 3])),
  306. case Received of
  307. [closed|_] -> ok;
  308. [headers, closed|_] -> ok;
  309. [headers, data, closed] -> ok
  310. end.
  311. http_upgrade_reject_invalid_client_preface_settings(Config) ->
  312. doc("Servers must treat an invalid connection preface as a "
  313. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  314. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  315. ok = gen_tcp:send(Socket, [
  316. "GET / HTTP/1.1\r\n"
  317. "Host: localhost\r\n"
  318. "Connection: Upgrade, HTTP2-Settings\r\n"
  319. "Upgrade: h2c\r\n"
  320. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  321. "\r\n"]),
  322. ok = do_recv_101(Socket),
  323. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  324. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", << 0:24, 4:8, 0:9, 1:31 >>]),
  325. %% Receive the server preface.
  326. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  327. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  328. %% We expect the server to close the connection when it receives a bad preface.
  329. %% The server may however have already started sending the response to the
  330. %% initial HTTP/1.1 request.
  331. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  332. case gen_tcp:recv(Socket, 9, 1000) of
  333. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  334. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  335. [headers|Acc];
  336. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  337. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  338. [data|Acc];
  339. {error, _} ->
  340. [closed|Acc]
  341. end
  342. end, [], [1, 2, 3])),
  343. case Received of
  344. [closed|_] -> ok;
  345. [headers, closed|_] -> ok;
  346. [headers, data, closed] -> ok
  347. end.
  348. http_upgrade_accept_client_preface_empty_settings(Config) ->
  349. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.2, RFC7540 3.5)"),
  350. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  351. ok = gen_tcp:send(Socket, [
  352. "GET / HTTP/1.1\r\n"
  353. "Host: localhost\r\n"
  354. "Connection: Upgrade, HTTP2-Settings\r\n"
  355. "Upgrade: h2c\r\n"
  356. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  357. "\r\n"]),
  358. ok = do_recv_101(Socket),
  359. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  360. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  361. %% Receive the server preface.
  362. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  363. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  364. %% Receive the SETTINGS ack.
  365. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  366. ok.
  367. http_upgrade_client_preface_settings_ack_timeout(Config) ->
  368. doc("The SETTINGS frames sent by the client must be acknowledged. (RFC7540 3.5, RFC7540 6.5.3)"),
  369. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  370. ok = gen_tcp:send(Socket, [
  371. "GET / HTTP/1.1\r\n"
  372. "Host: localhost\r\n"
  373. "Connection: Upgrade, HTTP2-Settings\r\n"
  374. "Upgrade: h2c\r\n"
  375. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  376. "\r\n"]),
  377. ok = do_recv_101(Socket),
  378. %% Send a valid preface.
  379. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  380. %% Receive the server preface.
  381. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  382. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  383. %% Receive the SETTINGS ack.
  384. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  385. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  386. {ok, << _:24, 7:8, _:72, 4:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  387. ok.
  388. %% @todo We need a successful test with actual options in HTTP2-Settings.
  389. %% SETTINGS_MAX_FRAME_SIZE is probably the easiest to test. The relevant
  390. %% RFC quote is:
  391. %%
  392. %% 3.2.1
  393. %% A server decodes and interprets these values as it would any other
  394. %% SETTINGS frame. Explicit acknowledgement of these settings
  395. %% (Section 6.5.3) is not necessary, since a 101 response serves as
  396. %% implicit acknowledgement.
  397. %% @todo We need to test an upgrade with a request body. It is probably
  398. %% worth having a configuration value for how much we accept while still
  399. %% upgrading (if too big, we would just stay on HTTP/1.1). We therefore
  400. %% needs a test for when the body is small enough, and one for when the
  401. %% body is larger than we accept. The relevant RFC quote is:
  402. %%
  403. %% 3.2
  404. %% Requests that contain a payload body MUST be sent in their entirety
  405. %% before the client can send HTTP/2 frames. This means that a large
  406. %% request can block the use of the connection until it is completely
  407. %% sent.
  408. %% @todo We should definitely have a test with OPTIONS. The relevant
  409. %% RFC quote is:
  410. %%
  411. %% 3.2
  412. %% If concurrency of an initial request with subsequent requests is
  413. %% important, an OPTIONS request can be used to perform the upgrade to
  414. %% HTTP/2, at the cost of an additional round trip.
  415. %% @todo If we ever handle priority, we need to check that the initial
  416. %% HTTP/1.1 request has default priority. The relevant RFC quote is:
  417. %%
  418. %% 3.2
  419. %% The HTTP/1.1 request that is sent prior to upgrade is assigned a
  420. %% stream identifier of 1 (see Section 5.1.1) with default priority
  421. %% values (Section 5.3.5).
  422. http_upgrade_response(Config) ->
  423. doc("A response must be sent to the initial HTTP/1.1 request "
  424. "after switching to HTTP/2. The response must use "
  425. "the stream identifier 1. (RFC7540 3.2)"),
  426. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  427. ok = gen_tcp:send(Socket, [
  428. "GET / HTTP/1.1\r\n"
  429. "Host: localhost\r\n"
  430. "Connection: Upgrade, HTTP2-Settings\r\n"
  431. "Upgrade: h2c\r\n"
  432. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  433. "\r\n"]),
  434. ok = do_recv_101(Socket),
  435. %% Send a valid preface.
  436. %% @todo Use non-empty SETTINGS here. Just because.
  437. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  438. %% Receive the server preface.
  439. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  440. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  441. %% Send the SETTINGS ack.
  442. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  443. %% Receive the SETTINGS ack, and the response HEADERS and DATA (Stream ID 1).
  444. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  445. case gen_tcp:recv(Socket, 9, 1000) of
  446. {ok, << 0:24, 4:8, 1:8, 0:32 >>} ->
  447. [settings_ack|Acc];
  448. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  449. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  450. [headers|Acc];
  451. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  452. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  453. [data|Acc]
  454. end
  455. end, [], [1, 2, 3])),
  456. case Received of
  457. [settings_ack, headers, data] -> ok;
  458. [headers, settings_ack, data] -> ok;
  459. [headers, data, settings_ack] -> ok
  460. end.
  461. http_upgrade_response_half_closed(Config) ->
  462. doc("The stream for the initial HTTP/1.1 request is half-closed. (RFC7540 3.2)"),
  463. %% Try sending more data after the upgrade and get an error.
  464. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  465. ok = gen_tcp:send(Socket, [
  466. "GET / HTTP/1.1\r\n"
  467. "Host: localhost\r\n"
  468. "Connection: Upgrade, HTTP2-Settings\r\n"
  469. "Upgrade: h2c\r\n"
  470. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  471. "\r\n"]),
  472. ok = do_recv_101(Socket),
  473. %% Send a valid preface followed by an unexpected DATA frame.
  474. ok = gen_tcp:send(Socket, [
  475. "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
  476. cow_http2:settings(#{}),
  477. cow_http2:data(1, fin, <<"Unexpected DATA frame.">>)
  478. ]),
  479. %% Receive the server preface.
  480. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  481. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  482. %% Skip the SETTINGS ack, receive the response HEADERS, DATA and RST_STREAM (streamid 1).
  483. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  484. case gen_tcp:recv(Socket, 9, 1000) of
  485. {ok, << 0:24, 4:8, 1:8, 0:32 >>} ->
  486. Acc;
  487. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  488. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  489. [headers|Acc];
  490. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  491. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  492. [data|Acc];
  493. {ok, << 4:24, 3:8, 0:8, 1:32 >>} ->
  494. %% We expect a STREAM_CLOSED reason.
  495. {ok, << 5:32 >>} = gen_tcp:recv(Socket, 4, 1000),
  496. [rst_stream|Acc];
  497. {error, _} ->
  498. %% Can be timeouts, ignore them.
  499. Acc
  500. end
  501. end, [], [1, 2, 3, 4])),
  502. case Received of
  503. [rst_stream] -> ok;
  504. [headers, rst_stream] -> ok;
  505. [headers, data, rst_stream] -> ok
  506. end.
  507. %% Starting HTTP/2 for "https" URIs.
  508. alpn_ignore_h2c(Config) ->
  509. doc("An h2c ALPN protocol identifier must be ignored. (RFC7540 3.3)"),
  510. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  511. [{alpn_advertised_protocols, [<<"h2c">>, <<"http/1.1">>]}, binary, {active, false}]),
  512. {ok, <<"http/1.1">>} = ssl:negotiated_protocol(Socket),
  513. ok.
  514. alpn_server_preface(Config) ->
  515. doc("The first frame must be a SETTINGS frame "
  516. "for the server connection preface. (RFC7540 3.3, RFC7540 3.5, RFC7540 6.5)"),
  517. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  518. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  519. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  520. %% Receive the server preface.
  521. {ok, << _:24, 4:8, 0:40 >>} = ssl:recv(Socket, 9, 1000),
  522. ok.
  523. alpn_client_preface_timeout(Config) ->
  524. doc("Clients negotiating HTTP/2 and not sending a preface in "
  525. "a timely manner must be disconnected."),
  526. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  527. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  528. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  529. %% Receive the server preface.
  530. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  531. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  532. %% Do not send the preface. Wait for the server to disconnect us.
  533. {error, closed} = ssl:recv(Socket, 3, 6000),
  534. ok.
  535. alpn_reject_missing_client_preface(Config) ->
  536. doc("Servers must treat an invalid connection preface as a "
  537. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  538. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  539. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  540. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  541. %% Send a SETTINGS frame directly instead of the proper preface.
  542. ok = ssl:send(Socket, cow_http2:settings(#{})),
  543. %% Receive the server preface.
  544. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  545. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  546. %% We expect the server to close the connection when it receives a bad preface.
  547. {error, closed} = ssl:recv(Socket, 3, 1000),
  548. ok.
  549. alpn_reject_invalid_client_preface(Config) ->
  550. doc("Servers must treat an invalid connection preface as a "
  551. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  552. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  553. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  554. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  555. %% Send a slightly incorrect preface.
  556. ok = ssl:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  557. %% Receive the server preface.
  558. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  559. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  560. %% We expect the server to close the connection when it receives a bad preface.
  561. {error, closed} = ssl:recv(Socket, 3, 1000),
  562. ok.
  563. alpn_reject_missing_client_preface_settings(Config) ->
  564. doc("Servers must treat an invalid connection preface as a "
  565. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  566. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  567. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  568. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  569. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  570. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  571. %% Receive the server preface.
  572. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  573. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  574. %% We expect the server to close the connection when it receives a bad preface.
  575. {error, closed} = ssl:recv(Socket, 3, 1000),
  576. ok.
  577. alpn_reject_invalid_client_preface_settings(Config) ->
  578. doc("Servers must treat an invalid connection preface as a "
  579. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  580. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  581. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  582. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  583. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  584. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", << 0:24, 4:8, 0:9, 1:31 >>]),
  585. %% Receive the server preface.
  586. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  587. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  588. %% We expect the server to close the connection when it receives a bad preface.
  589. {error, closed} = ssl:recv(Socket, 3, 1000),
  590. ok.
  591. alpn_accept_client_preface_empty_settings(Config) ->
  592. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.3, RFC7540 3.5)"),
  593. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  594. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  595. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  596. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  597. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  598. %% Receive the server preface.
  599. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  600. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  601. %% Receive the SETTINGS ack.
  602. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  603. ok.
  604. alpn_client_preface_settings_ack_timeout(Config) ->
  605. doc("Failure to acknowledge the server's SETTINGS frame "
  606. "results in a SETTINGS_TIMEOUT connection error. (RFC7540 3.5, RFC7540 6.5.3)"),
  607. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  608. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  609. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  610. %% Send a valid preface.
  611. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  612. %% Receive the server preface.
  613. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  614. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  615. %% Receive the SETTINGS ack.
  616. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  617. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  618. {ok, << _:24, 7:8, _:72, 4:32 >>} = ssl:recv(Socket, 17, 6000),
  619. ok.
  620. alpn(Config) ->
  621. doc("Successful ALPN negotiation. (RFC7540 3.3)"),
  622. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  623. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  624. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  625. %% Send a valid preface.
  626. %% @todo Use non-empty SETTINGS here. Just because.
  627. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  628. %% Receive the server preface.
  629. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  630. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  631. %% Send the SETTINGS ack.
  632. ok = ssl:send(Socket, cow_http2:settings_ack()),
  633. %% Receive the SETTINGS ack.
  634. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  635. %% Wait until after the SETTINGS ack timeout was supposed to trigger.
  636. receive after 6000 -> ok end,
  637. %% Send a PING.
  638. ok = ssl:send(Socket, cow_http2:ping(0)),
  639. %% Receive a PING ack back, indicating the connection is still up.
  640. {ok, << 8:24, 6:8, 0:7, 1:1, 0:96 >>} = ssl:recv(Socket, 17, 1000),
  641. ok.
  642. %% Starting HTTP/2 with prior knowledge.
  643. prior_knowledge_reject_tls(Config) ->
  644. doc("Implementations that support HTTP/2 over TLS must use ALPN. (RFC7540 3.4)"),
  645. {ok, Socket} = ssl:connect("localhost", config(port, Config), [binary, {active, false}]),
  646. %% Send a valid preface.
  647. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  648. %% We expect the server to send an HTTP 400 error
  649. %% when trying to use HTTP/2 without going through ALPN negotiation.
  650. {ok, <<"HTTP/1.1 400">>} = ssl:recv(Socket, 12, 1000),
  651. ok.
  652. prior_knowledge_server_preface(Config) ->
  653. doc("The first frame must be a SETTINGS frame "
  654. "for the server connection preface. (RFC7540 3.4, RFC7540 3.5, RFC7540 6.5)"),
  655. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  656. %% Send a valid preface.
  657. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  658. %% Receive the server preface.
  659. {ok, << _:24, 4:8, 0:40 >>} = gen_tcp:recv(Socket, 9, 1000),
  660. ok.
  661. %% Note: the client preface timeout doesn't apply in this case,
  662. %% so we don't test it. An HTTP/1.1 client that does not send
  663. %% a request in a timely manner will get disconnected by the
  664. %% HTTP protocol code, not by HTTP/2's.
  665. %% Note: the test that starts by sending a SETTINGS frame is
  666. %% redundant with tests sending garbage on the connection.
  667. %% From the point of view of an HTTP/1.1 connection, a
  668. %% SETTINGS frame is indistinguishable from garbage.
  669. prior_knowledge_reject_invalid_client_preface(Config) ->
  670. doc("An incorrect preface is an invalid HTTP/1.1 request. (RFC7540 3.4)"),
  671. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  672. %% Send a slightly incorrect preface.
  673. ok = gen_tcp:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  674. %% We propagate to HTTP/2 after checking only the request-line.
  675. %% The server then sends its preface before checking the full client preface.
  676. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  677. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  678. %% We expect the server to close the connection when it receives a bad preface.
  679. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  680. ok.
  681. prior_knowledge_reject_missing_client_preface_settings(Config) ->
  682. doc("Servers must treat an invalid connection preface as a "
  683. "connection error of type PROTOCOL_ERROR. (RFC7540 3.4, RFC7540 3.5)"),
  684. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  685. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  686. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  687. %% Receive the server preface.
  688. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  689. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  690. %% We expect the server to close the connection when it receives a bad preface.
  691. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  692. ok.
  693. prior_knowledge_reject_invalid_client_preface_settings(Config) ->
  694. doc("Servers must treat an invalid connection preface as a "
  695. "connection error of type PROTOCOL_ERROR. (RFC7540 3.4, RFC7540 3.5)"),
  696. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  697. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  698. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", << 0:24, 4:8, 0:9, 1:31 >>]),
  699. %% Receive the server preface.
  700. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  701. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  702. %% We expect the server to close the connection when it receives a bad preface.
  703. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  704. ok.
  705. prior_knowledge_accept_client_preface_empty_settings(Config) ->
  706. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.4, RFC7540 3.5)"),
  707. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  708. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  709. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  710. %% Receive the server preface.
  711. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  712. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  713. %% Receive the SETTINGS ack.
  714. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  715. ok.
  716. prior_knowledge_client_preface_settings_ack_timeout(Config) ->
  717. doc("The SETTINGS frames sent by the client must be acknowledged. (RFC7540 3.5, RFC7540 6.5.3)"),
  718. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  719. %% Send a valid preface.
  720. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  721. %% Receive the server preface.
  722. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  723. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  724. %% Receive the SETTINGS ack.
  725. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  726. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  727. {ok, << _:24, 7:8, _:72, 4:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  728. ok.
  729. %% Do a prior knowledge handshake.
  730. do_handshake(Config) ->
  731. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  732. %% Send a valid preface.
  733. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  734. %% Receive the server preface.
  735. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  736. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  737. %% Send the SETTINGS ack.
  738. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  739. %% Receive the SETTINGS ack.
  740. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  741. {ok, Socket}.
  742. prior_knowledge(Config) ->
  743. doc("Streams can be initiated after a successful HTTP/2 connection "
  744. "with prior knowledge of server capabilities. (RFC7540 3.4)"),
  745. %% @todo Use non-empty SETTINGS here. Just because.
  746. {ok, Socket} = do_handshake(Config),
  747. %% Wait until after the SETTINGS ack timeout was supposed to trigger.
  748. receive after 6000 -> ok end,
  749. %% Send a PING.
  750. ok = gen_tcp:send(Socket, cow_http2:ping(0)),
  751. %% Receive a PING ack back, indicating the connection is still up.
  752. {ok, << 8:24, 6:8, 0:7, 1:1, 0:96 >>} = gen_tcp:recv(Socket, 17, 1000),
  753. ok.
  754. %% @todo If we ever add an option to disable HTTP/2, we need to check
  755. %% the following things:
  756. %% * HTTP/1.1 Upgrade returns an HTTP/1.1 response (3.2)
  757. %% * HTTP/1.1 Upgrade errors out if the client sends HTTP/2 frames
  758. %% without waiting for the 101 response (3.2, 3.5)
  759. %% * Prior knowledge handshake fails (3.4)
  760. %% * ALPN selects HTTP/1.1 (3.3)
  761. %% Frame format.
  762. ignore_unknown_frames(Config) ->
  763. doc("Frames of unknown type must be ignored and discarded. (RFC7540 4.1)"),
  764. {ok, Socket} = do_handshake(Config),
  765. %% Send a POST request with a single DATA frame,
  766. %% and an unknown frame type interleaved.
  767. {HeadersBlock, _} = cow_hpack:encode([
  768. {<<":method">>, <<"POST">>},
  769. {<<":scheme">>, <<"http">>},
  770. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  771. {<<":path">>, <<"/echo/read_body">>}
  772. ]),
  773. ok = gen_tcp:send(Socket, [
  774. cow_http2:headers(1, nofin, HeadersBlock),
  775. << 10:24, 99:8, 0:40, 0:80 >>,
  776. cow_http2:data(1, fin, << 0:100/unit:8 >>)
  777. ]),
  778. %% Receive a response with the same DATA frame.
  779. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  780. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  781. {ok, << 100:24, 0:8, 1:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  782. {ok, << 0:100/unit:8 >>} = gen_tcp:recv(Socket, 100, 1000),
  783. ok.
  784. ignore_data_unknown_flags(Config) ->
  785. doc("Undefined DATA frame flags must be ignored. (RFC7540 4.1, RFC7540 6.1)"),
  786. {ok, Socket} = do_handshake(Config),
  787. %% Send a POST request with a DATA frame with unknown flags.
  788. {HeadersBlock, _} = cow_hpack:encode([
  789. {<<":method">>, <<"POST">>},
  790. {<<":scheme">>, <<"http">>},
  791. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  792. {<<":path">>, <<"/echo/read_body">>}
  793. ]),
  794. ok = gen_tcp:send(Socket, [
  795. cow_http2:headers(1, nofin, HeadersBlock),
  796. << 100:24, 0:8,
  797. 1:1, 1:1, 1:1, 1:1, %% Undefined.
  798. 0:1, %% PADDED.
  799. 1:1, 1:1, %% Undefined.
  800. 1:1, %% END_STREAM.
  801. 0:1, 1:31, 0:100/unit:8 >>
  802. ]),
  803. %% Receive a response with the same DATA frame.
  804. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  805. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  806. {ok, << 100:24, 0:8, 1:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  807. {ok, << 0:100/unit:8 >>} = gen_tcp:recv(Socket, 100, 1000),
  808. ok.
  809. ignore_headers_unknown_flags(Config) ->
  810. doc("Undefined HEADERS frame flags must be ignored. (RFC7540 4.1, RFC7540 6.2)"),
  811. {ok, Socket} = do_handshake(Config),
  812. %% Send a POST request with a HEADERS frame with unknown flags.
  813. {HeadersBlock, _} = cow_hpack:encode([
  814. {<<":method">>, <<"POST">>},
  815. {<<":scheme">>, <<"http">>},
  816. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  817. {<<":path">>, <<"/echo/read_body">>}
  818. ]),
  819. Len = iolist_size(HeadersBlock),
  820. ok = gen_tcp:send(Socket, [
  821. << Len:24, 1:8,
  822. 1:1, 1:1, %% Undefined.
  823. 0:1, %% PRIORITY.
  824. 1:1, %% Undefined.
  825. 0:1, %% PADDED.
  826. 1:1, %% END_HEADERS.
  827. 1:1, %% Undefined.
  828. 0:1, %% END_STREAM.
  829. 0:1, 1:31 >>,
  830. HeadersBlock,
  831. cow_http2:data(1, fin, << 0:100/unit:8 >>)
  832. ]),
  833. %% Receive a response with the same DATA frame.
  834. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  835. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  836. {ok, << 100:24, 0:8, 1:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  837. {ok, << 0:100/unit:8 >>} = gen_tcp:recv(Socket, 100, 1000),
  838. ok.
  839. ignore_priority_unknown_flags(Config) ->
  840. doc("Undefined PRIORITY frame flags must be ignored. (RFC7540 4.1, RFC7540 6.3)"),
  841. {ok, Socket} = do_handshake(Config),
  842. %% Send a POST request with an interleaved PRIORITY frame with unknown flags.
  843. {HeadersBlock, _} = cow_hpack:encode([
  844. {<<":method">>, <<"POST">>},
  845. {<<":scheme">>, <<"http">>},
  846. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  847. {<<":path">>, <<"/echo/read_body">>}
  848. ]),
  849. ok = gen_tcp:send(Socket, [
  850. cow_http2:headers(1, nofin, HeadersBlock),
  851. << 5:24, 2:8,
  852. 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, %% Undefined.
  853. 0:1, 1:31, 0:1, 3:31, 0:8 >>,
  854. cow_http2:data(1, fin, << 0:100/unit:8 >>)
  855. ]),
  856. %% Receive a response with the same DATA frame.
  857. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  858. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  859. {ok, << 100:24, 0:8, 1:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  860. {ok, << 0:100/unit:8 >>} = gen_tcp:recv(Socket, 100, 1000),
  861. ok.
  862. ignore_rst_stream_unknown_flags(Config) ->
  863. doc("Undefined RST_STREAM frame flags must be ignored. (RFC7540 4.1, RFC7540 6.4)"),
  864. {ok, Socket} = do_handshake(Config),
  865. %% Send a POST request then cancel it with an RST_STREAM frame with unknown flags.
  866. {HeadersBlock, _} = cow_hpack:encode([
  867. {<<":method">>, <<"POST">>},
  868. {<<":scheme">>, <<"http">>},
  869. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  870. {<<":path">>, <<"/echo/read_body">>}
  871. ]),
  872. ok = gen_tcp:send(Socket, [
  873. cow_http2:headers(1, nofin, HeadersBlock),
  874. << 4:24, 3:8,
  875. 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, %% Undefined.
  876. 0:1, 1:31, 8:32 >>,
  877. cow_http2:headers(3, nofin, HeadersBlock),
  878. cow_http2:data(3, fin, << 0:100/unit:8 >>)
  879. ]),
  880. %% Receive a response with the same DATA frame.
  881. {ok, << SkipLen:24, 1:8, _:8, 3:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  882. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  883. {ok, << 100:24, 0:8, 1:8, 3:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  884. {ok, << 0:100/unit:8 >>} = gen_tcp:recv(Socket, 100, 1000),
  885. ok.
  886. ignore_settings_unknown_flags(Config) ->
  887. doc("Undefined SETTINGS frame flags must be ignored. (RFC7540 4.1, RFC7540 6.5)"),
  888. {ok, Socket} = do_handshake(Config),
  889. %% Send a SETTINGS frame with unknown flags.
  890. ok = gen_tcp:send(Socket, << 6:24, 4:8,
  891. 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, %% Undefined.
  892. 0:1, %% ACK.
  893. 0:32, 2:16, 0:32 >>),
  894. %% Receive a SETTINGS ack.
  895. {ok, << 0:24, 4:8, 0:7, 1:1, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  896. ok.
  897. ignore_push_promise_unknown_flags(Config) ->
  898. doc("Undefined PUSH_PROMISE frame flags must be ignored. (RFC7540 4.1, RFC7540 6.6)"),
  899. {ok, Socket} = do_handshake(Config),
  900. %% Send a PUSH_PROMISE frame with unknown flags.
  901. ok = gen_tcp:send(Socket, << 4:24, 5:8,
  902. 1:1, 1:1, 1:1, 1:1, %% Undefined.
  903. 0:1, %% PADDED.
  904. 1:1, %% END_HEADERS.
  905. 1:1, 1:1, %% Undefined.
  906. 0:1, 1:31, 0:1, 3:31 >>
  907. ),
  908. %% Receive a PROTOCOL_ERROR connection error.
  909. %%
  910. %% Note that it is not possible to distinguish between the expected
  911. %% result and the server rejecting PUSH_PROMISE frames.
  912. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  913. ok.
  914. ignore_ping_unknown_flags(Config) ->
  915. doc("Undefined PING frame flags must be ignored. (RFC7540 4.1, RFC7540 6.7)"),
  916. {ok, Socket} = do_handshake(Config),
  917. %% Send a PING frame with unknown flags.
  918. ok = gen_tcp:send(Socket, << 8:24, 6:8,
  919. 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, %% Undefined.
  920. 0:1, %% ACK.
  921. 0:32, 0:64 >>),
  922. %% Receive a PING ACK in return.
  923. {ok, << 8:24, 6:8, _:7, 1:1, _:32, 0:64 >>} = gen_tcp:recv(Socket, 17, 6000),
  924. ok.
  925. ignore_goaway_unknown_flags(Config) ->
  926. doc("Undefined GOAWAY frame flags must be ignored. (RFC7540 4.1, RFC7540 6.8)"),
  927. {ok, Socket} = do_handshake(Config),
  928. %% Send a GOAWAY frame with unknown flags.
  929. ok = gen_tcp:send(Socket, << 8:24, 7:8,
  930. 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, %% Undefined.
  931. 0:32, 0:64 >>),
  932. %% Receive a GOAWAY frame back.
  933. {ok, << _:24, 7:8, _:72, 0:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  934. ok.
  935. ignore_window_update_unknown_flags(Config) ->
  936. doc("Undefined WINDOW_UPDATE frame flags must be ignored. (RFC7540 4.1, RFC7540 6.9)"),
  937. {ok, Socket} = do_handshake(Config),
  938. %% Send a WINDOW_UPDATE frame with unknown flags.
  939. ok = gen_tcp:send(Socket, << 4:24, 8:8,
  940. 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, 1:1, %% Undefined.
  941. 0:32, 1000:32 >>),
  942. %% We expect no errors or replies, therefore we send a PING frame.
  943. ok = gen_tcp:send(Socket, cow_http2:ping(0)),
  944. %% And receive a PING ACK in return.
  945. {ok, << 8:24, 6:8, _:7, 1:1, _:32, 0:64 >>} = gen_tcp:recv(Socket, 17, 6000),
  946. ok.
  947. ignore_continuation_unknown_flags(Config) ->
  948. doc("Undefined CONTINUATION frame flags must be ignored. (RFC7540 4.1, RFC7540 6.10)"),
  949. {ok, Socket} = do_handshake(Config),
  950. %% Send a POST request with a CONTINUATION frame with unknown flags.
  951. {HeadersBlock, _} = cow_hpack:encode([
  952. {<<":method">>, <<"POST">>},
  953. {<<":scheme">>, <<"http">>},
  954. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  955. {<<":path">>, <<"/echo/read_body">>}
  956. ]),
  957. Len = iolist_size(HeadersBlock),
  958. ok = gen_tcp:send(Socket, [
  959. << 0:24, 1:8, 0:8, 0:1, 1:31 >>,
  960. << Len:24, 9:8,
  961. 1:1, 1:1, 1:1, 1:1, 1:1, %% Undefined.
  962. 1:1, %% END_HEADERS.
  963. 1:1, 1:1, %% Undefined.
  964. 0:1, 1:31 >>,
  965. HeadersBlock,
  966. cow_http2:data(1, fin, << 0:100/unit:8 >>)
  967. ]),
  968. %% Receive a response with the same DATA frame.
  969. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  970. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  971. {ok, << 100:24, 0:8, 1:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  972. {ok, << 0:100/unit:8 >>} = gen_tcp:recv(Socket, 100, 1000),
  973. ok.
  974. %% @todo Flags that have no defined semantics for
  975. %% a particular frame type MUST be left unset (0x0) when sending. (RFC7540 4.1)
  976. ignore_data_reserved_bit(Config) ->
  977. doc("Reserved 1-bit field of DATA frame must be ignored. (RFC7540 4.1, RFC7540 6.1)"),
  978. {ok, Socket} = do_handshake(Config),
  979. %% Send a POST request with a DATA frame with the reserved bit set.
  980. {HeadersBlock, _} = cow_hpack:encode([
  981. {<<":method">>, <<"POST">>},
  982. {<<":scheme">>, <<"http">>},
  983. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  984. {<<":path">>, <<"/echo/read_body">>}
  985. ]),
  986. ok = gen_tcp:send(Socket, [
  987. cow_http2:headers(1, nofin, HeadersBlock),
  988. << 100:24, 0:8, 0:7, 1:1,
  989. 1:1, %% Reserved bit.
  990. 1:31, 0:100/unit:8 >>
  991. ]),
  992. %% Receive a response with the same DATA frame.
  993. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  994. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  995. {ok, << 100:24, 0:8, 1:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  996. {ok, << 0:100/unit:8 >>} = gen_tcp:recv(Socket, 100, 1000),
  997. ok.
  998. ignore_headers_reserved_bit(Config) ->
  999. doc("Reserved 1-bit field of HEADERS frame must be ignored. (RFC7540 4.1, RFC7540 6.2)"),
  1000. {ok, Socket} = do_handshake(Config),
  1001. %% Send a POST request with a HEADERS frame with the reserved bit set.
  1002. {HeadersBlock, _} = cow_hpack:encode([
  1003. {<<":method">>, <<"POST">>},
  1004. {<<":scheme">>, <<"http">>},
  1005. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1006. {<<":path">>, <<"/echo/read_body">>}
  1007. ]),
  1008. Len = iolist_size(HeadersBlock),
  1009. ok = gen_tcp:send(Socket, [
  1010. << Len:24, 1:8, 0:5, 1:1, 0:2,
  1011. 1:1, %% Reserved bit.
  1012. 1:31 >>,
  1013. HeadersBlock,
  1014. cow_http2:data(1, fin, << 0:100/unit:8 >>)
  1015. ]),
  1016. %% Receive a response with the same DATA frame.
  1017. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  1018. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  1019. {ok, << 100:24, 0:8, 1:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  1020. {ok, << 0:100/unit:8 >>} = gen_tcp:recv(Socket, 100, 1000),
  1021. ok.
  1022. ignore_priority_reserved_bit(Config) ->
  1023. doc("Reserved 1-bit field of PRIORITY frame must be ignored. (RFC7540 4.1, RFC7540 6.3)"),
  1024. {ok, Socket} = do_handshake(Config),
  1025. %% Send a POST request with an interleaved PRIORITY frame with the reserved bit set.
  1026. {HeadersBlock, _} = cow_hpack:encode([
  1027. {<<":method">>, <<"POST">>},
  1028. {<<":scheme">>, <<"http">>},
  1029. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1030. {<<":path">>, <<"/echo/read_body">>}
  1031. ]),
  1032. ok = gen_tcp:send(Socket, [
  1033. cow_http2:headers(1, nofin, HeadersBlock),
  1034. << 5:24, 2:8, 0:8,
  1035. 1:1, %% Reserved bit.
  1036. 1:31, 0:1, 3:31, 0:8 >>,
  1037. cow_http2:data(1, fin, << 0:100/unit:8 >>)
  1038. ]),
  1039. %% Receive a response with the same DATA frame.
  1040. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  1041. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  1042. {ok, << 100:24, 0:8, 1:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  1043. {ok, << 0:100/unit:8 >>} = gen_tcp:recv(Socket, 100, 1000),
  1044. ok.
  1045. ignore_rst_stream_reserved_bit(Config) ->
  1046. doc("Reserved 1-bit field of RST_STREAM frame must be ignored. (RFC7540 4.1, RFC7540 6.4)"),
  1047. {ok, Socket} = do_handshake(Config),
  1048. %% Send a POST request then cancel it with an RST_STREAM frame with the reserved bit set.
  1049. {HeadersBlock, _} = cow_hpack:encode([
  1050. {<<":method">>, <<"POST">>},
  1051. {<<":scheme">>, <<"http">>},
  1052. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1053. {<<":path">>, <<"/echo/read_body">>}
  1054. ]),
  1055. ok = gen_tcp:send(Socket, [
  1056. cow_http2:headers(1, nofin, HeadersBlock),
  1057. << 4:24, 3:8, 0:8,
  1058. 1:1, %% Reserved bit.
  1059. 1:31, 8:32 >>,
  1060. cow_http2:headers(3, nofin, HeadersBlock),
  1061. cow_http2:data(3, fin, << 0:100/unit:8 >>)
  1062. ]),
  1063. %% Receive a response with the same DATA frame.
  1064. {ok, << SkipLen:24, 1:8, _:8, 3:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  1065. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  1066. {ok, << 100:24, 0:8, 1:8, 3:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  1067. {ok, << 0:100/unit:8 >>} = gen_tcp:recv(Socket, 100, 1000),
  1068. ok.
  1069. ignore_settings_reserved_bit(Config) ->
  1070. doc("Reserved 1-bit field of SETTINGS frame must be ignored. (RFC7540 4.1, RFC7540 6.5)"),
  1071. {ok, Socket} = do_handshake(Config),
  1072. %% Send a SETTINGS frame with the reserved bit set.
  1073. ok = gen_tcp:send(Socket, << 6:24, 4:8, 0:8,
  1074. 1:1, %% Reserved bit.
  1075. 0:31, 2:16, 0:32 >>),
  1076. %% Receive a SETTINGS ack.
  1077. {ok, << 0:24, 4:8, 0:7, 1:1, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  1078. ok.
  1079. ignore_push_promise_reserved_bit(Config) ->
  1080. doc("Reserved 1-bit field of PUSH_PROMISE frame must be ignored. (RFC7540 4.1, RFC7540 6.6)"),
  1081. {ok, Socket} = do_handshake(Config),
  1082. %% Send a PUSH_PROMISE frame with the reserved bit set.
  1083. ok = gen_tcp:send(Socket, << 4:24, 5:8, 0:5, 1:1, 0:2,
  1084. 1:1, %% Reserved bit.
  1085. 1:31, 0:1, 3:31 >>
  1086. ),
  1087. %% Receive a PROTOCOL_ERROR connection error.
  1088. %%
  1089. %% Note that it is not possible to distinguish between the expected
  1090. %% result and the server rejecting PUSH_PROMISE frames.
  1091. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1092. ok.
  1093. ignore_ping_reserved_bit(Config) ->
  1094. doc("Reserved 1-bit field of PING frame must be ignored. (RFC7540 4.1, RFC7540 6.7)"),
  1095. {ok, Socket} = do_handshake(Config),
  1096. %% Send a PING frame with the reserved bit set.
  1097. ok = gen_tcp:send(Socket, << 8:24, 6:8, 0:8,
  1098. 1:1, %% Reserved bit.
  1099. 0:31, 0:64 >>),
  1100. %% Receive a PING ACK in return.
  1101. {ok, << 8:24, 6:8, _:7, 1:1, _:32, 0:64 >>} = gen_tcp:recv(Socket, 17, 6000),
  1102. ok.
  1103. ignore_goaway_reserved_bit(Config) ->
  1104. doc("Reserved 1-bit field of GOAWAY frame must be ignored. (RFC7540 4.1, RFC7540 6.8)"),
  1105. {ok, Socket} = do_handshake(Config),
  1106. %% Send a GOAWAY frame with the reserved bit set.
  1107. ok = gen_tcp:send(Socket, << 8:24, 7:8, 0:8,
  1108. 1:1, %% Reserved bit.
  1109. 0:31, 0:64 >>),
  1110. %% Receive a GOAWAY frame back.
  1111. {ok, << _:24, 7:8, _:72, 0:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1112. ok.
  1113. ignore_window_update_reserved_bit(Config) ->
  1114. doc("Reserved 1-bit field of WINDOW_UPDATE frame must be ignored. (RFC7540 4.1, RFC7540 6.9)"),
  1115. {ok, Socket} = do_handshake(Config),
  1116. %% Send a WINDOW_UPDATE frame with the reserved bit set.
  1117. ok = gen_tcp:send(Socket, << 4:24, 8:8, 0:8,
  1118. 1:1, %% Reserved bit.
  1119. 0:31, 1000:32 >>),
  1120. %% We expect no errors or replies, therefore we send a PING frame.
  1121. ok = gen_tcp:send(Socket, cow_http2:ping(0)),
  1122. %% And receive a PING ACK in return.
  1123. {ok, << 8:24, 6:8, _:7, 1:1, _:32, 0:64 >>} = gen_tcp:recv(Socket, 17, 6000),
  1124. ok.
  1125. ignore_continuation_reserved_bit(Config) ->
  1126. doc("Reserved 1-bit field of CONTINUATION frame must be ignored. (RFC7540 4.1, RFC7540 6.10)"),
  1127. {ok, Socket} = do_handshake(Config),
  1128. %% Send a POST request with a CONTINUATION frame with the reserved bit set.
  1129. {HeadersBlock, _} = cow_hpack:encode([
  1130. {<<":method">>, <<"POST">>},
  1131. {<<":scheme">>, <<"http">>},
  1132. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1133. {<<":path">>, <<"/echo/read_body">>}
  1134. ]),
  1135. Len = iolist_size(HeadersBlock),
  1136. ok = gen_tcp:send(Socket, [
  1137. << 0:24, 1:8, 0:8, 0:1, 1:31 >>,
  1138. << Len:24, 9:8, 0:5, 1:1, 0:2,
  1139. 1:1, %% Reserved bit.
  1140. 1:31 >>,
  1141. HeadersBlock,
  1142. cow_http2:data(1, fin, << 0:100/unit:8 >>)
  1143. ]),
  1144. %% Receive a response with the same DATA frame.
  1145. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  1146. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  1147. {ok, << 100:24, 0:8, 1:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  1148. {ok, << 0:100/unit:8 >>} = gen_tcp:recv(Socket, 100, 1000),
  1149. ok.
  1150. %% @todo The reserved 1-bit field MUST remain unset (0x0) when sending. (RFC7540 4.1)
  1151. %% Frame size.
  1152. max_frame_size_allow_exactly_default(Config) ->
  1153. doc("All implementations must allow frame sizes of at least 16384. (RFC7540 4.1, RFC7540 4.2)"),
  1154. {ok, Socket} = do_handshake(Config),
  1155. %% Send a POST request with a DATA frame of exactly 16384 bytes.
  1156. {HeadersBlock, _} = cow_hpack:encode([
  1157. {<<":method">>, <<"POST">>},
  1158. {<<":scheme">>, <<"http">>},
  1159. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1160. {<<":path">>, <<"/echo/read_body">>}
  1161. ]),
  1162. ok = gen_tcp:send(Socket, [
  1163. cow_http2:headers(1, nofin, HeadersBlock),
  1164. cow_http2:data(1, fin, << 0:16384/unit:8 >>)
  1165. ]),
  1166. %% Receive a response with the same DATA frame.
  1167. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = case gen_tcp:recv(Socket, 9, 1000) of
  1168. %% We received a WINDOW_UPDATE first. Skip it and the next.
  1169. {ok, <<4:24, 8:8, 0:40>>} ->
  1170. {ok, _} = gen_tcp:recv(Socket, 4 + 13, 1000),
  1171. gen_tcp:recv(Socket, 9, 1000);
  1172. Res ->
  1173. Res
  1174. end,
  1175. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  1176. {ok, << 16384:24, 0:8, 1:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  1177. {ok, << 0:16384/unit:8 >>} = gen_tcp:recv(Socket, 16384, 1000),
  1178. ok.
  1179. max_frame_size_reject_larger_than_default(Config) ->
  1180. doc("A FRAME_SIZE_ERROR connection error must be sent when receiving "
  1181. "frames larger than the default 16384 length. (RFC7540 4.1, RFC7540 4.2)"),
  1182. {ok, Socket} = do_handshake(Config),
  1183. %% Send a POST request with a DATA frame larger than 16384 bytes.
  1184. {HeadersBlock, _} = cow_hpack:encode([
  1185. {<<":method">>, <<"POST">>},
  1186. {<<":scheme">>, <<"http">>},
  1187. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1188. {<<":path">>, <<"/echo/read_body">>}
  1189. ]),
  1190. ok = gen_tcp:send(Socket, [
  1191. cow_http2:headers(1, nofin, HeadersBlock),
  1192. cow_http2:data(1, fin, << 0:16385/unit:8 >>)
  1193. ]),
  1194. %% Receive a FRAME_SIZE_ERROR connection error.
  1195. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1196. ok.
  1197. max_frame_size_allow_exactly_custom(Config0) ->
  1198. doc("An endpoint that sets SETTINGS_MAX_FRAME_SIZE must allow frames "
  1199. "of up to that size. (RFC7540 4.2, RFC7540 6.5.2)"),
  1200. %% Create a new listener that sets the maximum frame size to 30000.
  1201. Config = cowboy_test:init_http(name(), #{
  1202. env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
  1203. max_frame_size_received => 30000
  1204. }, Config0),
  1205. %% Do the handshake.
  1206. {ok, Socket} = do_handshake(Config),
  1207. %% Send a HEADERS frame initiating a stream followed by
  1208. %% a single 30000 bytes DATA frame.
  1209. Headers = [
  1210. {<<":method">>, <<"POST">>},
  1211. {<<":scheme">>, <<"http">>},
  1212. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1213. {<<":path">>, <<"/long_polling">>}
  1214. ],
  1215. {HeadersBlock, _} = cow_hpack:encode(Headers),
  1216. ok = gen_tcp:send(Socket, [
  1217. cow_http2:headers(1, nofin, HeadersBlock),
  1218. cow_http2:data(1, fin, <<0:30000/unit:8>>)
  1219. ]),
  1220. %% Receive a proper response.
  1221. {ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1222. {ok, _} = gen_tcp:recv(Socket, Len2, 6000),
  1223. %% No errors follow due to our sending of a 25000 bytes frame.
  1224. {error, timeout} = gen_tcp:recv(Socket, 0, 1000),
  1225. ok.
  1226. max_frame_size_reject_larger_than_custom(Config0) ->
  1227. doc("An endpoint that sets SETTINGS_MAX_FRAME_SIZE must reject frames "
  1228. "of up to that size with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.5.2)"),
  1229. %% Create a new listener that sets the maximum frame size to 30000.
  1230. Config = cowboy_test:init_http(name(), #{
  1231. env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
  1232. max_frame_size_received => 30000
  1233. }, Config0),
  1234. %% Do the handshake.
  1235. {ok, Socket} = do_handshake(Config),
  1236. %% Send a HEADERS frame initiating a stream followed by
  1237. %% a single DATA frame larger than 30000 bytes.
  1238. Headers = [
  1239. {<<":method">>, <<"POST">>},
  1240. {<<":scheme">>, <<"http">>},
  1241. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1242. {<<":path">>, <<"/long_polling">>}
  1243. ],
  1244. {HeadersBlock, _} = cow_hpack:encode(Headers),
  1245. ok = gen_tcp:send(Socket, [
  1246. cow_http2:headers(1, nofin, HeadersBlock),
  1247. cow_http2:data(1, fin, <<0:30001/unit:8>>)
  1248. ]),
  1249. %% Receive a FRAME_SIZE_ERROR connection error.
  1250. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1251. ok.
  1252. %% I am using FRAME_SIZE_ERROR here because the information in the
  1253. %% frame header tells us this frame is at least 1 byte long, while
  1254. %% the given length is smaller; i.e. it is too small to contain
  1255. %% mandatory frame data (the pad length).
  1256. data_reject_frame_size_0_padded_flag(Config) ->
  1257. doc("DATA frames of size 0 with the PADDED flag set must be rejected "
  1258. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.1)"),
  1259. {ok, Socket} = do_handshake(Config),
  1260. %% Send a POST request with an incorrect padded DATA frame size.
  1261. {HeadersBlock, _} = cow_hpack:encode([
  1262. {<<":method">>, <<"POST">>},
  1263. {<<":scheme">>, <<"http">>},
  1264. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1265. {<<":path">>, <<"/echo/read_body">>}
  1266. ]),
  1267. ok = gen_tcp:send(Socket, [
  1268. cow_http2:headers(1, nofin, HeadersBlock),
  1269. << 0:24, 0:8, 0:4, 1:1, 0:2, 1:1, 0:1, 1:31 >>
  1270. ]),
  1271. %% Receive a FRAME_SIZE_ERROR connection error.
  1272. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1273. ok.
  1274. %% This case on the other hand is noted specifically in the RFC
  1275. %% as being a PROTOCOL_ERROR. It can be thought of as the Pad Length
  1276. %% being incorrect, rather than the frame size.
  1277. data_reject_frame_size_too_small_padded_flag(Config) ->
  1278. doc("DATA frames with Pad Length >= Length must be rejected "
  1279. "with a PROTOCOL_ERROR connection error. (RFC7540 6.1)"),
  1280. {ok, Socket} = do_handshake(Config),
  1281. %% Send a POST request with an incorrect padded DATA frame size.
  1282. {HeadersBlock, _} = cow_hpack:encode([
  1283. {<<":method">>, <<"POST">>},
  1284. {<<":scheme">>, <<"http">>},
  1285. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1286. {<<":path">>, <<"/echo/read_body">>}
  1287. ]),
  1288. ok = gen_tcp:send(Socket, [
  1289. cow_http2:headers(1, nofin, HeadersBlock),
  1290. << 10:24, 0:8, 0:4, 1:1, 0:2, 1:1, 0:1, 1:31, 10:8, 0:80 >>
  1291. ]),
  1292. %% Receive a PROTOCOL_ERROR connection error.
  1293. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1294. ok.
  1295. headers_reject_frame_size_0_padded_flag(Config) ->
  1296. doc("HEADERS frames of size 0 with the PADDED flag set must be rejected "
  1297. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.2)"),
  1298. {ok, Socket} = do_handshake(Config),
  1299. %% Send a padded HEADERS frame with an incorrect size.
  1300. ok = gen_tcp:send(Socket, << 0:24, 1:8, 0:4, 1:1, 0:2, 1:1, 0:1, 1:31 >>),
  1301. %% Receive a FRAME_SIZE_ERROR connection error.
  1302. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1303. ok.
  1304. headers_reject_frame_size_too_small_padded_flag(Config) ->
  1305. doc("HEADERS frames with no priority flag and Pad Length >= Length "
  1306. "must be rejected with a PROTOCOL_ERROR connection error. (RFC7540 6.2)"),
  1307. {ok, Socket} = do_handshake(Config),
  1308. %% Send a padded HEADERS frame with an incorrect size.
  1309. ok = gen_tcp:send(Socket, << 10:24, 1:8, 0:4, 1:1, 0:2, 1:1, 0:1, 1:31, 10:8, 0:80 >>),
  1310. %% Receive a PROTOCOL_ERROR connection error.
  1311. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1312. ok.
  1313. headers_reject_frame_size_too_small_priority_flag(Config) ->
  1314. doc("HEADERS frames of size smaller than 5 with the PRIORITY flag set must be rejected "
  1315. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.2)"),
  1316. {ok, Socket} = do_handshake(Config),
  1317. %% Send a HEADERS frame with priority set and an incorrect size.
  1318. ok = gen_tcp:send(Socket, << 4:24, 1:8,
  1319. 0:2, 1:1, 0:4, 1:1, 0:1, 1:31, 0:1, 3:31, 0:8 >>),
  1320. %% Receive a FRAME_SIZE_ERROR connection error.
  1321. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1322. ok.
  1323. headers_reject_frame_size_5_padded_and_priority_flags(Config) ->
  1324. doc("HEADERS frames of size smaller than 6 with the PADDED "
  1325. "and PRIORITY flags set must be rejected "
  1326. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.2)"),
  1327. {ok, Socket} = do_handshake(Config),
  1328. %% Send a padded HEADERS frame with an incorrect size.
  1329. ok = gen_tcp:send(Socket, << 5:24, 1:8,
  1330. 0:2, 1:1, 0:1, 1:1, 0:2, 1:1, 0:1, 1:31, 0:8, 0:1, 3:31, 0:8 >>),
  1331. %% Receive a FRAME_SIZE_ERROR connection error.
  1332. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1333. ok.
  1334. headers_reject_frame_size_too_small_padded_and_priority_flags(Config) ->
  1335. doc("HEADERS frames of size smaller than Length+6 with the PADDED and PRIORITY flags set "
  1336. "must be rejected with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.2)"),
  1337. {ok, Socket} = do_handshake(Config),
  1338. %% Send a padded HEADERS frame with an incorrect size.
  1339. ok = gen_tcp:send(Socket, << 15:24, 1:8,
  1340. 0:2, 1:1, 0:1, 1:1, 0:2, 1:1, 0:1, 1:31, 10:8, 0:1, 3:31, 0:8, 0:80 >>),
  1341. %% Receive a PROTOCOL_ERROR connection error.
  1342. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1343. ok.
  1344. priority_reject_frame_size_too_small(Config) ->
  1345. doc("PRIORITY frames of size smaller than 5 must be rejected "
  1346. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.3)"),
  1347. {ok, Socket} = do_handshake(Config),
  1348. %% Send a PRIORITY frame with an incorrect size.
  1349. ok = gen_tcp:send(Socket, << 4:24, 2:8, 0:9, 1:31, 0:1, 3:31, 0:8 >>),
  1350. %% Receive a FRAME_SIZE_ERROR stream error.
  1351. {ok, << _:24, 3:8, _:40, 6:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  1352. ok.
  1353. priority_reject_frame_size_too_large(Config) ->
  1354. doc("PRIORITY frames of size larger than 5 must be rejected "
  1355. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.3)"),
  1356. {ok, Socket} = do_handshake(Config),
  1357. %% Send a PRIORITY frame with an incorrect size.
  1358. ok = gen_tcp:send(Socket, << 6:24, 2:8, 0:9, 1:31, 0:1, 3:31, 0:16 >>),
  1359. %% Receive a FRAME_SIZE_ERROR stream error.
  1360. {ok, << _:24, 3:8, _:40, 6:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  1361. ok.
  1362. rst_stream_reject_frame_size_too_small(Config) ->
  1363. doc("RST_STREAM frames of size smaller than 4 must be rejected "
  1364. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.4)"),
  1365. {ok, Socket} = do_handshake(Config),
  1366. %% Send a request and reset it immediately.
  1367. {HeadersBlock, _} = cow_hpack:encode([
  1368. {<<":method">>, <<"GET">>},
  1369. {<<":scheme">>, <<"http">>},
  1370. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1371. {<<":path">>, <<"/">>}
  1372. ]),
  1373. ok = gen_tcp:send(Socket, [
  1374. cow_http2:headers(1, fin, HeadersBlock),
  1375. << 3:24, 3:8, 0:9, 1:31, 8:32 >>
  1376. ]),
  1377. %% Receive a FRAME_SIZE_ERROR connection error.
  1378. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1379. ok.
  1380. rst_stream_reject_frame_size_too_large(Config) ->
  1381. doc("RST_STREAM frames of size larger than 4 must be rejected "
  1382. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.4)"),
  1383. {ok, Socket} = do_handshake(Config),
  1384. %% Send a request and reset it immediately.
  1385. {HeadersBlock, _} = cow_hpack:encode([
  1386. {<<":method">>, <<"GET">>},
  1387. {<<":scheme">>, <<"http">>},
  1388. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1389. {<<":path">>, <<"/">>}
  1390. ]),
  1391. ok = gen_tcp:send(Socket, [
  1392. cow_http2:headers(1, fin, HeadersBlock),
  1393. << 5:24, 3:8, 0:9, 1:31, 8:32 >>
  1394. ]),
  1395. %% Receive a FRAME_SIZE_ERROR connection error.
  1396. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1397. ok.
  1398. settings_reject_bad_frame_size(Config) ->
  1399. doc("SETTINGS frames must have a size multiple of 6 or be rejected "
  1400. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.5)"),
  1401. {ok, Socket} = do_handshake(Config),
  1402. %% Send a SETTINGS frame with an incorrect size.
  1403. ok = gen_tcp:send(Socket, << 5:24, 4:8, 0:40, 1:16, 4096:32 >>),
  1404. %% Receive a FRAME_SIZE_ERROR connection error.
  1405. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1406. ok.
  1407. settings_ack_reject_non_empty_frame_size(Config) ->
  1408. doc("SETTINGS frames with the ACK flag set and a non-empty payload "
  1409. "must be rejected with a FRAME_SIZE_ERROR connection error (RFC7540 4.2, RFC7540 6.5)"),
  1410. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  1411. %% Send a valid preface.
  1412. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  1413. %% Receive the server preface.
  1414. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  1415. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  1416. %% Send a SETTINGS ack with a payload.
  1417. ok = gen_tcp:send(Socket, << 6:24, 4:8, 0:7, 1:1, 0:32, 1:16, 4096:32 >>),
  1418. %% Receive the SETTINGS ack.
  1419. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  1420. %% Receive a FRAME_SIZE_ERROR connection error.
  1421. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1422. ok.
  1423. %% Note that clients are not supposed to send PUSH_PROMISE frames.
  1424. %% However when they do, we need to be able to parse it in order
  1425. %% to reject it, and so these errors may still occur.
  1426. push_promise_reject_frame_size_too_small(Config) ->
  1427. doc("PUSH_PROMISE frames of size smaller than 4 must be rejected "
  1428. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.6)"),
  1429. {ok, Socket} = do_handshake(Config),
  1430. %% Send a PUSH_PROMISE frame with an incorrect size.
  1431. ok = gen_tcp:send(Socket, << 3:24, 5:8, 0:5, 1:1, 0:3, 1:31, 0:1, 3:31 >>),
  1432. %% Receive a FRAME_SIZE_ERROR connection error.
  1433. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1434. ok.
  1435. push_promise_reject_frame_size_4_padded_flag(Config) ->
  1436. doc("PUSH_PROMISE frames of size smaller than 5 with the PADDED flag set must be rejected "
  1437. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.6)"),
  1438. {ok, Socket} = do_handshake(Config),
  1439. %% Send a PUSH_PROMISE frame with an incorrect size.
  1440. ok = gen_tcp:send(Socket, << 4:24, 5:8, 0:4, 1:1, 1:1, 0:3, 1:31, 0:1, 0:8, 3:31 >>),
  1441. %% Receive a FRAME_SIZE_ERROR connection error.
  1442. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1443. ok.
  1444. push_promise_reject_frame_size_too_small_padded_flag(Config) ->
  1445. doc("PUSH_PROMISE frames of size smaller than Length+5 with the PADDED flag set "
  1446. "must be rejected with a PROTOCOL_ERROR connection error. (RFC7540 4.2, RFC7540 6.6)"),
  1447. {ok, Socket} = do_handshake(Config),
  1448. %% Send a PUSH_PROMISE frame with an incorrect size.
  1449. {HeadersBlock, _} = cow_hpack:encode([
  1450. {<<":method">>, <<"GET">>},
  1451. {<<":scheme">>, <<"http">>},
  1452. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1453. {<<":path">>, <<"/">>}
  1454. ]),
  1455. Len = 14 + iolist_size(HeadersBlock),
  1456. ok = gen_tcp:send(Socket, [
  1457. << Len:24, 5:8, 0:4, 1:1, 1:1, 0:3, 1:31, 10:8, 0:1, 3:31 >>,
  1458. HeadersBlock,
  1459. << 0:80 >>
  1460. ]),
  1461. %% Receive a PROTOCOL_ERROR connection error.
  1462. %%
  1463. %% Note that it is not possible to distinguish between a Pad Length
  1464. %% error and the server rejecting PUSH_PROMISE frames.
  1465. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1466. ok.
  1467. ping_reject_frame_size_too_small(Config) ->
  1468. doc("PING frames of size smaller than 8 must be rejected "
  1469. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.7)"),
  1470. {ok, Socket} = do_handshake(Config),
  1471. %% Send a PING frame with an incorrect size.
  1472. ok = gen_tcp:send(Socket, << 7:24, 6:8, 0:40, 0:56 >>),
  1473. %% Receive a FRAME_SIZE_ERROR connection error.
  1474. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1475. ok.
  1476. ping_reject_frame_size_too_large(Config) ->
  1477. doc("PING frames of size larger than 8 must be rejected "
  1478. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.7)"),
  1479. {ok, Socket} = do_handshake(Config),
  1480. %% Send a PING frame with an incorrect size.
  1481. ok = gen_tcp:send(Socket, << 9:24, 6:8, 0:40, 0:72 >>),
  1482. %% Receive a FRAME_SIZE_ERROR connection error.
  1483. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1484. ok.
  1485. goaway_reject_frame_size_too_small(Config) ->
  1486. doc("GOAWAY frames of size smaller than 8 must be rejected "
  1487. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.8)"),
  1488. {ok, Socket} = do_handshake(Config),
  1489. %% Send a GOAWAY frame with an incorrect size.
  1490. ok = gen_tcp:send(Socket, << 7:24, 7:8, 0:40, 0:56 >>),
  1491. %% Receive a FRAME_SIZE_ERROR connection error.
  1492. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1493. ok.
  1494. goaway_allow_frame_size_too_large(Config) ->
  1495. doc("GOAWAY frames of size larger than 8 must be allowed. (RFC7540 6.8)"),
  1496. {ok, Socket} = do_handshake(Config),
  1497. %% Send a GOAWAY frame with debug data.
  1498. ok = gen_tcp:send(Socket, << 12:24, 7:8, 0:40, 0:64, 99999:32 >>),
  1499. %% Receive a GOAWAY frame back.
  1500. {ok, << _:24, 7:8, _:72, 0:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1501. ok.
  1502. window_update_reject_frame_size_too_small(Config) ->
  1503. doc("WINDOW_UPDATE frames of size smaller than 4 must be rejected "
  1504. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.9)"),
  1505. {ok, Socket} = do_handshake(Config),
  1506. %% Send a WINDOW_UPDATE frame with an incorrect size.
  1507. ok = gen_tcp:send(Socket, << 3:24, 8:8, 0:40, 1000:24 >>),
  1508. %% Receive a FRAME_SIZE_ERROR connection error.
  1509. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1510. ok.
  1511. window_update_reject_frame_size_too_large(Config) ->
  1512. doc("WINDOW_UPDATE frames of size larger than 4 must be rejected "
  1513. "with a FRAME_SIZE_ERROR connection error. (RFC7540 4.2, RFC7540 6.9)"),
  1514. {ok, Socket} = do_handshake(Config),
  1515. %% Send a WINDOW_UPDATE frame with an incorrect size.
  1516. ok = gen_tcp:send(Socket, << 5:24, 8:8, 0:40, 1000:40 >>),
  1517. %% Receive a FRAME_SIZE_ERROR connection error.
  1518. {ok, << _:24, 7:8, _:72, 6:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1519. ok.
  1520. %% Note: There is no particular limits on the size of CONTINUATION frames,
  1521. %% they can go from 0 to SETTINGS_MAX_FRAME_SIZE.
  1522. %% Header compression and decompression.
  1523. headers_compression_error(Config) ->
  1524. doc("A decoding error in a HEADERS frame's header block must be rejected "
  1525. "with a COMPRESSION_ERROR connection error. (RFC7540 4.3, RFC7540 6.2)"),
  1526. {ok, Socket} = do_handshake(Config),
  1527. %% Send a HEADERS frame with an invalid header block.
  1528. ok = gen_tcp:send(Socket, << 10:24, 1:8, 0:5, 1:1, 0:1, 1:1, 0:1, 1:31, 0:10/unit:8 >>),
  1529. %% Receive a COMPRESSION_ERROR connection error.
  1530. {ok, << _:24, 7:8, _:72, 9:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1531. ok.
  1532. continuation_compression_error(Config) ->
  1533. doc("A decoding error in a CONTINUATION frame's header block must be rejected "
  1534. "with a COMPRESSION_ERROR connection error. (RFC7540 4.3, RFC7540 6.10)"),
  1535. {ok, Socket} = do_handshake(Config),
  1536. %% Send a CONTINUATION frame with an invalid header block.
  1537. ok = gen_tcp:send(Socket, [
  1538. << 0:24, 1:8, 0:7, 1:1, 0:1, 1:31 >>,
  1539. << 10:24, 9:8, 0:5, 1:1, 0:3, 1:31, 0:10/unit:8 >>
  1540. ]),
  1541. %% Receive a COMPRESSION_ERROR connection error.
  1542. {ok, << _:24, 7:8, _:72, 9:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1543. ok.
  1544. continuation_with_frame_interleaved_error(Config) ->
  1545. doc("Frames interleaved in a header block must be rejected "
  1546. "with a PROTOCOL_ERROR connection error. (RFC7540 4.3, RFC7540 6.2, RFC7540 6.10)"),
  1547. {ok, Socket} = do_handshake(Config),
  1548. %% Send an unterminated HEADERS frame followed by a PING frame.
  1549. ok = gen_tcp:send(Socket, [
  1550. << 0:24, 1:8, 0:7, 1:1, 0:1, 1:31 >>,
  1551. cow_http2:ping(0)
  1552. ]),
  1553. %% Receive a PROTOCOL_ERROR connection error.
  1554. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1555. ok.
  1556. continuation_wrong_stream_error(Config) ->
  1557. doc("CONTINUATION frames with an incorrect stream identifier must be rejected "
  1558. "with a PROTOCOL_ERROR connection error. (RFC7540 4.3, RFC7540 6.2)"),
  1559. {ok, Socket} = do_handshake(Config),
  1560. %% Send an unterminated HEADERS frame followed by a CONTINUATION frame for another stream.
  1561. ok = gen_tcp:send(Socket, [
  1562. << 0:24, 1:8, 0:7, 1:1, 0:1, 1:31 >>,
  1563. << 0:24, 9:8, 0:9, 3:31 >>
  1564. ]),
  1565. %% Receive a PROTOCOL_ERROR connection error.
  1566. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1567. ok.
  1568. %% Stream states.
  1569. idle_stream_reject_data(Config) ->
  1570. doc("DATA frames received on an idle stream must be rejected "
  1571. "with a PROTOCOL_ERROR connection error. (RFC7540 5.1, RFC7540 6.1)"),
  1572. {ok, Socket} = do_handshake(Config),
  1573. %% Send a DATA frame on an idle stream.
  1574. ok = gen_tcp:send(Socket, cow_http2:data(1, fin, <<"Unexpected DATA frame.">>)),
  1575. %% Receive a PROTOCOL_ERROR connection error.
  1576. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1577. ok.
  1578. idle_stream_accept_headers(Config) ->
  1579. doc("HEADERS frames received on an idle stream must be accepted. (RFC7540 5.1)"),
  1580. {ok, Socket} = do_handshake(Config),
  1581. %% Send a HEADERS frame on an idle stream.
  1582. {HeadersBlock, _} = cow_hpack:encode([
  1583. {<<":method">>, <<"GET">>},
  1584. {<<":scheme">>, <<"http">>},
  1585. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1586. {<<":path">>, <<"/">>}
  1587. ]),
  1588. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1589. %% Receive a HEADERS frame as a response.
  1590. {ok, << _:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1591. ok.
  1592. idle_stream_accept_priority(Config) ->
  1593. doc("PRIORITY frames received on an idle stream must be accepted. (RFC7540 5.1)"),
  1594. {ok, Socket} = do_handshake(Config),
  1595. %% Send a PRIORITY frame on an idle stream.
  1596. ok = gen_tcp:send(Socket, cow_http2:priority(1, shared, 3, 123)),
  1597. %% Receive no error.
  1598. {error, timeout} = gen_tcp:recv(Socket, 7, 1000),
  1599. %% Send a HEADERS frame on the same stream.
  1600. {HeadersBlock, _} = cow_hpack:encode([
  1601. {<<":method">>, <<"GET">>},
  1602. {<<":scheme">>, <<"http">>},
  1603. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1604. {<<":path">>, <<"/">>}
  1605. ]),
  1606. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1607. %% Receive a HEADERS frame as a response.
  1608. {ok, << _:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1609. ok.
  1610. idle_stream_reject_rst_stream(Config) ->
  1611. doc("RST_STREAM frames received on an idle stream must be rejected "
  1612. "with a PROTOCOL_ERROR connection error. (RFC7540 5.1)"),
  1613. {ok, Socket} = do_handshake(Config),
  1614. %% Send an RST_STREAM frame on an idle stream.
  1615. ok = gen_tcp:send(Socket, cow_http2:rst_stream(1, no_error)),
  1616. %% Receive a PROTOCOL_ERROR connection error.
  1617. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1618. ok.
  1619. idle_stream_reject_push_promise(Config) ->
  1620. doc("PUSH_PROMISE frames received on an idle stream must be rejected "
  1621. "with a PROTOCOL_ERROR connection error. (RFC7540 5.1)"),
  1622. {ok, Socket} = do_handshake(Config),
  1623. %% Send a PUSH_PROMISE frame on an idle stream.
  1624. {HeadersBlock, _} = cow_hpack:encode([
  1625. {<<":method">>, <<"GET">>},
  1626. {<<":scheme">>, <<"http">>},
  1627. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1628. {<<":path">>, <<"/">>}
  1629. ]),
  1630. ok = gen_tcp:send(Socket, cow_http2:push_promise(1, 3, HeadersBlock)),
  1631. %% Receive a PROTOCOL_ERROR connection error.
  1632. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1633. ok.
  1634. idle_stream_reject_window_update(Config) ->
  1635. doc("WINDOW_UPDATE frames received on an idle stream must be rejected "
  1636. "with a PROTOCOL_ERROR connection error. (RFC7540 5.1)"),
  1637. {ok, Socket} = do_handshake(Config),
  1638. %% Send a WINDOW_UPDATE frame on an idle stream.
  1639. ok = gen_tcp:send(Socket, cow_http2:window_update(1, 12345)),
  1640. %% Receive a PROTOCOL_ERROR connection error.
  1641. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1642. ok.
  1643. %reserved (local) - after sending PUSH_PROMISE:
  1644. % An endpoint MUST NOT send any type of frame other than HEADERS,
  1645. % RST_STREAM, or PRIORITY in this state.
  1646. %%% how to test this?
  1647. %
  1648. % A PRIORITY or WINDOW_UPDATE frame MAY be received in this state.
  1649. % Receiving any type of frame other than RST_STREAM, PRIORITY, or
  1650. % WINDOW_UPDATE on a stream in this state MUST be treated as a
  1651. % connection error (Section 5.4.1) of type PROTOCOL_ERROR.
  1652. %%% we need to use a large enough file for this
  1653. %
  1654. %reserved_local_reject_data
  1655. %reserved_local_reject_headers
  1656. %reserved_local_accept_priority
  1657. %reserved_local_accept_rst_stream
  1658. %reserved_local_reject_push_promise %% do we even care? we reject it always
  1659. %reserved_local_accept_window_update
  1660. %
  1661. %half-closed (remote):
  1662. % If an endpoint receives additional frames, other than
  1663. % WINDOW_UPDATE, PRIORITY, or RST_STREAM, for a stream that is in
  1664. % this state, it MUST respond with a stream error (Section 5.4.2) of
  1665. % type STREAM_CLOSED.
  1666. half_closed_remote_reject_data(Config) ->
  1667. doc("DATA frames received on a half-closed (remote) stream must be rejected "
  1668. "with a STREAM_CLOSED stream error. (RFC7540 5.1, RFC7540 6.1)"),
  1669. {ok, Socket} = do_handshake(Config),
  1670. %% Send a HEADERS frame with the FIN flag set.
  1671. {HeadersBlock, _} = cow_hpack:encode([
  1672. {<<":method">>, <<"GET">>},
  1673. {<<":scheme">>, <<"http">>},
  1674. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1675. {<<":path">>, <<"/">>}
  1676. ]),
  1677. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1678. %% Send a DATA frame on that now half-closed (remote) stream.
  1679. ok = gen_tcp:send(Socket, cow_http2:data(1, fin, <<"Unexpected DATA frame.">>)),
  1680. %% Receive a STREAM_CLOSED stream error.
  1681. {ok, << _:24, 3:8, _:8, 1:32, 5:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  1682. ok.
  1683. %% We reject all invalid HEADERS with a connection error because
  1684. %% we do not want to waste resources decoding them.
  1685. half_closed_remote_reject_headers(Config) ->
  1686. doc("HEADERS frames received on a half-closed (remote) stream must be rejected "
  1687. "with a STREAM_CLOSED connection error. (RFC7540 4.3, RFC7540 5.1)"),
  1688. {ok, Socket} = do_handshake(Config),
  1689. %% Send a HEADERS frame with the FIN flag set.
  1690. {HeadersBlock, _} = cow_hpack:encode([
  1691. {<<":method">>, <<"GET">>},
  1692. {<<":scheme">>, <<"http">>},
  1693. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1694. {<<":path">>, <<"/">>}
  1695. ]),
  1696. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1697. %% Send a HEADERS frame on that now half-closed (remote) stream.
  1698. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1699. %% Receive a STREAM_CLOSED connection error.
  1700. {ok, << _:24, 7:8, _:72, 5:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1701. ok.
  1702. half_closed_remote_accept_priority(Config) ->
  1703. doc("PRIORITY frames received on a half-closed stream must be accepted. (RFC7540 5.1)"),
  1704. {ok, Socket} = do_handshake(Config),
  1705. %% Send a HEADERS frame with the FIN flag set.
  1706. {HeadersBlock, _} = cow_hpack:encode([
  1707. {<<":method">>, <<"GET">>},
  1708. {<<":scheme">>, <<"http">>},
  1709. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1710. {<<":path">>, <<"/">>}
  1711. ]),
  1712. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1713. %% Send a PRIORITY frame on that now half-closed (remote) stream.
  1714. ok = gen_tcp:send(Socket, cow_http2:priority(1, shared, 3, 123)),
  1715. %% Receive a HEADERS frame as a response.
  1716. {ok, << _:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1717. ok.
  1718. half_closed_remote_accept_rst_stream(Config) ->
  1719. doc("RST_STREAM frames received on a half-closed stream must be accepted. (RFC7540 5.1)"),
  1720. {ok, Socket} = do_handshake(Config),
  1721. %% Send a HEADERS frame with the FIN flag set.
  1722. {HeadersBlock, _} = cow_hpack:encode([
  1723. {<<":method">>, <<"GET">>},
  1724. {<<":scheme">>, <<"http">>},
  1725. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1726. {<<":path">>, <<"/">>}
  1727. ]),
  1728. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1729. %% Send an RST_STREAM frame on that now half-closed (remote) stream.
  1730. ok = gen_tcp:send(Socket, cow_http2:rst_stream(1, no_error)),
  1731. %% Receive nothing back.
  1732. {error, timeout} = gen_tcp:recv(Socket, 9, 6000),
  1733. ok.
  1734. %% half_closed_remote_reject_push_promise
  1735. %%
  1736. %% We respond to all PUSH_PROMISE frames with a PROTOCOL_ERROR connection error
  1737. %% because PUSH is disabled in that direction. We therefore cannot test other
  1738. %% error conditions.
  1739. half_closed_remote_accept_window_update(Config) ->
  1740. doc("WINDOW_UPDATE frames received on a half-closed stream must be accepted. (RFC7540 5.1)"),
  1741. {ok, Socket} = do_handshake(Config),
  1742. %% Send a HEADERS frame with the FIN flag set.
  1743. {HeadersBlock, _} = cow_hpack:encode([
  1744. {<<":method">>, <<"GET">>},
  1745. {<<":scheme">>, <<"http">>},
  1746. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1747. {<<":path">>, <<"/">>}
  1748. ]),
  1749. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1750. %% Send a WINDOW_UPDATE frame on that now half-closed (remote) stream.
  1751. ok = gen_tcp:send(Socket, cow_http2:window_update(1, 12345)),
  1752. %% Receive a HEADERS frame as a response.
  1753. {ok, << _:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1754. ok.
  1755. %% We reject DATA frames sent on closed streams with a STREAM_CLOSED
  1756. %% connection error regardless of how the stream was closed to simplify
  1757. %% the implementation. This excludes the few frames we ignore from
  1758. %% lingering streams that we canceled.
  1759. rst_stream_closed_reject_data(Config) ->
  1760. doc("DATA frames received on a stream closed via RST_STREAM must be rejected "
  1761. "with a STREAM_CLOSED connection error. (RFC7540 5.1, RFC7540 6.1)"),
  1762. {ok, Socket} = do_handshake(Config),
  1763. %% Send a HEADERS frame.
  1764. {HeadersBlock, _} = cow_hpack:encode([
  1765. {<<":method">>, <<"GET">>},
  1766. {<<":scheme">>, <<"http">>},
  1767. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1768. {<<":path">>, <<"/">>}
  1769. ]),
  1770. ok = gen_tcp:send(Socket, cow_http2:headers(1, nofin, HeadersBlock)),
  1771. %% Send an RST_STREAM frame to close the stream.
  1772. ok = gen_tcp:send(Socket, cow_http2:rst_stream(1, cancel)),
  1773. %% Send a DATA frame on the now RST_STREAM closed stream.
  1774. ok = gen_tcp:send(Socket, cow_http2:data(1, fin, <<"Unexpected DATA frame.">>)),
  1775. %% Receive a STREAM_CLOSED connection error.
  1776. {ok, << _:24, 7:8, _:72, 5:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1777. ok.
  1778. %% We reject all invalid HEADERS with a connection error because
  1779. %% we do not want to waste resources decoding them.
  1780. rst_stream_closed_reject_headers(Config) ->
  1781. doc("HEADERS frames received on a stream closed via RST_STREAM must be rejected "
  1782. "with a STREAM_CLOSED connection error. (RFC7540 4.3, RFC7540 5.1)"),
  1783. {ok, Socket} = do_handshake(Config),
  1784. %% Send a HEADERS frame.
  1785. {HeadersBlock, _} = cow_hpack:encode([
  1786. {<<":method">>, <<"GET">>},
  1787. {<<":scheme">>, <<"http">>},
  1788. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1789. {<<":path">>, <<"/">>}
  1790. ]),
  1791. ok = gen_tcp:send(Socket, cow_http2:headers(1, nofin, HeadersBlock)),
  1792. %% Send an RST_STREAM frame to close the stream.
  1793. ok = gen_tcp:send(Socket, cow_http2:rst_stream(1, cancel)),
  1794. %% Send a HEADERS frame on the now RST_STREAM closed stream.
  1795. ok = gen_tcp:send(Socket, cow_http2:headers(1, nofin, HeadersBlock)),
  1796. %% Receive a STREAM_CLOSED connection error.
  1797. {ok, << _:24, 7:8, _:72, 5:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1798. ok.
  1799. rst_stream_closed_accept_priority(Config) ->
  1800. doc("PRIORITY frames received on a stream closed via RST_STREAM "
  1801. "must be accepted. (RFC7540 5.1)"),
  1802. {ok, Socket} = do_handshake(Config),
  1803. %% Send a HEADERS frame.
  1804. {HeadersBlock, _} = cow_hpack:encode([
  1805. {<<":method">>, <<"GET">>},
  1806. {<<":scheme">>, <<"http">>},
  1807. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1808. {<<":path">>, <<"/">>}
  1809. ]),
  1810. ok = gen_tcp:send(Socket, cow_http2:headers(1, nofin, HeadersBlock)),
  1811. %% Send an RST_STREAM frame to close the stream.
  1812. ok = gen_tcp:send(Socket, cow_http2:rst_stream(1, cancel)),
  1813. %% Send a PRIORITY frame on that now RST_STREAM closed stream.
  1814. ok = gen_tcp:send(Socket, cow_http2:priority(1, shared, 3, 123)),
  1815. %% Receive nothing back.
  1816. {error, timeout} = gen_tcp:recv(Socket, 9, 6000),
  1817. ok.
  1818. rst_stream_closed_ignore_rst_stream(Config) ->
  1819. doc("RST_STREAM frames received on a stream closed via RST_STREAM "
  1820. "must be ignored to avoid looping. (RFC7540 5.1, RFC7540 5.4.2)"),
  1821. {ok, Socket} = do_handshake(Config),
  1822. %% Send a HEADERS frame.
  1823. {HeadersBlock, _} = cow_hpack:encode([
  1824. {<<":method">>, <<"GET">>},
  1825. {<<":scheme">>, <<"http">>},
  1826. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1827. {<<":path">>, <<"/">>}
  1828. ]),
  1829. ok = gen_tcp:send(Socket, cow_http2:headers(1, nofin, HeadersBlock)),
  1830. %% Send an RST_STREAM frame to close the stream.
  1831. ok = gen_tcp:send(Socket, cow_http2:rst_stream(1, cancel)),
  1832. %% Send an extra RST_STREAM.
  1833. ok = gen_tcp:send(Socket, cow_http2:rst_stream(1, cancel)),
  1834. %% Receive nothing back.
  1835. {error, timeout} = gen_tcp:recv(Socket, 9, 6000),
  1836. ok.
  1837. %% rst_stream_closed_reject_push_promise
  1838. %%
  1839. %% We respond to all PUSH_PROMISE frames with a PROTOCOL_ERROR connection error
  1840. %% because PUSH is disabled in that direction. We therefore cannot test other
  1841. %% error conditions.
  1842. rst_stream_closed_reject_window_update(Config) ->
  1843. doc("WINDOW_UPDATE frames received on a stream closed via RST_STREAM "
  1844. "must be rejected with a STREAM_CLOSED stream error. (RFC7540 5.1)"),
  1845. {ok, Socket} = do_handshake(Config),
  1846. %% Send a HEADERS frame.
  1847. {HeadersBlock, _} = cow_hpack:encode([
  1848. {<<":method">>, <<"GET">>},
  1849. {<<":scheme">>, <<"http">>},
  1850. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1851. {<<":path">>, <<"/">>}
  1852. ]),
  1853. ok = gen_tcp:send(Socket, cow_http2:headers(1, nofin, HeadersBlock)),
  1854. %% Send an RST_STREAM frame to close the stream.
  1855. ok = gen_tcp:send(Socket, cow_http2:rst_stream(1, cancel)),
  1856. %% Send a WINDOW_UPDATE frame on the now RST_STREAM closed stream.
  1857. ok = gen_tcp:send(Socket, cow_http2:window_update(1, 12345)),
  1858. %% Receive a STREAM_CLOSED stream error.
  1859. {ok, << _:24, 3:8, _:8, 1:32, 5:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  1860. ok.
  1861. stream_closed_reject_data(Config) ->
  1862. doc("DATA frames received on a stream closed normally must be rejected "
  1863. "with a STREAM_CLOSED connection error. (RFC7540 5.1, RFC7540 6.1)"),
  1864. {ok, Socket} = do_handshake(Config),
  1865. %% Send a HEADERS frame.
  1866. {HeadersBlock, _} = cow_hpack:encode([
  1867. {<<":method">>, <<"GET">>},
  1868. {<<":scheme">>, <<"http">>},
  1869. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1870. {<<":path">>, <<"/">>}
  1871. ]),
  1872. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1873. %% Receive the response.
  1874. {ok, << Length1:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1875. {ok, _} = gen_tcp:recv(Socket, Length1, 6000),
  1876. {ok, << Length2:24, 0:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1877. {ok, _} = gen_tcp:recv(Socket, Length2, 6000),
  1878. %% Send a DATA frame on the now closed stream.
  1879. ok = gen_tcp:send(Socket, cow_http2:data(1, fin, <<"Unexpected DATA frame.">>)),
  1880. %% Receive a STREAM_CLOSED connection error.
  1881. {ok, << _:24, 7:8, _:72, 5:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1882. ok.
  1883. stream_closed_reject_headers(Config) ->
  1884. doc("HEADERS frames received on a stream closed normally must be rejected "
  1885. "with a STREAM_CLOSED connection error. (RFC7540 5.1)"),
  1886. {ok, Socket} = do_handshake(Config),
  1887. %% Send a HEADERS frame.
  1888. {HeadersBlock, _} = cow_hpack:encode([
  1889. {<<":method">>, <<"GET">>},
  1890. {<<":scheme">>, <<"http">>},
  1891. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1892. {<<":path">>, <<"/">>}
  1893. ]),
  1894. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1895. %% Receive the response.
  1896. {ok, << Length1:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1897. {ok, _} = gen_tcp:recv(Socket, Length1, 6000),
  1898. {ok, << Length2:24, 0:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1899. {ok, _} = gen_tcp:recv(Socket, Length2, 6000),
  1900. %% Send a HEADERS frame on the now closed stream.
  1901. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1902. %% Receive a STREAM_CLOSED connection error.
  1903. {ok, << _:24, 7:8, _:72, 5:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  1904. ok.
  1905. stream_closed_accept_priority(Config) ->
  1906. doc("PRIORITY frames received on a stream closed normally must be accepted. (RFC7540 5.1)"),
  1907. {ok, Socket} = do_handshake(Config),
  1908. %% Send a HEADERS frame.
  1909. {HeadersBlock, _} = cow_hpack:encode([
  1910. {<<":method">>, <<"GET">>},
  1911. {<<":scheme">>, <<"http">>},
  1912. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1913. {<<":path">>, <<"/">>}
  1914. ]),
  1915. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1916. %% Receive the response.
  1917. {ok, << Length1:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1918. {ok, _} = gen_tcp:recv(Socket, Length1, 6000),
  1919. {ok, << Length2:24, 0:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1920. {ok, _} = gen_tcp:recv(Socket, Length2, 6000),
  1921. %% Send a PRIORITY frame on the now closed stream.
  1922. ok = gen_tcp:send(Socket, cow_http2:priority(1, shared, 3, 123)),
  1923. %% Receive nothing back.
  1924. {error, timeout} = gen_tcp:recv(Socket, 9, 6000),
  1925. ok.
  1926. stream_closed_accept_rst_stream(Config) ->
  1927. doc("RST_STREAM frames received on a stream closed normally "
  1928. "must be accepted for a short period. (RFC7540 5.1)"),
  1929. {ok, Socket} = do_handshake(Config),
  1930. %% Send a HEADERS frame.
  1931. {HeadersBlock, _} = cow_hpack:encode([
  1932. {<<":method">>, <<"GET">>},
  1933. {<<":scheme">>, <<"http">>},
  1934. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1935. {<<":path">>, <<"/">>}
  1936. ]),
  1937. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1938. %% Receive the response.
  1939. {ok, << Length1:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1940. {ok, _} = gen_tcp:recv(Socket, Length1, 6000),
  1941. {ok, << Length2:24, 0:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1942. {ok, _} = gen_tcp:recv(Socket, Length2, 6000),
  1943. %% Send an RST_STREAM frame on the now closed stream.
  1944. ok = gen_tcp:send(Socket, cow_http2:rst_stream(1, cancel)),
  1945. %% Receive nothing back.
  1946. {error, timeout} = gen_tcp:recv(Socket, 9, 6000),
  1947. ok.
  1948. %% stream_closed_reject_push_promise
  1949. %%
  1950. %% We respond to all PUSH_PROMISE frames with a PROTOCOL_ERROR connection error
  1951. %% because PUSH is disabled in that direction. We therefore cannot test other
  1952. %% error conditions.
  1953. stream_closed_accept_window_update(Config) ->
  1954. doc("WINDOW_UPDATE frames received on a stream closed normally "
  1955. "must be accepted for a short period. (RFC7540 5.1)"),
  1956. {ok, Socket} = do_handshake(Config),
  1957. %% Send a HEADERS frame.
  1958. {HeadersBlock, _} = cow_hpack:encode([
  1959. {<<":method">>, <<"GET">>},
  1960. {<<":scheme">>, <<"http">>},
  1961. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1962. {<<":path">>, <<"/">>}
  1963. ]),
  1964. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1965. %% Receive the response.
  1966. {ok, << Length1:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1967. {ok, _} = gen_tcp:recv(Socket, Length1, 6000),
  1968. {ok, << Length2:24, 0:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  1969. {ok, _} = gen_tcp:recv(Socket, Length2, 6000),
  1970. %% Send a WINDOW_UPDATE frame on the now closed stream.
  1971. ok = gen_tcp:send(Socket, cow_http2:window_update(1, 12345)),
  1972. %% Receive nothing back.
  1973. {error, timeout} = gen_tcp:recv(Socket, 9, 6000),
  1974. ok.
  1975. %% @todo While we accept RST_STREAM and WINDOW_UPDATE for a short period
  1976. %% after the stream closed normally, we may want to reject the ones coming
  1977. %% a significant amount of time after that.
  1978. %% @todo Frames may arrive on a stream after we send an RST_STREAM for it.
  1979. %% They must be ignored for a short period of time:
  1980. %
  1981. % If this state is reached as a result of sending a RST_STREAM
  1982. % frame, the peer that receives the RST_STREAM might have already
  1983. % sent -- or enqueued for sending -- frames on the stream that
  1984. % cannot be withdrawn. An endpoint MUST ignore frames that it
  1985. % receives on closed streams after it has sent a RST_STREAM frame.
  1986. % An endpoint MAY choose to limit the period over which it ignores
  1987. % frames and treat frames that arrive after this time as being in
  1988. % error.
  1989. %% @todo Ensure that rejected DATA frames result in the connection
  1990. %% flow-control window being updated. How to test this?
  1991. %
  1992. % Flow-controlled frames (i.e., DATA) received after sending
  1993. % RST_STREAM are counted toward the connection flow-control window.
  1994. % Even though these frames might be ignored, because they are sent
  1995. % before the sender receives the RST_STREAM, the sender will
  1996. % consider the frames to count against the flow-control window.
  1997. %% Stream identifiers.
  1998. reject_streamid_even(Config) ->
  1999. doc("HEADERS frames received with an even-numbered streamid "
  2000. "must be rejected with a PROTOCOL_ERROR connection error. (RFC7540 5.1.1)"),
  2001. {ok, Socket} = do_handshake(Config),
  2002. %% Send a HEADERS frame with an even-numbered streamid.
  2003. {HeadersBlock, _} = cow_hpack:encode([
  2004. {<<":method">>, <<"GET">>},
  2005. {<<":scheme">>, <<"http">>},
  2006. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2007. {<<":path">>, <<"/">>}
  2008. ]),
  2009. ok = gen_tcp:send(Socket, cow_http2:headers(2, fin, HeadersBlock)),
  2010. %% Receive a PROTOCOL_ERROR connection error.
  2011. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  2012. ok.
  2013. reject_streamid_0(Config) ->
  2014. doc("HEADERS frames received with streamid 0 (zero) "
  2015. "must be rejected with a PROTOCOL_ERROR connection error. (RFC7540 5.1.1)"),
  2016. {ok, Socket} = do_handshake(Config),
  2017. %% Send a HEADERS frame with an streamid 0.
  2018. {HeadersBlock, _} = cow_hpack:encode([
  2019. {<<":method">>, <<"GET">>},
  2020. {<<":scheme">>, <<"http">>},
  2021. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2022. {<<":path">>, <<"/">>}
  2023. ]),
  2024. ok = gen_tcp:send(Socket, cow_http2:headers(0, fin, HeadersBlock)),
  2025. %% Receive a PROTOCOL_ERROR connection error.
  2026. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  2027. ok.
  2028. %% See the comment for reject_streamid_lower for the rationale behind
  2029. %% having a STREAM_CLOSED connection error.
  2030. http_upgrade_reject_reuse_streamid_1(Config) ->
  2031. doc("Attempts to reuse streamid 1 after upgrading to HTTP/2 "
  2032. "must be rejected with a STREAM_CLOSED connection error. (RFC7540 5.1.1)"),
  2033. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  2034. ok = gen_tcp:send(Socket, [
  2035. "GET / HTTP/1.1\r\n"
  2036. "Host: localhost\r\n"
  2037. "Connection: Upgrade, HTTP2-Settings\r\n"
  2038. "Upgrade: h2c\r\n"
  2039. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  2040. "\r\n"]),
  2041. ok = do_recv_101(Socket),
  2042. %% Send a valid preface.
  2043. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  2044. %% Receive the server preface.
  2045. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  2046. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  2047. %% Send the SETTINGS ack.
  2048. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  2049. %% Receive the SETTINGS ack, and the response HEADERS and DATA (Stream ID 1).
  2050. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  2051. case gen_tcp:recv(Socket, 9, 1000) of
  2052. {ok, << 0:24, 4:8, 1:8, 0:32 >>} ->
  2053. [settings_ack|Acc];
  2054. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  2055. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  2056. [headers|Acc];
  2057. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  2058. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  2059. [data|Acc]
  2060. end
  2061. end, [], [1, 2, 3])),
  2062. case Received of
  2063. [settings_ack, headers, data] -> ok;
  2064. [headers, settings_ack, data] -> ok;
  2065. [headers, data, settings_ack] -> ok
  2066. end,
  2067. %% Send a HEADERS frame with streamid 1.
  2068. {HeadersBlock, _} = cow_hpack:encode([
  2069. {<<":method">>, <<"GET">>},
  2070. {<<":scheme">>, <<"http">>},
  2071. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2072. {<<":path">>, <<"/">>}
  2073. ]),
  2074. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  2075. %% Receive a STREAM_CLOSED connection error.
  2076. {ok, << _:24, 7:8, _:72, 5:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  2077. ok.
  2078. %% The RFC gives us various error codes to return for this case,
  2079. %% depending on whether the stream existed previously and how it
  2080. %% ended up being (half-)closed. Cowboy rejects all these HEADERS
  2081. %% frames the same way: with a STREAM_CLOSED connection error.
  2082. %% Making it a connection error is particularly important in the
  2083. %% cases where a stream error would be allowed because we avoid
  2084. %% having to decode the headers and save up resources.
  2085. reject_streamid_lower(Config) ->
  2086. doc("HEADERS frames received with streamid lower than the previous stream "
  2087. "must be rejected with a STREAM_CLOSED connection error. (RFC7540 5.1.1)"),
  2088. {ok, Socket} = do_handshake(Config),
  2089. %% Send a HEADERS frame with streamid 5.
  2090. {HeadersBlock, _} = cow_hpack:encode([
  2091. {<<":method">>, <<"GET">>},
  2092. {<<":scheme">>, <<"http">>},
  2093. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2094. {<<":path">>, <<"/">>}
  2095. ]),
  2096. ok = gen_tcp:send(Socket, cow_http2:headers(5, fin, HeadersBlock)),
  2097. %% Receive the response.
  2098. {ok, << Length1:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  2099. {ok, _} = gen_tcp:recv(Socket, Length1, 6000),
  2100. {ok, << Length2:24, 0:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  2101. {ok, _} = gen_tcp:recv(Socket, Length2, 6000),
  2102. %% Send a HEADERS frame with streamid 3.
  2103. ok = gen_tcp:send(Socket, cow_http2:headers(3, fin, HeadersBlock)),
  2104. %% Receive a STREAM_CLOSED connection error.
  2105. {ok, << _:24, 7:8, _:72, 5:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  2106. ok.
  2107. %% @todo We need an option to limit the number of streams one can open
  2108. %% on a connection. And we need to enforce it. (RFC7540 5.1.1)
  2109. %
  2110. % Stream identifiers cannot be reused. Long-lived connections can
  2111. % result in an endpoint exhausting the available range of stream
  2112. % identifiers. A server
  2113. % that is unable to establish a new stream identifier can send a GOAWAY
  2114. % frame so that the client is forced to open a new connection for new
  2115. % streams.
  2116. %% (RFC7540 5.2.1)
  2117. % 3. Flow control is directional with overall control provided by the
  2118. % receiver. A receiver MAY choose to set any window size that it
  2119. % desires for each stream and for the entire connection. A sender
  2120. % MUST respect flow-control limits imposed by a receiver. Clients,
  2121. % servers, and intermediaries all independently advertise their
  2122. % flow-control window as a receiver and abide by the flow-control
  2123. % limits set by their peer when sending.
  2124. %
  2125. % 4. The initial value for the flow-control window is 65,535 octets
  2126. % for both new streams and the overall connection.
  2127. %
  2128. % 5. The frame type determines whether flow control applies to a
  2129. % frame. Of the frames specified in this document, only DATA
  2130. % frames are subject to flow control; all other frame types do not
  2131. % consume space in the advertised flow-control window. This
  2132. % ensures that important control frames are not blocked by flow
  2133. % control.
  2134. %
  2135. % 6. Flow control cannot be disabled.
  2136. %% (RFC7540 5.2.2)
  2137. % Even with full awareness of the current bandwidth-delay product,
  2138. % implementation of flow control can be difficult. When using flow
  2139. % control, the receiver MUST read from the TCP receive buffer in a
  2140. % timely fashion. Failure to do so could lead to a deadlock when
  2141. % critical frames, such as WINDOW_UPDATE, are not read and acted upon.
  2142. %% @todo Stream priorities. (RFC7540 5.3 5.3.x)
  2143. %% (RFC7540 5.4.1)
  2144. % An endpoint that encounters a connection error SHOULD first send a
  2145. % GOAWAY frame (Section 6.8) with the stream identifier of the last
  2146. % stream that it successfully received from its peer. The GOAWAY frame
  2147. % includes an error code that indicates why the connection is
  2148. % terminating. After sending the GOAWAY frame for an error condition,
  2149. % the endpoint MUST close the TCP connection.
  2150. %
  2151. % An endpoint can end a connection at any time. In particular, an
  2152. % endpoint MAY choose to treat a stream error as a connection error.
  2153. % Endpoints SHOULD send a GOAWAY frame when ending a connection,
  2154. % providing that circumstances permit it.
  2155. %% (RFC7540 5.4.2)
  2156. % A RST_STREAM is the last frame that an endpoint can send on a stream.
  2157. % The peer that sends the RST_STREAM frame MUST be prepared to receive
  2158. % any frames that were sent or enqueued for sending by the remote peer.
  2159. % These frames can be ignored, except where they modify connection
  2160. % state (such as the state maintained for header compression
  2161. % (Section 4.3) or flow control).
  2162. %
  2163. % Normally, an endpoint SHOULD NOT send more than one RST_STREAM frame
  2164. % for any stream. However, an endpoint MAY send additional RST_STREAM
  2165. % frames if it receives frames on a closed stream after more than a
  2166. % round-trip time. This behavior is permitted to deal with misbehaving
  2167. % implementations.
  2168. %
  2169. % To avoid looping, an endpoint MUST NOT send a RST_STREAM in response
  2170. % to a RST_STREAM frame.
  2171. %% (RFC7540 5.5)
  2172. % Extensions are permitted to use new frame types (Section 4.1), new
  2173. % settings (Section 6.5.2), or new error codes (Section 7). Registries
  2174. % are established for managing these extension points: frame types
  2175. % (Section 11.2), settings (Section 11.3), and error codes
  2176. % (Section 11.4).
  2177. %
  2178. % Implementations MUST ignore unknown or unsupported values in all
  2179. % extensible protocol elements. Implementations MUST discard frames
  2180. % that have unknown or unsupported types. This means that any of these
  2181. % extension points can be safely used by extensions without prior
  2182. % arrangement or negotiation. However, extension frames that appear in
  2183. % the middle of a header block (Section 4.3) are not permitted; these
  2184. % MUST be treated as a connection error (Section 5.4.1) of type
  2185. % PROTOCOL_ERROR.
  2186. continuation_with_extension_frame_interleaved_error(Config) ->
  2187. doc("Extension frames interleaved in a header block must be rejected "
  2188. "with a PROTOCOL_ERROR connection error. "
  2189. "(RFC7540 4.3, RFC7540 5.5, RFC7540 6.2, RFC7540 6.10)"),
  2190. {ok, Socket} = do_handshake(Config),
  2191. %% Send an unterminated HEADERS frame followed by an extension frame.
  2192. ok = gen_tcp:send(Socket, [
  2193. << 0:24, 1:8, 0:7, 1:1, 0:1, 1:31 >>,
  2194. << 0:24, 128:8, 0:8, 0:32 >>
  2195. ]),
  2196. %% Receive a PROTOCOL_ERROR connection error.
  2197. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  2198. ok.
  2199. %% (RFC7540 6.1) DATA
  2200. % Padding: Padding octets that contain no application semantic value.
  2201. % Padding octets MUST be set to zero when sending. A receiver is
  2202. % not obligated to verify padding but MAY treat non-zero padding as
  2203. % a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
  2204. %
  2205. % DATA frames MUST be associated with a stream. If a DATA frame is
  2206. % received whose stream identifier field is 0x0, the recipient MUST
  2207. % respond with a connection error (Section 5.4.1) of type
  2208. % PROTOCOL_ERROR.
  2209. %% (RFC7540 6.2) HEADERS
  2210. % Padding: Padding octets that contain no application semantic value.
  2211. % Padding octets MUST be set to zero when sending. A receiver is
  2212. % not obligated to verify padding but MAY treat non-zero padding as
  2213. % a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
  2214. %
  2215. % A HEADERS frame carries the END_STREAM flag that signals the end
  2216. % of a stream. However, a HEADERS frame with the END_STREAM flag
  2217. % set can be followed by CONTINUATION frames on the same stream.
  2218. % Logically, the CONTINUATION frames are part of the HEADERS frame.
  2219. %
  2220. %% @todo We probably need a test for the server sending HEADERS too large.
  2221. % The payload of a HEADERS frame contains a header block fragment
  2222. % (Section 4.3). A header block that does not fit within a HEADERS
  2223. % frame is continued in a CONTINUATION frame (Section 6.10).
  2224. %
  2225. % HEADERS frames MUST be associated with a stream. If a HEADERS frame
  2226. % is received whose stream identifier field is 0x0, the recipient MUST
  2227. % respond with a connection error (Section 5.4.1) of type
  2228. % PROTOCOL_ERROR.
  2229. %% (RFC7540 6.3) PRIORITY
  2230. % The PRIORITY frame always identifies a stream. If a PRIORITY frame
  2231. % is received with a stream identifier of 0x0, the recipient MUST
  2232. % respond with a connection error (Section 5.4.1) of type
  2233. % PROTOCOL_ERROR.
  2234. %% (RFC7540 6.4) RST_STREAM
  2235. % The RST_STREAM frame fully terminates the referenced stream and
  2236. % causes it to enter the "closed" state. After receiving a RST_STREAM
  2237. % on a stream, the receiver MUST NOT send additional frames for that
  2238. % stream, with the exception of PRIORITY. However, after sending the
  2239. % RST_STREAM, the sending endpoint MUST be prepared to receive and
  2240. % process additional frames sent on the stream that might have been
  2241. % sent by the peer prior to the arrival of the RST_STREAM.
  2242. %
  2243. % RST_STREAM frames MUST be associated with a stream. If a RST_STREAM
  2244. % frame is received with a stream identifier of 0x0, the recipient MUST
  2245. % treat this as a connection error (Section 5.4.1) of type
  2246. % PROTOCOL_ERROR.
  2247. %% (RFC7540 6.5) SETTINGS
  2248. % A SETTINGS frame MUST be sent by both endpoints at the start of a
  2249. % connection and MAY be sent at any other time by either endpoint over
  2250. % the lifetime of the connection. Implementations MUST support all of
  2251. % the parameters defined by this specification.
  2252. %
  2253. % SETTINGS frames always apply to a connection, never a single stream.
  2254. % The stream identifier for a SETTINGS frame MUST be zero (0x0). If an
  2255. % endpoint receives a SETTINGS frame whose stream identifier field is
  2256. % anything other than 0x0, the endpoint MUST respond with a connection
  2257. % error (Section 5.4.1) of type PROTOCOL_ERROR.
  2258. %
  2259. % The SETTINGS frame affects connection state. A badly formed or
  2260. % incomplete SETTINGS frame MUST be treated as a connection error
  2261. % (Section 5.4.1) of type PROTOCOL_ERROR.
  2262. %% Settings.
  2263. settings_header_table_size_client(Config) ->
  2264. doc("The SETTINGS_HEADER_TABLE_SIZE setting can be used to "
  2265. "inform the server of the maximum header table size "
  2266. "used by the client to decode header blocks. (RFC7540 6.5.2)"),
  2267. HeaderTableSize = 128,
  2268. %% Do the handhsake.
  2269. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  2270. %% Send a valid preface.
  2271. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
  2272. cow_http2:settings(#{header_table_size => HeaderTableSize})]),
  2273. %% Receive the server preface.
  2274. {ok, << Len0:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  2275. {ok, << 4:8, 0:40, _:Len0/binary >>} = gen_tcp:recv(Socket, 6 + Len0, 1000),
  2276. %% Send the SETTINGS ack.
  2277. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  2278. %% Receive the SETTINGS ack.
  2279. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2280. %% Initialize decoding/encoding states.
  2281. DecodeState = cow_hpack:set_max_size(HeaderTableSize, cow_hpack:init()),
  2282. EncodeState = cow_hpack:init(),
  2283. %% Send a HEADERS frame as a request.
  2284. {ReqHeadersBlock1, _} = cow_hpack:encode([
  2285. {<<":method">>, <<"GET">>},
  2286. {<<":scheme">>, <<"http">>},
  2287. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2288. {<<":path">>, <<"/">>}
  2289. ], EncodeState),
  2290. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, ReqHeadersBlock1)),
  2291. %% Receive a HEADERS frame as a response.
  2292. {ok, << Len1:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  2293. {ok, RespHeadersBlock1} = gen_tcp:recv(Socket, Len1, 6000),
  2294. {RespHeaders, _} = cow_hpack:decode(RespHeadersBlock1, DecodeState),
  2295. {_, <<"200">>} = lists:keyfind(<<":status">>, 1, RespHeaders),
  2296. %% The decoding succeeded, confirming that the table size is
  2297. %% lower than or equal to HeaderTableSize.
  2298. ok.
  2299. settings_header_table_size_server(Config0) ->
  2300. doc("The SETTINGS_HEADER_TABLE_SIZE setting can be used to "
  2301. "inform the client of the maximum header table size "
  2302. "used by the server to decode header blocks. (RFC7540 6.5.2)"),
  2303. HeaderTableSize = 128,
  2304. %% Create a new listener that allows larger header table sizes.
  2305. Config = cowboy_test:init_http(name(), #{
  2306. env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
  2307. max_decode_table_size => HeaderTableSize
  2308. }, Config0),
  2309. %% Do the handhsake.
  2310. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  2311. %% Send a valid preface.
  2312. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
  2313. cow_http2:settings(#{header_table_size => HeaderTableSize})]),
  2314. %% Receive the server preface.
  2315. {ok, << Len0:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  2316. {ok, Data = <<_:48, _:Len0/binary>>} = gen_tcp:recv(Socket, 6 + Len0, 1000),
  2317. %% Confirm the server's SETTINGS_HEADERS_TABLE_SIZE uses HeaderTableSize.
  2318. {ok, {settings, #{header_table_size := HeaderTableSize}}, <<>>}
  2319. = cow_http2:parse(<<Len0:24, Data/binary>>),
  2320. %% Send the SETTINGS ack.
  2321. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  2322. %% Receive the SETTINGS ack.
  2323. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2324. %% Initialize decoding/encoding states.
  2325. DecodeState = cow_hpack:init(),
  2326. EncodeState = cow_hpack:set_max_size(HeaderTableSize, cow_hpack:init()),
  2327. %% Send a HEADERS frame as a request.
  2328. {ReqHeadersBlock1, _} = cow_hpack:encode([
  2329. {<<":method">>, <<"GET">>},
  2330. {<<":scheme">>, <<"http">>},
  2331. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2332. {<<":path">>, <<"/">>}
  2333. ], EncodeState),
  2334. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, ReqHeadersBlock1)),
  2335. %% Receive a HEADERS frame as a response.
  2336. {ok, << Len1:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  2337. {ok, RespHeadersBlock1} = gen_tcp:recv(Socket, Len1, 6000),
  2338. {RespHeaders, _} = cow_hpack:decode(RespHeadersBlock1, DecodeState),
  2339. {_, <<"200">>} = lists:keyfind(<<":status">>, 1, RespHeaders),
  2340. %% The decoding succeeded on the server, confirming that
  2341. %% the table size was updated to HeaderTableSize.
  2342. ok.
  2343. % SETTINGS_ENABLE_PUSH (0x2): This setting can be used to disable
  2344. % server push (Section 8.2). An endpoint MUST NOT send a
  2345. % PUSH_PROMISE frame if it receives this parameter set to a value of
  2346. % 0. An endpoint that has both set this parameter to 0 and had it
  2347. % acknowledged MUST treat the receipt of a PUSH_PROMISE frame as a
  2348. % connection error (Section 5.4.1) of type PROTOCOL_ERROR.
  2349. %% @todo settings_disable_push
  2350. settings_max_concurrent_streams(Config0) ->
  2351. doc("The SETTINGS_MAX_CONCURRENT_STREAMS setting can be used to "
  2352. "restrict the number of concurrent streams. (RFC7540 5.1.2, RFC7540 6.5.2)"),
  2353. %% Create a new listener that allows only a single concurrent stream.
  2354. Config = cowboy_test:init_http(name(), #{
  2355. env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
  2356. max_concurrent_streams => 1
  2357. }, Config0),
  2358. {ok, Socket} = do_handshake(Config),
  2359. %% Send two HEADERS frames as two separate streams.
  2360. Headers = [
  2361. {<<":method">>, <<"GET">>},
  2362. {<<":scheme">>, <<"http">>},
  2363. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2364. {<<":path">>, <<"/long_polling">>}
  2365. ],
  2366. {ReqHeadersBlock1, EncodeState} = cow_hpack:encode(Headers),
  2367. {ReqHeadersBlock2, _} = cow_hpack:encode(Headers, EncodeState),
  2368. ok = gen_tcp:send(Socket, [
  2369. cow_http2:headers(1, fin, ReqHeadersBlock1),
  2370. cow_http2:headers(3, fin, ReqHeadersBlock2)
  2371. ]),
  2372. %% Receive a REFUSED_STREAM stream error.
  2373. {ok, << _:24, 3:8, _:8, 3:32, 7:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  2374. ok.
  2375. settings_max_concurrent_streams_0(Config0) ->
  2376. doc("The SETTINGS_MAX_CONCURRENT_STREAMS setting can be set to "
  2377. "0 to refuse all incoming streams. (RFC7540 5.1.2, RFC7540 6.5.2)"),
  2378. %% Create a new listener that allows only a single concurrent stream.
  2379. Config = cowboy_test:init_http(name(), #{
  2380. env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
  2381. max_concurrent_streams => 0
  2382. }, Config0),
  2383. {ok, Socket} = do_handshake(Config),
  2384. %% Send a HEADERS frame.
  2385. {HeadersBlock, _} = cow_hpack:encode([
  2386. {<<":method">>, <<"GET">>},
  2387. {<<":scheme">>, <<"http">>},
  2388. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2389. {<<":path">>, <<"/long_polling">>}
  2390. ]),
  2391. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  2392. %% Receive a REFUSED_STREAM stream error.
  2393. {ok, << _:24, 3:8, _:8, 1:32, 7:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  2394. ok.
  2395. %% @todo The client can limit the number of concurrent streams too. (RFC7540 5.1.2)
  2396. %
  2397. % A peer can limit the number of concurrently active streams using the
  2398. % SETTINGS_MAX_CONCURRENT_STREAMS parameter (see Section 6.5.2) within
  2399. % a SETTINGS frame. The maximum concurrent streams setting is specific
  2400. % to each endpoint and applies only to the peer that receives the
  2401. % setting. That is, clients specify the maximum number of concurrent
  2402. % streams the server can initiate, and servers specify the maximum
  2403. % number of concurrent streams the client can initiate.
  2404. %
  2405. % Endpoints MUST NOT exceed the limit set by their peer. An endpoint
  2406. % that receives a HEADERS frame that causes its advertised concurrent
  2407. % stream limit to be exceeded MUST treat this as a stream error
  2408. % (Section 5.4.2) of type PROTOCOL_ERROR or REFUSED_STREAM. The choice
  2409. % of error code determines whether the endpoint wishes to enable
  2410. % automatic retry (see Section 8.1.4) for details).
  2411. settings_initial_window_size(Config0) ->
  2412. doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
  2413. "change the initial window size of streams. (RFC7540 6.5.2)"),
  2414. %% Create a new listener that sets initial window sizes to 100000.
  2415. Config = cowboy_test:init_http(name(), #{
  2416. env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
  2417. initial_connection_window_size => 100000,
  2418. initial_stream_window_size => 100000
  2419. }, Config0),
  2420. %% We need to do the handshake manually because a WINDOW_UPDATE
  2421. %% frame will be sent to update the connection window.
  2422. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  2423. %% Send a valid preface.
  2424. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  2425. %% Receive the server preface.
  2426. {ok, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  2427. {ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
  2428. %% Send the SETTINGS ack.
  2429. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  2430. %% Receive the WINDOW_UPDATE for the connection.
  2431. {ok, << 4:24, 8:8, 0:40, _:32 >>} = gen_tcp:recv(Socket, 13, 1000),
  2432. %% Receive the SETTINGS ack.
  2433. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2434. %% Send a HEADERS frame initiating a stream followed by
  2435. %% DATA frames totaling 90000 bytes of body.
  2436. Headers = [
  2437. {<<":method">>, <<"POST">>},
  2438. {<<":scheme">>, <<"http">>},
  2439. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2440. {<<":path">>, <<"/long_polling">>}
  2441. ],
  2442. {HeadersBlock, _} = cow_hpack:encode(Headers),
  2443. ok = gen_tcp:send(Socket, [
  2444. cow_http2:headers(1, nofin, HeadersBlock),
  2445. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2446. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2447. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2448. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2449. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2450. cow_http2:data(1, fin, <<0:15000/unit:8>>)
  2451. ]),
  2452. %% Receive a proper response.
  2453. {ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  2454. {ok, _} = gen_tcp:recv(Socket, Len2, 6000),
  2455. %% No errors follow due to our sending of more than 65535 bytes of data.
  2456. {error, timeout} = gen_tcp:recv(Socket, 0, 1000),
  2457. ok.
  2458. settings_initial_window_size_after_ack(Config0) ->
  2459. doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
  2460. "change the initial window size of streams. It is applied "
  2461. "to all existing streams upon receipt of the SETTINGS ack. (RFC7540 6.5.2)"),
  2462. %% Create a new listener that sets the initial stream window sizes to 0.
  2463. Config = cowboy_test:init_http(name(), #{
  2464. env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
  2465. initial_stream_window_size => 0
  2466. }, Config0),
  2467. %% We need to do the handshake manually because we don't
  2468. %% want to send the SETTINGS ack immediately.
  2469. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  2470. %% Send a valid preface.
  2471. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  2472. %% Receive the server preface.
  2473. {ok, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  2474. {ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
  2475. %%
  2476. %% Don't send the SETTINGS ack yet! We want to create a stream first.
  2477. %%
  2478. %% Receive the SETTINGS ack.
  2479. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2480. %% Send a HEADERS frame initiating a stream, a SETTINGS ack
  2481. %% and a small DATA frame despite no window available in the stream.
  2482. Headers = [
  2483. {<<":method">>, <<"POST">>},
  2484. {<<":scheme">>, <<"http">>},
  2485. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2486. {<<":path">>, <<"/long_polling">>}
  2487. ],
  2488. {HeadersBlock, _} = cow_hpack:encode(Headers),
  2489. ok = gen_tcp:send(Socket, [
  2490. cow_http2:headers(1, nofin, HeadersBlock),
  2491. cow_http2:settings_ack(),
  2492. cow_http2:data(1, fin, <<0:32/unit:8>>)
  2493. ]),
  2494. %% Receive a FLOW_CONTROL_ERROR stream error.
  2495. {ok, << _:24, 3:8, _:8, 1:32, 3:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  2496. ok.
  2497. settings_initial_window_size_before_ack(Config0) ->
  2498. doc("The SETTINGS_INITIAL_WINDOW_SIZE setting can be used to "
  2499. "change the initial window size of streams. It is only "
  2500. "applied upon receipt of the SETTINGS ack. (RFC7540 6.5.2)"),
  2501. %% Create a new listener that sets the initial stream window sizes to 0.
  2502. Config = cowboy_test:init_http(name(), #{
  2503. env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
  2504. initial_stream_window_size => 0
  2505. }, Config0),
  2506. %% We need to do the handshake manually because we don't
  2507. %% want to send the SETTINGS ack.
  2508. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  2509. %% Send a valid preface.
  2510. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  2511. %% Receive the server preface.
  2512. {ok, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  2513. {ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
  2514. %%
  2515. %% Don't send the SETTINGS ack! We want the server to keep the original settings.
  2516. %%
  2517. %% Receive the SETTINGS ack.
  2518. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2519. %% Send a HEADERS frame initiating a stream followed by
  2520. %% DATA frames totaling 60000 bytes of body.
  2521. Headers = [
  2522. {<<":method">>, <<"POST">>},
  2523. {<<":scheme">>, <<"http">>},
  2524. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2525. {<<":path">>, <<"/long_polling">>}
  2526. ],
  2527. {HeadersBlock, _} = cow_hpack:encode(Headers),
  2528. ok = gen_tcp:send(Socket, [
  2529. cow_http2:headers(1, nofin, HeadersBlock),
  2530. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2531. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2532. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2533. cow_http2:data(1, fin, <<0:15000/unit:8>>)
  2534. ]),
  2535. %% Receive a proper response.
  2536. {ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  2537. {ok, _} = gen_tcp:recv(Socket, Len2, 6000),
  2538. %% No errors follow due to our sending of more than 0 bytes of data.
  2539. {error, timeout} = gen_tcp:recv(Socket, 0, 1000),
  2540. ok.
  2541. settings_max_frame_size(Config0) ->
  2542. doc("The SETTINGS_MAX_FRAME_SIZE setting can be used to "
  2543. "change the maximum frame size allowed. (RFC7540 6.5.2)"),
  2544. %% Create a new listener that sets the maximum frame size to 30000.
  2545. Config = cowboy_test:init_http(name(), #{
  2546. env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
  2547. max_frame_size_received => 30000
  2548. }, Config0),
  2549. %% Do the handshake.
  2550. {ok, Socket} = do_handshake(Config),
  2551. %% Send a HEADERS frame initiating a stream followed by
  2552. %% a single 25000 bytes DATA frame.
  2553. Headers = [
  2554. {<<":method">>, <<"POST">>},
  2555. {<<":scheme">>, <<"http">>},
  2556. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2557. {<<":path">>, <<"/long_polling">>}
  2558. ],
  2559. {HeadersBlock, _} = cow_hpack:encode(Headers),
  2560. ok = gen_tcp:send(Socket, [
  2561. cow_http2:headers(1, nofin, HeadersBlock),
  2562. cow_http2:data(1, fin, <<0:25000/unit:8>>)
  2563. ]),
  2564. %% Receive a proper response.
  2565. {ok, << Len2:24, 1:8, _:40 >>} = gen_tcp:recv(Socket, 9, 6000),
  2566. {ok, _} = gen_tcp:recv(Socket, Len2, 6000),
  2567. %% No errors follow due to our sending of a 25000 bytes frame.
  2568. {error, timeout} = gen_tcp:recv(Socket, 0, 1000),
  2569. ok.
  2570. settings_max_frame_size_reject_too_small(Config) ->
  2571. doc("A SETTINGS_MAX_FRAME_SIZE smaller than 16384 must be rejected "
  2572. "with a PROTOCOL_ERROR connection error. (RFC7540 6.5.2)"),
  2573. {ok, Socket} = do_handshake(Config),
  2574. %% Send a SETTINGS frame with a SETTINGS_MAX_FRAME_SIZE lower than 16384.
  2575. ok = gen_tcp:send(Socket, << 6:24, 4:8, 0:40, 5:16, 16383:32 >>),
  2576. %% Receive a PROTOCOL_ERROR connection error.
  2577. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  2578. ok.
  2579. settings_max_frame_size_reject_too_large(Config) ->
  2580. doc("A SETTINGS_MAX_FRAME_SIZE larger than 16777215 must be rejected "
  2581. "with a PROTOCOL_ERROR connection error. (RFC7540 6.5.2)"),
  2582. {ok, Socket} = do_handshake(Config),
  2583. %% Send a SETTINGS frame with a SETTINGS_MAX_FRAME_SIZE larger than 16777215.
  2584. ok = gen_tcp:send(Socket, << 6:24, 4:8, 0:40, 5:16, 16777216:32 >>),
  2585. %% Receive a PROTOCOL_ERROR connection error.
  2586. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  2587. ok.
  2588. % SETTINGS_MAX_HEADER_LIST_SIZE (0x6): This advisory setting informs a
  2589. % peer of the maximum size of header list that the sender is
  2590. % prepared to accept, in octets. The value is based on the
  2591. % uncompressed size of header fields, including the length of the
  2592. % name and value in octets plus an overhead of 32 octets for each
  2593. % header field.
  2594. %
  2595. % For any given request, a lower limit than what is advertised MAY
  2596. % be enforced. The initial value of this setting is unlimited.
  2597. %
  2598. % An endpoint that receives a SETTINGS frame with any unknown or
  2599. % unsupported identifier MUST ignore that setting. (6.5.2 and 6.5.3)
  2600. %% (RFC7540 6.5.3)
  2601. % Upon receiving a SETTINGS frame with the ACK flag set, the
  2602. % sender of the altered parameters can rely on the setting having been
  2603. % applied.
  2604. %
  2605. % If the sender of a SETTINGS frame does not receive an acknowledgement
  2606. % within a reasonable amount of time, it MAY issue a connection error
  2607. % (Section 5.4.1) of type SETTINGS_TIMEOUT.
  2608. %% (RFC7540 6.6) PUSH_PROMISE
  2609. % @todo PUSH_PROMISE frames have a reserved bit in the payload that must be ignored.
  2610. %
  2611. % Padding: Padding octets that contain no application semantic value.
  2612. % Padding octets MUST be set to zero when sending. A receiver is
  2613. % not obligated to verify padding but MAY treat non-zero padding as
  2614. % a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
  2615. %
  2616. % PUSH_PROMISE frames MUST only be sent on a peer-initiated stream that
  2617. % is in either the "open" or "half-closed (remote)" state. The stream
  2618. % identifier of a PUSH_PROMISE frame indicates the stream it is
  2619. % associated with. If the stream identifier field specifies the value
  2620. % 0x0, a recipient MUST respond with a connection error (Section 5.4.1)
  2621. % of type PROTOCOL_ERROR.
  2622. %
  2623. % PUSH_PROMISE MUST NOT be sent if the SETTINGS_ENABLE_PUSH setting of
  2624. % the peer endpoint is set to 0. An endpoint that has set this setting
  2625. % and has received acknowledgement MUST treat the receipt of a
  2626. % PUSH_PROMISE frame as a connection error (Section 5.4.1) of type
  2627. % PROTOCOL_ERROR.
  2628. %
  2629. % Since PUSH_PROMISE reserves a stream, ignoring a PUSH_PROMISE frame
  2630. % causes the stream state to become indeterminate. A receiver MUST
  2631. % treat the receipt of a PUSH_PROMISE on a stream that is neither
  2632. % "open" nor "half-closed (local)" as a connection error
  2633. % (Section 5.4.1) of type PROTOCOL_ERROR.
  2634. %
  2635. % A receiver MUST treat the receipt of a PUSH_PROMISE that promises an
  2636. % illegal stream identifier (Section 5.1.1) as a connection error
  2637. % (Section 5.4.1) of type PROTOCOL_ERROR. Note that an illegal stream
  2638. % identifier is an identifier for a stream that is not currently in the
  2639. % "idle" state.
  2640. %% (RFC7540 6.7) PING
  2641. % PING frames are not associated with any individual stream. If a PING
  2642. % frame is received with a stream identifier field value other than
  2643. % 0x0, the recipient MUST respond with a connection error
  2644. % (Section 5.4.1) of type PROTOCOL_ERROR.
  2645. %% (RFC7540 6.8) GOAWAY
  2646. % @todo GOAWAY frames have a reserved bit in the payload that must be ignored.
  2647. %
  2648. %% @todo We should eventually implement the mechanism for gracefully
  2649. %% shutting down of the connection. (Send the GOAWAY, finish processing
  2650. %% the current set of streams, give up after a certain timeout.)
  2651. %
  2652. %% @todo If we graceful shutdown and receive a GOAWAY, we give up too.
  2653. % A GOAWAY frame might not immediately precede closing of the
  2654. % connection; a receiver of a GOAWAY that has no more use for the
  2655. % connection SHOULD still send a GOAWAY frame before terminating the
  2656. % connection.
  2657. %
  2658. %% @todo And it gets more complex when you think about h1 to h2 proxies.
  2659. % A server that is attempting to gracefully shut down a
  2660. % connection SHOULD send an initial GOAWAY frame with the last stream
  2661. % identifier set to 2^31-1 and a NO_ERROR code. This signals to the
  2662. % client that a shutdown is imminent and that initiating further
  2663. % requests is prohibited. After allowing time for any in-flight stream
  2664. % creation (at least one round-trip time), the server can send another
  2665. % GOAWAY frame with an updated last stream identifier. This ensures
  2666. % that a connection can be cleanly shut down without losing requests.
  2667. %
  2668. %% @todo And of course even if we shutdown we need to be careful about
  2669. %% the connection state.
  2670. % After sending a GOAWAY frame, the sender can discard frames for
  2671. % streams initiated by the receiver with identifiers higher than the
  2672. % identified last stream. However, any frames that alter connection
  2673. % state cannot be completely ignored. For instance, HEADERS,
  2674. % PUSH_PROMISE, and CONTINUATION frames MUST be minimally processed to
  2675. % ensure the state maintained for header compression is consistent (see
  2676. % Section 4.3); similarly, DATA frames MUST be counted toward the
  2677. % connection flow-control window. Failure to process these frames can
  2678. % cause flow control or header compression state to become
  2679. % unsynchronized.
  2680. %
  2681. % The GOAWAY frame applies to the connection, not a specific stream.
  2682. % An endpoint MUST treat a GOAWAY frame with a stream identifier other
  2683. % than 0x0 as a connection error (Section 5.4.1) of type
  2684. % PROTOCOL_ERROR.
  2685. %% (RFC7540 6.9) WINDOW_UPDATE
  2686. % @todo WINDOW_UPDATE frames have a reserved bit in the payload that must be ignored.
  2687. window_update_reject_0(Config) ->
  2688. doc("WINDOW_UPDATE frames with an increment of 0 for the connection "
  2689. "flow control window must be rejected with a "
  2690. "PROTOCOL_ERROR connection error. (RFC7540 6.9.1)"),
  2691. {ok, Socket} = do_handshake(Config),
  2692. %% Send connection-wide WINDOW_UPDATE frame with a value of 0.
  2693. ok = gen_tcp:send(Socket, [
  2694. cow_http2:window_update(0)
  2695. ]),
  2696. %% Receive a PROTOCOL_ERROR connection error.
  2697. {ok, << _:24, 7:8, _:72, 1:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  2698. ok.
  2699. window_update_reject_0_stream(Config) ->
  2700. doc("WINDOW_UPDATE frames with an increment of 0 for a stream "
  2701. "flow control window must be rejected with a "
  2702. "PROTOCOL_ERROR stream error. (RFC7540 6.9.1)"),
  2703. {ok, Socket} = do_handshake(Config),
  2704. %% Send a HEADERS frame immediately followed by
  2705. %% a WINDOW_UPDATE frame with a value of 0.
  2706. {HeadersBlock, _} = cow_hpack:encode([
  2707. {<<":method">>, <<"GET">>},
  2708. {<<":scheme">>, <<"http">>},
  2709. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2710. {<<":path">>, <<"/">>}
  2711. ]),
  2712. ok = gen_tcp:send(Socket, [
  2713. cow_http2:headers(1, fin, HeadersBlock),
  2714. cow_http2:window_update(1, 0)
  2715. ]),
  2716. %% Receive a PROTOCOL_ERROR stream error.
  2717. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  2718. ok.
  2719. % A receiver that receives a flow-controlled frame MUST always account
  2720. % for its contribution against the connection flow-control window,
  2721. % unless the receiver treats this as a connection error
  2722. % (Section 5.4.1). This is necessary even if the frame is in error.
  2723. % The sender counts the frame toward the flow-control window, but if
  2724. % the receiver does not, the flow-control window at the sender and
  2725. % receiver can become different.
  2726. data_reject_overflow(Config0) ->
  2727. doc("DATA frames that cause the connection flow control window "
  2728. "to overflow must be rejected with a FLOW_CONTROL_ERROR "
  2729. "connection error. (RFC7540 6.9.1)"),
  2730. %% Create a new listener that allows only a single concurrent stream.
  2731. Config = cowboy_test:init_http(name(), #{
  2732. env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
  2733. initial_stream_window_size => 100000
  2734. }, Config0),
  2735. {ok, Socket} = do_handshake(Config),
  2736. %% Send a HEADERS frame initiating a stream followed by
  2737. %% DATA frames totaling 90000 bytes of body.
  2738. Headers = [
  2739. {<<":method">>, <<"POST">>},
  2740. {<<":scheme">>, <<"http">>},
  2741. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2742. {<<":path">>, <<"/long_polling">>}
  2743. ],
  2744. {HeadersBlock, _} = cow_hpack:encode(Headers),
  2745. ok = gen_tcp:send(Socket, [
  2746. cow_http2:headers(1, nofin, HeadersBlock),
  2747. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2748. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2749. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2750. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2751. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2752. cow_http2:data(1, fin, <<0:15000/unit:8>>)
  2753. ]),
  2754. %% Receive a FLOW_CONTROL_ERROR connection error.
  2755. {ok, << _:24, 7:8, _:72, 3:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  2756. ok.
  2757. data_reject_overflow_stream(Config0) ->
  2758. doc("DATA frames that cause the stream flow control window "
  2759. "to overflow must be rejected with a FLOW_CONTROL_ERROR "
  2760. "stream error. (RFC7540 6.9.1)"),
  2761. %% Create a new listener that allows only a single concurrent stream.
  2762. Config = cowboy_test:init_http(name(), #{
  2763. env => #{dispatch => cowboy_router:compile(init_routes(Config0))},
  2764. initial_connection_window_size => 100000
  2765. }, Config0),
  2766. %% We need to do the handshake manually because a WINDOW_UPDATE
  2767. %% frame will be sent to update the connection window.
  2768. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  2769. %% Send a valid preface.
  2770. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  2771. %% Receive the server preface.
  2772. {ok, << Len1:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  2773. {ok, << 4:8, 0:40, _:Len1/binary >>} = gen_tcp:recv(Socket, 6 + Len1, 1000),
  2774. %% Send the SETTINGS ack.
  2775. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  2776. %% Receive the WINDOW_UPDATE for the connection.
  2777. {ok, << 4:24, 8:8, 0:40, _:32 >>} = gen_tcp:recv(Socket, 13, 1000),
  2778. %% Receive the SETTINGS ack.
  2779. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2780. %% Send a HEADERS frame initiating a stream followed by
  2781. %% DATA frames totaling 90000 bytes of body.
  2782. Headers = [
  2783. {<<":method">>, <<"POST">>},
  2784. {<<":scheme">>, <<"http">>},
  2785. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2786. {<<":path">>, <<"/long_polling">>}
  2787. ],
  2788. {HeadersBlock, _} = cow_hpack:encode(Headers),
  2789. ok = gen_tcp:send(Socket, [
  2790. cow_http2:headers(1, nofin, HeadersBlock),
  2791. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2792. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2793. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2794. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2795. cow_http2:data(1, nofin, <<0:15000/unit:8>>),
  2796. cow_http2:data(1, fin, <<0:15000/unit:8>>)
  2797. ]),
  2798. %% Receive a FLOW_CONTROL_ERROR stream error.
  2799. {ok, << _:24, 3:8, _:8, 1:32, 3:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  2800. ok.
  2801. %% (RFC7540 6.9.1)
  2802. % Frames with zero length with the END_STREAM flag set (that
  2803. % is, an empty DATA frame) MAY be sent if there is no available space
  2804. % in either flow-control window.
  2805. window_update_reject_overflow(Config) ->
  2806. doc("WINDOW_UPDATE frames that cause the connection flow control "
  2807. "window to exceed 2^31-1 must be rejected with a "
  2808. "FLOW_CONTROL_ERROR connection error. (RFC7540 6.9.1)"),
  2809. {ok, Socket} = do_handshake(Config),
  2810. %% Send a connection-wide WINDOW_UPDATE frame that causes the window to overflow.
  2811. ok = gen_tcp:send(Socket, [
  2812. cow_http2:window_update(16#7fffffff)
  2813. ]),
  2814. %% Receive a FLOW_CONTROL_ERROR connection error.
  2815. {ok, << _:24, 7:8, _:72, 3:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  2816. ok.
  2817. window_update_reject_overflow_stream(Config) ->
  2818. doc("WINDOW_UPDATE frames that cause a stream flow control "
  2819. "window to exceed 2^31-1 must be rejected with a "
  2820. "FLOW_CONTROL_ERROR stream error. (RFC7540 6.9.1)"),
  2821. {ok, Socket} = do_handshake(Config),
  2822. %% Send a HEADERS frame immediately followed by a WINDOW_UPDATE
  2823. %% frame that causes the stream window to overflow.
  2824. {HeadersBlock, _} = cow_hpack:encode([
  2825. {<<":method">>, <<"GET">>},
  2826. {<<":scheme">>, <<"http">>},
  2827. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2828. {<<":path">>, <<"/">>}
  2829. ]),
  2830. ok = gen_tcp:send(Socket, [
  2831. cow_http2:headers(1, fin, HeadersBlock),
  2832. cow_http2:window_update(1, 16#7fffffff)
  2833. ]),
  2834. %% Receive a FLOW_CONTROL_ERROR stream error.
  2835. {ok, << _:24, 3:8, _:8, 1:32, 3:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  2836. ok.
  2837. settings_initial_window_size_changes(Config) ->
  2838. doc("When the value of SETTINGS_INITIAL_WINDOW_SIZE changes, the server "
  2839. "must adjust the size of the flow control windows of the active "
  2840. "streams. (RFC7540 6.9.2)"),
  2841. {ok, Socket} = do_handshake(Config),
  2842. %% Set SETTINGS_INITIAL_WINDOW_SIZE to 0 to prevent sending of DATA.
  2843. ok = gen_tcp:send(Socket, cow_http2:settings(#{initial_window_size => 0})),
  2844. %% Receive the SETTINGS ack.
  2845. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2846. %% Send a HEADERS frame.
  2847. {HeadersBlock, _} = cow_hpack:encode([
  2848. {<<":method">>, <<"GET">>},
  2849. {<<":scheme">>, <<"http">>},
  2850. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2851. {<<":path">>, <<"/">>}
  2852. ]),
  2853. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  2854. %% Receive a response but no DATA frames are coming.
  2855. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2856. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  2857. {error, timeout} = gen_tcp:recv(Socket, 9, 1000),
  2858. %% Set SETTINGS_INITIAL_WINDOW_SIZE to a larger value.
  2859. ok = gen_tcp:send(Socket, cow_http2:settings(#{initial_window_size => 5})),
  2860. %% Receive the SETTINGS ack.
  2861. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2862. %% Receive a DATA frame of that size and no other.
  2863. {ok, << 5:24, 0:8, 0:8, 1:32, "Hello" >>} = gen_tcp:recv(Socket, 14, 1000),
  2864. {error, timeout} = gen_tcp:recv(Socket, 9, 1000),
  2865. %% Set SETTINGS_INITIAL_WINDOW_SIZE to exactly the size in the body.
  2866. ok = gen_tcp:send(Socket, cow_http2:settings(#{initial_window_size => 12})),
  2867. %% Receive the SETTINGS ack.
  2868. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2869. %% Receive the rest of the response.
  2870. {ok, << 7:24, 0:8, 1:8, 1:32, " world!" >>} = gen_tcp:recv(Socket, 16, 1000),
  2871. ok.
  2872. settings_initial_window_size_changes_negative(Config) ->
  2873. doc("When the value of SETTINGS_INITIAL_WINDOW_SIZE changes, the server "
  2874. "must adjust the size of the flow control windows of the active "
  2875. "streams even if their window end up negative. (RFC7540 6.9.2)"),
  2876. {ok, Socket} = do_handshake(Config),
  2877. %% Set SETTINGS_INITIAL_WINDOW_SIZE to 5.
  2878. ok = gen_tcp:send(Socket, cow_http2:settings(#{initial_window_size => 5})),
  2879. %% Receive the SETTINGS ack.
  2880. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2881. %% Send a HEADERS frame.
  2882. {HeadersBlock, _} = cow_hpack:encode([
  2883. {<<":method">>, <<"GET">>},
  2884. {<<":scheme">>, <<"http">>},
  2885. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2886. {<<":path">>, <<"/">>}
  2887. ]),
  2888. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  2889. %% Receive a response with a single DATA frame of the initial size we set.
  2890. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2891. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  2892. {ok, << 5:24, 0:8, 0:8, 1:32, "Hello" >>} = gen_tcp:recv(Socket, 14, 1000),
  2893. {error, timeout} = gen_tcp:recv(Socket, 9, 1000),
  2894. %% Set SETTINGS_INITIAL_WINDOW_SIZE to 0 to make the stream's window negative.
  2895. ok = gen_tcp:send(Socket, cow_http2:settings(#{initial_window_size => 0})),
  2896. %% Receive the SETTINGS ack.
  2897. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2898. %% Set SETTINGS_INITIAL_WINDOW_SIZE to exactly the size in the body.
  2899. ok = gen_tcp:send(Socket, cow_http2:settings(#{initial_window_size => 12})),
  2900. %% Receive the SETTINGS ack.
  2901. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  2902. %% Receive the rest of the response.
  2903. {ok, << 7:24, 0:8, 1:8, 1:32, " world!" >>} = gen_tcp:recv(Socket, 16, 1000),
  2904. ok.
  2905. settings_initial_window_size_reject_overflow(Config) ->
  2906. doc("A SETTINGS_INITIAL_WINDOW_SIZE that causes a flow control window "
  2907. "to exceed 2^31-1 must be rejected with a FLOW_CONTROL_ERROR "
  2908. "connection error. (RFC7540 6.5.2, RFC7540 6.9.2)"),
  2909. {ok, Socket} = do_handshake(Config),
  2910. %% Set SETTINGS_INITIAL_WINDOW_SIZE to 2^31.
  2911. ok = gen_tcp:send(Socket, cow_http2:settings(#{initial_window_size => 16#80000000})),
  2912. %% Receive a FLOW_CONTROL_ERROR connection error.
  2913. {ok, << _:24, 7:8, _:72, 3:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  2914. ok.
  2915. %% (RFC7540 6.9.3)
  2916. %% @todo The right way to do this seems to be to wait for the SETTINGS ack
  2917. %% before we KNOW the flow control window was updated on the other side.
  2918. % A receiver that wishes to use a smaller flow-control window than the
  2919. % current size can send a new SETTINGS frame. However, the receiver
  2920. % MUST be prepared to receive data that exceeds this window size, since
  2921. % the sender might send data that exceeds the lower limit prior to
  2922. % processing the SETTINGS frame.
  2923. %% (RFC7540 6.10) CONTINUATION
  2924. % CONTINUATION frames MUST be associated with a stream. If a
  2925. % CONTINUATION frame is received whose stream identifier field is 0x0,
  2926. % the recipient MUST respond with a connection error (Section 5.4.1) of
  2927. % type PROTOCOL_ERROR.
  2928. %
  2929. % A CONTINUATION frame MUST be preceded by a HEADERS, PUSH_PROMISE or
  2930. % CONTINUATION frame without the END_HEADERS flag set. A recipient
  2931. % that observes violation of this rule MUST respond with a connection
  2932. % error (Section 5.4.1) of type PROTOCOL_ERROR.
  2933. %% (RFC7540 7) Error Codes
  2934. % Unknown or unsupported error codes MUST NOT trigger any special
  2935. % behavior. These MAY be treated by an implementation as being
  2936. % equivalent to INTERNAL_ERROR.
  2937. headers_informational_nofin(Config) ->
  2938. doc("Informational HEADERS frames must not have the END_STREAM flag set. (RFC7540 8.1)"),
  2939. {ok, Socket} = do_handshake(Config),
  2940. %% Send a HEADERS frame on an idle stream.
  2941. {HeadersBlock, _} = cow_hpack:encode([
  2942. {<<":method">>, <<"POST">>},
  2943. {<<":scheme">>, <<"http">>},
  2944. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2945. {<<":path">>, <<"/echo/read_body">>},
  2946. {<<"expect">>, <<"100-continue">>},
  2947. {<<"content-length">>, <<"1000000">>}
  2948. ]),
  2949. ok = gen_tcp:send(Socket, cow_http2:headers(1, nofin, HeadersBlock)),
  2950. %% Receive an informational HEADERS frame without the END_STREAM flag.
  2951. {ok, << Len:24, 1:8, 0:5, 1:1, 0:2, _:32 >>} = gen_tcp:recv(Socket, 9, 6000),
  2952. {ok, RespHeadersBlock} = gen_tcp:recv(Socket, Len, 6000),
  2953. %% Confirm it has a 100 status code.
  2954. {RespHeaders, _} = cow_hpack:decode(RespHeadersBlock),
  2955. {_, <<"100">>} = lists:keyfind(<<":status">>, 1, RespHeaders),
  2956. ok.
  2957. %% (RFC7540 8.1)
  2958. % A HEADERS frame (and associated CONTINUATION frames) can only appear
  2959. % at the start or end of a stream. An endpoint that receives a HEADERS
  2960. % frame without the END_STREAM flag set after receiving a final (non-
  2961. % informational) status code MUST treat the corresponding request or
  2962. % response as malformed (Section 8.1.2.6).
  2963. %
  2964. %% @todo This one is interesting to implement because Cowboy DOES this.
  2965. % A server can
  2966. % send a complete response prior to the client sending an entire
  2967. % request if the response does not depend on any portion of the request
  2968. % that has not been sent and received. When this is true, a server MAY
  2969. % request that the client abort transmission of a request without error
  2970. % by sending a RST_STREAM with an error code of NO_ERROR after sending
  2971. % a complete response (i.e., a frame with the END_STREAM flag).
  2972. headers_reject_uppercase_header_name(Config) ->
  2973. doc("Requests containing uppercase header names must be rejected "
  2974. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2, RFC7540 8.1.2.6)"),
  2975. {ok, Socket} = do_handshake(Config),
  2976. %% Send a HEADERS frame with a uppercase header name.
  2977. {HeadersBlock, _} = cow_hpack:encode([
  2978. {<<":method">>, <<"GET">>},
  2979. {<<":scheme">>, <<"http">>},
  2980. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2981. {<<":path">>, <<"/">>},
  2982. {<<"HELLO">>, <<"world">>}
  2983. ]),
  2984. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  2985. %% Receive a PROTOCOL_ERROR stream error.
  2986. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  2987. ok.
  2988. reject_response_pseudo_headers(Config) ->
  2989. doc("Requests containing response pseudo-headers must be rejected "
  2990. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.1, RFC7540 8.1.2.6)"),
  2991. {ok, Socket} = do_handshake(Config),
  2992. %% Send a HEADERS frame with a response pseudo-header.
  2993. {HeadersBlock, _} = cow_hpack:encode([
  2994. {<<":method">>, <<"GET">>},
  2995. {<<":scheme">>, <<"http">>},
  2996. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  2997. {<<":path">>, <<"/">>},
  2998. {<<":status">>, <<"200">>}
  2999. ]),
  3000. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3001. %% Receive a PROTOCOL_ERROR stream error.
  3002. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3003. ok.
  3004. reject_unknown_pseudo_headers(Config) ->
  3005. doc("Requests containing unknown pseudo-headers must be rejected "
  3006. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.1, RFC7540 8.1.2.6)"),
  3007. {ok, Socket} = do_handshake(Config),
  3008. %% Send a HEADERS frame with an unknown pseudo-header.
  3009. {HeadersBlock, _} = cow_hpack:encode([
  3010. {<<":method">>, <<"GET">>},
  3011. {<<":scheme">>, <<"http">>},
  3012. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3013. {<<":path">>, <<"/">>},
  3014. {<<":upgrade">>, <<"websocket">>}
  3015. ]),
  3016. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3017. %% Receive a PROTOCOL_ERROR stream error.
  3018. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3019. ok.
  3020. %% @todo Implement request trailers. reject_pseudo_headers_in_trailers(Config) ->
  3021. % Pseudo-header fields MUST NOT appear in trailers.
  3022. % Endpoints MUST treat a request or response that contains
  3023. % undefined or invalid pseudo-header fields as malformed
  3024. % (Section 8.1.2.6).
  3025. reject_pseudo_headers_after_regular_headers(Config) ->
  3026. doc("Requests containing pseudo-headers after regular headers must be rejected "
  3027. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.1, RFC7540 8.1.2.6)"),
  3028. {ok, Socket} = do_handshake(Config),
  3029. %% Send a HEADERS frame with a pseudo-header after regular headers.
  3030. {HeadersBlock, _} = cow_hpack:encode([
  3031. {<<":method">>, <<"GET">>},
  3032. {<<":scheme">>, <<"http">>},
  3033. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3034. {<<"content-length">>, <<"0">>},
  3035. {<<":path">>, <<"/">>}
  3036. ]),
  3037. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3038. %% Receive a PROTOCOL_ERROR stream error.
  3039. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3040. ok.
  3041. reject_connection_header(Config) ->
  3042. doc("Requests containing a connection header must be rejected "
  3043. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.2, RFC7540 8.1.2.6)"),
  3044. {ok, Socket} = do_handshake(Config),
  3045. %% Send a HEADERS frame with a connection header.
  3046. {HeadersBlock, _} = cow_hpack:encode([
  3047. {<<":method">>, <<"GET">>},
  3048. {<<":scheme">>, <<"http">>},
  3049. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3050. {<<":path">>, <<"/">>},
  3051. {<<"connection">>, <<"close">>}
  3052. ]),
  3053. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3054. %% Receive a PROTOCOL_ERROR stream error.
  3055. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3056. ok.
  3057. reject_keep_alive_header(Config) ->
  3058. doc("Requests containing a keep-alive header must be rejected "
  3059. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.2, RFC7540 8.1.2.6)"),
  3060. {ok, Socket} = do_handshake(Config),
  3061. %% Send a HEADERS frame with a keep-alive header.
  3062. {HeadersBlock, _} = cow_hpack:encode([
  3063. {<<":method">>, <<"GET">>},
  3064. {<<":scheme">>, <<"http">>},
  3065. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3066. {<<":path">>, <<"/">>},
  3067. {<<"keep-alive">>, <<"timeout=5, max=1000">>}
  3068. ]),
  3069. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3070. %% Receive a PROTOCOL_ERROR stream error.
  3071. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3072. ok.
  3073. reject_proxy_authenticate_header(Config) ->
  3074. doc("Requests containing a connection header must be rejected "
  3075. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.2, RFC7540 8.1.2.6)"),
  3076. {ok, Socket} = do_handshake(Config),
  3077. %% Send a HEADERS frame with a proxy-authenticate header.
  3078. {HeadersBlock, _} = cow_hpack:encode([
  3079. {<<":method">>, <<"GET">>},
  3080. {<<":scheme">>, <<"http">>},
  3081. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3082. {<<":path">>, <<"/">>},
  3083. {<<"proxy-authenticate">>, <<"Basic">>}
  3084. ]),
  3085. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3086. %% Receive a PROTOCOL_ERROR stream error.
  3087. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3088. ok.
  3089. reject_proxy_authorization_header(Config) ->
  3090. doc("Requests containing a connection header must be rejected "
  3091. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.2, RFC7540 8.1.2.6)"),
  3092. {ok, Socket} = do_handshake(Config),
  3093. %% Send a HEADERS frame with a proxy-authorization header.
  3094. {HeadersBlock, _} = cow_hpack:encode([
  3095. {<<":method">>, <<"GET">>},
  3096. {<<":scheme">>, <<"http">>},
  3097. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3098. {<<":path">>, <<"/">>},
  3099. {<<"proxy-authorization">>, <<"Basic YWxhZGRpbjpvcGVuc2VzYW1l">>}
  3100. ]),
  3101. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3102. %% Receive a PROTOCOL_ERROR stream error.
  3103. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3104. ok.
  3105. reject_transfer_encoding_header(Config) ->
  3106. doc("Requests containing a connection header must be rejected "
  3107. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.2, RFC7540 8.1.2.6)"),
  3108. {ok, Socket} = do_handshake(Config),
  3109. %% Send a HEADERS frame with a transfer-encoding header.
  3110. {HeadersBlock, _} = cow_hpack:encode([
  3111. {<<":method">>, <<"GET">>},
  3112. {<<":scheme">>, <<"http">>},
  3113. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3114. {<<":path">>, <<"/">>},
  3115. {<<"transfer-encoding">>, <<"chunked">>}
  3116. ]),
  3117. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3118. %% Receive a PROTOCOL_ERROR stream error.
  3119. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3120. ok.
  3121. reject_upgrade_header(Config) ->
  3122. doc("Requests containing a connection header must be rejected "
  3123. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.2, RFC7540 8.1.2.6)"),
  3124. {ok, Socket} = do_handshake(Config),
  3125. %% Send a HEADERS frame with a upgrade header.
  3126. {HeadersBlock, _} = cow_hpack:encode([
  3127. {<<":method">>, <<"GET">>},
  3128. {<<":scheme">>, <<"http">>},
  3129. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3130. {<<":path">>, <<"/">>},
  3131. {<<"upgrade">>, <<"websocket">>}
  3132. ]),
  3133. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3134. %% Receive a PROTOCOL_ERROR stream error.
  3135. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3136. ok.
  3137. accept_te_header_value_trailers(Config) ->
  3138. doc("Requests containing a TE header with a value of \"trailers\" "
  3139. "must be accepted. (RFC7540 8.1.2.2)"),
  3140. {ok, Socket} = do_handshake(Config),
  3141. %% Send a HEADERS frame with a TE header with value "trailers".
  3142. {HeadersBlock, _} = cow_hpack:encode([
  3143. {<<":method">>, <<"GET">>},
  3144. {<<":scheme">>, <<"http">>},
  3145. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3146. {<<":path">>, <<"/">>},
  3147. {<<"te">>, <<"trailers">>}
  3148. ]),
  3149. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3150. %% Receive a response.
  3151. {ok, << _:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  3152. ok.
  3153. reject_te_header_other_values(Config) ->
  3154. doc("Requests containing a TE header with a value other than \"trailers\" must be rejected "
  3155. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.2, RFC7540 8.1.2.6)"),
  3156. {ok, Socket} = do_handshake(Config),
  3157. %% Send a HEADERS frame with a TE header with a different value.
  3158. {HeadersBlock, _} = cow_hpack:encode([
  3159. {<<":method">>, <<"GET">>},
  3160. {<<":scheme">>, <<"http">>},
  3161. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3162. {<<":path">>, <<"/">>},
  3163. {<<"te">>, <<"trailers, deflate;q=0.5">>}
  3164. ]),
  3165. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3166. %% Receive a PROTOCOL_ERROR stream error.
  3167. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3168. ok.
  3169. %% (RFC7540 8.1.2.2)
  3170. % This means that an intermediary transforming an HTTP/1.x message to
  3171. % HTTP/2 will need to remove any header fields nominated by the
  3172. % Connection header field, along with the Connection header field
  3173. % itself. Such intermediaries SHOULD also remove other connection-
  3174. % specific header fields, such as Keep-Alive, Proxy-Connection,
  3175. % Transfer-Encoding, and Upgrade, even if they are not nominated by the
  3176. % Connection header field.
  3177. reject_userinfo(Config) ->
  3178. doc("An authority containing a userinfo component must be rejected "
  3179. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.3, RFC7540 8.1.2.6)"),
  3180. {ok, Socket} = do_handshake(Config),
  3181. %% Send a HEADERS frame with a userinfo authority component.
  3182. {HeadersBlock, _} = cow_hpack:encode([
  3183. {<<":method">>, <<"GET">>},
  3184. {<<":scheme">>, <<"http">>},
  3185. {<<":authority">>, <<"user@localhost">>}, %% @todo Correct port number.
  3186. {<<":path">>, <<"/">>}
  3187. ]),
  3188. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3189. %% Receive a PROTOCOL_ERROR stream error.
  3190. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3191. ok.
  3192. %% (RFC7540 8.1.2.3)
  3193. % To ensure that the HTTP/1.1 request line can be reproduced
  3194. % accurately, this pseudo-header field MUST be omitted when
  3195. % translating from an HTTP/1.1 request that has a request target in
  3196. % origin or asterisk form (see [RFC7230], Section 5.3). Clients
  3197. % that generate HTTP/2 requests directly SHOULD use the ":authority"
  3198. % pseudo-header field instead of the Host header field. An
  3199. % intermediary that converts an HTTP/2 request to HTTP/1.1 MUST
  3200. % create a Host header field if one is not present in a request by
  3201. % copying the value of the ":authority" pseudo-header field.
  3202. reject_empty_path(Config) ->
  3203. doc("A request containing an empty path component must be rejected "
  3204. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.3, RFC7540 8.1.2.6)"),
  3205. {ok, Socket} = do_handshake(Config),
  3206. %% Send a HEADERS frame with an empty path component.
  3207. {HeadersBlock, _} = cow_hpack:encode([
  3208. {<<":method">>, <<"GET">>},
  3209. {<<":scheme">>, <<"http">>},
  3210. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3211. {<<":path">>, <<>>}
  3212. ]),
  3213. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3214. %% Receive a PROTOCOL_ERROR stream error.
  3215. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3216. ok.
  3217. reject_missing_pseudo_header_method(Config) ->
  3218. doc("A request without a method component must be rejected "
  3219. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.3, RFC7540 8.1.2.6)"),
  3220. {ok, Socket} = do_handshake(Config),
  3221. %% Send a HEADERS frame without a :method pseudo-header.
  3222. {HeadersBlock, _} = cow_hpack:encode([
  3223. {<<":scheme">>, <<"http">>},
  3224. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3225. {<<":path">>, <<>>}
  3226. ]),
  3227. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3228. %% Receive a PROTOCOL_ERROR stream error.
  3229. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3230. ok.
  3231. reject_many_pseudo_header_method(Config) ->
  3232. doc("A request containing more than one method component must be rejected "
  3233. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.3, RFC7540 8.1.2.6)"),
  3234. {ok, Socket} = do_handshake(Config),
  3235. %% Send a HEADERS frame with more than one :method pseudo-header.
  3236. {HeadersBlock, _} = cow_hpack:encode([
  3237. {<<":method">>, <<"GET">>},
  3238. {<<":method">>, <<"GET">>},
  3239. {<<":scheme">>, <<"http">>},
  3240. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3241. {<<":path">>, <<>>}
  3242. ]),
  3243. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3244. %% Receive a PROTOCOL_ERROR stream error.
  3245. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3246. ok.
  3247. reject_missing_pseudo_header_scheme(Config) ->
  3248. doc("A request without a scheme component must be rejected "
  3249. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.3, RFC7540 8.1.2.6)"),
  3250. {ok, Socket} = do_handshake(Config),
  3251. %% Send a HEADERS frame without a :scheme pseudo-header.
  3252. {HeadersBlock, _} = cow_hpack:encode([
  3253. {<<":method">>, <<"GET">>},
  3254. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3255. {<<":path">>, <<>>}
  3256. ]),
  3257. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3258. %% Receive a PROTOCOL_ERROR stream error.
  3259. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3260. ok.
  3261. reject_many_pseudo_header_scheme(Config) ->
  3262. doc("A request containing more than one scheme component must be rejected "
  3263. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.3, RFC7540 8.1.2.6)"),
  3264. {ok, Socket} = do_handshake(Config),
  3265. %% Send a HEADERS frame with more than one :scheme pseudo-header.
  3266. {HeadersBlock, _} = cow_hpack:encode([
  3267. {<<":method">>, <<"GET">>},
  3268. {<<":scheme">>, <<"http">>},
  3269. {<<":scheme">>, <<"http">>},
  3270. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3271. {<<":path">>, <<>>}
  3272. ]),
  3273. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3274. %% Receive a PROTOCOL_ERROR stream error.
  3275. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3276. ok.
  3277. reject_missing_pseudo_header_authority(Config) ->
  3278. doc("A request without an authority component must be rejected "
  3279. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.3, RFC7540 8.1.2.6)"),
  3280. {ok, Socket} = do_handshake(Config),
  3281. %% Send a HEADERS frame without an :authority pseudo-header.
  3282. {HeadersBlock, _} = cow_hpack:encode([
  3283. {<<":method">>, <<"GET">>},
  3284. {<<":scheme">>, <<"http">>},
  3285. {<<":path">>, <<>>}
  3286. ]),
  3287. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3288. %% Receive a PROTOCOL_ERROR stream error.
  3289. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3290. ok.
  3291. reject_many_pseudo_header_authority(Config) ->
  3292. doc("A request containing more than one authority component must be rejected "
  3293. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.3, RFC7540 8.1.2.6)"),
  3294. {ok, Socket} = do_handshake(Config),
  3295. %% Send a HEADERS frame with more than one :authority pseudo-header.
  3296. {HeadersBlock, _} = cow_hpack:encode([
  3297. {<<":method">>, <<"GET">>},
  3298. {<<":scheme">>, <<"http">>},
  3299. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3300. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3301. {<<":path">>, <<>>}
  3302. ]),
  3303. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3304. %% Receive a PROTOCOL_ERROR stream error.
  3305. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3306. ok.
  3307. reject_missing_pseudo_header_path(Config) ->
  3308. doc("A request without a path component must be rejected "
  3309. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.3, RFC7540 8.1.2.6)"),
  3310. {ok, Socket} = do_handshake(Config),
  3311. %% Send a HEADERS frame without a :path pseudo-header.
  3312. {HeadersBlock, _} = cow_hpack:encode([
  3313. {<<":method">>, <<"GET">>},
  3314. {<<":scheme">>, <<"http">>},
  3315. {<<":authority">>, <<"localhost">>} %% @todo Correct port number.
  3316. ]),
  3317. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3318. %% Receive a PROTOCOL_ERROR stream error.
  3319. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3320. ok.
  3321. reject_many_pseudo_header_path(Config) ->
  3322. doc("A request containing more than one path component must be rejected "
  3323. "with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.3, RFC7540 8.1.2.6)"),
  3324. {ok, Socket} = do_handshake(Config),
  3325. %% Send a HEADERS frame with more than one :path pseudo-header.
  3326. {HeadersBlock, _} = cow_hpack:encode([
  3327. {<<":method">>, <<"GET">>},
  3328. {<<":scheme">>, <<"http">>},
  3329. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3330. {<<":path">>, <<>>},
  3331. {<<":path">>, <<>>}
  3332. ]),
  3333. ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  3334. %% Receive a PROTOCOL_ERROR stream error.
  3335. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3336. ok.
  3337. %% (RFC7540 8.1.2.4)
  3338. % For HTTP/2 responses, a single ":status" pseudo-header field is
  3339. % defined that carries the HTTP status code field (see [RFC7231],
  3340. % Section 6). This pseudo-header field MUST be included in all
  3341. % responses; otherwise, the response is malformed (Section 8.1.2.6).
  3342. %% (RFC7540 8.1.2.5)
  3343. % To allow for better compression efficiency, the Cookie header field
  3344. % MAY be split into separate header fields, each with one or more
  3345. % cookie-pairs. If there are multiple Cookie header fields after
  3346. % decompression, these MUST be concatenated into a single octet string
  3347. % using the two-octet delimiter of 0x3B, 0x20 (the ASCII string "; ")
  3348. % before being passed into a non-HTTP/2 context, such as an HTTP/1.1
  3349. % connection, or a generic HTTP server application.
  3350. %% (RFC7540 8.1.2.6)
  3351. % A request or response that includes a payload body can include a
  3352. % content-length header field. A request or response is also malformed
  3353. % if the value of a content-length header field does not equal the sum
  3354. % of the DATA frame payload lengths that form the body. A response
  3355. % that is defined to have no payload, as described in [RFC7230],
  3356. % Section 3.3.2, can have a non-zero content-length header field, even
  3357. % though no content is included in DATA frames.
  3358. reject_duplicate_content_length_header(Config) ->
  3359. doc("A request with duplicate content-length headers must be rejected "
  3360. "with a PROTOCOL_ERROR stream error. (RFC7230 3.3.2, RFC7540 8.1.2.6)"),
  3361. {ok, Socket} = do_handshake(Config),
  3362. %% Send a HEADERS frame with more than one content-length header.
  3363. {HeadersBlock, _} = cow_hpack:encode([
  3364. {<<":method">>, <<"GET">>},
  3365. {<<":scheme">>, <<"http">>},
  3366. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  3367. {<<":path">>, <<>>},
  3368. {<<"content-length">>, <<"12">>},
  3369. {<<"content-length">>, <<"12">>}
  3370. ]),
  3371. ok = gen_tcp:send(Socket, cow_http2:headers(1, nofin, HeadersBlock)),
  3372. %% Receive a PROTOCOL_ERROR stream error.
  3373. {ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
  3374. ok.
  3375. % Intermediaries that process HTTP requests or responses (i.e., any
  3376. % intermediary not acting as a tunnel) MUST NOT forward a malformed
  3377. % request or response. Malformed requests or responses that are
  3378. % detected MUST be treated as a stream error (Section 5.4.2) of type
  3379. % PROTOCOL_ERROR.
  3380. %
  3381. % For malformed requests, a server MAY send an HTTP response prior to
  3382. % closing or resetting the stream. Clients MUST NOT accept a malformed
  3383. % response. Note that these requirements are intended to protect
  3384. % against several types of common attacks against HTTP; they are
  3385. % deliberately strict because being permissive can expose
  3386. % implementations to these vulnerabilities.
  3387. %% @todo It migh be worth reproducing the good examples. (RFC7540 8.1.3)
  3388. %% (RFC7540 8.1.4)
  3389. % A server MUST NOT indicate that a stream has not been processed
  3390. % unless it can guarantee that fact. If frames that are on a stream
  3391. % are passed to the application layer for any stream, then
  3392. % REFUSED_STREAM MUST NOT be used for that stream, and a GOAWAY frame
  3393. % MUST include a stream identifier that is greater than or equal to the
  3394. % given stream identifier.
  3395. %% (RFC7540 8.2)
  3396. % Promised requests MUST be cacheable (see [RFC7231], Section 4.2.3),
  3397. % MUST be safe (see [RFC7231], Section 4.2.1), and MUST NOT include a
  3398. % request body.
  3399. %
  3400. % The server MUST include a value in the ":authority" pseudo-header
  3401. % field for which the server is authoritative (see Section 10.1).
  3402. %
  3403. % A client cannot push. Thus, servers MUST treat the receipt of a
  3404. % PUSH_PROMISE frame as a connection error (Section 5.4.1) of type
  3405. % PROTOCOL_ERROR.
  3406. %% (RFC7540 8.2.1)
  3407. % The header fields in PUSH_PROMISE and any subsequent CONTINUATION
  3408. % frames MUST be a valid and complete set of request header fields
  3409. % (Section 8.1.2.3). The server MUST include a method in the ":method"
  3410. % pseudo-header field that is safe and cacheable. If a client receives
  3411. % a PUSH_PROMISE that does not include a complete and valid set of
  3412. % header fields or the ":method" pseudo-header field identifies a
  3413. % method that is not safe, it MUST respond with a stream error
  3414. % (Section 5.4.2) of type PROTOCOL_ERROR.
  3415. %
  3416. %% @todo This probably should be documented.
  3417. % The server SHOULD send PUSH_PROMISE (Section 6.6) frames prior to
  3418. % sending any frames that reference the promised responses. This
  3419. % avoids a race where clients issue requests prior to receiving any
  3420. % PUSH_PROMISE frames.
  3421. %
  3422. % PUSH_PROMISE frames MUST NOT be sent by the client.
  3423. %
  3424. % PUSH_PROMISE frames can be sent by the server in response to any
  3425. % client-initiated stream, but the stream MUST be in either the "open"
  3426. % or "half-closed (remote)" state with respect to the server.
  3427. % PUSH_PROMISE frames are interspersed with the frames that comprise a
  3428. % response, though they cannot be interspersed with HEADERS and
  3429. % CONTINUATION frames that comprise a single header block.
  3430. %% (RFC7540 8.2.2)
  3431. % If the client determines, for any reason, that it does not wish to
  3432. % receive the pushed response from the server or if the server takes
  3433. % too long to begin sending the promised response, the client can send
  3434. % a RST_STREAM frame, using either the CANCEL or REFUSED_STREAM code
  3435. % and referencing the pushed stream's identifier.
  3436. %
  3437. % A client can use the SETTINGS_MAX_CONCURRENT_STREAMS setting to limit
  3438. % the number of responses that can be concurrently pushed by a server.
  3439. % Advertising a SETTINGS_MAX_CONCURRENT_STREAMS value of zero disables
  3440. % server push by preventing the server from creating the necessary
  3441. % streams. This does not prohibit a server from sending PUSH_PROMISE
  3442. % frames; clients need to reset any promised streams that are not
  3443. % wanted.
  3444. %% @todo Implement CONNECT. (RFC7540 8.3)
  3445. status_code_421(Config) ->
  3446. doc("The 421 Misdirected Request status code can be sent. (RFC7540 9.1.2)"),
  3447. ConnPid = gun_open(Config),
  3448. Ref = gun:get(ConnPid, "/resp/reply2/421"),
  3449. {response, fin, 421, _} = gun:await(ConnPid, Ref),
  3450. ok.
  3451. %% @todo Review (RFC7540 9.2, 9.2.1, 9.2.2) TLS 1.2 usage.
  3452. %% We probably want different ways to enforce these to simplify the life
  3453. %% of users. A function cowboy:start_h2_tls could do the same as start_tls
  3454. %% but with the security requirements of HTTP/2 enforced. Another way is to
  3455. %% have an option at the establishment of the connection that checks that
  3456. %% the security of the connection is adequate.
  3457. %% (RFC7540 10.3)
  3458. % The HTTP/2 header field encoding allows the expression of names that
  3459. % are not valid field names in the Internet Message Syntax used by
  3460. % HTTP/1.1. Requests or responses containing invalid header field
  3461. % names MUST be treated as malformed (Section 8.1.2.6).
  3462. %
  3463. % Similarly, HTTP/2 allows header field values that are not valid.
  3464. % While most of the values that can be encoded will not alter header
  3465. % field parsing, carriage return (CR, ASCII 0xd), line feed (LF, ASCII
  3466. % 0xa), and the zero character (NUL, ASCII 0x0) might be exploited by
  3467. % an attacker if they are translated verbatim. Any request or response
  3468. % that contains a character not permitted in a header field value MUST
  3469. % be treated as malformed (Section 8.1.2.6). Valid characters are
  3470. % defined by the "field-content" ABNF rule in Section 3.2 of [RFC7230].
  3471. %% (RFC7540 10.5) Denial-of-Service Considerations
  3472. % An endpoint that doesn't monitor this behavior exposes itself to a
  3473. % risk of denial-of-service attack. Implementations SHOULD track the
  3474. % use of these features and set limits on their use. An endpoint MAY
  3475. % treat activity that is suspicious as a connection error
  3476. % (Section 5.4.1) of type ENHANCE_YOUR_CALM.
  3477. %% (RFC7540 10.5.1)
  3478. % A server that receives a larger header block than it is willing to
  3479. % handle can send an HTTP 431 (Request Header Fields Too Large) status
  3480. % code [RFC6585]. A client can discard responses that it cannot
  3481. % process. The header block MUST be processed to ensure a consistent
  3482. % connection state, unless the connection is closed.
  3483. %% @todo Implement CONNECT and limit the number of CONNECT streams (RFC7540 10.5.2).
  3484. %% @todo This probably should be documented. (RFC7540 10.6)
  3485. % Implementations communicating on a secure channel MUST NOT compress
  3486. % content that includes both confidential and attacker-controlled data
  3487. % unless separate compression dictionaries are used for each source of
  3488. % data. Compression MUST NOT be used if the source of data cannot be
  3489. % reliably determined. Generic stream compression, such as that
  3490. % provided by TLS, MUST NOT be used with HTTP/2 (see Section 9.2).
  3491. %% (RFC7540 A)
  3492. % An HTTP/2 implementation MAY treat the negotiation of any of the
  3493. % following cipher suites with TLS 1.2 as a connection error
  3494. % (Section 5.4.1) of type INADEQUATE_SECURITY.