rfc7540_SUITE.erl 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  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. %% SETTINGS_MAX_FRAME_SIZE is probably the easiest to test. The relevant
  384. %% RFC quote is:
  385. %%
  386. %% 3.2.1
  387. %% A server decodes and interprets these values as it would any other
  388. %% SETTINGS frame. Explicit acknowledgement of these settings
  389. %% (Section 6.5.3) is not necessary, since a 101 response serves as
  390. %% implicit acknowledgement.
  391. %% @todo We need to test an upgrade with a request body. It is probably
  392. %% worth having a configuration value for how much we accept while still
  393. %% upgrading (if too big, we would just stay on HTTP/1.1). We therefore
  394. %% needs a test for when the body is small enough, and one for when the
  395. %% body is larger than we accept. The relevant RFC quote is:
  396. %%
  397. %% 3.2
  398. %% Requests that contain a payload body MUST be sent in their entirety
  399. %% before the client can send HTTP/2 frames. This means that a large
  400. %% request can block the use of the connection until it is completely
  401. %% sent.
  402. %% @todo We should definitely have a test with OPTIONS. The relevant
  403. %% RFC quote is:
  404. %%
  405. %% 3.2
  406. %% If concurrency of an initial request with subsequent requests is
  407. %% important, an OPTIONS request can be used to perform the upgrade to
  408. %% HTTP/2, at the cost of an additional round trip.
  409. %% @todo If we ever handle priority, we need to check that the initial
  410. %% HTTP/1.1 request has default priority. The relevant RFC quote is:
  411. %%
  412. %% 3.2
  413. %% The HTTP/1.1 request that is sent prior to upgrade is assigned a
  414. %% stream identifier of 1 (see Section 5.1.1) with default priority
  415. %% values (Section 5.3.5).
  416. http_upgrade_response(Config) ->
  417. doc("A response must be sent to the initial HTTP/1.1 request "
  418. "after switching to HTTP/2. The response must use "
  419. "the stream identifier 1. (RFC7540 3.2)"),
  420. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  421. ok = gen_tcp:send(Socket, [
  422. "GET / HTTP/1.1\r\n"
  423. "Host: localhost\r\n"
  424. "Connection: Upgrade, HTTP2-Settings\r\n"
  425. "Upgrade: h2c\r\n"
  426. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  427. "\r\n"]),
  428. ok = do_recv_101(Socket),
  429. %% Send a valid preface.
  430. %% @todo Use non-empty SETTINGS here. Just because.
  431. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  432. %% Receive the server preface.
  433. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  434. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  435. %% Send the SETTINGS ack.
  436. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  437. %% Receive the SETTINGS ack, and the response HEADERS and DATA (Stream ID 1).
  438. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  439. case gen_tcp:recv(Socket, 9, 1000) of
  440. {ok, << 0:24, 4:8, 1:8, 0:32 >>} ->
  441. [settings_ack|Acc];
  442. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  443. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  444. [headers|Acc];
  445. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  446. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  447. [data|Acc]
  448. end
  449. end, [], [1, 2, 3])),
  450. case Received of
  451. [settings_ack, headers, data] -> ok;
  452. [headers, settings_ack, data] -> ok;
  453. [headers, data, settings_ack] -> ok
  454. end.
  455. http_upgrade_response_half_closed(Config) ->
  456. doc("The stream for the initial HTTP/1.1 request is half-closed. (RFC7540 3.2)"),
  457. %% Try sending more data after the upgrade and get an error.
  458. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  459. ok = gen_tcp:send(Socket, [
  460. "GET / HTTP/1.1\r\n"
  461. "Host: localhost\r\n"
  462. "Connection: Upgrade, HTTP2-Settings\r\n"
  463. "Upgrade: h2c\r\n"
  464. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  465. "\r\n"]),
  466. ok = do_recv_101(Socket),
  467. %% Send a valid preface followed by an unexpected DATA frame.
  468. ok = gen_tcp:send(Socket, [
  469. "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
  470. cow_http2:settings(#{}),
  471. cow_http2:data(1, fin, <<"Unexpected DATA frame.">>)
  472. ]),
  473. %% Receive the server preface.
  474. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  475. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  476. %% Skip the SETTINGS ack, receive the response HEADERS, DATA and RST_STREAM (streamid 1).
  477. Received = lists:reverse(lists:foldl(fun(_, Acc) ->
  478. case gen_tcp:recv(Socket, 9, 1000) of
  479. {ok, << 0:24, 4:8, 1:8, 0:32 >>} ->
  480. Acc;
  481. {ok, << SkipLen:24, 1:8, _:8, 1:32 >>} ->
  482. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  483. [headers|Acc];
  484. {ok, << SkipLen:24, 0:8, _:8, 1:32 >>} ->
  485. {ok, _} = gen_tcp:recv(Socket, SkipLen, 1000),
  486. [data|Acc];
  487. {ok, << 4:24, 3:8, 0:8, 1:32 >>} ->
  488. %% We expect a STREAM_CLOSED reason.
  489. {ok, << 5:32 >>} = gen_tcp:recv(Socket, 4, 1000),
  490. [rst_stream|Acc];
  491. {error, _} ->
  492. %% Can be timeouts, ignore them.
  493. Acc
  494. end
  495. end, [], [1, 2, 3, 4])),
  496. case Received of
  497. [rst_stream] -> ok;
  498. [headers, rst_stream] -> ok;
  499. [headers, data, rst_stream] -> ok
  500. end.
  501. %% Starting HTTP/2 for "https" URIs.
  502. alpn_ignore_h2c(Config) ->
  503. doc("An h2c ALPN protocol identifier must be ignored. (RFC7540 3.3)"),
  504. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  505. [{alpn_advertised_protocols, [<<"h2c">>, <<"http/1.1">>]}, binary, {active, false}]),
  506. {ok, <<"http/1.1">>} = ssl:negotiated_protocol(Socket),
  507. ok.
  508. alpn_server_preface(Config) ->
  509. doc("The first frame must be a SETTINGS frame "
  510. "for the server connection preface. (RFC7540 3.3, RFC7540 3.5, RFC7540 6.5)"),
  511. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  512. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  513. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  514. %% Receive the server preface.
  515. {ok, << _:24, 4:8, 0:40 >>} = ssl:recv(Socket, 9, 1000),
  516. ok.
  517. alpn_client_preface_timeout(Config) ->
  518. doc("Clients negotiating HTTP/2 and not sending a preface in "
  519. "a timely manner must be disconnected."),
  520. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  521. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  522. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  523. %% Receive the server preface.
  524. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  525. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  526. %% Do not send the preface. Wait for the server to disconnect us.
  527. {error, closed} = ssl:recv(Socket, 3, 6000),
  528. ok.
  529. alpn_reject_missing_client_preface(Config) ->
  530. doc("Servers must treat an invalid connection preface as a "
  531. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  532. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  533. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  534. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  535. %% Send a SETTINGS frame directly instead of the proper preface.
  536. ok = ssl:send(Socket, cow_http2:settings(#{})),
  537. %% Receive the server preface.
  538. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  539. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  540. %% We expect the server to close the connection when it receives a bad preface.
  541. {error, closed} = ssl:recv(Socket, 3, 1000),
  542. ok.
  543. alpn_reject_invalid_client_preface(Config) ->
  544. doc("Servers must treat an invalid connection preface as a "
  545. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  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 slightly incorrect preface.
  550. ok = ssl:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  551. %% Receive the server preface.
  552. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  553. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  554. %% We expect the server to close the connection when it receives a bad preface.
  555. {error, closed} = ssl:recv(Socket, 3, 1000),
  556. ok.
  557. alpn_reject_missing_client_preface_settings(Config) ->
  558. doc("Servers must treat an invalid connection preface as a "
  559. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  560. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  561. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  562. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  563. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  564. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  565. %% Receive the server preface.
  566. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  567. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  568. %% We expect the server to close the connection when it receives a bad preface.
  569. {error, closed} = ssl:recv(Socket, 3, 1000),
  570. ok.
  571. alpn_reject_invalid_client_preface_settings(Config) ->
  572. doc("Servers must treat an invalid connection preface as a "
  573. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  574. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  575. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  576. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  577. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  578. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", << 0:24, 4:8, 0:9, 1:31 >>]),
  579. %% Receive the server preface.
  580. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  581. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  582. %% We expect the server to close the connection when it receives a bad preface.
  583. {error, closed} = ssl:recv(Socket, 3, 1000),
  584. ok.
  585. alpn_accept_client_preface_empty_settings(Config) ->
  586. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.3, RFC7540 3.5)"),
  587. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  588. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  589. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  590. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  591. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  592. %% Receive the server preface.
  593. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  594. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  595. %% Receive the SETTINGS ack.
  596. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  597. ok.
  598. alpn_client_preface_settings_ack_timeout(Config) ->
  599. doc("Failure to acknowledge the server's SETTINGS frame "
  600. "results in a SETTINGS_TIMEOUT connection error. (RFC7540 3.5, RFC7540 6.5.3)"),
  601. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  602. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  603. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  604. %% Send a valid preface.
  605. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  606. %% Receive the server preface.
  607. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  608. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  609. %% Receive the SETTINGS ack.
  610. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  611. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  612. {ok, << _:24, 7:8, _:72, 4:32 >>} = ssl:recv(Socket, 17, 6000),
  613. ok.
  614. alpn(Config) ->
  615. doc("Successful ALPN negotiation. (RFC7540 3.3)"),
  616. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  617. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  618. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  619. %% Send a valid preface.
  620. %% @todo Use non-empty SETTINGS here. Just because.
  621. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  622. %% Receive the server preface.
  623. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  624. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  625. %% Send the SETTINGS ack.
  626. ok = ssl:send(Socket, cow_http2:settings_ack()),
  627. %% Receive the SETTINGS ack.
  628. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  629. %% Wait until after the SETTINGS ack timeout was supposed to trigger.
  630. receive after 6000 -> ok end,
  631. %% Send a PING.
  632. ok = ssl:send(Socket, cow_http2:ping(0)),
  633. %% Receive a PING ack back, indicating the connection is still up.
  634. {ok, << 8:24, 6:8, 0:7, 1:1, 0:96 >>} = ssl:recv(Socket, 17, 1000),
  635. ok.
  636. %% Starting HTTP/2 with prior knowledge.
  637. prior_knowledge_reject_tls(Config) ->
  638. doc("Implementations that support HTTP/2 over TLS must use ALPN. (RFC7540 3.4)"),
  639. {ok, Socket} = ssl:connect("localhost", config(port, Config), [binary, {active, false}]),
  640. %% Send a valid preface.
  641. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  642. %% We expect the server to send an HTTP 400 error
  643. %% when trying to use HTTP/2 without going through ALPN negotiation.
  644. {ok, <<"HTTP/1.1 400">>} = ssl:recv(Socket, 12, 1000),
  645. ok.
  646. prior_knowledge_server_preface(Config) ->
  647. doc("The first frame must be a SETTINGS frame "
  648. "for the server connection preface. (RFC7540 3.4, RFC7540 3.5, RFC7540 6.5)"),
  649. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  650. %% Send a valid preface.
  651. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  652. %% Receive the server preface.
  653. {ok, << _:24, 4:8, 0:40 >>} = gen_tcp:recv(Socket, 9, 1000),
  654. ok.
  655. %% Note: the client preface timeout doesn't apply in this case,
  656. %% so we don't test it. An HTTP/1.1 client that does not send
  657. %% a request in a timely manner will get disconnected by the
  658. %% HTTP protocol code, not by HTTP/2's.
  659. %% Note: the test that starts by sending a SETTINGS frame is
  660. %% redundant with tests sending garbage on the connection.
  661. %% From the point of view of an HTTP/1.1 connection, a
  662. %% SETTINGS frame is indistinguishable from garbage.
  663. prior_knowledge_reject_invalid_client_preface(Config) ->
  664. doc("An incorrect preface is an invalid HTTP/1.1 request. (RFC7540 3.4)"),
  665. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  666. %% Send a slightly incorrect preface.
  667. ok = gen_tcp:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  668. %% We propagate to HTTP/2 after checking only the request-line.
  669. %% The server then sends its preface before checking the full client preface.
  670. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  671. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  672. %% We expect the server to close the connection when it receives a bad preface.
  673. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  674. ok.
  675. prior_knowledge_reject_missing_client_preface_settings(Config) ->
  676. doc("Servers must treat an invalid connection preface as a "
  677. "connection error of type PROTOCOL_ERROR. (RFC7540 3.4, RFC7540 3.5)"),
  678. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  679. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  680. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  681. %% Receive the server preface.
  682. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  683. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  684. %% We expect the server to close the connection when it receives a bad preface.
  685. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  686. ok.
  687. prior_knowledge_reject_invalid_client_preface_settings(Config) ->
  688. doc("Servers must treat an invalid connection preface as a "
  689. "connection error of type PROTOCOL_ERROR. (RFC7540 3.4, RFC7540 3.5)"),
  690. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  691. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  692. 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 >>]),
  693. %% Receive the server preface.
  694. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  695. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  696. %% We expect the server to close the connection when it receives a bad preface.
  697. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  698. ok.
  699. prior_knowledge_accept_client_preface_empty_settings(Config) ->
  700. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.4, RFC7540 3.5)"),
  701. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  702. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  703. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  704. %% Receive the server preface.
  705. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  706. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  707. %% Receive the SETTINGS ack.
  708. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  709. ok.
  710. prior_knowledge_client_preface_settings_ack_timeout(Config) ->
  711. doc("The SETTINGS frames sent by the client must be acknowledged. (RFC7540 3.5, RFC7540 6.5.3)"),
  712. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  713. %% Send a valid preface.
  714. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  715. %% Receive the server preface.
  716. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  717. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  718. %% Receive the SETTINGS ack.
  719. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  720. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  721. {ok, << _:24, 7:8, _:72, 4:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  722. ok.
  723. prior_knowledge(Config) ->
  724. doc("Streams can be initiated after a successful HTTP/2 connection "
  725. "with prior knowledge of server capabilities. (RFC7540 3.4)"),
  726. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  727. %% Send a valid preface.
  728. %% @todo Use non-empty SETTINGS here. Just because.
  729. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  730. %% Receive the server preface.
  731. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  732. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  733. %% Send the SETTINGS ack.
  734. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  735. %% Receive the SETTINGS ack.
  736. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  737. %% Wait until after the SETTINGS ack timeout was supposed to trigger.
  738. receive after 6000 -> ok end,
  739. %% Send a PING.
  740. ok = gen_tcp:send(Socket, cow_http2:ping(0)),
  741. %% Receive a PING ack back, indicating the connection is still up.
  742. {ok, << 8:24, 6:8, 0:7, 1:1, 0:96 >>} = gen_tcp:recv(Socket, 17, 1000),
  743. ok.
  744. %% @todo If we ever add an option to disable HTTP/2, we need to check
  745. %% the following things:
  746. %% * HTTP/1.1 Upgrade returns an HTTP/1.1 response (3.2)
  747. %% * HTTP/1.1 Upgrade errors out if the client sends HTTP/2 frames
  748. %% without waiting for the 101 response (3.2, 3.5)
  749. %% * Prior knowledge handshake fails (3.4)
  750. %% * ALPN selects HTTP/1.1 (3.3)