rfc7540_SUITE.erl 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. %% Copyright (c) 2016, Loïc Hoguin <essen@ninenines.eu>
  2. %%
  3. %% Permission to use, copy, modify, and/or distribute this software for any
  4. %% purpose with or without fee is hereby granted, provided that the above
  5. %% copyright notice and this permission notice appear in all copies.
  6. %%
  7. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. -module(rfc7540_SUITE).
  15. -compile(export_all).
  16. -import(ct_helper, [config/2]).
  17. -import(ct_helper, [doc/1]).
  18. -import(cowboy_test, [raw_open/1]).
  19. -import(cowboy_test, [raw_send/2]).
  20. -import(cowboy_test, [raw_recv_head/1]).
  21. -import(cowboy_test, [raw_recv/3]).
  22. all() -> [{group, clear}, {group, tls}].
  23. groups() ->
  24. Modules = ct_helper:all(?MODULE),
  25. Clear = [M || M <- Modules, lists:sublist(atom_to_list(M), 4) =/= "alpn"] -- [prior_knowledge_reject_tls],
  26. TLS = [M || M <- Modules, lists:sublist(atom_to_list(M), 4) =:= "alpn"] ++ [prior_knowledge_reject_tls],
  27. [{clear, [parallel], Clear}, {tls, [parallel], TLS}].
  28. init_per_group(Name = clear, Config) ->
  29. cowboy_test:init_http(Name = clear, #{
  30. env => #{dispatch => cowboy_router:compile(init_routes(Config))}
  31. }, Config);
  32. init_per_group(Name = tls, Config) ->
  33. cowboy_test:init_http2(Name = tls, #{
  34. env => #{dispatch => cowboy_router:compile(init_routes(Config))}
  35. }, Config).
  36. end_per_group(Name, _) ->
  37. ok = cowboy:stop_listener(Name).
  38. init_routes(_) -> [
  39. {"localhost", [
  40. {"/", hello_h, []}
  41. ]}
  42. ].
  43. %% Starting HTTP/2 for "http" URIs.
  44. http_upgrade_ignore_h2(Config) ->
  45. doc("An h2 token in an Upgrade field must be ignored. (RFC7540 3.2)"),
  46. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  47. ok = gen_tcp:send(Socket, [
  48. "GET / HTTP/1.1\r\n"
  49. "Host: localhost\r\n"
  50. "Connection: Upgrade, HTTP2-Settings\r\n"
  51. "Upgrade: h2\r\n"
  52. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  53. "\r\n"]),
  54. {ok, <<"HTTP/1.1 200">>} = gen_tcp:recv(Socket, 12, 1000),
  55. ok.
  56. http_upgrade_ignore_if_http_10(Config) ->
  57. doc("The Upgrade header must be ignored if part of an HTTP/1.0 request. (RFC7230 6.7)"),
  58. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  59. ok = gen_tcp:send(Socket, [
  60. "GET / HTTP/1.0\r\n"
  61. "Host: localhost\r\n"
  62. "Connection: Upgrade, HTTP2-Settings\r\n"
  63. "Upgrade: h2c\r\n"
  64. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  65. "\r\n"]),
  66. {ok, <<"HTTP/1.1 200">>} = gen_tcp:recv(Socket, 12, 1000),
  67. ok.
  68. http_upgrade_ignore_missing_upgrade_in_connection(Config) ->
  69. doc("The Upgrade header must be listed in the "
  70. "Connection header field. (RFC7230 6.7)"),
  71. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  72. ok = gen_tcp:send(Socket, [
  73. "GET / HTTP/1.1\r\n"
  74. "Host: localhost\r\n"
  75. "Connection: HTTP2-Settings\r\n"
  76. "Upgrade: h2c\r\n"
  77. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  78. "\r\n"]),
  79. {ok, <<"HTTP/1.1 200">>} = gen_tcp:recv(Socket, 12, 1000),
  80. ok.
  81. http_upgrade_ignore_missing_http2_settings_in_connection(Config) ->
  82. doc("The HTTP2-Settings header must be listed in the "
  83. "Connection header field. (RFC7540 3.2.1, RFC7230 6.7)"),
  84. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  85. ok = gen_tcp:send(Socket, [
  86. "GET / HTTP/1.1\r\n"
  87. "Host: localhost\r\n"
  88. "Connection: Upgrade\r\n"
  89. "Upgrade: h2c\r\n"
  90. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  91. "\r\n"]),
  92. {ok, <<"HTTP/1.1 200">>} = gen_tcp:recv(Socket, 12, 1000),
  93. ok.
  94. http_upgrade_ignore_zero_http2_settings_header(Config) ->
  95. doc("The HTTP Upgrade request must include "
  96. "exactly one HTTP2-Settings header field (RFC7540 3.2, RFC7540 3.2.1)"),
  97. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  98. ok = gen_tcp:send(Socket, [
  99. "GET / HTTP/1.1\r\n"
  100. "Host: localhost\r\n"
  101. "Connection: Upgrade, HTTP2-Settings\r\n"
  102. "Upgrade: h2c\r\n"
  103. "\r\n"]),
  104. {ok, <<"HTTP/1.1 200">>} = gen_tcp:recv(Socket, 12, 1000),
  105. ok.
  106. http_upgrade_reject_two_http2_settings_header(Config) ->
  107. doc("The HTTP Upgrade request must include "
  108. "exactly one HTTP2-Settings header field (RFC7540 3.2, RFC7540 3.2.1)"),
  109. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  110. ok = gen_tcp:send(Socket, [
  111. "GET / HTTP/1.1\r\n"
  112. "Host: localhost\r\n"
  113. "Connection: Upgrade, HTTP2-Settings\r\n"
  114. "Upgrade: h2c\r\n"
  115. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  116. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  117. "\r\n"]),
  118. {ok, <<"HTTP/1.1 400">>} = gen_tcp:recv(Socket, 12, 1000),
  119. ok.
  120. http_upgrade_reject_bad_http2_settings_header(Config) ->
  121. doc("The HTTP Upgrade request must include "
  122. "a valid HTTP2-Settings header field (RFC7540 3.2, RFC7540 3.2.1)"),
  123. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  124. ok = gen_tcp:send(Socket, [
  125. "GET / HTTP/1.1\r\n"
  126. "Host: localhost\r\n"
  127. "Connection: Upgrade, HTTP2-Settings\r\n"
  128. "Upgrade: h2c\r\n"
  129. %% We send a full SETTINGS frame on purpose.
  130. "HTTP2-Settings: ", base64:encode(cow_http2:settings(#{})), "\r\n",
  131. "\r\n"]),
  132. {ok, <<"HTTP/1.1 400">>} = gen_tcp:recv(Socket, 12, 1000),
  133. ok.
  134. %% Match directly for now.
  135. do_recv_101(Socket) ->
  136. {ok, <<
  137. "HTTP/1.1 101 Switching Protocols\r\n"
  138. "connection: Upgrade\r\n"
  139. "upgrade: h2c\r\n"
  140. "\r\n"
  141. >>} = gen_tcp:recv(Socket, 71, 1000),
  142. ok.
  143. http_upgrade_101(Config) ->
  144. doc("A 101 response must be sent on successful upgrade "
  145. "to HTTP/2 when using the HTTP Upgrade mechanism. (RFC7540 3.2, RFC7230 6.7)"),
  146. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  147. ok = gen_tcp:send(Socket, [
  148. "GET / HTTP/1.1\r\n"
  149. "Host: localhost\r\n"
  150. "Connection: Upgrade, HTTP2-Settings\r\n"
  151. "Upgrade: h2c\r\n"
  152. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  153. "\r\n"]),
  154. ok = do_recv_101(Socket),
  155. ok.
  156. http_upgrade_server_preface(Config) ->
  157. doc("The first frame after the upgrade must be a "
  158. "SETTINGS frame for the server connection preface. (RFC7540 3.2, RFC7540 3.5, RFC7540 6.5)"),
  159. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  160. ok = gen_tcp:send(Socket, [
  161. "GET / HTTP/1.1\r\n"
  162. "Host: localhost\r\n"
  163. "Connection: Upgrade, HTTP2-Settings\r\n"
  164. "Upgrade: h2c\r\n"
  165. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  166. "\r\n"]),
  167. ok = do_recv_101(Socket),
  168. %% Receive the server preface.
  169. {ok, << _:24, 4:8, 0:40 >>} = gen_tcp:recv(Socket, 9, 1000),
  170. ok.
  171. http_upgrade_client_preface_timeout(Config) ->
  172. doc("Clients negotiating HTTP/2 and not sending a preface in "
  173. "a timely manner must be disconnected."),
  174. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  175. ok = gen_tcp:send(Socket, [
  176. "GET / HTTP/1.1\r\n"
  177. "Host: localhost\r\n"
  178. "Connection: Upgrade, HTTP2-Settings\r\n"
  179. "Upgrade: h2c\r\n"
  180. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  181. "\r\n"]),
  182. ok = do_recv_101(Socket),
  183. %% Receive the server preface.
  184. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  185. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  186. %% Do not send the preface. Wait for the server to disconnect us.
  187. {error, closed} = gen_tcp:recv(Socket, 9, 6000),
  188. ok.
  189. http_upgrade_reject_missing_client_preface(Config) ->
  190. doc("Servers must treat an invalid connection preface as a "
  191. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  192. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  193. ok = gen_tcp:send(Socket, [
  194. "GET / HTTP/1.1\r\n"
  195. "Host: localhost\r\n"
  196. "Connection: Upgrade, HTTP2-Settings\r\n"
  197. "Upgrade: h2c\r\n"
  198. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  199. "\r\n"]),
  200. ok = do_recv_101(Socket),
  201. %% Send a SETTINGS frame directly instead of the proper preface.
  202. ok = gen_tcp:send(Socket, cow_http2:settings(#{})),
  203. %% Receive the server preface.
  204. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  205. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  206. %% We expect the server to close the connection when it receives a bad preface.
  207. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  208. ok.
  209. http_upgrade_reject_invalid_client_preface(Config) ->
  210. doc("Servers must treat an invalid connection preface as a "
  211. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  212. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  213. ok = gen_tcp:send(Socket, [
  214. "GET / HTTP/1.1\r\n"
  215. "Host: localhost\r\n"
  216. "Connection: Upgrade, HTTP2-Settings\r\n"
  217. "Upgrade: h2c\r\n"
  218. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  219. "\r\n"]),
  220. ok = do_recv_101(Socket),
  221. %% Send a slightly incorrect preface.
  222. ok = gen_tcp:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  223. %% Receive the server preface.
  224. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  225. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  226. %% We expect the server to close the connection when it receives a bad preface.
  227. %% The server may however have already started sending the response to the
  228. %% initial HTTP/1.1 request.
  229. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  230. case gen_tcp:recv(Socket, 9, 1000) of
  231. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  232. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  233. [headers|Acc];
  234. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  235. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  236. [data|Acc];
  237. {error, _} ->
  238. [closed|Acc]
  239. end
  240. end, [], [1, 2, 3])),
  241. case Received of
  242. [closed|_] -> ok;
  243. [headers, closed|_] -> ok;
  244. [headers, data, closed] -> ok
  245. end.
  246. http_upgrade_reject_missing_client_preface_settings(Config) ->
  247. doc("Servers must treat an invalid connection preface as a "
  248. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  249. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  250. ok = gen_tcp:send(Socket, [
  251. "GET / HTTP/1.1\r\n"
  252. "Host: localhost\r\n"
  253. "Connection: Upgrade, HTTP2-Settings\r\n"
  254. "Upgrade: h2c\r\n"
  255. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  256. "\r\n"]),
  257. ok = do_recv_101(Socket),
  258. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  259. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  260. %% Receive the server preface.
  261. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  262. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  263. %% We expect the server to close the connection when it receives a bad preface.
  264. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  265. ok.
  266. http_upgrade_reject_invalid_client_preface_settings(Config) ->
  267. doc("Servers must treat an invalid connection preface as a "
  268. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  269. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  270. ok = gen_tcp:send(Socket, [
  271. "GET / HTTP/1.1\r\n"
  272. "Host: localhost\r\n"
  273. "Connection: Upgrade, HTTP2-Settings\r\n"
  274. "Upgrade: h2c\r\n"
  275. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  276. "\r\n"]),
  277. ok = do_recv_101(Socket),
  278. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  279. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", << 0:24, 4:8, 0:9, 1:31 >>]),
  280. %% Receive the server preface.
  281. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  282. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  283. %% We expect the server to close the connection when it receives a bad preface.
  284. %% The server may however have already started sending the response to the
  285. %% initial HTTP/1.1 request.
  286. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  287. case gen_tcp:recv(Socket, 9, 1000) of
  288. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  289. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  290. [headers|Acc];
  291. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  292. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  293. [data|Acc];
  294. {error, _} ->
  295. [closed|Acc]
  296. end
  297. end, [], [1, 2, 3])),
  298. case Received of
  299. [closed|_] -> ok;
  300. [headers, closed|_] -> ok;
  301. [headers, data, closed] -> ok
  302. end.
  303. http_upgrade_accept_client_preface_empty_settings(Config) ->
  304. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.2, RFC7540 3.5)"),
  305. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  306. ok = gen_tcp:send(Socket, [
  307. "GET / HTTP/1.1\r\n"
  308. "Host: localhost\r\n"
  309. "Connection: Upgrade, HTTP2-Settings\r\n"
  310. "Upgrade: h2c\r\n"
  311. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  312. "\r\n"]),
  313. ok = do_recv_101(Socket),
  314. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  315. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  316. %% Receive the server preface.
  317. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  318. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  319. %% Receive the SETTINGS ack.
  320. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  321. ok.
  322. http_upgrade_client_preface_settings_ack_timeout(Config) ->
  323. doc("The SETTINGS frames sent by the client must be acknowledged. (RFC7540 3.5, RFC7540 6.5.3)"),
  324. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  325. ok = gen_tcp:send(Socket, [
  326. "GET / HTTP/1.1\r\n"
  327. "Host: localhost\r\n"
  328. "Connection: Upgrade, HTTP2-Settings\r\n"
  329. "Upgrade: h2c\r\n"
  330. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  331. "\r\n"]),
  332. ok = do_recv_101(Socket),
  333. %% Send a valid preface.
  334. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  335. %% Receive the server preface.
  336. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  337. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  338. %% Receive the SETTINGS ack.
  339. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  340. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  341. {ok, << _:24, 7:8, _:72, 4:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  342. ok.
  343. %% @todo We need a successful test with actual options in HTTP2-Settings.
  344. %% @todo We need to test an upgrade with a request body (small and too large).
  345. %% @todo Also assigned default priority values but not sure how to test that.
  346. http_upgrade_response(Config) ->
  347. doc("A response must be sent to the initial HTTP/1.1 request "
  348. "after switching to HTTP/2. The response must use "
  349. "the stream identifier 1. (RFC7540 3.2)"),
  350. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  351. ok = gen_tcp:send(Socket, [
  352. "GET / HTTP/1.1\r\n"
  353. "Host: localhost\r\n"
  354. "Connection: Upgrade, HTTP2-Settings\r\n"
  355. "Upgrade: h2c\r\n"
  356. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  357. "\r\n"]),
  358. ok = do_recv_101(Socket),
  359. %% Send a valid preface.
  360. %% @todo Use non-empty SETTINGS here. Just because.
  361. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  362. %% Receive the server preface.
  363. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  364. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  365. %% Send the SETTINGS ack.
  366. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  367. %% Receive the SETTINGS ack, and the response HEADERS and DATA (streamid 1).
  368. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  369. case gen_tcp:recv(Socket, 9, 1000) of
  370. {ok, << 0:24, 4:8, 1:8, 0:32 >>} ->
  371. [settings_ack|Acc];
  372. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  373. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  374. [headers|Acc];
  375. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  376. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  377. [data|Acc]
  378. end
  379. end, [], [1, 2, 3])),
  380. case Received of
  381. [settings_ack, headers, data] -> ok;
  382. [headers, settings_ack, data] -> ok;
  383. [headers, data, settings_ack] -> ok
  384. end.
  385. http_upgrade_response_half_closed(Config) ->
  386. doc("The stream for the initial HTTP/1.1 request is half-closed. (RFC7540 3.2)"),
  387. %% Try sending more data after the upgrade and get an error.
  388. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  389. ok = gen_tcp:send(Socket, [
  390. "GET / HTTP/1.1\r\n"
  391. "Host: localhost\r\n"
  392. "Connection: Upgrade, HTTP2-Settings\r\n"
  393. "Upgrade: h2c\r\n"
  394. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  395. "\r\n"]),
  396. ok = do_recv_101(Socket),
  397. %% Send a valid preface followed by an unexpected DATA frame.
  398. ok = gen_tcp:send(Socket, [
  399. "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
  400. cow_http2:settings(#{}),
  401. cow_http2:data(1, fin, <<"Unexpected DATA frame.">>)
  402. ]),
  403. %% Receive the server preface.
  404. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  405. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  406. %% Skip the SETTINGS ack, receive the response HEADERS, DATA and RST_STREAM (streamid 1).
  407. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  408. case gen_tcp:recv(Socket, 9, 1000) of
  409. {ok, << 0:24, 4:8, 1:8, 0:32 >>} ->
  410. Acc;
  411. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  412. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  413. [headers|Acc];
  414. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  415. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  416. [data|Acc];
  417. {ok, << 4:24, 3:8, 0:8, 1:32 >>} ->
  418. %% We expect a STREAM_CLOSED reason.
  419. {ok, << 5:32 >>} = gen_tcp:recv(Socket, 4, 1000),
  420. [rst_stream|Acc];
  421. {error, _} ->
  422. %% Can be timeouts, ignore them.
  423. Acc
  424. end
  425. end, [], [1, 2, 3, 4])),
  426. case Received of
  427. [rst_stream] -> ok;
  428. [headers, rst_stream] -> ok;
  429. [headers, data, rst_stream] -> ok
  430. end.
  431. %% Starting HTTP/2 for "https" URIs.
  432. alpn_ignore_h2c(Config) ->
  433. doc("An h2c ALPN protocol identifier must be ignored. (RFC7540 3.3)"),
  434. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  435. [{alpn_advertised_protocols, [<<"h2c">>, <<"http/1.1">>]}, binary, {active, false}]),
  436. {ok, <<"http/1.1">>} = ssl:negotiated_protocol(Socket),
  437. ok.
  438. alpn_server_preface(Config) ->
  439. doc("The first frame must be a SETTINGS frame "
  440. "for the server connection preface. (RFC7540 3.3, RFC7540 3.5, RFC7540 6.5)"),
  441. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  442. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  443. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  444. %% Receive the server preface.
  445. {ok, << _:24, 4:8, 0:40 >>} = ssl:recv(Socket, 9, 1000),
  446. ok.
  447. alpn_client_preface_timeout(Config) ->
  448. doc("Clients negotiating HTTP/2 and not sending a preface in "
  449. "a timely manner must be disconnected."),
  450. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  451. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  452. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  453. %% Receive the server preface.
  454. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  455. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  456. %% Do not send the preface. Wait for the server to disconnect us.
  457. {error, closed} = ssl:recv(Socket, 3, 6000),
  458. ok.
  459. alpn_reject_missing_client_preface(Config) ->
  460. doc("Servers must treat an invalid connection preface as a "
  461. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  462. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  463. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  464. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  465. %% Send a SETTINGS frame directly instead of the proper preface.
  466. ok = ssl:send(Socket, cow_http2:settings(#{})),
  467. %% Receive the server preface.
  468. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  469. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  470. %% We expect the server to close the connection when it receives a bad preface.
  471. {error, closed} = ssl:recv(Socket, 3, 1000),
  472. ok.
  473. alpn_reject_invalid_client_preface(Config) ->
  474. doc("Servers must treat an invalid connection preface as a "
  475. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  476. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  477. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  478. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  479. %% Send a slightly incorrect preface.
  480. ok = ssl:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  481. %% Receive the server preface.
  482. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  483. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  484. %% We expect the server to close the connection when it receives a bad preface.
  485. {error, closed} = ssl:recv(Socket, 3, 1000),
  486. ok.
  487. alpn_reject_missing_client_preface_settings(Config) ->
  488. doc("Servers must treat an invalid connection preface as a "
  489. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  490. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  491. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  492. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  493. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  494. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  495. %% Receive the server preface.
  496. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  497. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  498. %% We expect the server to close the connection when it receives a bad preface.
  499. {error, closed} = ssl:recv(Socket, 3, 1000),
  500. ok.
  501. alpn_reject_invalid_client_preface_settings(Config) ->
  502. doc("Servers must treat an invalid connection preface as a "
  503. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  504. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  505. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  506. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  507. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  508. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", << 0:24, 4:8, 0:9, 1:31 >>]),
  509. %% Receive the server preface.
  510. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  511. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  512. %% We expect the server to close the connection when it receives a bad preface.
  513. {error, closed} = ssl:recv(Socket, 3, 1000),
  514. ok.
  515. alpn_accept_client_preface_empty_settings(Config) ->
  516. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.3, RFC7540 3.5)"),
  517. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  518. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  519. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  520. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  521. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  522. %% Receive the server preface.
  523. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  524. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  525. %% Receive the SETTINGS ack.
  526. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  527. ok.
  528. alpn_client_preface_settings_ack_timeout(Config) ->
  529. doc("Failure to acknowledge the server's SETTINGS frame "
  530. "results in a SETTINGS_TIMEOUT connection error. (RFC7540 3.5, RFC7540 6.5.3)"),
  531. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  532. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  533. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  534. %% Send a valid preface.
  535. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  536. %% Receive the server preface.
  537. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  538. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  539. %% Receive the SETTINGS ack.
  540. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  541. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  542. {ok, << _:24, 7:8, _:72, 4:32 >>} = ssl:recv(Socket, 17, 6000),
  543. ok.
  544. alpn(Config) ->
  545. doc("Successful ALPN negotiation. (RFC7540 3.3)"),
  546. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  547. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  548. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  549. %% Send a valid preface.
  550. %% @todo Use non-empty SETTINGS here. Just because.
  551. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  552. %% Receive the server preface.
  553. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  554. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  555. %% Send the SETTINGS ack.
  556. ok = ssl:send(Socket, cow_http2:settings_ack()),
  557. %% Receive the SETTINGS ack.
  558. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  559. %% Wait until after the SETTINGS ack timeout was supposed to trigger.
  560. receive after 6000 -> ok end,
  561. %% Send a PING.
  562. ok = ssl:send(Socket, cow_http2:ping(0)),
  563. %% Receive a PING ack back, indicating the connection is still up.
  564. {ok, << 8:24, 6:8, 0:7, 1:1, 0:96 >>} = ssl:recv(Socket, 17, 1000),
  565. ok.
  566. %% Starting HTTP/2 with prior knowledge.
  567. prior_knowledge_reject_tls(Config) ->
  568. doc("Implementations that support HTTP/2 over TLS must use ALPN. (RFC7540 3.4)"),
  569. {ok, Socket} = ssl:connect("localhost", config(port, Config), [binary, {active, false}]),
  570. %% Send a valid preface.
  571. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  572. %% We expect the server to send an HTTP 400 error
  573. %% when trying to use HTTP/2 without going through ALPN negotiation.
  574. {ok, <<"HTTP/1.1 400">>} = ssl:recv(Socket, 12, 1000),
  575. ok.
  576. prior_knowledge_server_preface(Config) ->
  577. doc("The first frame must be a SETTINGS frame "
  578. "for the server connection preface. (RFC7540 3.4, RFC7540 3.5, RFC7540 6.5)"),
  579. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  580. %% Send a valid preface.
  581. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  582. %% Receive the server preface.
  583. {ok, << _:24, 4:8, 0:40 >>} = gen_tcp:recv(Socket, 9, 1000),
  584. ok.
  585. %% Note: the client preface timeout doesn't apply in this case,
  586. %% so we don't test it. An HTTP/1.1 client that does not send
  587. %% a request in a timely manner will get disconnected by the
  588. %% HTTP protocol code, not by HTTP/2's.
  589. %% Note: the test that starts by sending a SETTINGS frame is
  590. %% redundant with tests sending garbage on the connection.
  591. %% From the point of view of an HTTP/1.1 connection, a
  592. %% SETTINGS frame is indistinguishable from garbage.
  593. prior_knowledge_reject_invalid_client_preface(Config) ->
  594. doc("An incorrect preface is an invalid HTTP/1.1 request. (RFC7540 3.4)"),
  595. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  596. %% Send a slightly incorrect preface.
  597. ok = gen_tcp:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  598. %% We propagate to HTTP/2 after checking only the request-line.
  599. %% The server then sends its preface before checking the full client preface.
  600. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  601. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  602. %% We expect the server to close the connection when it receives a bad preface.
  603. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  604. ok.
  605. prior_knowledge_reject_missing_client_preface_settings(Config) ->
  606. doc("Servers must treat an invalid connection preface as a "
  607. "connection error of type PROTOCOL_ERROR. (RFC7540 3.4, RFC7540 3.5)"),
  608. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  609. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  610. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  611. %% Receive the server preface.
  612. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  613. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  614. %% We expect the server to close the connection when it receives a bad preface.
  615. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  616. ok.
  617. prior_knowledge_reject_invalid_client_preface_settings(Config) ->
  618. doc("Servers must treat an invalid connection preface as a "
  619. "connection error of type PROTOCOL_ERROR. (RFC7540 3.4, RFC7540 3.5)"),
  620. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  621. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  622. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", << 0:24, 4:8, 0:9, 1:31 >>]),
  623. %% Receive the server preface.
  624. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  625. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  626. %% We expect the server to close the connection when it receives a bad preface.
  627. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  628. ok.
  629. prior_knowledge_accept_client_preface_empty_settings(Config) ->
  630. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.4, RFC7540 3.5)"),
  631. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  632. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  633. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  634. %% Receive the server preface.
  635. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  636. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  637. %% Receive the SETTINGS ack.
  638. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  639. ok.
  640. prior_knowledge_client_preface_settings_ack_timeout(Config) ->
  641. doc("The SETTINGS frames sent by the client must be acknowledged. (RFC7540 3.5, RFC7540 6.5.3)"),
  642. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  643. %% Send a valid preface.
  644. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  645. %% Receive the server preface.
  646. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  647. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  648. %% Receive the SETTINGS ack.
  649. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  650. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  651. {ok, << _:24, 7:8, _:72, 4:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  652. ok.
  653. prior_knowledge(Config) ->
  654. doc("Streams can be initiated after a successful HTTP/2 connection "
  655. "with prior knowledge of server capabilities. (RFC7540 3.4)"),
  656. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  657. %% Send a valid preface.
  658. %% @todo Use non-empty SETTINGS here. Just because.
  659. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  660. %% Receive the server preface.
  661. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  662. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  663. %% Send the SETTINGS ack.
  664. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  665. %% Receive the SETTINGS ack.
  666. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  667. %% Wait until after the SETTINGS ack timeout was supposed to trigger.
  668. receive after 6000 -> ok end,
  669. %% Send a PING.
  670. ok = gen_tcp:send(Socket, cow_http2:ping(0)),
  671. %% Receive a PING ack back, indicating the connection is still up.
  672. {ok, << 8:24, 6:8, 0:7, 1:1, 0:96 >>} = gen_tcp:recv(Socket, 17, 1000),
  673. ok.
  674. %% Tests still need to be added for the following points:
  675. %% @todo how to test this?
  676. %3.2.1
  677. % A server decodes and interprets these values as it would any other
  678. % SETTINGS frame.
  679. %3.2
  680. % @todo (maybe an option to disable HTTP/2?)
  681. % A server that does not support HTTP/2 can respond to the request as
  682. % though the Upgrade header field were absent
  683. %% @todo Do we reject "http" requests over TLS and "https" requests over TCP?
  684. %% Yes, see 421 status code. But maybe some configuration is in order.
  685. %3.5
  686. %% @todo yeah idk
  687. %(if the upgrade failed and the connection send this before 101 then
  688. % we should 400 and close the connection, but not sure how it can fail)
  689. % @todo (maybe an option to disable HTTP/2?)
  690. % The client sends
  691. % the client connection preface immediately upon receipt of a 101
  692. % (Switching Protocols) response (indicating a successful upgrade)
  693. %% @todo
  694. %3.5
  695. %(big mess)
  696. % To avoid unnecessary latency, clients are permitted to send
  697. % additional frames to the server immediately after sending the client
  698. % connection preface, without waiting to receive the server connection
  699. % preface. It is important to note, however, that the server
  700. % connection preface SETTINGS frame might include parameters that
  701. % necessarily alter how a client is expected to communicate with the
  702. % server. Upon receiving the SETTINGS frame, the client is expected to
  703. % honor any parameters established. In some configurations, it is
  704. % possible for the server to transmit SETTINGS before the client sends
  705. % additional frames, providing an opportunity to avoid this issue.