rfc9114_SUITE.erl 74 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909
  1. %% Copyright (c) 2023, 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(rfc9114_SUITE).
  15. -compile(export_all).
  16. -compile(nowarn_export_all).
  17. -import(ct_helper, [config/2]).
  18. -import(ct_helper, [doc/1]).
  19. -include_lib("quicer/include/quicer.hrl").
  20. all() -> [{group, quic}].
  21. groups() ->
  22. %% @todo Enable parallel tests but for this issues in the
  23. %% QUIC accept loop need to be figured out (can't connect
  24. %% concurrently somehow, no backlog?).
  25. [{quic, [], ct_helper:all(?MODULE)}].
  26. init_per_group(Name = quic, Config) ->
  27. cowboy_test:init_http3(Name, #{
  28. env => #{dispatch => cowboy_router:compile(init_routes(Config))}
  29. }, Config).
  30. end_per_group(_Name, _) ->
  31. ok. %% @todo = cowboy:stop_listener(Name).
  32. init_routes(_) -> [
  33. {"localhost", [
  34. {"/", hello_h, []}%,
  35. % {"/echo/:key", echo_h, []},
  36. % {"/delay_hello", delay_hello_h, 1200},
  37. % {"/long_polling", long_polling_h, []},
  38. % {"/loop_handler_abort", loop_handler_abort_h, []},
  39. % {"/resp/:key[/:arg]", resp_h, []}
  40. ]}
  41. ].
  42. %% Starting HTTP/3 for "https" URIs.
  43. alpn(Config) ->
  44. doc("Successful ALPN negotiation. (RFC9114 3.1)"),
  45. {ok, Conn} = quicer:connect("localhost", config(port, Config),
  46. #{alpn => ["h3"], verify => none}, 5000),
  47. {ok, <<"h3">>} = quicer:getopt(Conn, param_tls_negotiated_alpn, quic_tls),
  48. %% To make sure the connection is fully established we wait
  49. %% to receive the SETTINGS frame on the control stream.
  50. {ok, _ControlRef, _Settings} = do_wait_settings(Conn),
  51. ok.
  52. alpn_error(Config) ->
  53. doc("Failed ALPN negotiation using the 'h2' token. (RFC9114 3.1)"),
  54. {error, transport_down, #{status := alpn_neg_failure}}
  55. = quicer:connect("localhost", config(port, Config),
  56. #{alpn => ["h2"], verify => none}, 5000),
  57. ok.
  58. %% @todo 3.2. Connection Establishment
  59. %% After the QUIC connection is established, a SETTINGS frame MUST be sent by each endpoint as the initial frame of their respective HTTP control stream.
  60. %% @todo 3.3. Connection Reuse
  61. %% Servers are encouraged to maintain open HTTP/3 connections for as long as
  62. %possible but are permitted to terminate idle connections if necessary. When
  63. %either endpoint chooses to close the HTTP/3 connection, the terminating
  64. %endpoint SHOULD first send a GOAWAY frame (Section 5.2) so that both endpoints
  65. %can reliably determine whether previously sent frames have been processed and
  66. %gracefully complete or terminate any necessary remaining tasks.
  67. %% Frame format.
  68. req_stream(Config) ->
  69. doc("Complete lifecycle of a request stream. (RFC9114 4.1)"),
  70. {ok, Conn} = quicer:connect("localhost", config(port, Config),
  71. #{alpn => ["h3"], verify => none}, 5000),
  72. %% To make sure the connection is fully established we wait
  73. %% to receive the SETTINGS frame on the control stream.
  74. {ok, ControlRef, _Settings} = do_wait_settings(Conn),
  75. %% Send a request on a request stream.
  76. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  77. {ok, EncodedRequest, _EncData, _EncSt} = cow_qpack:encode_field_section([
  78. {<<":method">>, <<"GET">>},
  79. {<<":scheme">>, <<"https">>},
  80. {<<":authority">>, <<"localhost">>},
  81. {<<":path">>, <<"/">>},
  82. {<<"content-length">>, <<"0">>}
  83. ], 0, cow_qpack:init()),
  84. {ok, _} = quicer:send(StreamRef, [
  85. <<1>>, %% HEADERS frame.
  86. cow_http3:encode_int(iolist_size(EncodedRequest)),
  87. EncodedRequest
  88. ]),
  89. ok = do_async_stream_shutdown(StreamRef),
  90. %% Receive the response.
  91. {ok, Data} = do_receive_data(StreamRef),
  92. {HLenEnc, HLenBits} = do_guess_int_encoding(Data),
  93. <<
  94. 1, %% HEADERS frame.
  95. HLenEnc:2, HLen:HLenBits,
  96. EncodedResponse:HLen/bytes,
  97. Rest/bits
  98. >> = Data,
  99. {ok, DecodedResponse, _DecData, _DecSt}
  100. = cow_qpack:decode_field_section(EncodedResponse, 0, cow_qpack:init()),
  101. #{
  102. <<":status">> := <<"200">>,
  103. <<"content-length">> := BodyLen
  104. } = maps:from_list(DecodedResponse),
  105. {DLenEnc, DLenBits} = do_guess_int_encoding(Data),
  106. <<
  107. 0, %% DATA frame.
  108. DLenEnc:2, DLen:DLenBits,
  109. Body:DLen/bytes
  110. >> = Rest,
  111. <<"Hello world!">> = Body,
  112. BodyLen = integer_to_binary(byte_size(Body)),
  113. ok = do_wait_peer_send_shutdown(StreamRef),
  114. ok = do_wait_stream_closed(StreamRef).
  115. %% @todo Same test as above but with content-length unset?
  116. req_stream_two_requests(Config) ->
  117. doc("Receipt of multiple requests on a single stream must "
  118. "be rejected with an H3_MESSAGE_ERROR stream error. "
  119. "(RFC9114 4.1, RFC9114 4.1.2)"),
  120. {ok, Conn} = quicer:connect("localhost", config(port, Config),
  121. #{alpn => ["h3"], verify => none}, 5000),
  122. %% To make sure the connection is fully established we wait
  123. %% to receive the SETTINGS frame on the control stream.
  124. {ok, ControlRef, _Settings} = do_wait_settings(Conn),
  125. %% Send two requests on a request stream.
  126. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  127. {ok, EncodedRequest1, _EncData1, EncSt0} = cow_qpack:encode_field_section([
  128. {<<":method">>, <<"GET">>},
  129. {<<":scheme">>, <<"https">>},
  130. {<<":authority">>, <<"localhost">>},
  131. {<<":path">>, <<"/">>},
  132. {<<"content-length">>, <<"0">>}
  133. ], 0, cow_qpack:init()),
  134. {ok, EncodedRequest2, _EncData2, _EncSt} = cow_qpack:encode_field_section([
  135. {<<":method">>, <<"GET">>},
  136. {<<":scheme">>, <<"https">>},
  137. {<<":authority">>, <<"localhost">>},
  138. {<<":path">>, <<"/">>},
  139. {<<"content-length">>, <<"0">>}
  140. ], 0, EncSt0),
  141. {ok, _} = quicer:send(StreamRef, [
  142. <<1>>, %% HEADERS frame.
  143. cow_http3:encode_int(iolist_size(EncodedRequest1)),
  144. EncodedRequest1,
  145. <<1>>, %% HEADERS frame.
  146. cow_http3:encode_int(iolist_size(EncodedRequest2)),
  147. EncodedRequest2
  148. ]),
  149. %% The stream should have been aborted.
  150. #{reason := h3_message_error} = do_wait_stream_aborted(StreamRef),
  151. ok.
  152. req_stream_two_requests_sequential(Config) ->
  153. doc("Receipt of multiple requests on a single stream must "
  154. "be rejected with an H3_MESSAGE_ERROR stream error. "
  155. "(RFC9114 4.1, RFC9114 4.1.2)"),
  156. {ok, Conn} = quicer:connect("localhost", config(port, Config),
  157. #{alpn => ["h3"], verify => none}, 5000),
  158. %% To make sure the connection is fully established we wait
  159. %% to receive the SETTINGS frame on the control stream.
  160. {ok, ControlRef, _Settings} = do_wait_settings(Conn),
  161. %% Send a first request.
  162. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  163. {ok, EncodedRequest1, _EncData1, EncSt0} = cow_qpack:encode_field_section([
  164. {<<":method">>, <<"GET">>},
  165. {<<":scheme">>, <<"https">>},
  166. {<<":authority">>, <<"localhost">>},
  167. {<<":path">>, <<"/">>},
  168. {<<"content-length">>, <<"0">>}
  169. ], 0, cow_qpack:init()),
  170. {ok, _} = quicer:send(StreamRef, [
  171. <<1>>, %% HEADERS frame.
  172. cow_http3:encode_int(iolist_size(EncodedRequest1)),
  173. EncodedRequest1
  174. ]),
  175. %% Receive the response.
  176. {ok, Data} = do_receive_data(StreamRef),
  177. {HLenEnc, HLenBits} = do_guess_int_encoding(Data),
  178. <<
  179. 1, %% HEADERS frame.
  180. HLenEnc:2, HLen:HLenBits,
  181. EncodedResponse:HLen/bytes,
  182. Rest/bits
  183. >> = Data,
  184. {ok, DecodedResponse, _DecData, _DecSt}
  185. = cow_qpack:decode_field_section(EncodedResponse, 0, cow_qpack:init()),
  186. #{
  187. <<":status">> := <<"200">>,
  188. <<"content-length">> := BodyLen
  189. } = maps:from_list(DecodedResponse),
  190. {DLenEnc, DLenBits} = do_guess_int_encoding(Data),
  191. <<
  192. 0, %% DATA frame.
  193. DLenEnc:2, DLen:DLenBits,
  194. Body:DLen/bytes
  195. >> = Rest,
  196. <<"Hello world!">> = Body,
  197. BodyLen = integer_to_binary(byte_size(Body)),
  198. ok = do_wait_peer_send_shutdown(StreamRef),
  199. %% Send a second request.
  200. {ok, EncodedRequest2, _EncData2, _EncSt} = cow_qpack:encode_field_section([
  201. {<<":method">>, <<"GET">>},
  202. {<<":scheme">>, <<"https">>},
  203. {<<":authority">>, <<"localhost">>},
  204. {<<":path">>, <<"/">>},
  205. {<<"content-length">>, <<"0">>}
  206. ], 0, EncSt0),
  207. {ok, _} = quicer:send(StreamRef, [
  208. <<1>>, %% HEADERS frame.
  209. cow_http3:encode_int(iolist_size(EncodedRequest2)),
  210. EncodedRequest2
  211. ]),
  212. %% The stream should have been aborted.
  213. #{reason := h3_message_error} = do_wait_stream_aborted(StreamRef),
  214. ok.
  215. headers_then_trailers(Config) ->
  216. doc("Receipt of HEADERS followed by trailer HEADERS must be accepted. (RFC9114 4.1)"),
  217. #{conn := Conn} = do_connect(Config),
  218. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  219. {ok, EncodedHeaders, _EncData1, EncSt0} = cow_qpack:encode_field_section([
  220. {<<":method">>, <<"GET">>},
  221. {<<":scheme">>, <<"https">>},
  222. {<<":authority">>, <<"localhost">>},
  223. {<<":path">>, <<"/">>},
  224. {<<"content-length">>, <<"0">>}
  225. ], 0, cow_qpack:init()),
  226. {ok, EncodedTrailers, _EncData2, _EncSt} = cow_qpack:encode_field_section([
  227. {<<"content-type">>, <<"text/plain">>}
  228. ], 0, EncSt0),
  229. {ok, _} = quicer:send(StreamRef, [
  230. <<1>>, %% HEADERS frame.
  231. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  232. EncodedHeaders,
  233. <<1>>, %% HEADERS frame for trailers.
  234. cow_http3:encode_int(iolist_size(EncodedTrailers)),
  235. EncodedTrailers
  236. ]),
  237. ok = do_async_stream_shutdown(StreamRef),
  238. #{
  239. headers := #{<<":status">> := <<"200">>},
  240. body := <<"Hello world!">>
  241. } = do_receive_response(StreamRef),
  242. ok.
  243. headers_then_data_then_trailers(Config) ->
  244. doc("Receipt of HEADERS followed by DATA followed by trailer HEADERS "
  245. "must be accepted. (RFC9114 4.1)"),
  246. #{conn := Conn} = do_connect(Config),
  247. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  248. {ok, EncodedHeaders, _EncData1, EncSt0} = cow_qpack:encode_field_section([
  249. {<<":method">>, <<"GET">>},
  250. {<<":scheme">>, <<"https">>},
  251. {<<":authority">>, <<"localhost">>},
  252. {<<":path">>, <<"/">>},
  253. {<<"content-length">>, <<"13">>}
  254. ], 0, cow_qpack:init()),
  255. {ok, EncodedTrailers, _EncData2, _EncSt} = cow_qpack:encode_field_section([
  256. {<<"content-type">>, <<"text/plain">>}
  257. ], 0, EncSt0),
  258. {ok, _} = quicer:send(StreamRef, [
  259. <<1>>, %% HEADERS frame.
  260. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  261. EncodedHeaders,
  262. <<0>>, %% DATA frame.
  263. cow_http3:encode_int(13),
  264. <<"Hello server!">>,
  265. <<1>>, %% HEADERS frame for trailers.
  266. cow_http3:encode_int(iolist_size(EncodedTrailers)),
  267. EncodedTrailers
  268. ]),
  269. ok = do_async_stream_shutdown(StreamRef),
  270. #{
  271. headers := #{<<":status">> := <<"200">>},
  272. body := <<"Hello world!">>
  273. } = do_receive_response(StreamRef),
  274. ok.
  275. data_then_headers(Config) ->
  276. doc("Receipt of DATA before HEADERS must be rejected "
  277. "with an H3_FRAME_UNEXPECTED connection error. (RFC9114 4.1)"),
  278. #{conn := Conn} = do_connect(Config),
  279. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  280. {ok, EncodedHeaders, _EncData1, _EncSt} = cow_qpack:encode_field_section([
  281. {<<":method">>, <<"GET">>},
  282. {<<":scheme">>, <<"https">>},
  283. {<<":authority">>, <<"localhost">>},
  284. {<<":path">>, <<"/">>},
  285. {<<"content-length">>, <<"13">>}
  286. ], 0, cow_qpack:init()),
  287. {ok, _} = quicer:send(StreamRef, [
  288. <<0>>, %% DATA frame.
  289. cow_http3:encode_int(13),
  290. <<"Hello server!">>,
  291. <<1>>, %% HEADERS frame.
  292. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  293. EncodedHeaders
  294. ]),
  295. ok = do_async_stream_shutdown(StreamRef),
  296. %% The connection should have been closed.
  297. #{reason := h3_frame_unexpected} = do_wait_connection_closed(Conn),
  298. ok.
  299. headers_then_trailers_then_data(Config) ->
  300. doc("Receipt of DATA after trailer HEADERS must be rejected "
  301. "with an H3_FRAME_UNEXPECTED connection error. (RFC9114 4.1)"),
  302. #{conn := Conn} = do_connect(Config),
  303. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  304. {ok, EncodedHeaders, _EncData1, EncSt0} = cow_qpack:encode_field_section([
  305. {<<":method">>, <<"GET">>},
  306. {<<":scheme">>, <<"https">>},
  307. {<<":authority">>, <<"localhost">>},
  308. {<<":path">>, <<"/">>},
  309. {<<"content-length">>, <<"13">>}
  310. ], 0, cow_qpack:init()),
  311. {ok, EncodedTrailers, _EncData2, _EncSt} = cow_qpack:encode_field_section([
  312. {<<"content-type">>, <<"text/plain">>}
  313. ], 0, EncSt0),
  314. {ok, _} = quicer:send(StreamRef, [
  315. <<1>>, %% HEADERS frame.
  316. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  317. EncodedHeaders,
  318. <<1>>, %% HEADERS frame for trailers.
  319. cow_http3:encode_int(iolist_size(EncodedTrailers)),
  320. EncodedTrailers,
  321. <<0>>, %% DATA frame.
  322. cow_http3:encode_int(13),
  323. <<"Hello server!">>
  324. ]),
  325. ok = do_async_stream_shutdown(StreamRef),
  326. %% The connection should have been closed.
  327. #{reason := h3_frame_unexpected} = do_wait_connection_closed(Conn),
  328. ok.
  329. headers_then_data_then_trailers_then_data(Config) ->
  330. doc("Receipt of DATA after trailer HEADERS must be rejected "
  331. "with an H3_FRAME_UNEXPECTED connection error. (RFC9114 4.1)"),
  332. #{conn := Conn} = do_connect(Config),
  333. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  334. {ok, EncodedHeaders, _EncData1, EncSt0} = cow_qpack:encode_field_section([
  335. {<<":method">>, <<"GET">>},
  336. {<<":scheme">>, <<"https">>},
  337. {<<":authority">>, <<"localhost">>},
  338. {<<":path">>, <<"/">>},
  339. {<<"content-length">>, <<"13">>}
  340. ], 0, cow_qpack:init()),
  341. {ok, EncodedTrailers, _EncData2, _EncSt} = cow_qpack:encode_field_section([
  342. {<<"content-type">>, <<"text/plain">>}
  343. ], 0, EncSt0),
  344. {ok, _} = quicer:send(StreamRef, [
  345. <<1>>, %% HEADERS frame.
  346. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  347. EncodedHeaders,
  348. <<0>>, %% DATA frame.
  349. cow_http3:encode_int(13),
  350. <<"Hello server!">>,
  351. <<1>>, %% HEADERS frame for trailers.
  352. cow_http3:encode_int(iolist_size(EncodedTrailers)),
  353. EncodedTrailers,
  354. <<0>>, %% DATA frame.
  355. cow_http3:encode_int(13),
  356. <<"Hello server!">>
  357. ]),
  358. ok = do_async_stream_shutdown(StreamRef),
  359. %% The connection should have been closed.
  360. #{reason := h3_frame_unexpected} = do_wait_connection_closed(Conn),
  361. ok.
  362. headers_then_data_then_trailers_then_trailers(Config) ->
  363. doc("Receipt of DATA after trailer HEADERS must be rejected "
  364. "with an H3_FRAME_UNEXPECTED connection error. (RFC9114 4.1)"),
  365. #{conn := Conn} = do_connect(Config),
  366. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  367. {ok, EncodedHeaders, _EncData1, EncSt0} = cow_qpack:encode_field_section([
  368. {<<":method">>, <<"GET">>},
  369. {<<":scheme">>, <<"https">>},
  370. {<<":authority">>, <<"localhost">>},
  371. {<<":path">>, <<"/">>},
  372. {<<"content-length">>, <<"13">>}
  373. ], 0, cow_qpack:init()),
  374. {ok, EncodedTrailers1, _EncData2, EncSt1} = cow_qpack:encode_field_section([
  375. {<<"content-type">>, <<"text/plain">>}
  376. ], 0, EncSt0),
  377. {ok, EncodedTrailers2, _EncData3, _EncSt} = cow_qpack:encode_field_section([
  378. {<<"content-type">>, <<"text/plain">>}
  379. ], 0, EncSt1),
  380. {ok, _} = quicer:send(StreamRef, [
  381. <<1>>, %% HEADERS frame.
  382. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  383. EncodedHeaders,
  384. <<0>>, %% DATA frame.
  385. cow_http3:encode_int(13),
  386. <<"Hello server!">>,
  387. <<1>>, %% HEADERS frame for trailers.
  388. cow_http3:encode_int(iolist_size(EncodedTrailers1)),
  389. EncodedTrailers1,
  390. <<1>>, %% HEADERS frame for trailers.
  391. cow_http3:encode_int(iolist_size(EncodedTrailers2)),
  392. EncodedTrailers2
  393. ]),
  394. ok = do_async_stream_shutdown(StreamRef),
  395. %% The connection should have been closed.
  396. #{reason := h3_frame_unexpected} = do_wait_connection_closed(Conn),
  397. ok.
  398. unknown_then_headers(Config) ->
  399. doc("Receipt of unknown frame followed by HEADERS "
  400. "must be accepted. (RFC9114 4.1, RFC9114 9)"),
  401. unknown_then_headers(Config, do_unknown_frame_type(),
  402. rand:bytes(rand:uniform(4096))).
  403. unknown_then_headers(Config, Type, Bytes) ->
  404. #{conn := Conn} = do_connect(Config),
  405. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  406. {ok, EncodedHeaders, _EncData, _EncSt} = cow_qpack:encode_field_section([
  407. {<<":method">>, <<"GET">>},
  408. {<<":scheme">>, <<"https">>},
  409. {<<":authority">>, <<"localhost">>},
  410. {<<":path">>, <<"/">>},
  411. {<<"content-length">>, <<"0">>}
  412. ], 0, cow_qpack:init()),
  413. {ok, _} = quicer:send(StreamRef, [
  414. cow_http3:encode_int(Type), %% Unknown frame.
  415. cow_http3:encode_int(iolist_size(Bytes)),
  416. Bytes,
  417. <<1>>, %% HEADERS frame.
  418. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  419. EncodedHeaders
  420. ]),
  421. ok = do_async_stream_shutdown(StreamRef),
  422. #{
  423. headers := #{<<":status">> := <<"200">>},
  424. body := <<"Hello world!">>
  425. } = do_receive_response(StreamRef),
  426. ok.
  427. headers_then_unknown(Config) ->
  428. doc("Receipt of HEADERS followed by unknown frame "
  429. "must be accepted. (RFC9114 4.1, RFC9114 9)"),
  430. headers_then_unknown(Config, do_unknown_frame_type(),
  431. rand:bytes(rand:uniform(4096))).
  432. headers_then_unknown(Config, Type, Bytes) ->
  433. #{conn := Conn} = do_connect(Config),
  434. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  435. {ok, EncodedHeaders, _EncData, _EncSt} = cow_qpack:encode_field_section([
  436. {<<":method">>, <<"GET">>},
  437. {<<":scheme">>, <<"https">>},
  438. {<<":authority">>, <<"localhost">>},
  439. {<<":path">>, <<"/">>},
  440. {<<"content-length">>, <<"0">>}
  441. ], 0, cow_qpack:init()),
  442. {ok, _} = quicer:send(StreamRef, [
  443. <<1>>, %% HEADERS frame.
  444. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  445. EncodedHeaders,
  446. cow_http3:encode_int(Type), %% Unknown frame.
  447. cow_http3:encode_int(iolist_size(Bytes)),
  448. Bytes
  449. ]),
  450. ok = do_async_stream_shutdown(StreamRef),
  451. #{
  452. headers := #{<<":status">> := <<"200">>},
  453. body := <<"Hello world!">>
  454. } = do_receive_response(StreamRef),
  455. ok.
  456. headers_then_data_then_unknown(Config) ->
  457. doc("Receipt of HEADERS followed by DATA followed by unknown frame "
  458. "must be accepted. (RFC9114 4.1, RFC9114 9)"),
  459. headers_then_data_then_unknown(Config, do_unknown_frame_type(),
  460. rand:bytes(rand:uniform(4096))).
  461. headers_then_data_then_unknown(Config, Type, Bytes) ->
  462. #{conn := Conn} = do_connect(Config),
  463. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  464. {ok, EncodedHeaders, _EncData, _EncSt} = cow_qpack:encode_field_section([
  465. {<<":method">>, <<"GET">>},
  466. {<<":scheme">>, <<"https">>},
  467. {<<":authority">>, <<"localhost">>},
  468. {<<":path">>, <<"/">>},
  469. {<<"content-length">>, <<"13">>}
  470. ], 0, cow_qpack:init()),
  471. {ok, _} = quicer:send(StreamRef, [
  472. <<1>>, %% HEADERS frame.
  473. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  474. EncodedHeaders,
  475. <<0>>, %% DATA frame.
  476. cow_http3:encode_int(13),
  477. <<"Hello server!">>,
  478. cow_http3:encode_int(Type), %% Unknown frame.
  479. cow_http3:encode_int(iolist_size(Bytes)),
  480. Bytes
  481. ]),
  482. ok = do_async_stream_shutdown(StreamRef),
  483. #{
  484. headers := #{<<":status">> := <<"200">>},
  485. body := <<"Hello world!">>
  486. } = do_receive_response(StreamRef),
  487. ok.
  488. headers_then_trailers_then_unknown(Config) ->
  489. doc("Receipt of HEADERS followed by trailer HEADERS followed by unknown frame "
  490. "must be accepted. (RFC9114 4.1, RFC9114 9)"),
  491. headers_then_data_then_unknown(Config, do_unknown_frame_type(),
  492. rand:bytes(rand:uniform(4096))).
  493. headers_then_trailers_then_unknown(Config, Type, Bytes) ->
  494. #{conn := Conn} = do_connect(Config),
  495. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  496. {ok, EncodedHeaders, _EncData, EncSt0} = cow_qpack:encode_field_section([
  497. {<<":method">>, <<"GET">>},
  498. {<<":scheme">>, <<"https">>},
  499. {<<":authority">>, <<"localhost">>},
  500. {<<":path">>, <<"/">>},
  501. {<<"content-length">>, <<"13">>}
  502. ], 0, cow_qpack:init()),
  503. {ok, EncodedTrailers, _EncData2, _EncSt} = cow_qpack:encode_field_section([
  504. {<<"content-type">>, <<"text/plain">>}
  505. ], 0, EncSt0),
  506. {ok, _} = quicer:send(StreamRef, [
  507. <<1>>, %% HEADERS frame.
  508. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  509. EncodedHeaders,
  510. <<1>>, %% HEADERS frame for trailers.
  511. cow_http3:encode_int(iolist_size(EncodedTrailers)),
  512. EncodedTrailers,
  513. cow_http3:encode_int(Type), %% Unknown frame.
  514. cow_http3:encode_int(iolist_size(Bytes)),
  515. Bytes
  516. ]),
  517. ok = do_async_stream_shutdown(StreamRef),
  518. #{
  519. headers := #{<<":status">> := <<"200">>},
  520. body := <<"Hello world!">>
  521. } = do_receive_response(StreamRef),
  522. ok.
  523. headers_then_data_then_unknown_then_trailers(Config) ->
  524. doc("Receipt of HEADERS followed by DATA followed by "
  525. "unknown frame followed by trailer HEADERS "
  526. "must be accepted. (RFC9114 4.1, RFC9114 9)"),
  527. headers_then_data_then_unknown_then_trailers(Config,
  528. do_unknown_frame_type(), rand:bytes(rand:uniform(4096))).
  529. headers_then_data_then_unknown_then_trailers(Config, Type, Bytes) ->
  530. #{conn := Conn} = do_connect(Config),
  531. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  532. {ok, EncodedHeaders, _EncData, EncSt0} = cow_qpack:encode_field_section([
  533. {<<":method">>, <<"GET">>},
  534. {<<":scheme">>, <<"https">>},
  535. {<<":authority">>, <<"localhost">>},
  536. {<<":path">>, <<"/">>},
  537. {<<"content-length">>, <<"13">>}
  538. ], 0, cow_qpack:init()),
  539. {ok, EncodedTrailers, _EncData2, _EncSt} = cow_qpack:encode_field_section([
  540. {<<"content-type">>, <<"text/plain">>}
  541. ], 0, EncSt0),
  542. {ok, _} = quicer:send(StreamRef, [
  543. <<1>>, %% HEADERS frame.
  544. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  545. EncodedHeaders,
  546. <<0>>, %% DATA frame.
  547. cow_http3:encode_int(13),
  548. <<"Hello server!">>,
  549. cow_http3:encode_int(Type), %% Unknown frame.
  550. cow_http3:encode_int(iolist_size(Bytes)),
  551. Bytes,
  552. <<1>>, %% HEADERS frame for trailers.
  553. cow_http3:encode_int(iolist_size(EncodedTrailers)),
  554. EncodedTrailers
  555. ]),
  556. ok = do_async_stream_shutdown(StreamRef),
  557. #{
  558. headers := #{<<":status">> := <<"200">>},
  559. body := <<"Hello world!">>
  560. } = do_receive_response(StreamRef),
  561. ok.
  562. headers_then_data_then_unknown_then_data(Config) ->
  563. doc("Receipt of HEADERS followed by DATA followed by "
  564. "unknown frame followed by DATA "
  565. "must be accepted. (RFC9114 4.1, RFC9114 9)"),
  566. headers_then_data_then_unknown_then_data(Config,
  567. do_unknown_frame_type(), rand:bytes(rand:uniform(4096))).
  568. headers_then_data_then_unknown_then_data(Config, Type, Bytes) ->
  569. #{conn := Conn} = do_connect(Config),
  570. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  571. {ok, EncodedHeaders, _EncData, _EncSt} = cow_qpack:encode_field_section([
  572. {<<":method">>, <<"GET">>},
  573. {<<":scheme">>, <<"https">>},
  574. {<<":authority">>, <<"localhost">>},
  575. {<<":path">>, <<"/">>},
  576. {<<"content-length">>, <<"13">>}
  577. ], 0, cow_qpack:init()),
  578. {ok, _} = quicer:send(StreamRef, [
  579. <<1>>, %% HEADERS frame.
  580. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  581. EncodedHeaders,
  582. <<0>>, %% DATA frame.
  583. cow_http3:encode_int(6),
  584. <<"Hello ">>,
  585. cow_http3:encode_int(Type), %% Unknown frame.
  586. cow_http3:encode_int(iolist_size(Bytes)),
  587. Bytes,
  588. <<0>>, %% DATA frame.
  589. cow_http3:encode_int(7),
  590. <<"server!">>
  591. ]),
  592. ok = do_async_stream_shutdown(StreamRef),
  593. #{
  594. headers := #{<<":status">> := <<"200">>},
  595. body := <<"Hello world!">>
  596. } = do_receive_response(StreamRef),
  597. ok.
  598. headers_then_data_then_trailers_then_unknown(Config) ->
  599. doc("Receipt of HEADERS followed by DATA followed by "
  600. "trailer HEADERS followed by unknown frame "
  601. "must be accepted. (RFC9114 4.1, RFC9114 9)"),
  602. headers_then_data_then_trailers_then_unknown(Config,
  603. do_unknown_frame_type(), rand:bytes(rand:uniform(4096))).
  604. headers_then_data_then_trailers_then_unknown(Config, Type, Bytes) ->
  605. #{conn := Conn} = do_connect(Config),
  606. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  607. {ok, EncodedHeaders, _EncData, EncSt0} = cow_qpack:encode_field_section([
  608. {<<":method">>, <<"GET">>},
  609. {<<":scheme">>, <<"https">>},
  610. {<<":authority">>, <<"localhost">>},
  611. {<<":path">>, <<"/">>},
  612. {<<"content-length">>, <<"13">>}
  613. ], 0, cow_qpack:init()),
  614. {ok, EncodedTrailers, _EncData2, _EncSt} = cow_qpack:encode_field_section([
  615. {<<"content-type">>, <<"text/plain">>}
  616. ], 0, EncSt0),
  617. {ok, _} = quicer:send(StreamRef, [
  618. <<1>>, %% HEADERS frame.
  619. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  620. EncodedHeaders,
  621. <<0>>, %% DATA frame.
  622. cow_http3:encode_int(13),
  623. <<"Hello server!">>,
  624. <<1>>, %% HEADERS frame for trailers.
  625. cow_http3:encode_int(iolist_size(EncodedTrailers)),
  626. EncodedTrailers,
  627. cow_http3:encode_int(Type), %% Unknown frame.
  628. cow_http3:encode_int(iolist_size(Bytes)),
  629. Bytes
  630. ]),
  631. ok = do_async_stream_shutdown(StreamRef),
  632. #{
  633. headers := #{<<":status">> := <<"200">>},
  634. body := <<"Hello world!">>
  635. } = do_receive_response(StreamRef),
  636. ok.
  637. do_unknown_frame_type() ->
  638. Type = rand:uniform(4611686018427387904) - 1,
  639. %% Retry if we get a value that's specified.
  640. case lists:member(Type, [
  641. 16#0, 16#1, 16#3, 16#4, 16#5, 16#7, 16#d, %% HTTP/3 core frame types.
  642. 16#2, 16#6, 16#8, 16#9 %% HTTP/3 reserved frame types that must be rejected.
  643. ]) of
  644. true -> do_unknown_frame_type();
  645. false -> Type
  646. end.
  647. reserved_then_headers(Config) ->
  648. doc("Receipt of reserved frame followed by HEADERS "
  649. "must be accepted when the reserved frame type is "
  650. "of the format 0x1f * N + 0x21. (RFC9114 4.1, RFC9114 7.2.8)"),
  651. unknown_then_headers(Config, do_reserved_frame_type(),
  652. rand:bytes(rand:uniform(4096))).
  653. headers_then_reserved(Config) ->
  654. doc("Receipt of HEADERS followed by reserved frame "
  655. "must be accepted when the reserved frame type is "
  656. "of the format 0x1f * N + 0x21. (RFC9114 4.1, RFC9114 7.2.8)"),
  657. headers_then_unknown(Config, do_reserved_frame_type(),
  658. rand:bytes(rand:uniform(4096))).
  659. headers_then_data_then_reserved(Config) ->
  660. doc("Receipt of HEADERS followed by DATA followed by reserved frame "
  661. "must be accepted when the reserved frame type is "
  662. "of the format 0x1f * N + 0x21. (RFC9114 4.1, RFC9114 7.2.8)"),
  663. headers_then_data_then_unknown(Config, do_reserved_frame_type(),
  664. rand:bytes(rand:uniform(4096))).
  665. headers_then_trailers_then_reserved(Config) ->
  666. doc("Receipt of HEADERS followed by trailer HEADERS followed by reserved frame "
  667. "must be accepted when the reserved frame type is "
  668. "of the format 0x1f * N + 0x21. (RFC9114 4.1, RFC9114 7.2.8)"),
  669. headers_then_trailers_then_unknown(Config, do_reserved_frame_type(),
  670. rand:bytes(rand:uniform(4096))).
  671. headers_then_data_then_reserved_then_trailers(Config) ->
  672. doc("Receipt of HEADERS followed by DATA followed by "
  673. "reserved frame followed by trailer HEADERS "
  674. "must be accepted when the reserved frame type is "
  675. "of the format 0x1f * N + 0x21. (RFC9114 4.1, RFC9114 7.2.8)"),
  676. headers_then_data_then_unknown_then_trailers(Config,
  677. do_reserved_frame_type(), rand:bytes(rand:uniform(4096))).
  678. headers_then_data_then_reserved_then_data(Config) ->
  679. doc("Receipt of HEADERS followed by DATA followed by "
  680. "reserved frame followed by DATA "
  681. "must be accepted when the reserved frame type is "
  682. "of the format 0x1f * N + 0x21. (RFC9114 4.1, RFC9114 7.2.8)"),
  683. headers_then_data_then_unknown_then_data(Config,
  684. do_reserved_frame_type(), rand:bytes(rand:uniform(4096))).
  685. headers_then_data_then_trailers_then_reserved(Config) ->
  686. doc("Receipt of HEADERS followed by DATA followed by "
  687. "trailer HEADERS followed by reserved frame "
  688. "must be accepted when the reserved frame type is "
  689. "of the format 0x1f * N + 0x21. (RFC9114 4.1, RFC9114 7.2.8)"),
  690. headers_then_data_then_trailers_then_unknown(Config,
  691. do_reserved_frame_type(), rand:bytes(rand:uniform(4096))).
  692. do_reserved_frame_type() ->
  693. 16#1f * (rand:uniform(148764065110560900) - 1) + 16#21.
  694. reject_transfer_encoding_header_with_body(Config) ->
  695. doc("Requests containing a transfer-encoding header must be rejected "
  696. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.1, RFC9114 4.1.2, RFC9114 4.2)"),
  697. #{conn := Conn} = do_connect(Config),
  698. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  699. {ok, EncodedHeaders, _EncData1, _EncSt0} = cow_qpack:encode_field_section([
  700. {<<":method">>, <<"GET">>},
  701. {<<":scheme">>, <<"https">>},
  702. {<<":authority">>, <<"localhost">>},
  703. {<<":path">>, <<"/">>},
  704. {<<"transfer-encoding">>, <<"chunked">>}
  705. ], 0, cow_qpack:init()),
  706. {ok, _} = quicer:send(StreamRef, [
  707. <<1>>, %% HEADERS frame.
  708. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  709. EncodedHeaders,
  710. <<0>>, %% DATA frame.
  711. cow_http3:encode_int(24),
  712. <<"13\r\nHello server!\r\n0\r\n\r\n">>
  713. ]),
  714. %% The stream should have been aborted.
  715. #{reason := h3_message_error} = do_wait_stream_aborted(StreamRef),
  716. ok.
  717. %% 4. Expressing HTTP Semantics in HTTP/3
  718. %% 4.1. HTTP Message Framing
  719. %% An HTTP request/response exchange fully consumes a client-initiated
  720. %bidirectional QUIC stream. After sending a request, a client MUST close the
  721. %stream for sending. Unless using the CONNECT method (see Section 4.4), clients
  722. %MUST NOT make stream closure dependent on receiving a response to their
  723. %request. After sending a final response, the server MUST close the stream for
  724. %sending. At this point, the QUIC stream is fully closed.
  725. %% @todo What to do with clients that DON'T close the stream
  726. %% for sending after the request is sent?
  727. %% If a client-initiated stream terminates without enough of the HTTP message
  728. %to provide a complete response, the server SHOULD abort its response stream
  729. %with the error code H3_REQUEST_INCOMPLETE.
  730. %% @todo difficult!!
  731. %% When the server does not need to receive the remainder of the request, it
  732. %MAY abort reading the request stream, send a complete response, and cleanly
  733. %close the sending part of the stream. The error code H3_NO_ERROR SHOULD be
  734. %used when requesting that the client stop sending on the request stream.
  735. %% @todo read_body related; h2 has this behavior but there is no corresponding test
  736. %% 4.1.1. Request Cancellation and Rejection
  737. %% When possible, it is RECOMMENDED that servers send an HTTP response with an
  738. %appropriate status code rather than cancelling a request it has already begun
  739. %processing.
  740. %% Implementations SHOULD cancel requests by abruptly terminating any
  741. %directions of a stream that are still open. To do so, an implementation resets
  742. %the sending parts of streams and aborts reading on the receiving parts of
  743. %streams; see Section 2.4 of [QUIC-TRANSPORT].
  744. %% When the server cancels a request without performing any application
  745. %processing, the request is considered "rejected". The server SHOULD abort its
  746. %response stream with the error code H3_REQUEST_REJECTED. In this context,
  747. %"processed" means that some data from the stream was passed to some higher
  748. %layer of software that might have taken some action as a result. The client
  749. %can treat requests rejected by the server as though they had never been sent
  750. %at all, thereby allowing them to be retried later.
  751. %% Servers MUST NOT use the H3_REQUEST_REJECTED error code for requests that
  752. %were partially or fully processed. When a server abandons a response after
  753. %partial processing, it SHOULD abort its response stream with the error code
  754. %H3_REQUEST_CANCELLED.
  755. %% @todo
  756. %% Client SHOULD use the error code H3_REQUEST_CANCELLED to cancel requests.
  757. %Upon receipt of this error code, a server MAY abruptly terminate the response
  758. %using the error code H3_REQUEST_REJECTED if no processing was performed.
  759. %Clients MUST NOT use the H3_REQUEST_REJECTED error code, except when a server
  760. %has requested closure of the request stream with this error code.
  761. %% @todo
  762. %4.1.2. Malformed Requests and Responses
  763. %A malformed request or response is one that is an otherwise valid sequence of
  764. %frames but is invalid due to:
  765. %
  766. %the presence of prohibited fields or pseudo-header fields,
  767. %% @todo reject_response_pseudo_headers
  768. %% @todo reject_unknown_pseudo_headers
  769. %% @todo reject_pseudo_headers_in_trailers
  770. %the absence of mandatory pseudo-header fields,
  771. %invalid values for pseudo-header fields,
  772. %pseudo-header fields after fields,
  773. %% @todo reject_pseudo_headers_after_regular_headers
  774. %an invalid sequence of HTTP messages,
  775. %the inclusion of invalid characters in field names or values.
  776. %
  777. %A request or response that is defined as having content when it contains a
  778. %Content-Length header field (Section 8.6 of [HTTP]) is malformed if the value
  779. %of the Content-Length header field does not equal the sum of the DATA frame
  780. %lengths received. A response that is defined as never having content, even
  781. %when a Content-Length is present, can have a non-zero Content-Length header
  782. %field even though no content is included in DATA frames.
  783. %
  784. %For malformed requests, a server MAY send an HTTP response indicating the
  785. %error prior to closing or resetting the stream.
  786. %% @todo All the malformed tests
  787. headers_reject_uppercase_header_name(Config) ->
  788. doc("Requests containing uppercase header names must be rejected "
  789. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.2, RFC9114 4.1.2)"),
  790. do_reject_malformed_header(Config,
  791. {<<"I-AM-GIGANTIC">>, <<"How's the weather up there?">>}
  792. ).
  793. %% 4.2. HTTP Fields
  794. %% An endpoint MUST NOT generate an HTTP/3 field section containing
  795. %connection-specific fields; any message containing connection-specific fields
  796. %MUST be treated as malformed.
  797. reject_connection_header(Config) ->
  798. doc("Requests containing a connection header must be rejected "
  799. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.2, RFC9114 4.1.2)"),
  800. do_reject_malformed_header(Config,
  801. {<<"connection">>, <<"close">>}
  802. ).
  803. reject_keep_alive_header(Config) ->
  804. doc("Requests containing a keep-alive header must be rejected "
  805. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.2, RFC9114 4.1.2)"),
  806. do_reject_malformed_header(Config,
  807. {<<"keep-alive">>, <<"timeout=5, max=1000">>}
  808. ).
  809. reject_proxy_authenticate_header(Config) ->
  810. doc("Requests containing a proxy-authenticate header must be rejected "
  811. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.2, RFC9114 4.1.2)"),
  812. do_reject_malformed_header(Config,
  813. {<<"proxy-authenticate">>, <<"Basic">>}
  814. ).
  815. reject_proxy_authorization_header(Config) ->
  816. doc("Requests containing a proxy-authorization header must be rejected "
  817. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.2, RFC9114 4.1.2)"),
  818. do_reject_malformed_header(Config,
  819. {<<"proxy-authorization">>, <<"Basic YWxhZGRpbjpvcGVuc2VzYW1l">>}
  820. ).
  821. reject_transfer_encoding_header(Config) ->
  822. doc("Requests containing a transfer-encoding header must be rejected "
  823. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.2, RFC9114 4.1.2)"),
  824. do_reject_malformed_header(Config,
  825. {<<"transfer-encoding">>, <<"chunked">>}
  826. ).
  827. reject_upgrade_header(Config) ->
  828. doc("Requests containing an upgrade header must be rejected "
  829. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.2, RFC9114 4.5, RFC9114 4.1.2)"),
  830. do_reject_malformed_header(Config,
  831. {<<"upgrade">>, <<"websocket">>}
  832. ).
  833. accept_te_header_value_trailers(Config) ->
  834. doc("Requests containing a TE header with a value of \"trailers\" "
  835. "must be accepted. (RFC9114 4.2)"),
  836. #{conn := Conn} = do_connect(Config),
  837. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  838. {ok, EncodedHeaders, _EncData1, EncSt0} = cow_qpack:encode_field_section([
  839. {<<":method">>, <<"GET">>},
  840. {<<":scheme">>, <<"https">>},
  841. {<<":authority">>, <<"localhost">>},
  842. {<<":path">>, <<"/">>},
  843. {<<"content-length">>, <<"0">>},
  844. {<<"te">>, <<"trailers">>}
  845. ], 0, cow_qpack:init()),
  846. {ok, EncodedTrailers, _EncData2, _EncSt} = cow_qpack:encode_field_section([
  847. {<<"content-type">>, <<"text/plain">>}
  848. ], 0, EncSt0),
  849. {ok, _} = quicer:send(StreamRef, [
  850. <<1>>, %% HEADERS frame.
  851. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  852. EncodedHeaders,
  853. <<1>>, %% HEADERS frame for trailers.
  854. cow_http3:encode_int(iolist_size(EncodedTrailers)),
  855. EncodedTrailers
  856. ]),
  857. ok = do_async_stream_shutdown(StreamRef),
  858. #{
  859. headers := #{<<":status">> := <<"200">>},
  860. body := <<"Hello world!">>
  861. } = do_receive_response(StreamRef),
  862. ok.
  863. reject_te_header_other_values(Config) ->
  864. doc("Requests containing a TE header with a value other than \"trailers\" must be rejected "
  865. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.2, RFC9114 4.1.2)"),
  866. do_reject_malformed_header(Config,
  867. {<<"te">>, <<"trailers, deflate;q=0.5">>}
  868. ).
  869. %% @todo response_dont_send_header_in_connection
  870. %% @todo response_dont_send_connection_header
  871. %% @todo response_dont_send_keep_alive_header
  872. %% @todo response_dont_send_proxy_connection_header
  873. %% @todo response_dont_send_transfer_encoding_header
  874. %% @todo response_dont_send_upgrade_header
  875. %% 4.2.1. Field Compression
  876. %% To allow for better compression efficiency, the Cookie header field
  877. %([COOKIES]) MAY be split into separate field lines, each with one or more
  878. %cookie-pairs, before compression. If a decompressed field section contains
  879. %multiple cookie field lines, these MUST be concatenated into a single byte
  880. %string using the two-byte delimiter of "; " (ASCII 0x3b, 0x20) before being
  881. %passed into a context other than HTTP/2 or HTTP/3, such as an HTTP/1.1
  882. %connection, or a generic HTTP server application.
  883. %% 4.2.2. Header Size Constraints
  884. %% An HTTP/3 implementation MAY impose a limit on the maximum size of the
  885. %message header it will accept on an individual HTTP message. A server that
  886. %receives a larger header section than it is willing to handle can send an HTTP
  887. %431 (Request Header Fields Too Large) status code ([RFC6585]). The size of a
  888. %field list is calculated based on the uncompressed size of fields, including
  889. %the length of the name and value in bytes plus an overhead of 32 bytes for
  890. %each field.
  891. %% If an implementation wishes to advise its peer of this limit, it can be
  892. %conveyed as a number of bytes in the SETTINGS_MAX_FIELD_SECTION_SIZE
  893. %parameter.
  894. reject_unknown_pseudo_headers(Config) ->
  895. doc("Requests containing unknown pseudo-headers must be rejected "
  896. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3, RFC9114 4.1.2)"),
  897. do_reject_malformed_header(Config,
  898. {<<":upgrade">>, <<"websocket">>}
  899. ).
  900. reject_response_pseudo_headers(Config) ->
  901. doc("Requests containing response pseudo-headers must be rejected "
  902. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3, RFC9114 4.1.2)"),
  903. do_reject_malformed_header(Config,
  904. {<<":status">>, <<"200">>}
  905. ).
  906. reject_pseudo_headers_in_trailers(Config) ->
  907. doc("Requests containing pseudo-headers in trailers must be rejected "
  908. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3, RFC9114 4.1.2)"),
  909. #{conn := Conn} = do_connect(Config),
  910. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  911. {ok, EncodedHeaders, _EncData1, EncSt0} = cow_qpack:encode_field_section([
  912. {<<":method">>, <<"GET">>},
  913. {<<":scheme">>, <<"https">>},
  914. {<<":authority">>, <<"localhost">>},
  915. {<<":path">>, <<"/">>},
  916. {<<"trailer">>, <<"x-checksum">>}
  917. ], 0, cow_qpack:init()),
  918. {ok, EncodedTrailers, _EncData2, _EncSt} = cow_qpack:encode_field_section([
  919. {<<"x-checksum">>, <<"md5:4cc909a007407f3706399b6496babec3">>},
  920. {<<":path">>, <<"/">>}
  921. ], 0, EncSt0),
  922. {ok, _} = quicer:send(StreamRef, [
  923. <<1>>, %% HEADERS frame.
  924. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  925. EncodedHeaders,
  926. <<0>>, %% DATA frame.
  927. cow_http3:encode_int(10000),
  928. <<0:10000/unit:8>>,
  929. <<1>>, %% HEADERS frame for trailers.
  930. cow_http3:encode_int(iolist_size(EncodedTrailers)),
  931. EncodedTrailers
  932. ]),
  933. %% The stream should have been aborted.
  934. #{reason := h3_message_error} = do_wait_stream_aborted(StreamRef),
  935. ok.
  936. reject_pseudo_headers_after_regular_headers(Config) ->
  937. doc("Requests containing pseudo-headers after regular headers must be rejected "
  938. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3, RFC9114 4.1.2)"),
  939. do_reject_malformed_headers(Config, [
  940. {<<":method">>, <<"GET">>},
  941. {<<":scheme">>, <<"https">>},
  942. {<<":authority">>, <<"localhost">>},
  943. {<<"content-length">>, <<"0">>},
  944. {<<":path">>, <<"/">>}
  945. ]).
  946. reject_userinfo(Config) ->
  947. doc("An authority containing a userinfo component must be rejected "
  948. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  949. do_reject_malformed_headers(Config, [
  950. {<<":method">>, <<"GET">>},
  951. {<<":scheme">>, <<"http">>},
  952. {<<":authority">>, <<"user@localhost">>},
  953. {<<":path">>, <<"/">>}
  954. ]).
  955. %% To ensure that the HTTP/1.1 request line can be reproduced accurately, this
  956. %% pseudo-header field (:authority) MUST be omitted when translating from an
  957. %% HTTP/1.1 request that has a request target in a method-specific form;
  958. %% see Section 7.1 of [HTTP].
  959. reject_empty_path(Config) ->
  960. doc("A request containing an empty path component must be rejected "
  961. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  962. do_reject_malformed_headers(Config, [
  963. {<<":method">>, <<"GET">>},
  964. {<<":scheme">>, <<"http">>},
  965. {<<":authority">>, <<"localhost">>},
  966. {<<":path">>, <<>>}
  967. ]).
  968. reject_missing_pseudo_header_method(Config) ->
  969. doc("A request without a method component must be rejected "
  970. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  971. do_reject_malformed_headers(Config, [
  972. {<<":scheme">>, <<"http">>},
  973. {<<":authority">>, <<"localhost">>},
  974. {<<":path">>, <<"/">>}
  975. ]).
  976. reject_many_pseudo_header_method(Config) ->
  977. doc("A request containing more than one method component must be rejected "
  978. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  979. do_reject_malformed_headers(Config, [
  980. {<<":method">>, <<"GET">>},
  981. {<<":method">>, <<"GET">>},
  982. {<<":scheme">>, <<"http">>},
  983. {<<":authority">>, <<"localhost">>},
  984. {<<":path">>, <<"/">>}
  985. ]).
  986. reject_missing_pseudo_header_scheme(Config) ->
  987. doc("A request without a scheme component must be rejected "
  988. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  989. do_reject_malformed_headers(Config, [
  990. {<<":method">>, <<"GET">>},
  991. {<<":authority">>, <<"localhost">>},
  992. {<<":path">>, <<"/">>}
  993. ]).
  994. reject_many_pseudo_header_scheme(Config) ->
  995. doc("A request containing more than one scheme component must be rejected "
  996. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  997. do_reject_malformed_headers(Config, [
  998. {<<":method">>, <<"GET">>},
  999. {<<":scheme">>, <<"http">>},
  1000. {<<":scheme">>, <<"http">>},
  1001. {<<":authority">>, <<"localhost">>},
  1002. {<<":path">>, <<"/">>}
  1003. ]).
  1004. reject_missing_pseudo_header_authority(Config) ->
  1005. doc("A request without an authority or host component must be rejected "
  1006. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  1007. do_reject_malformed_headers(Config, [
  1008. {<<":method">>, <<"GET">>},
  1009. {<<":scheme">>, <<"http">>},
  1010. {<<":path">>, <<"/">>}
  1011. ]).
  1012. accept_host_header_on_missing_pseudo_header_authority(Config) ->
  1013. doc("A request without an authority but with a host header must be accepted. "
  1014. "(RFC9114 4.3.1)"),
  1015. #{conn := Conn} = do_connect(Config),
  1016. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  1017. {ok, EncodedHeaders, _EncData1, _EncSt0} = cow_qpack:encode_field_section([
  1018. {<<":method">>, <<"GET">>},
  1019. {<<":scheme">>, <<"https">>},
  1020. {<<":path">>, <<"/">>},
  1021. {<<"host">>, <<"localhost">>}
  1022. ], 0, cow_qpack:init()),
  1023. {ok, _} = quicer:send(StreamRef, [
  1024. <<1>>, %% HEADERS frame.
  1025. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  1026. EncodedHeaders
  1027. ]),
  1028. % ok = do_async_stream_shutdown(StreamRef),
  1029. #{
  1030. headers := #{<<":status">> := <<"200">>},
  1031. body := <<"Hello world!">>
  1032. } = do_receive_response(StreamRef),
  1033. ok.
  1034. %% @todo
  1035. %% If the :scheme pseudo-header field identifies a scheme that has a mandatory
  1036. %% authority component (including "http" and "https"), the request MUST contain
  1037. %% either an :authority pseudo-header field or a Host header field.
  1038. %% - If both fields are present, they MUST NOT be empty.
  1039. %% - If both fields are present, they MUST contain the same value.
  1040. reject_many_pseudo_header_authority(Config) ->
  1041. doc("A request containing more than one authority component must be rejected "
  1042. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  1043. do_reject_malformed_headers(Config, [
  1044. {<<":method">>, <<"GET">>},
  1045. {<<":scheme">>, <<"http">>},
  1046. {<<":authority">>, <<"localhost">>},
  1047. {<<":authority">>, <<"localhost">>},
  1048. {<<":path">>, <<"/">>}
  1049. ]).
  1050. reject_missing_pseudo_header_path(Config) ->
  1051. doc("A request without a path component must be rejected "
  1052. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  1053. do_reject_malformed_headers(Config, [
  1054. {<<":method">>, <<"GET">>},
  1055. {<<":scheme">>, <<"http">>},
  1056. {<<":authority">>, <<"localhost">>}
  1057. ]).
  1058. reject_many_pseudo_header_path(Config) ->
  1059. doc("A request containing more than one path component must be rejected "
  1060. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  1061. do_reject_malformed_headers(Config, [
  1062. {<<":method">>, <<"GET">>},
  1063. {<<":scheme">>, <<"http">>},
  1064. {<<":authority">>, <<"localhost">>},
  1065. {<<":path">>, <<"/">>},
  1066. {<<":path">>, <<"/">>}
  1067. ]).
  1068. do_reject_malformed_header(Config, Header) ->
  1069. do_reject_malformed_headers(Config, [
  1070. {<<":method">>, <<"GET">>},
  1071. {<<":scheme">>, <<"https">>},
  1072. {<<":authority">>, <<"localhost">>},
  1073. {<<":path">>, <<"/">>},
  1074. Header
  1075. ]).
  1076. do_reject_malformed_headers(Config, Headers) ->
  1077. #{conn := Conn} = do_connect(Config),
  1078. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  1079. {ok, EncodedHeaders, _EncData1, _EncSt0}
  1080. = cow_qpack:encode_field_section(Headers, 0, cow_qpack:init()),
  1081. {ok, _} = quicer:send(StreamRef, [
  1082. <<1>>, %% HEADERS frame.
  1083. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  1084. EncodedHeaders
  1085. ]),
  1086. %% The stream should have been aborted.
  1087. #{reason := h3_message_error} = do_wait_stream_aborted(StreamRef),
  1088. ok.
  1089. %% For responses, a single ":status" pseudo-header field is defined that
  1090. %% carries the HTTP status code; see Section 15 of [HTTP]. This pseudo-header
  1091. %% field MUST be included in all responses; otherwise, the response is malformed
  1092. %% (see Section 4.1.2).
  1093. %% @todo Implement CONNECT. (RFC9114 4.4. The CONNECT Method)
  1094. %% @todo Maybe block the sending of 101 responses? (RFC9114 4.5. HTTP Upgrade) - also HTTP/2.
  1095. %% @todo Implement server push (RFC9114 4.6. Server Push)
  1096. %% @todo 5.2 Connection Shutdown - need a way to list connections.
  1097. %% @todo 5.3. Immediate Application Closure
  1098. bidi_allow_at_least_a_hundred(Config) ->
  1099. doc("Endpoints must allow the peer to create at least "
  1100. "one hundred bidirectional streams. (RFC9114 6.1"),
  1101. #{conn := Conn} = do_connect(Config),
  1102. receive
  1103. {quic, streams_available, Conn, #{bidi_streams := NumStreams}} ->
  1104. true = NumStreams >= 100,
  1105. ok
  1106. after 5000 ->
  1107. error(timeout)
  1108. end.
  1109. unidi_allow_at_least_three(Config) ->
  1110. doc("Endpoints must allow the peer to create at least "
  1111. "three unidirectional streams. (RFC9114 6.2"),
  1112. #{conn := Conn} = do_connect(Config),
  1113. %% Confirm that the server advertised support for at least 3 unidi streams.
  1114. receive
  1115. {quic, streams_available, Conn, #{unidi_streams := NumStreams}} ->
  1116. true = NumStreams >= 3,
  1117. ok
  1118. after 5000 ->
  1119. error(timeout)
  1120. end,
  1121. %% Confirm that we can create the unidi streams.
  1122. {ok, SettingsBin, _HTTP3Machine0} = cow_http3_machine:init(server, #{}),
  1123. {ok, ControlRef} = quicer:start_stream(Conn,
  1124. #{open_flag => ?QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL}),
  1125. {ok, _} = quicer:send(ControlRef, [<<0>>, SettingsBin]),
  1126. {ok, EncoderRef} = quicer:start_stream(Conn,
  1127. #{open_flag => ?QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL}),
  1128. {ok, _} = quicer:send(EncoderRef, <<2>>),
  1129. {ok, DecoderRef} = quicer:start_stream(Conn,
  1130. #{open_flag => ?QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL}),
  1131. {ok, _} = quicer:send(DecoderRef, <<3>>),
  1132. %% Streams shouldn't get closed.
  1133. receive
  1134. Msg ->
  1135. error(Msg)
  1136. after 1000 ->
  1137. ok
  1138. end.
  1139. unidi_create_critical_first(Config) ->
  1140. doc("Endpoints should create the HTTP control stream as well as "
  1141. "the QPACK encoder and decoder streams first. (RFC9114 6.2"),
  1142. %% The control stream is accepted in the do_connect/1 function.
  1143. #{conn := Conn} = do_connect(Config, #{peer_unidi_stream_count => 3}),
  1144. Unidi1 = do_accept_qpack_stream(Conn),
  1145. Unidi2 = do_accept_qpack_stream(Conn),
  1146. case {Unidi1, Unidi2} of
  1147. {{encoder, _}, {decoder, _}} ->
  1148. ok;
  1149. {{decoder, _}, {encoder, _}} ->
  1150. ok
  1151. end.
  1152. do_accept_qpack_stream(Conn) ->
  1153. receive
  1154. {quic, new_stream, StreamRef, #{flags := Flags}} ->
  1155. ok = quicer:setopt(StreamRef, active, true),
  1156. true = quicer:is_unidirectional(Flags),
  1157. receive {quic, <<Type>>, StreamRef, _} ->
  1158. {case Type of
  1159. 2 -> encoder;
  1160. 3 -> decoder
  1161. end, StreamRef}
  1162. after 5000 ->
  1163. error(timeout)
  1164. end
  1165. after 5000 ->
  1166. error(timeout)
  1167. end.
  1168. %% @todo We should also confirm that there's at least 1,024 bytes of
  1169. %% flow-control credit for each unidi stream the server creates. (How?)
  1170. %% It can be set via stream_recv_window_default in quicer.
  1171. %% Recipients of unknown stream types MUST either abort reading of the stream
  1172. %% or discard incoming data without further processing. If reading is aborted,
  1173. %% the recipient SHOULD use the H3_STREAM_CREATION_ERROR error code or a reserved
  1174. %% error code (Section 8.1). The recipient MUST NOT consider unknown stream types
  1175. %% to be a connection error of any kind.
  1176. %% @todo Cowboy limits the number of unidi streams to 3. But trying to create
  1177. %% more streams doesn't seem to generate an error from QUIC, it swallows it.
  1178. %% As certain stream types can affect connection state, a recipient SHOULD NOT
  1179. %% discard data from incoming unidirectional streams prior to reading the stream type.
  1180. %% Implementations MAY send stream types before knowing whether the peer
  1181. %supports them. However, stream types that could modify the state or semantics
  1182. %of existing protocol components, including QPACK or other extensions, MUST NOT
  1183. %be sent until the peer is known to support them.
  1184. %% @todo It may make sense for Cowboy to delay the creation of unidi streams
  1185. %% a little in order to save resources. We could create them when the
  1186. %% client does as well, or something similar.
  1187. %% A receiver MUST tolerate unidirectional streams being closed or reset prior
  1188. %% to the reception of the unidirectional stream header.
  1189. %% A control stream is indicated by a stream type of 0x00. Data on this stream
  1190. %% consists of HTTP/3 frames, as defined in Section 7.2.
  1191. %% Each side MUST initiate a single control stream at the beginning of the
  1192. %% connection and send its SETTINGS frame as the first frame on this stream.
  1193. %% @todo What to do when the client never opens a control stream?
  1194. %% @todo Similarly, a stream could be opened but with no data being sent.
  1195. %% @todo Similarly, a control stream could be opened with no SETTINGS frame sent.
  1196. %% If
  1197. %% the first frame of the control stream is any other frame type, this MUST be
  1198. %% treated as a connection error of type H3_MISSING_SETTINGS.
  1199. control_reject_first_frame_data(Config) ->
  1200. doc("The first frame on a control stream "
  1201. "must be a SETTINGS frame. (RFC9114 6.2.1)"),
  1202. #{conn := Conn} = do_connect(Config),
  1203. {ok, ControlRef} = quicer:start_stream(Conn,
  1204. #{open_flag => ?QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL}),
  1205. {ok, _} = quicer:send(ControlRef, [<<0>>, <<0, 12, "Hello world!">>]),
  1206. %% The connection should have been closed.
  1207. #{reason := h3_missing_settings} = do_wait_connection_closed(Conn),
  1208. ok.
  1209. %% @todo
  1210. %control_reject_first_frame_headers(Config) ->
  1211. %control_reject_first_frame_cancel_push(Config) ->
  1212. %control_reject_first_frame_push_promise(Config) ->
  1213. %control_accept_first_frame_settings(Config) ->
  1214. %control_reject_first_frame_goaway(Config) ->
  1215. %control_reject_first_frame_max_push_id(Config) ->
  1216. %control_reject_first_frame_reserved(Config) ->
  1217. control_reject_multiple(Config) ->
  1218. doc("Endpoints must not create multiple control streams. (RFC9114 6.2.1)"),
  1219. #{conn := Conn} = do_connect(Config),
  1220. %% Create two control streams.
  1221. {ok, SettingsBin, _HTTP3Machine0} = cow_http3_machine:init(server, #{}),
  1222. {ok, ControlRef1} = quicer:start_stream(Conn,
  1223. #{open_flag => ?QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL}),
  1224. {ok, _} = quicer:send(ControlRef1, [<<0>>, SettingsBin]),
  1225. {ok, ControlRef2} = quicer:start_stream(Conn,
  1226. #{open_flag => ?QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL}),
  1227. {ok, _} = quicer:send(ControlRef2, [<<0>>, SettingsBin]),
  1228. %% The connection should have been closed.
  1229. #{reason := h3_stream_creation_error} = do_wait_connection_closed(Conn),
  1230. ok.
  1231. control_local_closed_abort(Config) ->
  1232. doc("Endpoints must not close the control stream. (RFC9114 6.2.1)"),
  1233. #{conn := Conn} = do_connect(Config),
  1234. {ok, SettingsBin, _HTTP3Machine0} = cow_http3_machine:init(server, #{}),
  1235. {ok, ControlRef} = quicer:start_stream(Conn,
  1236. #{open_flag => ?QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL}),
  1237. {ok, _} = quicer:send(ControlRef, [<<0>>, SettingsBin]),
  1238. %% Wait a little to make sure the stream data was received before we abort.
  1239. timer:sleep(100),
  1240. %% Close the control stream.
  1241. quicer:async_shutdown_stream(ControlRef, ?QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0),
  1242. %% The connection should have been closed.
  1243. #{reason := h3_closed_critical_stream} = do_wait_connection_closed(Conn),
  1244. ok.
  1245. control_local_closed_graceful(Config) ->
  1246. doc("Endpoints must not close the control stream. (RFC9114 6.2.1)"),
  1247. #{conn := Conn} = do_connect(Config),
  1248. {ok, SettingsBin, _HTTP3Machine0} = cow_http3_machine:init(server, #{}),
  1249. {ok, ControlRef} = quicer:start_stream(Conn,
  1250. #{open_flag => ?QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL}),
  1251. {ok, _} = quicer:send(ControlRef, [<<0>>, SettingsBin]),
  1252. %% Close the control stream.
  1253. quicer:async_shutdown_stream(ControlRef, ?QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0),
  1254. %% The connection should have been closed.
  1255. #{reason := h3_closed_critical_stream} = do_wait_connection_closed(Conn),
  1256. ok.
  1257. control_remote_closed_abort(Config) ->
  1258. doc("Endpoints must not close the control stream. (RFC9114 6.2.1)"),
  1259. #{conn := Conn, control := ControlRef} = do_connect(Config),
  1260. %% Close the control stream.
  1261. quicer:async_shutdown_stream(ControlRef, ?QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0),
  1262. %% The connection should have been closed.
  1263. #{reason := h3_closed_critical_stream} = do_wait_connection_closed(Conn),
  1264. ok.
  1265. %% We cannot gracefully shutdown a remote unidi stream; only abort reading.
  1266. %% Because the contents of the control stream are used to manage the behavior
  1267. %of other streams, endpoints SHOULD provide enough flow-control credit to keep
  1268. %the peer's control stream from becoming blocked.
  1269. %% 2 control streams => error
  1270. %% no stream type sent (= no control stream)
  1271. %% no settings frame sent
  1272. %% another frame sent instead of settings
  1273. %% close control stream
  1274. %% flow control?
  1275. %% Helper functions.
  1276. do_connect(Config) ->
  1277. do_connect(Config, #{}).
  1278. do_connect(Config, Opts) ->
  1279. {ok, Conn} = quicer:connect("localhost", config(port, Config),
  1280. Opts#{alpn => ["h3"], verify => none}, 5000),
  1281. %% To make sure the connection is fully established we wait
  1282. %% to receive the SETTINGS frame on the control stream.
  1283. {ok, ControlRef, _Settings} = do_wait_settings(Conn),
  1284. #{
  1285. conn => Conn,
  1286. control => ControlRef %% This is the peer control stream.
  1287. }.
  1288. do_wait_settings(Conn) ->
  1289. receive
  1290. {quic, new_stream, StreamRef, #{flags := Flags}} ->
  1291. ok = quicer:setopt(StreamRef, active, true),
  1292. true = quicer:is_unidirectional(Flags),
  1293. receive {quic, <<
  1294. 0, %% Control stream.
  1295. 4, 0 %% Empty SETTINGS frame.
  1296. >>, StreamRef, _} ->
  1297. {ok, StreamRef, #{}}
  1298. after 5000 ->
  1299. {error, timeout}
  1300. end
  1301. after 5000 ->
  1302. {error, timeout}
  1303. end.
  1304. do_receive_data(StreamRef) ->
  1305. receive
  1306. {quic, Data, StreamRef, _Flags} when is_binary(Data) ->
  1307. {ok, Data}
  1308. after 5000 ->
  1309. {error, timeout}
  1310. end.
  1311. do_guess_int_encoding(Data) ->
  1312. SizeWithLen = byte_size(Data) - 1,
  1313. if
  1314. SizeWithLen < 64 + 1 ->
  1315. {0, 6};
  1316. SizeWithLen < 16384 + 2 ->
  1317. {1, 14};
  1318. SizeWithLen < 1073741824 + 4 ->
  1319. {2, 30};
  1320. SizeWithLen < 4611686018427387904 + 8 ->
  1321. {3, 62}
  1322. end.
  1323. do_async_stream_shutdown(StreamRef) ->
  1324. quicer:async_shutdown_stream(StreamRef, ?QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0),
  1325. receive
  1326. {quic, send_shutdown_complete, StreamRef, true} ->
  1327. ok
  1328. after 5000 ->
  1329. {error, timeout}
  1330. end.
  1331. do_wait_peer_send_shutdown(StreamRef) ->
  1332. receive
  1333. {quic, peer_send_shutdown, StreamRef, undefined} ->
  1334. ok
  1335. after 5000 ->
  1336. {error, timeout}
  1337. end.
  1338. do_wait_stream_aborted(StreamRef) ->
  1339. receive
  1340. {quic, peer_send_aborted, StreamRef, Code} ->
  1341. Reason = cow_http3:code_to_error(Code),
  1342. #{reason => Reason};
  1343. {quic, peer_receive_aborted, StreamRef, Code} ->
  1344. Reason = cow_http3:code_to_error(Code),
  1345. #{reason => Reason}
  1346. after 5000 ->
  1347. {error, timeout}
  1348. end.
  1349. do_wait_stream_closed(StreamRef) ->
  1350. receive
  1351. {quic, stream_closed, StreamRef, #{error := Error, is_conn_shutdown := false}} ->
  1352. 0 = Error,
  1353. ok
  1354. after 5000 ->
  1355. {error, timeout}
  1356. end.
  1357. do_receive_response(StreamRef) ->
  1358. {ok, Data} = do_receive_data(StreamRef),
  1359. {HLenEnc, HLenBits} = do_guess_int_encoding(Data),
  1360. <<
  1361. 1, %% HEADERS frame.
  1362. HLenEnc:2, HLen:HLenBits,
  1363. EncodedResponse:HLen/bytes,
  1364. Rest/bits
  1365. >> = Data,
  1366. {ok, DecodedResponse, _DecData, _DecSt}
  1367. = cow_qpack:decode_field_section(EncodedResponse, 0, cow_qpack:init()),
  1368. Headers = maps:from_list(DecodedResponse),
  1369. #{<<"content-length">> := BodyLen} = Headers,
  1370. {DLenEnc, DLenBits} = do_guess_int_encoding(Data),
  1371. <<
  1372. 0, %% DATA frame.
  1373. DLenEnc:2, DLen:DLenBits,
  1374. Body:DLen/bytes
  1375. >> = Rest,
  1376. BodyLen = integer_to_binary(byte_size(Body)),
  1377. ok = do_wait_peer_send_shutdown(StreamRef),
  1378. % ok = do_wait_stream_closed(StreamRef),
  1379. #{
  1380. headers => Headers,
  1381. body => Body
  1382. }.
  1383. do_wait_connection_closed(Conn) ->
  1384. receive
  1385. {quic, shutdown, Conn, {unknown_quic_status, Code}} ->
  1386. Reason = cow_http3:code_to_error(Code),
  1387. #{reason => Reason}
  1388. after 5000 ->
  1389. {error, timeout}
  1390. end.
  1391. %% 5.2. Connection Shutdown
  1392. %% Endpoints initiate the graceful shutdown of an HTTP/3 connection by sending
  1393. %a GOAWAY frame. The GOAWAY frame contains an identifier that indicates to the
  1394. %receiver the range of requests or pushes that were or might be processed in
  1395. %this connection. The server sends a client-initiated bidirectional stream ID;
  1396. %the client sends a push ID. Requests or pushes with the indicated identifier
  1397. %or greater are rejected (Section 4.1.1) by the sender of the GOAWAY. This
  1398. %identifier MAY be zero if no requests or pushes were processed.
  1399. %% Upon sending a GOAWAY frame, the endpoint SHOULD explicitly cancel (see
  1400. %Sections 4.1.1 and 7.2.3) any requests or pushes that have identifiers greater
  1401. %than or equal to the one indicated, in order to clean up transport state for
  1402. %the affected streams. The endpoint SHOULD continue to do so as more requests
  1403. %or pushes arrive.
  1404. %% Endpoints MUST NOT initiate new requests or promise new pushes on the
  1405. %connection after receipt of a GOAWAY frame from the peer.
  1406. %% Requests on stream IDs less than the stream ID in a GOAWAY frame from the
  1407. %server might have been processed; their status cannot be known until a
  1408. %response is received, the stream is reset individually, another GOAWAY is
  1409. %received with a lower stream ID than that of the request in question, or the
  1410. %connection terminates.
  1411. %% Servers MAY reject individual requests on streams below the indicated ID if
  1412. %these requests were not processed.
  1413. %% If a server receives a GOAWAY frame after having promised pushes with a push
  1414. %ID greater than or equal to the identifier contained in the GOAWAY frame,
  1415. %those pushes will not be accepted.
  1416. %% Servers SHOULD send a GOAWAY frame when the closing of a connection is known
  1417. %in advance, even if the advance notice is small, so that the remote peer can
  1418. %know whether or not a request has been partially processed.
  1419. %% An endpoint MAY send multiple GOAWAY frames indicating different
  1420. %identifiers, but the identifier in each frame MUST NOT be greater than the
  1421. %identifier in any previous frame, since clients might already have retried
  1422. %unprocessed requests on another HTTP connection. Receiving a GOAWAY containing
  1423. %a larger identifier than previously received MUST be treated as a connection
  1424. %error of type H3_ID_ERROR.
  1425. %% An endpoint that is attempting to gracefully shut down a connection can send
  1426. %a GOAWAY frame with a value set to the maximum possible value (2^62-4 for
  1427. %servers, 2^62-1 for clients).
  1428. %% Even when a GOAWAY indicates that a given request or push will not be
  1429. %processed or accepted upon receipt, the underlying transport resources still
  1430. %exist. The endpoint that initiated these requests can cancel them to clean up
  1431. %transport state.
  1432. %% Once all accepted requests and pushes have been processed, the endpoint can
  1433. %permit the connection to become idle, or it MAY initiate an immediate closure
  1434. %of the connection. An endpoint that completes a graceful shutdown SHOULD use
  1435. %the H3_NO_ERROR error code when closing the connection.
  1436. %% If a client has consumed all available bidirectional stream IDs with
  1437. %requests, the server need not send a GOAWAY frame, since the client is unable
  1438. %to make further requests. @todo OK that one's some weird stuff lol
  1439. %% 5.3. Immediate Application Closure
  1440. %% Before closing the connection, a GOAWAY frame MAY be sent to allow the
  1441. %client to retry some requests. Including the GOAWAY frame in the same packet
  1442. %as the QUIC CONNECTION_CLOSE frame improves the chances of the frame being
  1443. %received by clients.
  1444. %% 6.2.2. Push Streams
  1445. %% A push stream is indicated by a stream type of 0x01, followed by the push ID
  1446. %of the promise that it fulfills, encoded as a variable-length integer. The
  1447. %remaining data on this stream consists of HTTP/3 frames, as defined in Section
  1448. %7.2, and fulfills a promised server push by zero or more interim HTTP
  1449. %responses followed by a single final HTTP response, as defined in Section 4.1.
  1450. %Server push and push IDs are described in Section 4.6.
  1451. %% Only servers can push; if a server receives a client-initiated push stream,
  1452. %this MUST be treated as a connection error of type H3_STREAM_CREATION_ERROR.
  1453. %% Each push ID MUST only be used once in a push stream header. If a client
  1454. %detects that a push stream header includes a push ID that was used in another
  1455. %push stream header, the client MUST treat this as a connection error of type
  1456. %H3_ID_ERROR.
  1457. %% 6.2.3. Reserved Stream Types
  1458. %% Stream types of the format 0x1f * N + 0x21 for non-negative integer values
  1459. %of N are reserved to exercise the requirement that unknown types be ignored.
  1460. %These streams have no semantics, and they can be sent when application-layer
  1461. %padding is desired. They MAY also be sent on connections where no data is
  1462. %currently being transferred. Endpoints MUST NOT consider these streams to have
  1463. %any meaning upon receipt.
  1464. %% The payload and length of the stream are selected in any manner the sending
  1465. %implementation chooses. When sending a reserved stream type, the
  1466. %implementation MAY either terminate the stream cleanly or reset it. When
  1467. %resetting the stream, either the H3_NO_ERROR error code or a reserved error
  1468. %code (Section 8.1) SHOULD be used.
  1469. %% 7. HTTP Framing Layer
  1470. %% Note that, unlike QUIC frames, HTTP/3 frames can span multiple packets.
  1471. %% 7.1. Frame Layout
  1472. %% Each frame's payload MUST contain exactly the fields identified in its
  1473. %description. A frame payload that contains additional bytes after the
  1474. %identified fields or a frame payload that terminates before the end of the
  1475. %identified fields MUST be treated as a connection error of type
  1476. %H3_FRAME_ERROR. In particular, redundant length encodings MUST be verified to
  1477. %be self-consistent; see Section 10.8.
  1478. %% When a stream terminates cleanly, if the last frame on the stream was
  1479. %truncated, this MUST be treated as a connection error of type H3_FRAME_ERROR.
  1480. %Streams that terminate abruptly may be reset at any point in a frame.
  1481. %% 7.2. Frame Definitions
  1482. %% 7.2.1. DATA
  1483. %% DATA frames MUST be associated with an HTTP request or response. If a DATA
  1484. %frame is received on a control stream, the recipient MUST respond with a
  1485. %connection error of type H3_FRAME_UNEXPECTED.
  1486. %% 7.2.2. HEADERS
  1487. %% HEADERS frames can only be sent on request streams or push streams. If a
  1488. %HEADERS frame is received on a control stream, the recipient MUST respond with
  1489. %a connection error of type H3_FRAME_UNEXPECTED.
  1490. %% 7.2.3. CANCEL_PUSH
  1491. %% When a client sends a CANCEL_PUSH frame, it is indicating that it does not
  1492. %wish to receive the promised resource. The server SHOULD abort sending the
  1493. %resource, but the mechanism to do so depends on the state of the corresponding
  1494. %push stream. If the server has not yet created a push stream, it does not
  1495. %create one. If the push stream is open, the server SHOULD abruptly terminate
  1496. %that stream. If the push stream has already ended, the server MAY still
  1497. %abruptly terminate the stream or MAY take no action.
  1498. %% A server sends a CANCEL_PUSH frame to indicate that it will not be
  1499. %fulfilling a promise that was previously sent. The client cannot expect the
  1500. %corresponding promise to be fulfilled, unless it has already received and
  1501. %processed the promised response. Regardless of whether a push stream has been
  1502. %opened, a server SHOULD send a CANCEL_PUSH frame when it determines that
  1503. %promise will not be fulfilled. If a stream has already been opened, the server
  1504. %can abort sending on the stream with an error code of H3_REQUEST_CANCELLED.
  1505. %% Sending a CANCEL_PUSH frame has no direct effect on the state of existing
  1506. %push streams. A client SHOULD NOT send a CANCEL_PUSH frame when it has already
  1507. %received a corresponding push stream. A push stream could arrive after a
  1508. %client has sent a CANCEL_PUSH frame, because a server might not have processed
  1509. %the CANCEL_PUSH. The client SHOULD abort reading the stream with an error code
  1510. %of H3_REQUEST_CANCELLED.
  1511. %% A CANCEL_PUSH frame is sent on the control stream. Receiving a CANCEL_PUSH
  1512. %frame on a stream other than the control stream MUST be treated as a
  1513. %connection error of type H3_FRAME_UNEXPECTED.
  1514. %% If a CANCEL_PUSH frame is received that references a push ID greater than
  1515. %currently allowed on the connection, this MUST be treated as a connection
  1516. %error of type H3_ID_ERROR.
  1517. %% If the client receives a CANCEL_PUSH frame, that frame might identify a push
  1518. %ID that has not yet been mentioned by a PUSH_PROMISE frame due to reordering.
  1519. %If a server receives a CANCEL_PUSH frame for a push ID that has not yet been
  1520. %mentioned by a PUSH_PROMISE frame, this MUST be treated as a connection error
  1521. %of type H3_ID_ERROR.
  1522. %% 7.2.4. SETTINGS
  1523. %% A SETTINGS frame MUST be sent as the first frame of each control stream (see
  1524. %Section 6.2.1) by each peer, and it MUST NOT be sent subsequently. If an
  1525. %endpoint receives a second SETTINGS frame on the control stream, the endpoint
  1526. %MUST respond with a connection error of type H3_FRAME_UNEXPECTED.
  1527. %% SETTINGS frames MUST NOT be sent on any stream other than the control
  1528. %stream. If an endpoint receives a SETTINGS frame on a different stream, the
  1529. %endpoint MUST respond with a connection error of type H3_FRAME_UNEXPECTED.
  1530. %% The same setting identifier MUST NOT occur more than once in the SETTINGS
  1531. %frame. A receiver MAY treat the presence of duplicate setting identifiers as a
  1532. %connection error of type H3_SETTINGS_ERROR.
  1533. %% An implementation MUST ignore any parameter with an identifier it does not understand.
  1534. %% 7.2.4.1. Defined SETTINGS Parameters
  1535. %% Setting identifiers of the format 0x1f * N + 0x21 for non-negative integer
  1536. %values of N are reserved to exercise the requirement that unknown identifiers
  1537. %be ignored. Such settings have no defined meaning. Endpoints SHOULD include at
  1538. %least one such setting in their SETTINGS frame. Endpoints MUST NOT consider
  1539. %such settings to have any meaning upon receipt.
  1540. %% -> try sending COW\0 BOY\0 if that fits the encoding and restrictions
  1541. %otherwise something similar
  1542. %% Setting identifiers that were defined in [HTTP/2] where there is no
  1543. %corresponding HTTP/3 setting have also been reserved (Section 11.2.2). These
  1544. %reserved settings MUST NOT be sent, and their receipt MUST be treated as a
  1545. %connection error of type H3_SETTINGS_ERROR.
  1546. %% 7.2.4.2. Initialization
  1547. %% An HTTP implementation MUST NOT send frames or requests that would be
  1548. %invalid based on its current understanding of the peer's settings.
  1549. %% All settings begin at an initial value. Each endpoint SHOULD use these
  1550. %initial values to send messages before the peer's SETTINGS frame has arrived,
  1551. %as packets carrying the settings can be lost or delayed. When the SETTINGS
  1552. %frame arrives, any settings are changed to their new values.
  1553. %% Endpoints MUST NOT require any data to be received from the peer prior to
  1554. %sending the SETTINGS frame; settings MUST be sent as soon as the transport is
  1555. %ready to send data.
  1556. %% A server MAY accept 0-RTT and subsequently provide different settings in its
  1557. %SETTINGS frame. If 0-RTT data is accepted by the server, its SETTINGS frame
  1558. %MUST NOT reduce any limits or alter any values that might be violated by the
  1559. %client with its 0-RTT data. The server MUST include all settings that differ
  1560. %from their default values. If a server accepts 0-RTT but then sends settings
  1561. %that are not compatible with the previously specified settings, this MUST be
  1562. %treated as a connection error of type H3_SETTINGS_ERROR. If a server accepts
  1563. %0-RTT but then sends a SETTINGS frame that omits a setting value that the
  1564. %client understands (apart from reserved setting identifiers) that was
  1565. %previously specified to have a non-default value, this MUST be treated as a
  1566. %connection error of type H3_SETTINGS_ERROR.
  1567. %% 7.2.5. PUSH_PROMISE
  1568. %% A server MUST NOT use a push ID that is larger than the client has provided
  1569. %in a MAX_PUSH_ID frame (Section 7.2.7).
  1570. %% A server MAY use the same push ID in multiple PUSH_PROMISE frames. If so,
  1571. %the decompressed request header sets MUST contain the same fields in the same
  1572. %order, and both the name and the value in each field MUST be exact matches.
  1573. %% Allowing duplicate references to the same push ID is primarily to reduce
  1574. %duplication caused by concurrent requests. A server SHOULD avoid reusing a
  1575. %push ID over a long period. Clients are likely to consume server push
  1576. %responses and not retain them for reuse over time. Clients that see a
  1577. %PUSH_PROMISE frame that uses a push ID that they have already consumed and
  1578. %discarded are forced to ignore the promise.
  1579. %% A client MUST NOT send a PUSH_PROMISE frame. A server MUST treat the receipt
  1580. %of a PUSH_PROMISE frame as a connection error of type H3_FRAME_UNEXPECTED.
  1581. %% 7.2.6. GOAWAY
  1582. %% (not sure what applies to the server, should the server reject GOAWAY on
  1583. %non-control stream too?)
  1584. %% 7.2.7. MAX_PUSH_ID
  1585. %% Receipt of a MAX_PUSH_ID frame on any other stream MUST be treated as a
  1586. %connection error of type H3_FRAME_UNEXPECTED.
  1587. %% The maximum push ID is unset when an HTTP/3 connection is created, meaning
  1588. %that a server cannot push until it receives a MAX_PUSH_ID frame.
  1589. %% A MAX_PUSH_ID frame cannot reduce the maximum push ID; receipt of a
  1590. %MAX_PUSH_ID frame that contains a smaller value than previously received MUST
  1591. %be treated as a connection error of type H3_ID_ERROR.
  1592. %% 7.2.8. Reserved Frame Types
  1593. %% These frames have no semantics, and they MAY be sent on any stream where
  1594. %frames are allowed to be sent. This enables their use for application-layer
  1595. %padding. Endpoints MUST NOT consider these frames to have any meaning upon
  1596. %receipt.
  1597. %% Frame types that were used in HTTP/2 where there is no corresponding HTTP/3
  1598. %frame have also been reserved (Section 11.2.1). These frame types MUST NOT be
  1599. %sent, and their receipt MUST be treated as a connection error of type
  1600. %H3_FRAME_UNEXPECTED.
  1601. %% 8. Error Handling
  1602. %% An endpoint MAY choose to treat a stream error as a connection error under
  1603. %certain circumstances, closing the entire connection in response to a
  1604. %condition on a single stream.
  1605. %% Because new error codes can be defined without negotiation (see Section 9),
  1606. %use of an error code in an unexpected context or receipt of an unknown error
  1607. %code MUST be treated as equivalent to H3_NO_ERROR.
  1608. %% 8.1. HTTP/3 Error Codes
  1609. %% H3_INTERNAL_ERROR (0x0102): An internal error has occurred in the HTTP stack.
  1610. %% H3_FRAME_ERROR (0x0106): A frame that fails to satisfy layout requirements
  1611. %or with an invalid size was received.
  1612. %% H3_EXCESSIVE_LOAD (0x0107): The endpoint detected that its peer is
  1613. %exhibiting a behavior that might be generating excessive load.
  1614. %% (more)
  1615. %% Error codes of the format 0x1f * N + 0x21 for non-negative integer values of
  1616. %N are reserved to exercise the requirement that unknown error codes be treated
  1617. %as equivalent to H3_NO_ERROR (Section 9). Implementations SHOULD select an
  1618. %error code from this space with some probability when they would have sent
  1619. %H3_NO_ERROR.
  1620. %% 9. Extensions to HTTP/3
  1621. %% Extensions are permitted to use new frame types (Section 7.2), new settings
  1622. %(Section 7.2.4.1), new error codes (Section 8), or new unidirectional stream
  1623. %types (Section 6.2). Registries are established for managing these extension
  1624. %points: frame types (Section 11.2.1), settings (Section 11.2.2), error codes
  1625. %(Section 11.2.3), and stream types (Section 11.2.4).
  1626. %% Implementations MUST ignore unknown or unsupported values in all extensible
  1627. %protocol elements. Implementations MUST discard data or abort reading on
  1628. %unidirectional streams that have unknown or unsupported types. This means that
  1629. %any of these extension points can be safely used by extensions without prior
  1630. %arrangement or negotiation. However, where a known frame type is required to
  1631. %be in a specific location, such as the SETTINGS frame as the first frame of
  1632. %the control stream (see Section 6.2.1), an unknown frame type does not satisfy
  1633. %that requirement and SHOULD be treated as an error.
  1634. %% If a setting is used for extension negotiation, the default value MUST be
  1635. %defined in such a fashion that the extension is disabled if the setting is
  1636. %omitted.
  1637. %% 10. Security Considerations
  1638. %% 10.3. Intermediary-Encapsulation Attacks
  1639. %% Requests or responses containing invalid field names MUST be treated as malformed.
  1640. %% Any request or response that contains a character not permitted in a field
  1641. %value MUST be treated as malformed.
  1642. %% 10.5. Denial-of-Service Considerations
  1643. %% Implementations SHOULD track the use of these features and set limits on
  1644. %their use. An endpoint MAY treat activity that is suspicious as a connection
  1645. %error of type H3_EXCESSIVE_LOAD, but false positives will result in disrupting
  1646. %valid connections and requests.
  1647. %% 10.5.1. Limits on Field Section Size
  1648. %% An endpoint can use the SETTINGS_MAX_FIELD_SECTION_SIZE (Section 4.2.2)
  1649. %setting to advise peers of limits that might apply on the size of field
  1650. %sections.
  1651. %% A server that receives a larger field section than it is willing to handle
  1652. %can send an HTTP 431 (Request Header Fields Too Large) status code
  1653. %([RFC6585]).
  1654. %% 10.6. Use of Compression
  1655. %% Implementations communicating on a secure channel MUST NOT compress content
  1656. %that includes both confidential and attacker-controlled data unless separate
  1657. %compression contexts are used for each source of data. Compression MUST NOT be
  1658. %used if the source of data cannot be reliably determined.
  1659. %% 10.8. Frame Parsing
  1660. %% An implementation MUST ensure that the length of a frame exactly matches the
  1661. %length of the fields it contains.
  1662. %% 10.9. Early Data
  1663. %% The anti-replay mitigations in [HTTP-REPLAY] MUST be applied when using HTTP/3 with 0-RTT.
  1664. %% 10.10. Migration
  1665. %% Certain HTTP implementations use the client address for logging or
  1666. %access-control purposes. Since a QUIC client's address might change during a
  1667. %connection (and future versions might support simultaneous use of multiple
  1668. %addresses), such implementations will need to either actively retrieve the
  1669. %client's current address or addresses when they are relevant or explicitly
  1670. %accept that the original address might change. -> documentation for now
  1671. %% 11.2.1. Frame Types
  1672. %% Reserved types: 0x02 0x06 0x08 0x09
  1673. %% 11.2.2. Settings Parameters
  1674. %% Reserved settings: 0x00 0x02 0x03 0x04 0x05
  1675. %% Appendix A. Considerations for Transitioning from HTTP/2
  1676. %% A.1. Streams
  1677. %% QUIC considers a stream closed when all data has been received and sent data
  1678. %has been acknowledged by the peer. HTTP/2 considers a stream closed when the
  1679. %frame containing the END_STREAM bit has been committed to the transport. As a
  1680. %result, the stream for an equivalent exchange could remain "active" for a
  1681. %longer period of time. HTTP/3 servers might choose to permit a larger number
  1682. %of concurrent client-initiated bidirectional streams to achieve equivalent
  1683. %concurrency to HTTP/2, depending on the expected usage patterns. ->
  1684. %documentation?
  1685. %% A.3. HTTP/2 SETTINGS Parameters
  1686. %% SETTINGS_MAX_FRAME_SIZE (0x05): This setting has no equivalent in HTTP/3.
  1687. %Specifying a setting with the identifier 0x05 (corresponding to the
  1688. %SETTINGS_MAX_FRAME_SIZE parameter) in the HTTP/3 SETTINGS frame is an error.
  1689. %-> do we still want a limit, if so how?