rfc7540_SUITE.erl 149 KB

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