rfc7540_SUITE.erl 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  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. %% Receive the response to the initial HTTP/1.1 request.
  187. {ok, << HeadersSkipLen:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  188. {ok, _} = gen_tcp:recv(Socket, HeadersSkipLen, 1000),
  189. {ok, << DataSkipLen:24, 0:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  190. {ok, _} = gen_tcp:recv(Socket, DataSkipLen, 1000),
  191. %% Do not send the preface. Wait for the server to disconnect us.
  192. {error, closed} = gen_tcp:recv(Socket, 9, 6000),
  193. ok.
  194. http_upgrade_reject_missing_client_preface(Config) ->
  195. doc("Servers must treat an invalid connection preface as a "
  196. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  197. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  198. ok = gen_tcp:send(Socket, [
  199. "GET / HTTP/1.1\r\n"
  200. "Host: localhost\r\n"
  201. "Connection: Upgrade, HTTP2-Settings\r\n"
  202. "Upgrade: h2c\r\n"
  203. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  204. "\r\n"]),
  205. ok = do_recv_101(Socket),
  206. %% Send a SETTINGS frame directly instead of the proper preface.
  207. ok = gen_tcp:send(Socket, cow_http2:settings(#{})),
  208. %% Receive the server preface.
  209. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  210. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  211. %% We expect the server to close the connection when it receives a bad preface.
  212. %% The server may however have already started sending the response to the
  213. %% initial HTTP/1.1 request.
  214. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  215. case gen_tcp:recv(Socket, 9, 1000) of
  216. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  217. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  218. [headers|Acc];
  219. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  220. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  221. [data|Acc];
  222. {error, _} ->
  223. [closed|Acc]
  224. end
  225. end, [], [1, 2, 3])),
  226. case Received of
  227. [closed|_] -> ok;
  228. [headers, closed|_] -> ok;
  229. [headers, data, closed] -> ok
  230. end.
  231. http_upgrade_reject_invalid_client_preface(Config) ->
  232. doc("Servers must treat an invalid connection preface as a "
  233. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  234. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  235. ok = gen_tcp:send(Socket, [
  236. "GET / HTTP/1.1\r\n"
  237. "Host: localhost\r\n"
  238. "Connection: Upgrade, HTTP2-Settings\r\n"
  239. "Upgrade: h2c\r\n"
  240. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  241. "\r\n"]),
  242. ok = do_recv_101(Socket),
  243. %% Send a slightly incorrect preface.
  244. ok = gen_tcp:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  245. %% Receive the server preface.
  246. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  247. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  248. %% We expect the server to close the connection when it receives a bad preface.
  249. %% The server may however have already started sending the response to the
  250. %% initial HTTP/1.1 request.
  251. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  252. case gen_tcp:recv(Socket, 9, 1000) of
  253. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  254. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  255. [headers|Acc];
  256. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  257. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  258. [data|Acc];
  259. {error, _} ->
  260. [closed|Acc]
  261. end
  262. end, [], [1, 2, 3])),
  263. case Received of
  264. [closed|_] -> ok;
  265. [headers, closed|_] -> ok;
  266. [headers, data, closed] -> ok
  267. end.
  268. http_upgrade_reject_missing_client_preface_settings(Config) ->
  269. doc("Servers must treat an invalid connection preface as a "
  270. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  271. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  272. ok = gen_tcp:send(Socket, [
  273. "GET / HTTP/1.1\r\n"
  274. "Host: localhost\r\n"
  275. "Connection: Upgrade, HTTP2-Settings\r\n"
  276. "Upgrade: h2c\r\n"
  277. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  278. "\r\n"]),
  279. ok = do_recv_101(Socket),
  280. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  281. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  282. %% Receive the server preface.
  283. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  284. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  285. %% We expect the server to close the connection when it receives a bad preface.
  286. %% The server may however have already started sending the response to the
  287. %% initial HTTP/1.1 request.
  288. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  289. case gen_tcp:recv(Socket, 9, 1000) of
  290. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  291. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  292. [headers|Acc];
  293. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  294. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  295. [data|Acc];
  296. {error, _} ->
  297. [closed|Acc]
  298. end
  299. end, [], [1, 2, 3])),
  300. case Received of
  301. [closed|_] -> ok;
  302. [headers, closed|_] -> ok;
  303. [headers, data, closed] -> ok
  304. end.
  305. http_upgrade_reject_invalid_client_preface_settings(Config) ->
  306. doc("Servers must treat an invalid connection preface as a "
  307. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  308. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  309. ok = gen_tcp:send(Socket, [
  310. "GET / HTTP/1.1\r\n"
  311. "Host: localhost\r\n"
  312. "Connection: Upgrade, HTTP2-Settings\r\n"
  313. "Upgrade: h2c\r\n"
  314. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  315. "\r\n"]),
  316. ok = do_recv_101(Socket),
  317. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  318. 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 >>]),
  319. %% Receive the server preface.
  320. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  321. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  322. %% We expect the server to close the connection when it receives a bad preface.
  323. %% The server may however have already started sending the response to the
  324. %% initial HTTP/1.1 request.
  325. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  326. case gen_tcp:recv(Socket, 9, 1000) of
  327. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  328. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  329. [headers|Acc];
  330. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  331. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  332. [data|Acc];
  333. {error, _} ->
  334. [closed|Acc]
  335. end
  336. end, [], [1, 2, 3])),
  337. case Received of
  338. [closed|_] -> ok;
  339. [headers, closed|_] -> ok;
  340. [headers, data, closed] -> ok
  341. end.
  342. http_upgrade_accept_client_preface_empty_settings(Config) ->
  343. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.2, RFC7540 3.5)"),
  344. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  345. ok = gen_tcp:send(Socket, [
  346. "GET / HTTP/1.1\r\n"
  347. "Host: localhost\r\n"
  348. "Connection: Upgrade, HTTP2-Settings\r\n"
  349. "Upgrade: h2c\r\n"
  350. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  351. "\r\n"]),
  352. ok = do_recv_101(Socket),
  353. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  354. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  355. %% Receive the server preface.
  356. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  357. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  358. %% Receive the SETTINGS ack.
  359. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  360. ok.
  361. http_upgrade_client_preface_settings_ack_timeout(Config) ->
  362. doc("The SETTINGS frames sent by the client must be acknowledged. (RFC7540 3.5, RFC7540 6.5.3)"),
  363. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  364. ok = gen_tcp:send(Socket, [
  365. "GET / HTTP/1.1\r\n"
  366. "Host: localhost\r\n"
  367. "Connection: Upgrade, HTTP2-Settings\r\n"
  368. "Upgrade: h2c\r\n"
  369. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  370. "\r\n"]),
  371. ok = do_recv_101(Socket),
  372. %% Send a valid preface.
  373. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  374. %% Receive the server preface.
  375. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  376. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  377. %% Receive the SETTINGS ack.
  378. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  379. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  380. {ok, << _:24, 7:8, _:72, 4:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  381. ok.
  382. %% @todo We need a successful test with actual options in HTTP2-Settings.
  383. %% @todo We need to test an upgrade with a request body (small and too large).
  384. %% @todo Also assigned default priority values but not sure how to test that.
  385. http_upgrade_response(Config) ->
  386. doc("A response must be sent to the initial HTTP/1.1 request "
  387. "after switching to HTTP/2. The response must use "
  388. "the stream identifier 1. (RFC7540 3.2)"),
  389. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  390. ok = gen_tcp:send(Socket, [
  391. "GET / HTTP/1.1\r\n"
  392. "Host: localhost\r\n"
  393. "Connection: Upgrade, HTTP2-Settings\r\n"
  394. "Upgrade: h2c\r\n"
  395. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  396. "\r\n"]),
  397. ok = do_recv_101(Socket),
  398. %% Send a valid preface.
  399. %% @todo Use non-empty SETTINGS here. Just because.
  400. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  401. %% Receive the server preface.
  402. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  403. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  404. %% Send the SETTINGS ack.
  405. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  406. %% Receive the SETTINGS ack, and the response HEADERS and DATA (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. [settings_ack|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. end
  418. end, [], [1, 2, 3])),
  419. case Received of
  420. [settings_ack, headers, data] -> ok;
  421. [headers, settings_ack, data] -> ok;
  422. [headers, data, settings_ack] -> ok
  423. end.
  424. http_upgrade_response_half_closed(Config) ->
  425. doc("The stream for the initial HTTP/1.1 request is half-closed. (RFC7540 3.2)"),
  426. %% Try sending more data after the upgrade and get an error.
  427. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  428. ok = gen_tcp:send(Socket, [
  429. "GET / HTTP/1.1\r\n"
  430. "Host: localhost\r\n"
  431. "Connection: Upgrade, HTTP2-Settings\r\n"
  432. "Upgrade: h2c\r\n"
  433. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  434. "\r\n"]),
  435. ok = do_recv_101(Socket),
  436. %% Send a valid preface followed by an unexpected DATA frame.
  437. ok = gen_tcp:send(Socket, [
  438. "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
  439. cow_http2:settings(#{}),
  440. cow_http2:data(1, fin, <<"Unexpected DATA frame.">>)
  441. ]),
  442. %% Receive the server preface.
  443. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  444. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  445. %% Skip the SETTINGS ack, receive the response HEADERS, DATA and RST_STREAM (streamid 1).
  446. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  447. case gen_tcp:recv(Socket, 9, 1000) of
  448. {ok, << 0:24, 4:8, 1:8, 0:32 >>} ->
  449. Acc;
  450. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  451. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  452. [headers|Acc];
  453. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  454. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  455. [data|Acc];
  456. {ok, << 4:24, 3:8, 0:8, 1:32 >>} ->
  457. %% We expect a STREAM_CLOSED reason.
  458. {ok, << 5:32 >>} = gen_tcp:recv(Socket, 4, 1000),
  459. [rst_stream|Acc];
  460. {error, _} ->
  461. %% Can be timeouts, ignore them.
  462. Acc
  463. end
  464. end, [], [1, 2, 3, 4])),
  465. case Received of
  466. [rst_stream] -> ok;
  467. [headers, rst_stream] -> ok;
  468. [headers, data, rst_stream] -> ok
  469. end.
  470. %% Starting HTTP/2 for "https" URIs.
  471. alpn_ignore_h2c(Config) ->
  472. doc("An h2c ALPN protocol identifier must be ignored. (RFC7540 3.3)"),
  473. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  474. [{alpn_advertised_protocols, [<<"h2c">>, <<"http/1.1">>]}, binary, {active, false}]),
  475. {ok, <<"http/1.1">>} = ssl:negotiated_protocol(Socket),
  476. ok.
  477. alpn_server_preface(Config) ->
  478. doc("The first frame must be a SETTINGS frame "
  479. "for the server connection preface. (RFC7540 3.3, RFC7540 3.5, RFC7540 6.5)"),
  480. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  481. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  482. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  483. %% Receive the server preface.
  484. {ok, << _:24, 4:8, 0:40 >>} = ssl:recv(Socket, 9, 1000),
  485. ok.
  486. alpn_client_preface_timeout(Config) ->
  487. doc("Clients negotiating HTTP/2 and not sending a preface in "
  488. "a timely manner must be disconnected."),
  489. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  490. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  491. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  492. %% Receive the server preface.
  493. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  494. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  495. %% Do not send the preface. Wait for the server to disconnect us.
  496. {error, closed} = ssl:recv(Socket, 3, 6000),
  497. ok.
  498. alpn_reject_missing_client_preface(Config) ->
  499. doc("Servers must treat an invalid connection preface as a "
  500. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  501. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  502. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  503. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  504. %% Send a SETTINGS frame directly instead of the proper preface.
  505. ok = ssl:send(Socket, cow_http2:settings(#{})),
  506. %% Receive the server preface.
  507. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  508. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  509. %% We expect the server to close the connection when it receives a bad preface.
  510. {error, closed} = ssl:recv(Socket, 3, 1000),
  511. ok.
  512. alpn_reject_invalid_client_preface(Config) ->
  513. doc("Servers must treat an invalid connection preface as a "
  514. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  515. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  516. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  517. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  518. %% Send a slightly incorrect preface.
  519. ok = ssl:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  520. %% Receive the server preface.
  521. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  522. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  523. %% We expect the server to close the connection when it receives a bad preface.
  524. {error, closed} = ssl:recv(Socket, 3, 1000),
  525. ok.
  526. alpn_reject_missing_client_preface_settings(Config) ->
  527. doc("Servers must treat an invalid connection preface as a "
  528. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  529. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  530. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  531. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  532. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  533. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  534. %% Receive the server preface.
  535. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  536. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  537. %% We expect the server to close the connection when it receives a bad preface.
  538. {error, closed} = ssl:recv(Socket, 3, 1000),
  539. ok.
  540. alpn_reject_invalid_client_preface_settings(Config) ->
  541. doc("Servers must treat an invalid connection preface as a "
  542. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  543. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  544. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  545. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  546. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  547. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", << 0:24, 4:8, 0:9, 1:31 >>]),
  548. %% Receive the server preface.
  549. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  550. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  551. %% We expect the server to close the connection when it receives a bad preface.
  552. {error, closed} = ssl:recv(Socket, 3, 1000),
  553. ok.
  554. alpn_accept_client_preface_empty_settings(Config) ->
  555. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.3, RFC7540 3.5)"),
  556. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  557. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  558. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  559. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  560. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  561. %% Receive the server preface.
  562. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  563. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  564. %% Receive the SETTINGS ack.
  565. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  566. ok.
  567. alpn_client_preface_settings_ack_timeout(Config) ->
  568. doc("Failure to acknowledge the server's SETTINGS frame "
  569. "results in a SETTINGS_TIMEOUT connection error. (RFC7540 3.5, RFC7540 6.5.3)"),
  570. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  571. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  572. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  573. %% Send a valid preface.
  574. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  575. %% Receive the server preface.
  576. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  577. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  578. %% Receive the SETTINGS ack.
  579. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  580. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  581. {ok, << _:24, 7:8, _:72, 4:32 >>} = ssl:recv(Socket, 17, 6000),
  582. ok.
  583. alpn(Config) ->
  584. doc("Successful ALPN negotiation. (RFC7540 3.3)"),
  585. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  586. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  587. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  588. %% Send a valid preface.
  589. %% @todo Use non-empty SETTINGS here. Just because.
  590. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  591. %% Receive the server preface.
  592. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  593. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  594. %% Send the SETTINGS ack.
  595. ok = ssl:send(Socket, cow_http2:settings_ack()),
  596. %% Receive the SETTINGS ack.
  597. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  598. %% Wait until after the SETTINGS ack timeout was supposed to trigger.
  599. receive after 6000 -> ok end,
  600. %% Send a PING.
  601. ok = ssl:send(Socket, cow_http2:ping(0)),
  602. %% Receive a PING ack back, indicating the connection is still up.
  603. {ok, << 8:24, 6:8, 0:7, 1:1, 0:96 >>} = ssl:recv(Socket, 17, 1000),
  604. ok.
  605. %% Starting HTTP/2 with prior knowledge.
  606. prior_knowledge_reject_tls(Config) ->
  607. doc("Implementations that support HTTP/2 over TLS must use ALPN. (RFC7540 3.4)"),
  608. {ok, Socket} = ssl:connect("localhost", config(port, Config), [binary, {active, false}]),
  609. %% Send a valid preface.
  610. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  611. %% We expect the server to send an HTTP 400 error
  612. %% when trying to use HTTP/2 without going through ALPN negotiation.
  613. {ok, <<"HTTP/1.1 400">>} = ssl:recv(Socket, 12, 1000),
  614. ok.
  615. prior_knowledge_server_preface(Config) ->
  616. doc("The first frame must be a SETTINGS frame "
  617. "for the server connection preface. (RFC7540 3.4, RFC7540 3.5, RFC7540 6.5)"),
  618. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  619. %% Send a valid preface.
  620. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  621. %% Receive the server preface.
  622. {ok, << _:24, 4:8, 0:40 >>} = gen_tcp:recv(Socket, 9, 1000),
  623. ok.
  624. %% Note: the client preface timeout doesn't apply in this case,
  625. %% so we don't test it. An HTTP/1.1 client that does not send
  626. %% a request in a timely manner will get disconnected by the
  627. %% HTTP protocol code, not by HTTP/2's.
  628. %% Note: the test that starts by sending a SETTINGS frame is
  629. %% redundant with tests sending garbage on the connection.
  630. %% From the point of view of an HTTP/1.1 connection, a
  631. %% SETTINGS frame is indistinguishable from garbage.
  632. prior_knowledge_reject_invalid_client_preface(Config) ->
  633. doc("An incorrect preface is an invalid HTTP/1.1 request. (RFC7540 3.4)"),
  634. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  635. %% Send a slightly incorrect preface.
  636. ok = gen_tcp:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  637. %% We propagate to HTTP/2 after checking only the request-line.
  638. %% The server then sends its preface before checking the full client preface.
  639. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  640. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  641. %% We expect the server to close the connection when it receives a bad preface.
  642. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  643. ok.
  644. prior_knowledge_reject_missing_client_preface_settings(Config) ->
  645. doc("Servers must treat an invalid connection preface as a "
  646. "connection error of type PROTOCOL_ERROR. (RFC7540 3.4, RFC7540 3.5)"),
  647. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  648. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  649. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  650. %% Receive the server preface.
  651. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  652. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  653. %% We expect the server to close the connection when it receives a bad preface.
  654. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  655. ok.
  656. prior_knowledge_reject_invalid_client_preface_settings(Config) ->
  657. doc("Servers must treat an invalid connection preface as a "
  658. "connection error of type PROTOCOL_ERROR. (RFC7540 3.4, RFC7540 3.5)"),
  659. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  660. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  661. 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 >>]),
  662. %% Receive the server preface.
  663. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  664. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  665. %% We expect the server to close the connection when it receives a bad preface.
  666. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  667. ok.
  668. prior_knowledge_accept_client_preface_empty_settings(Config) ->
  669. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.4, RFC7540 3.5)"),
  670. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  671. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  672. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  673. %% Receive the server preface.
  674. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  675. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  676. %% Receive the SETTINGS ack.
  677. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  678. ok.
  679. prior_knowledge_client_preface_settings_ack_timeout(Config) ->
  680. doc("The SETTINGS frames sent by the client must be acknowledged. (RFC7540 3.5, RFC7540 6.5.3)"),
  681. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  682. %% Send a valid preface.
  683. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  684. %% Receive the server preface.
  685. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  686. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  687. %% Receive the SETTINGS ack.
  688. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  689. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  690. {ok, << _:24, 7:8, _:72, 4:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  691. ok.
  692. prior_knowledge(Config) ->
  693. doc("Streams can be initiated after a successful HTTP/2 connection "
  694. "with prior knowledge of server capabilities. (RFC7540 3.4)"),
  695. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  696. %% Send a valid preface.
  697. %% @todo Use non-empty SETTINGS here. Just because.
  698. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  699. %% Receive the server preface.
  700. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  701. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  702. %% Send the SETTINGS ack.
  703. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  704. %% Receive the SETTINGS ack.
  705. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  706. %% Wait until after the SETTINGS ack timeout was supposed to trigger.
  707. receive after 6000 -> ok end,
  708. %% Send a PING.
  709. ok = gen_tcp:send(Socket, cow_http2:ping(0)),
  710. %% Receive a PING ack back, indicating the connection is still up.
  711. {ok, << 8:24, 6:8, 0:7, 1:1, 0:96 >>} = gen_tcp:recv(Socket, 17, 1000),
  712. ok.
  713. %% Tests still need to be added for the following points:
  714. %% @todo how to test this?
  715. %3.2.1
  716. % A server decodes and interprets these values as it would any other
  717. % SETTINGS frame.
  718. %3.2
  719. % @todo (maybe an option to disable HTTP/2?)
  720. % A server that does not support HTTP/2 can respond to the request as
  721. % though the Upgrade header field were absent
  722. %% @todo Do we reject "http" requests over TLS and "https" requests over TCP?
  723. %% Yes, see 421 status code. But maybe some configuration is in order.
  724. %3.5
  725. %% @todo yeah idk
  726. %(if the upgrade failed and the connection send this before 101 then
  727. % we should 400 and close the connection, but not sure how it can fail)
  728. % @todo (maybe an option to disable HTTP/2?)
  729. % The client sends
  730. % the client connection preface immediately upon receipt of a 101
  731. % (Switching Protocols) response (indicating a successful upgrade)
  732. %% @todo
  733. %3.5
  734. %(big mess)
  735. % To avoid unnecessary latency, clients are permitted to send
  736. % additional frames to the server immediately after sending the client
  737. % connection preface, without waiting to receive the server connection
  738. % preface. It is important to note, however, that the server
  739. % connection preface SETTINGS frame might include parameters that
  740. % necessarily alter how a client is expected to communicate with the
  741. % server. Upon receiving the SETTINGS frame, the client is expected to
  742. % honor any parameters established. In some configurations, it is
  743. % possible for the server to transmit SETTINGS before the client sends
  744. % additional frames, providing an opportunity to avoid this issue.