rfc9114_SUITE.erl 73 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802
  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.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. %% @todo
  1013. %accept_host_header_on_missing_pseudo_header_authority(Config) ->
  1014. % doc("A request without an authority but with a host header must be accepted. "
  1015. % "(RFC7540 8.1.2.3, RFC7540 8.1.3)"),
  1016. % {ok, Socket} = do_handshake(Config),
  1017. % %% Send a HEADERS frame with host header and without an :authority pseudo-header.
  1018. % {HeadersBlock, _} = cow_hpack:encode([
  1019. % {<<":method">>, <<"GET">>},
  1020. % {<<":scheme">>, <<"http">>},
  1021. % {<<":path">>, <<"/">>},
  1022. % {<<"host">>, <<"localhost">>}
  1023. % ]),
  1024. % ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1025. % %% Receive a 200 response.
  1026. % {ok, << Len:24, 1:8, _:8, _:32 >>} = gen_tcp:recv(Socket, 9, 6000),
  1027. % {ok, RespHeadersBlock} = gen_tcp:recv(Socket, Len, 6000),
  1028. % {RespHeaders, _} = cow_hpack:decode(RespHeadersBlock),
  1029. % {_, <<"200">>} = lists:keyfind(<<":status">>, 1, RespHeaders),
  1030. % ok.
  1031. %% @todo
  1032. %% If the :scheme pseudo-header field identifies a scheme that has a mandatory
  1033. %% authority component (including "http" and "https"), the request MUST contain
  1034. %% either an :authority pseudo-header field or a Host header field.
  1035. %% - If both fields are present, they MUST NOT be empty.
  1036. %% - If both fields are present, they MUST contain the same value.
  1037. reject_many_pseudo_header_authority(Config) ->
  1038. doc("A request containing more than one authority component must be rejected "
  1039. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  1040. do_reject_malformed_headers(Config, [
  1041. {<<":method">>, <<"GET">>},
  1042. {<<":scheme">>, <<"http">>},
  1043. {<<":authority">>, <<"localhost">>},
  1044. {<<":authority">>, <<"localhost">>},
  1045. {<<":path">>, <<"/">>}
  1046. ]).
  1047. reject_missing_pseudo_header_path(Config) ->
  1048. doc("A request without a path component must be rejected "
  1049. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  1050. do_reject_malformed_headers(Config, [
  1051. {<<":method">>, <<"GET">>},
  1052. {<<":scheme">>, <<"http">>},
  1053. {<<":authority">>, <<"localhost">>}
  1054. ]).
  1055. reject_many_pseudo_header_path(Config) ->
  1056. doc("A request containing more than one path component must be rejected "
  1057. "with an H3_MESSAGE_ERROR stream error. (RFC9114 4.3.1, RFC9114 4.1.2)"),
  1058. do_reject_malformed_headers(Config, [
  1059. {<<":method">>, <<"GET">>},
  1060. {<<":scheme">>, <<"http">>},
  1061. {<<":authority">>, <<"localhost">>},
  1062. {<<":path">>, <<"/">>},
  1063. {<<":path">>, <<"/">>}
  1064. ]).
  1065. do_reject_malformed_header(Config, Header) ->
  1066. do_reject_malformed_headers(Config, [
  1067. {<<":method">>, <<"GET">>},
  1068. {<<":scheme">>, <<"https">>},
  1069. {<<":authority">>, <<"localhost">>},
  1070. {<<":path">>, <<"/">>},
  1071. Header
  1072. ]).
  1073. do_reject_malformed_headers(Config, Headers) ->
  1074. #{conn := Conn} = do_connect(Config),
  1075. {ok, StreamRef} = quicer:start_stream(Conn, #{}),
  1076. {ok, EncodedHeaders, _EncData1, _EncSt0}
  1077. = cow_qpack:encode_field_section(Headers, 0, cow_qpack:init()),
  1078. {ok, _} = quicer:send(StreamRef, [
  1079. <<1>>, %% HEADERS frame.
  1080. cow_http3:encode_int(iolist_size(EncodedHeaders)),
  1081. EncodedHeaders
  1082. ]),
  1083. %% The stream should have been aborted.
  1084. #{reason := h3_message_error} = do_wait_stream_aborted(StreamRef),
  1085. ok.
  1086. %% Helper functions.
  1087. do_connect(Config) ->
  1088. {ok, Conn} = quicer:connect("localhost", config(port, Config),
  1089. #{alpn => ["h3"], verify => none}, 5000),
  1090. %% To make sure the connection is fully established we wait
  1091. %% to receive the SETTINGS frame on the control stream.
  1092. {ok, ControlRef, _Settings} = do_wait_settings(Conn),
  1093. #{
  1094. conn => Conn,
  1095. control => ControlRef
  1096. }.
  1097. do_wait_settings(Conn) ->
  1098. {ok, Conn} = quicer:async_accept_stream(Conn, []),
  1099. receive
  1100. {quic, new_stream, StreamRef, #{flags := Flags}} ->
  1101. true = quicer:is_unidirectional(Flags),
  1102. receive {quic, <<
  1103. 0, %% Control stream.
  1104. 4, 0 %% Empty SETTINGS frame.
  1105. >>, StreamRef, _} ->
  1106. {ok, StreamRef, #{}}
  1107. after 5000 ->
  1108. {error, timeout}
  1109. end
  1110. after 5000 ->
  1111. {error, timeout}
  1112. end.
  1113. do_receive_data(StreamRef) ->
  1114. receive
  1115. {quic, Data, StreamRef, _Flags} when is_binary(Data) ->
  1116. {ok, Data}
  1117. after 5000 ->
  1118. {error, timeout}
  1119. end.
  1120. do_guess_int_encoding(Data) ->
  1121. SizeWithLen = byte_size(Data) - 1,
  1122. if
  1123. SizeWithLen < 64 + 1 ->
  1124. {0, 6};
  1125. SizeWithLen < 16384 + 2 ->
  1126. {1, 14};
  1127. SizeWithLen < 1073741824 + 4 ->
  1128. {2, 30};
  1129. SizeWithLen < 4611686018427387904 + 8 ->
  1130. {3, 62}
  1131. end.
  1132. do_async_stream_shutdown(StreamRef) ->
  1133. quicer:async_shutdown_stream(StreamRef, ?QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL, 0),
  1134. receive
  1135. {quic, send_shutdown_complete, StreamRef, true} ->
  1136. ok
  1137. after 5000 ->
  1138. {error, timeout}
  1139. end.
  1140. do_wait_peer_send_shutdown(StreamRef) ->
  1141. receive
  1142. {quic, peer_send_shutdown, StreamRef, undefined} ->
  1143. ok
  1144. after 5000 ->
  1145. {error, timeout}
  1146. end.
  1147. do_wait_stream_aborted(StreamRef) ->
  1148. receive
  1149. {quic, peer_send_aborted, StreamRef, Code} ->
  1150. Reason = cow_http3:code_to_error(Code),
  1151. #{reason => Reason};
  1152. {quic, peer_receive_aborted, StreamRef, Code} ->
  1153. Reason = cow_http3:code_to_error(Code),
  1154. #{reason => Reason}
  1155. after 5000 ->
  1156. {error, timeout}
  1157. end.
  1158. do_wait_stream_closed(StreamRef) ->
  1159. receive
  1160. {quic, stream_closed, StreamRef, #{error := Error, is_conn_shutdown := false}} ->
  1161. 0 = Error,
  1162. ok
  1163. after 5000 ->
  1164. {error, timeout}
  1165. end.
  1166. do_receive_response(StreamRef) ->
  1167. {ok, Data} = do_receive_data(StreamRef),
  1168. {HLenEnc, HLenBits} = do_guess_int_encoding(Data),
  1169. <<
  1170. 1, %% HEADERS frame.
  1171. HLenEnc:2, HLen:HLenBits,
  1172. EncodedResponse:HLen/bytes,
  1173. Rest/bits
  1174. >> = Data,
  1175. {ok, DecodedResponse, _DecData, _DecSt}
  1176. = cow_qpack:decode_field_section(EncodedResponse, 0, cow_qpack:init()),
  1177. Headers = maps:from_list(DecodedResponse),
  1178. #{<<"content-length">> := BodyLen} = Headers,
  1179. {DLenEnc, DLenBits} = do_guess_int_encoding(Data),
  1180. <<
  1181. 0, %% DATA frame.
  1182. DLenEnc:2, DLen:DLenBits,
  1183. Body:DLen/bytes
  1184. >> = Rest,
  1185. BodyLen = integer_to_binary(byte_size(Body)),
  1186. ok = do_wait_peer_send_shutdown(StreamRef),
  1187. ok = do_wait_stream_closed(StreamRef),
  1188. #{
  1189. headers => Headers,
  1190. body => Body
  1191. }.
  1192. do_wait_connection_closed(Conn) ->
  1193. receive
  1194. {quic, shutdown, Conn, {unknown_quic_status, Code}} ->
  1195. Reason = cow_http3:code_to_error(Code),
  1196. #{reason => Reason}
  1197. after 5000 ->
  1198. {error, timeout}
  1199. end.
  1200. %% 4.3.2. Response Pseudo-Header Fields
  1201. %% For responses, a single ":status" pseudo-header field is defined that
  1202. %carries the HTTP status code; see Section 15 of [HTTP]. This pseudo-header
  1203. %field MUST be included in all responses; otherwise, the response is malformed
  1204. %(see Section 4.1.2).
  1205. %% HTTP/3 does not define a way to carry the version or reason phrase that is
  1206. %included in an HTTP/1.1 status line. HTTP/3 responses implicitly have a
  1207. %protocol version of "3.0".
  1208. %% 4.4. The CONNECT Method
  1209. %% A CONNECT request MUST be constructed as follows:
  1210. %%The :method pseudo-header field is set to "CONNECT"
  1211. %%The :scheme and :path pseudo-header fields are omitted
  1212. %%The :authority pseudo-header field contains the host and port to connect to
  1213. %(equivalent to the authority-form of the request-target of CONNECT requests;
  1214. %see Section 7.1 of [HTTP]).
  1215. %% The request stream remains open at the end of the request to carry the data
  1216. %to be transferred. A CONNECT request that does not conform to these
  1217. %restrictions is malformed.
  1218. %%
  1219. %% Once the CONNECT method has completed, only DATA frames are permitted to be
  1220. %sent on the stream. Extension frames MAY be used if specifically permitted by
  1221. %the definition of the extension. Receipt of any other known frame type MUST be
  1222. %treated as a connection error of type H3_FRAME_UNEXPECTED.%% @todo + review
  1223. %how it should work beyond the handling of the CONNECT request
  1224. %% 4.5. HTTP Upgrade
  1225. %% HTTP/3 does not support the HTTP Upgrade mechanism (Section 7.8 of [HTTP])
  1226. %or the 101 (Switching Protocols) informational status code (Section 15.2.2 of
  1227. %[HTTP]).
  1228. %% 4.6. Server Push
  1229. %% The push ID space begins at zero and ends at a maximum value set by the
  1230. %MAX_PUSH_ID frame. In particular, a server is not able to push until after the
  1231. %client sends a MAX_PUSH_ID frame. A client sends MAX_PUSH_ID frames to control
  1232. %the number of pushes that a server can promise. A server SHOULD use push IDs
  1233. %sequentially, beginning from zero. A client MUST treat receipt of a push
  1234. %stream as a connection error of type H3_ID_ERROR when no MAX_PUSH_ID frame has
  1235. %been sent or when the stream references a push ID that is greater than the
  1236. %maximum push ID.
  1237. %% When the same push ID is promised on multiple request streams, the
  1238. %decompressed request field sections MUST contain the same fields in the same
  1239. %order, and both the name and the value in each field MUST be identical.
  1240. %% Not all requests can be pushed. A server MAY push requests that have the following properties:
  1241. %cacheable; see Section 9.2.3 of [HTTP]
  1242. %safe; see Section 9.2.1 of [HTTP]
  1243. %does not include request content or a trailer section
  1244. %
  1245. %% The server MUST include a value in the :authority pseudo-header field for
  1246. %which the server is authoritative. If the client has not yet validated the
  1247. %connection for the origin indicated by the pushed request, it MUST perform the
  1248. %same verification process it would do before sending a request for that origin
  1249. %on the connection; see Section 3.3. If this verification fails, the client
  1250. %MUST NOT consider the server authoritative for that origin.
  1251. %% Clients SHOULD send a CANCEL_PUSH frame upon receipt of a PUSH_PROMISE frame
  1252. %carrying a request that is not cacheable, is not known to be safe, that
  1253. %indicates the presence of request content, or for which it does not consider
  1254. %the server authoritative. Any corresponding responses MUST NOT be used or
  1255. %cached.
  1256. %% Ordering of a PUSH_PROMISE frame in relation to certain parts of the
  1257. %response is important. The server SHOULD send PUSH_PROMISE frames prior to
  1258. %sending HEADERS or DATA frames that reference the promised responses. This
  1259. %reduces the chance that a client requests a resource that will be pushed by
  1260. %the server.
  1261. %% Push stream data can also arrive after a client has cancelled a push. In
  1262. %this case, the client can abort reading the stream with an error code of
  1263. %H3_REQUEST_CANCELLED. This asks the server not to transfer additional data and
  1264. %indicates that it will be discarded upon receipt.
  1265. %% 5. Connection Closure
  1266. %% 5.1. Idle Connections
  1267. %% HTTP/3 implementations will need to open a new HTTP/3 connection for new
  1268. %requests if the existing connection has been idle for longer than the idle
  1269. %timeout negotiated during the QUIC handshake, and they SHOULD do so if
  1270. %approaching the idle timeout; see Section 10.1 of [QUIC-TRANSPORT].
  1271. %% Servers SHOULD NOT actively keep connections open.
  1272. %% 5.2. Connection Shutdown
  1273. %% Endpoints initiate the graceful shutdown of an HTTP/3 connection by sending
  1274. %a GOAWAY frame. The GOAWAY frame contains an identifier that indicates to the
  1275. %receiver the range of requests or pushes that were or might be processed in
  1276. %this connection. The server sends a client-initiated bidirectional stream ID;
  1277. %the client sends a push ID. Requests or pushes with the indicated identifier
  1278. %or greater are rejected (Section 4.1.1) by the sender of the GOAWAY. This
  1279. %identifier MAY be zero if no requests or pushes were processed.
  1280. %% Upon sending a GOAWAY frame, the endpoint SHOULD explicitly cancel (see
  1281. %Sections 4.1.1 and 7.2.3) any requests or pushes that have identifiers greater
  1282. %than or equal to the one indicated, in order to clean up transport state for
  1283. %the affected streams. The endpoint SHOULD continue to do so as more requests
  1284. %or pushes arrive.
  1285. %% Endpoints MUST NOT initiate new requests or promise new pushes on the
  1286. %connection after receipt of a GOAWAY frame from the peer.
  1287. %% Requests on stream IDs less than the stream ID in a GOAWAY frame from the
  1288. %server might have been processed; their status cannot be known until a
  1289. %response is received, the stream is reset individually, another GOAWAY is
  1290. %received with a lower stream ID than that of the request in question, or the
  1291. %connection terminates.
  1292. %% Servers MAY reject individual requests on streams below the indicated ID if
  1293. %these requests were not processed.
  1294. %% If a server receives a GOAWAY frame after having promised pushes with a push
  1295. %ID greater than or equal to the identifier contained in the GOAWAY frame,
  1296. %those pushes will not be accepted.
  1297. %% Servers SHOULD send a GOAWAY frame when the closing of a connection is known
  1298. %in advance, even if the advance notice is small, so that the remote peer can
  1299. %know whether or not a request has been partially processed.
  1300. %% An endpoint MAY send multiple GOAWAY frames indicating different
  1301. %identifiers, but the identifier in each frame MUST NOT be greater than the
  1302. %identifier in any previous frame, since clients might already have retried
  1303. %unprocessed requests on another HTTP connection. Receiving a GOAWAY containing
  1304. %a larger identifier than previously received MUST be treated as a connection
  1305. %error of type H3_ID_ERROR.
  1306. %% An endpoint that is attempting to gracefully shut down a connection can send
  1307. %a GOAWAY frame with a value set to the maximum possible value (262-4 for
  1308. %servers, 262-1 for clients).
  1309. %% Even when a GOAWAY indicates that a given request or push will not be
  1310. %processed or accepted upon receipt, the underlying transport resources still
  1311. %exist. The endpoint that initiated these requests can cancel them to clean up
  1312. %transport state.
  1313. %% Once all accepted requests and pushes have been processed, the endpoint can
  1314. %permit the connection to become idle, or it MAY initiate an immediate closure
  1315. %of the connection. An endpoint that completes a graceful shutdown SHOULD use
  1316. %the H3_NO_ERROR error code when closing the connection.
  1317. %% If a client has consumed all available bidirectional stream IDs with
  1318. %requests, the server need not send a GOAWAY frame, since the client is unable
  1319. %to make further requests. @todo OK that one's some weird stuff lol
  1320. %% 5.3. Immediate Application Closure
  1321. %% Before closing the connection, a GOAWAY frame MAY be sent to allow the
  1322. %client to retry some requests. Including the GOAWAY frame in the same packet
  1323. %as the QUIC CONNECTION_CLOSE frame improves the chances of the frame being
  1324. %received by clients.
  1325. %% 6. Stream Mapping and Usage
  1326. %% 6.1. Bidirectional Streams
  1327. %% an HTTP/3 server SHOULD configure non-zero minimum values for the number of
  1328. %permitted streams and the initial stream flow-control window. So as to not
  1329. %unnecessarily limit parallelism, at least 100 request streams SHOULD be
  1330. %permitted at a time.
  1331. %% 6.2. Unidirectional Streams
  1332. %% Therefore, the transport parameters sent by both clients and servers MUST
  1333. %allow the peer to create at least three unidirectional streams. These
  1334. %transport parameters SHOULD also provide at least 1,024 bytes of flow-control
  1335. %credit to each unidirectional stream.
  1336. %% Note that an endpoint is not required to grant additional credits to create
  1337. %more unidirectional streams if its peer consumes all the initial credits
  1338. %before creating the critical unidirectional streams. Endpoints SHOULD create
  1339. %the HTTP control stream as well as the unidirectional streams required by
  1340. %mandatory extensions (such as the QPACK encoder and decoder streams) first,
  1341. %and then create additional streams as allowed by their peer.
  1342. %% Recipients of unknown stream types MUST either abort reading of the stream
  1343. %or discard incoming data without further processing. If reading is aborted,
  1344. %the recipient SHOULD use the H3_STREAM_CREATION_ERROR error code or a reserved
  1345. %error code (Section 8.1). The recipient MUST NOT consider unknown stream types
  1346. %to be a connection error of any kind.
  1347. %% As certain stream types can affect connection state, a recipient SHOULD NOT
  1348. %discard data from incoming unidirectional streams prior to reading the stream
  1349. %type.
  1350. %% Implementations MAY send stream types before knowing whether the peer
  1351. %supports them. However, stream types that could modify the state or semantics
  1352. %of existing protocol components, including QPACK or other extensions, MUST NOT
  1353. %be sent until the peer is known to support them.
  1354. %% A receiver MUST tolerate unidirectional streams being closed or reset prior
  1355. %to the reception of the unidirectional stream header.
  1356. %% 6.2.1. Control Streams
  1357. %% A control stream is indicated by a stream type of 0x00. Data on this stream
  1358. %consists of HTTP/3 frames, as defined in Section 7.2.
  1359. %% Each side MUST initiate a single control stream at the beginning of the
  1360. %connection and send its SETTINGS frame as the first frame on this stream. If
  1361. %the first frame of the control stream is any other frame type, this MUST be
  1362. %treated as a connection error of type H3_MISSING_SETTINGS. Only one control
  1363. %stream per peer is permitted; receipt of a second stream claiming to be a
  1364. %control stream MUST be treated as a connection error of type
  1365. %H3_STREAM_CREATION_ERROR. The sender MUST NOT close the control stream, and
  1366. %the receiver MUST NOT request that the sender close the control stream. If
  1367. %either control stream is closed at any point, this MUST be treated as a
  1368. %connection error of type H3_CLOSED_CRITICAL_STREAM. Connection errors are
  1369. %described in Section 8.
  1370. %% Because the contents of the control stream are used to manage the behavior
  1371. %of other streams, endpoints SHOULD provide enough flow-control credit to keep
  1372. %the peer's control stream from becoming blocked.
  1373. %% 6.2.2. Push Streams
  1374. %% A push stream is indicated by a stream type of 0x01, followed by the push ID
  1375. %of the promise that it fulfills, encoded as a variable-length integer. The
  1376. %remaining data on this stream consists of HTTP/3 frames, as defined in Section
  1377. %7.2, and fulfills a promised server push by zero or more interim HTTP
  1378. %responses followed by a single final HTTP response, as defined in Section 4.1.
  1379. %Server push and push IDs are described in Section 4.6.
  1380. %% Only servers can push; if a server receives a client-initiated push stream,
  1381. %this MUST be treated as a connection error of type H3_STREAM_CREATION_ERROR.
  1382. %% Each push ID MUST only be used once in a push stream header. If a client
  1383. %detects that a push stream header includes a push ID that was used in another
  1384. %push stream header, the client MUST treat this as a connection error of type
  1385. %H3_ID_ERROR.
  1386. %% 6.2.3. Reserved Stream Types
  1387. %% Stream types of the format 0x1f * N + 0x21 for non-negative integer values
  1388. %of N are reserved to exercise the requirement that unknown types be ignored.
  1389. %These streams have no semantics, and they can be sent when application-layer
  1390. %padding is desired. They MAY also be sent on connections where no data is
  1391. %currently being transferred. Endpoints MUST NOT consider these streams to have
  1392. %any meaning upon receipt.
  1393. %% The payload and length of the stream are selected in any manner the sending
  1394. %implementation chooses. When sending a reserved stream type, the
  1395. %implementation MAY either terminate the stream cleanly or reset it. When
  1396. %resetting the stream, either the H3_NO_ERROR error code or a reserved error
  1397. %code (Section 8.1) SHOULD be used.
  1398. %% 7. HTTP Framing Layer
  1399. %% Note that, unlike QUIC frames, HTTP/3 frames can span multiple packets.
  1400. %% 7.1. Frame Layout
  1401. %% Each frame's payload MUST contain exactly the fields identified in its
  1402. %description. A frame payload that contains additional bytes after the
  1403. %identified fields or a frame payload that terminates before the end of the
  1404. %identified fields MUST be treated as a connection error of type
  1405. %H3_FRAME_ERROR. In particular, redundant length encodings MUST be verified to
  1406. %be self-consistent; see Section 10.8.
  1407. %% When a stream terminates cleanly, if the last frame on the stream was
  1408. %truncated, this MUST be treated as a connection error of type H3_FRAME_ERROR.
  1409. %Streams that terminate abruptly may be reset at any point in a frame.
  1410. %% 7.2. Frame Definitions
  1411. %% 7.2.1. DATA
  1412. %% DATA frames MUST be associated with an HTTP request or response. If a DATA
  1413. %frame is received on a control stream, the recipient MUST respond with a
  1414. %connection error of type H3_FRAME_UNEXPECTED.
  1415. %% 7.2.2. HEADERS
  1416. %% HEADERS frames can only be sent on request streams or push streams. If a
  1417. %HEADERS frame is received on a control stream, the recipient MUST respond with
  1418. %a connection error of type H3_FRAME_UNEXPECTED.
  1419. %% 7.2.3. CANCEL_PUSH
  1420. %% When a client sends a CANCEL_PUSH frame, it is indicating that it does not
  1421. %wish to receive the promised resource. The server SHOULD abort sending the
  1422. %resource, but the mechanism to do so depends on the state of the corresponding
  1423. %push stream. If the server has not yet created a push stream, it does not
  1424. %create one. If the push stream is open, the server SHOULD abruptly terminate
  1425. %that stream. If the push stream has already ended, the server MAY still
  1426. %abruptly terminate the stream or MAY take no action.
  1427. %% A server sends a CANCEL_PUSH frame to indicate that it will not be
  1428. %fulfilling a promise that was previously sent. The client cannot expect the
  1429. %corresponding promise to be fulfilled, unless it has already received and
  1430. %processed the promised response. Regardless of whether a push stream has been
  1431. %opened, a server SHOULD send a CANCEL_PUSH frame when it determines that
  1432. %promise will not be fulfilled. If a stream has already been opened, the server
  1433. %can abort sending on the stream with an error code of H3_REQUEST_CANCELLED.
  1434. %% Sending a CANCEL_PUSH frame has no direct effect on the state of existing
  1435. %push streams. A client SHOULD NOT send a CANCEL_PUSH frame when it has already
  1436. %received a corresponding push stream. A push stream could arrive after a
  1437. %client has sent a CANCEL_PUSH frame, because a server might not have processed
  1438. %the CANCEL_PUSH. The client SHOULD abort reading the stream with an error code
  1439. %of H3_REQUEST_CANCELLED.
  1440. %% A CANCEL_PUSH frame is sent on the control stream. Receiving a CANCEL_PUSH
  1441. %frame on a stream other than the control stream MUST be treated as a
  1442. %connection error of type H3_FRAME_UNEXPECTED.
  1443. %% If a CANCEL_PUSH frame is received that references a push ID greater than
  1444. %currently allowed on the connection, this MUST be treated as a connection
  1445. %error of type H3_ID_ERROR.
  1446. %% If the client receives a CANCEL_PUSH frame, that frame might identify a push
  1447. %ID that has not yet been mentioned by a PUSH_PROMISE frame due to reordering.
  1448. %If a server receives a CANCEL_PUSH frame for a push ID that has not yet been
  1449. %mentioned by a PUSH_PROMISE frame, this MUST be treated as a connection error
  1450. %of type H3_ID_ERROR.
  1451. %% 7.2.4. SETTINGS
  1452. %% A SETTINGS frame MUST be sent as the first frame of each control stream (see
  1453. %Section 6.2.1) by each peer, and it MUST NOT be sent subsequently. If an
  1454. %endpoint receives a second SETTINGS frame on the control stream, the endpoint
  1455. %MUST respond with a connection error of type H3_FRAME_UNEXPECTED.
  1456. %% SETTINGS frames MUST NOT be sent on any stream other than the control
  1457. %stream. If an endpoint receives a SETTINGS frame on a different stream, the
  1458. %endpoint MUST respond with a connection error of type H3_FRAME_UNEXPECTED.
  1459. %% The same setting identifier MUST NOT occur more than once in the SETTINGS
  1460. %frame. A receiver MAY treat the presence of duplicate setting identifiers as a
  1461. %connection error of type H3_SETTINGS_ERROR.
  1462. %% An implementation MUST ignore any parameter with an identifier it does not understand.
  1463. %% 7.2.4.1. Defined SETTINGS Parameters
  1464. %% Setting identifiers of the format 0x1f * N + 0x21 for non-negative integer
  1465. %values of N are reserved to exercise the requirement that unknown identifiers
  1466. %be ignored. Such settings have no defined meaning. Endpoints SHOULD include at
  1467. %least one such setting in their SETTINGS frame. Endpoints MUST NOT consider
  1468. %such settings to have any meaning upon receipt.
  1469. %% -> try sending COW\0 BOY\0 if that fits the encoding and restrictions
  1470. %otherwise something similar
  1471. %% Setting identifiers that were defined in [HTTP/2] where there is no
  1472. %corresponding HTTP/3 setting have also been reserved (Section 11.2.2). These
  1473. %reserved settings MUST NOT be sent, and their receipt MUST be treated as a
  1474. %connection error of type H3_SETTINGS_ERROR.
  1475. %% 7.2.4.2. Initialization
  1476. %% An HTTP implementation MUST NOT send frames or requests that would be
  1477. %invalid based on its current understanding of the peer's settings.
  1478. %% All settings begin at an initial value. Each endpoint SHOULD use these
  1479. %initial values to send messages before the peer's SETTINGS frame has arrived,
  1480. %as packets carrying the settings can be lost or delayed. When the SETTINGS
  1481. %frame arrives, any settings are changed to their new values.
  1482. %% Endpoints MUST NOT require any data to be received from the peer prior to
  1483. %sending the SETTINGS frame; settings MUST be sent as soon as the transport is
  1484. %ready to send data.
  1485. %% A server MAY accept 0-RTT and subsequently provide different settings in its
  1486. %SETTINGS frame. If 0-RTT data is accepted by the server, its SETTINGS frame
  1487. %MUST NOT reduce any limits or alter any values that might be violated by the
  1488. %client with its 0-RTT data. The server MUST include all settings that differ
  1489. %from their default values. If a server accepts 0-RTT but then sends settings
  1490. %that are not compatible with the previously specified settings, this MUST be
  1491. %treated as a connection error of type H3_SETTINGS_ERROR. If a server accepts
  1492. %0-RTT but then sends a SETTINGS frame that omits a setting value that the
  1493. %client understands (apart from reserved setting identifiers) that was
  1494. %previously specified to have a non-default value, this MUST be treated as a
  1495. %connection error of type H3_SETTINGS_ERROR.
  1496. %% 7.2.5. PUSH_PROMISE
  1497. %% A server MUST NOT use a push ID that is larger than the client has provided
  1498. %in a MAX_PUSH_ID frame (Section 7.2.7).
  1499. %% A server MAY use the same push ID in multiple PUSH_PROMISE frames. If so,
  1500. %the decompressed request header sets MUST contain the same fields in the same
  1501. %order, and both the name and the value in each field MUST be exact matches.
  1502. %% Allowing duplicate references to the same push ID is primarily to reduce
  1503. %duplication caused by concurrent requests. A server SHOULD avoid reusing a
  1504. %push ID over a long period. Clients are likely to consume server push
  1505. %responses and not retain them for reuse over time. Clients that see a
  1506. %PUSH_PROMISE frame that uses a push ID that they have already consumed and
  1507. %discarded are forced to ignore the promise.
  1508. %% A client MUST NOT send a PUSH_PROMISE frame. A server MUST treat the receipt
  1509. %of a PUSH_PROMISE frame as a connection error of type H3_FRAME_UNEXPECTED.
  1510. %% 7.2.6. GOAWAY
  1511. %% (not sure what applies to the server, should the server reject GOAWAY on
  1512. %non-control stream too?)
  1513. %% 7.2.7. MAX_PUSH_ID
  1514. %% Receipt of a MAX_PUSH_ID frame on any other stream MUST be treated as a
  1515. %connection error of type H3_FRAME_UNEXPECTED.
  1516. %% The maximum push ID is unset when an HTTP/3 connection is created, meaning
  1517. %that a server cannot push until it receives a MAX_PUSH_ID frame.
  1518. %% A MAX_PUSH_ID frame cannot reduce the maximum push ID; receipt of a
  1519. %MAX_PUSH_ID frame that contains a smaller value than previously received MUST
  1520. %be treated as a connection error of type H3_ID_ERROR.
  1521. %% 7.2.8. Reserved Frame Types
  1522. %% These frames have no semantics, and they MAY be sent on any stream where
  1523. %frames are allowed to be sent. This enables their use for application-layer
  1524. %padding. Endpoints MUST NOT consider these frames to have any meaning upon
  1525. %receipt.
  1526. %% Frame types that were used in HTTP/2 where there is no corresponding HTTP/3
  1527. %frame have also been reserved (Section 11.2.1). These frame types MUST NOT be
  1528. %sent, and their receipt MUST be treated as a connection error of type
  1529. %H3_FRAME_UNEXPECTED.
  1530. %% 8. Error Handling
  1531. %% An endpoint MAY choose to treat a stream error as a connection error under
  1532. %certain circumstances, closing the entire connection in response to a
  1533. %condition on a single stream.
  1534. %% Because new error codes can be defined without negotiation (see Section 9),
  1535. %use of an error code in an unexpected context or receipt of an unknown error
  1536. %code MUST be treated as equivalent to H3_NO_ERROR.
  1537. %% 8.1. HTTP/3 Error Codes
  1538. %% H3_INTERNAL_ERROR (0x0102): An internal error has occurred in the HTTP stack.
  1539. %% H3_FRAME_ERROR (0x0106): A frame that fails to satisfy layout requirements
  1540. %or with an invalid size was received.
  1541. %% H3_EXCESSIVE_LOAD (0x0107): The endpoint detected that its peer is
  1542. %exhibiting a behavior that might be generating excessive load.
  1543. %% (more)
  1544. %% Error codes of the format 0x1f * N + 0x21 for non-negative integer values of
  1545. %N are reserved to exercise the requirement that unknown error codes be treated
  1546. %as equivalent to H3_NO_ERROR (Section 9). Implementations SHOULD select an
  1547. %error code from this space with some probability when they would have sent
  1548. %H3_NO_ERROR.
  1549. %% 9. Extensions to HTTP/3
  1550. %% Extensions are permitted to use new frame types (Section 7.2), new settings
  1551. %(Section 7.2.4.1), new error codes (Section 8), or new unidirectional stream
  1552. %types (Section 6.2). Registries are established for managing these extension
  1553. %points: frame types (Section 11.2.1), settings (Section 11.2.2), error codes
  1554. %(Section 11.2.3), and stream types (Section 11.2.4).
  1555. %% Implementations MUST ignore unknown or unsupported values in all extensible
  1556. %protocol elements. Implementations MUST discard data or abort reading on
  1557. %unidirectional streams that have unknown or unsupported types. This means that
  1558. %any of these extension points can be safely used by extensions without prior
  1559. %arrangement or negotiation. However, where a known frame type is required to
  1560. %be in a specific location, such as the SETTINGS frame as the first frame of
  1561. %the control stream (see Section 6.2.1), an unknown frame type does not satisfy
  1562. %that requirement and SHOULD be treated as an error.
  1563. %% If a setting is used for extension negotiation, the default value MUST be
  1564. %defined in such a fashion that the extension is disabled if the setting is
  1565. %omitted.
  1566. %% 10. Security Considerations
  1567. %% 10.3. Intermediary-Encapsulation Attacks
  1568. %% Requests or responses containing invalid field names MUST be treated as malformed.
  1569. %% Any request or response that contains a character not permitted in a field
  1570. %value MUST be treated as malformed.
  1571. %% 10.5. Denial-of-Service Considerations
  1572. %% Implementations SHOULD track the use of these features and set limits on
  1573. %their use. An endpoint MAY treat activity that is suspicious as a connection
  1574. %error of type H3_EXCESSIVE_LOAD, but false positives will result in disrupting
  1575. %valid connections and requests.
  1576. %% 10.5.1. Limits on Field Section Size
  1577. %% An endpoint can use the SETTINGS_MAX_FIELD_SECTION_SIZE (Section 4.2.2)
  1578. %setting to advise peers of limits that might apply on the size of field
  1579. %sections.
  1580. %% A server that receives a larger field section than it is willing to handle
  1581. %can send an HTTP 431 (Request Header Fields Too Large) status code
  1582. %([RFC6585]).
  1583. %% 10.6. Use of Compression
  1584. %% Implementations communicating on a secure channel MUST NOT compress content
  1585. %that includes both confidential and attacker-controlled data unless separate
  1586. %compression contexts are used for each source of data. Compression MUST NOT be
  1587. %used if the source of data cannot be reliably determined.
  1588. %% 10.8. Frame Parsing
  1589. %% An implementation MUST ensure that the length of a frame exactly matches the
  1590. %length of the fields it contains.
  1591. %% 10.9. Early Data
  1592. %% The anti-replay mitigations in [HTTP-REPLAY] MUST be applied when using HTTP/3 with 0-RTT.
  1593. %% 10.10. Migration
  1594. %% Certain HTTP implementations use the client address for logging or
  1595. %access-control purposes. Since a QUIC client's address might change during a
  1596. %connection (and future versions might support simultaneous use of multiple
  1597. %addresses), such implementations will need to either actively retrieve the
  1598. %client's current address or addresses when they are relevant or explicitly
  1599. %accept that the original address might change. -> documentation for now
  1600. %% 11.2.1. Frame Types
  1601. %% Reserved types: 0x02 0x06 0x08 0x09
  1602. %% 11.2.2. Settings Parameters
  1603. %% Reserved settings: 0x00 0x02 0x03 0x04 0x05
  1604. %% Appendix A. Considerations for Transitioning from HTTP/2
  1605. %% A.1. Streams
  1606. %% QUIC considers a stream closed when all data has been received and sent data
  1607. %has been acknowledged by the peer. HTTP/2 considers a stream closed when the
  1608. %frame containing the END_STREAM bit has been committed to the transport. As a
  1609. %result, the stream for an equivalent exchange could remain "active" for a
  1610. %longer period of time. HTTP/3 servers might choose to permit a larger number
  1611. %of concurrent client-initiated bidirectional streams to achieve equivalent
  1612. %concurrency to HTTP/2, depending on the expected usage patterns. ->
  1613. %documentation?
  1614. %% A.3. HTTP/2 SETTINGS Parameters
  1615. %% SETTINGS_MAX_FRAME_SIZE (0x05): This setting has no equivalent in HTTP/3.
  1616. %Specifying a setting with the identifier 0x05 (corresponding to the
  1617. %SETTINGS_MAX_FRAME_SIZE parameter) in the HTTP/3 SETTINGS frame is an error.
  1618. %-> do we still want a limit, if so how?