rfc7540_SUITE.erl 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  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_reject_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 400">>} = 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.
  398. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  399. %% Send more data on the stream to trigger an error.
  400. ok = gen_tcp:send(Socket, cow_http2:data(1, fin, <<>>)),
  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. %% Receive the SETTINGS ack.
  405. %% @todo It's possible that we receive the response before the SETTINGS ack or RST_STREAM.
  406. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  407. %% The server resets the stream with reason STREAM_CLOSED.
  408. {ok, << 4:24, 3:8, 0:8, 1:32, 5:32 >>} = gen_tcp:recv(Socket, 13, 1000),
  409. ok.
  410. %% Starting HTTP/2 for "https" URIs.
  411. alpn_ignore_h2c(Config) ->
  412. doc("An h2c ALPN protocol identifier must be ignored. (RFC7540 3.3)"),
  413. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  414. [{alpn_advertised_protocols, [<<"h2c">>, <<"http/1.1">>]}, binary, {active, false}]),
  415. {ok, <<"http/1.1">>} = ssl:negotiated_protocol(Socket),
  416. ok.
  417. alpn_server_preface(Config) ->
  418. doc("The first frame must be a SETTINGS frame "
  419. "for the server connection preface. (RFC7540 3.3, RFC7540 3.5, RFC7540 6.5)"),
  420. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  421. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  422. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  423. %% Receive the server preface.
  424. {ok, << _:24, 4:8, 0:40 >>} = ssl:recv(Socket, 9, 1000),
  425. ok.
  426. alpn_client_preface_timeout(Config) ->
  427. doc("Clients negotiating HTTP/2 and not sending a preface in "
  428. "a timely manner must be disconnected."),
  429. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  430. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  431. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  432. %% Receive the server preface.
  433. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  434. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  435. %% Do not send the preface. Wait for the server to disconnect us.
  436. {error, closed} = ssl:recv(Socket, 3, 6000),
  437. ok.
  438. alpn_reject_missing_client_preface(Config) ->
  439. doc("Servers must treat an invalid connection preface as a "
  440. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.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. %% Send a SETTINGS frame directly instead of the proper preface.
  445. ok = ssl:send(Socket, cow_http2:settings(#{})),
  446. %% Receive the server preface.
  447. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  448. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  449. %% We expect the server to close the connection when it receives a bad preface.
  450. {error, closed} = ssl:recv(Socket, 3, 1000),
  451. ok.
  452. alpn_reject_invalid_client_preface(Config) ->
  453. doc("Servers must treat an invalid connection preface as a "
  454. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  455. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  456. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  457. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  458. %% Send a slightly incorrect preface.
  459. ok = ssl:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  460. %% Receive the server preface.
  461. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  462. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  463. %% We expect the server to close the connection when it receives a bad preface.
  464. {error, closed} = ssl:recv(Socket, 3, 1000),
  465. ok.
  466. alpn_reject_missing_client_preface_settings(Config) ->
  467. doc("Servers must treat an invalid connection preface as a "
  468. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  469. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  470. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  471. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  472. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  473. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  474. %% Receive the server preface.
  475. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  476. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  477. %% We expect the server to close the connection when it receives a bad preface.
  478. {error, closed} = ssl:recv(Socket, 3, 1000),
  479. ok.
  480. alpn_reject_invalid_client_preface_settings(Config) ->
  481. doc("Servers must treat an invalid connection preface as a "
  482. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  483. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  484. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  485. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  486. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  487. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", << 0:24, 4:8, 0:9, 1:31 >>]),
  488. %% Receive the server preface.
  489. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  490. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  491. %% We expect the server to close the connection when it receives a bad preface.
  492. {error, closed} = ssl:recv(Socket, 3, 1000),
  493. ok.
  494. alpn_accept_client_preface_empty_settings(Config) ->
  495. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.3, RFC7540 3.5)"),
  496. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  497. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  498. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  499. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  500. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  501. %% Receive the server preface.
  502. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  503. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  504. %% Receive the SETTINGS ack.
  505. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  506. ok.
  507. alpn_client_preface_settings_ack_timeout(Config) ->
  508. doc("Failure to acknowledge the server's SETTINGS frame "
  509. "results in a SETTINGS_TIMEOUT connection error. (RFC7540 3.5, RFC7540 6.5.3)"),
  510. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  511. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  512. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  513. %% Send a valid preface.
  514. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  515. %% Receive the server preface.
  516. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  517. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  518. %% Receive the SETTINGS ack.
  519. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  520. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  521. {ok, << _:24, 7:8, _:72, 4:32 >>} = ssl:recv(Socket, 17, 6000),
  522. ok.
  523. alpn(Config) ->
  524. doc("Successful ALPN negotiation. (RFC7540 3.3)"),
  525. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  526. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  527. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  528. %% Send a valid preface.
  529. %% @todo Use non-empty SETTINGS here. Just because.
  530. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  531. %% Receive the server preface.
  532. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  533. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  534. %% Send the SETTINGS ack.
  535. ok = ssl:send(Socket, cow_http2:settings_ack()),
  536. %% Receive the SETTINGS ack.
  537. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  538. %% Wait until after the SETTINGS ack timeout was supposed to trigger.
  539. receive after 6000 -> ok end,
  540. %% Send a PING.
  541. ok = ssl:send(Socket, cow_http2:ping(0)),
  542. %% Receive a PING ack back, indicating the connection is still up.
  543. {ok, << 8:24, 6:8, 0:7, 1:1, 0:96 >>} = ssl:recv(Socket, 17, 1000),
  544. ok.
  545. %% Starting HTTP/2 with prior knowledge.
  546. prior_knowledge_reject_tls(Config) ->
  547. doc("Implementations that support HTTP/2 over TLS must use ALPN. (RFC7540 3.4)"),
  548. {ok, Socket} = ssl:connect("localhost", config(port, Config), [binary, {active, false}]),
  549. %% Send a valid preface.
  550. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  551. %% We expect the server to send an HTTP 400 error
  552. %% when trying to use HTTP/2 without going through ALPN negotiation.
  553. {ok, <<"HTTP/1.1 400">>} = ssl:recv(Socket, 12, 1000),
  554. ok.
  555. prior_knowledge_server_preface(Config) ->
  556. doc("The first frame must be a SETTINGS frame "
  557. "for the server connection preface. (RFC7540 3.4, RFC7540 3.5, RFC7540 6.5)"),
  558. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  559. %% Send a valid preface.
  560. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  561. %% Receive the server preface.
  562. {ok, << _:24, 4:8, 0:40 >>} = gen_tcp:recv(Socket, 9, 1000),
  563. ok.
  564. %% Note: the client preface timeout doesn't apply in this case,
  565. %% so we don't test it. An HTTP/1.1 client that does not send
  566. %% a request in a timely manner will get disconnected by the
  567. %% HTTP protocol code, not by HTTP/2's.
  568. %% Note: the test that starts by sending a SETTINGS frame is
  569. %% redundant with tests sending garbage on the connection.
  570. %% From the point of view of an HTTP/1.1 connection, a
  571. %% SETTINGS frame is indistinguishable from garbage.
  572. prior_knowledge_reject_invalid_client_preface(Config) ->
  573. doc("An incorrect preface is an invalid HTTP/1.1 request. (RFC7540 3.4)"),
  574. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  575. %% Send a slightly incorrect preface.
  576. ok = gen_tcp:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  577. %% We propagate to HTTP/2 after checking only the request-line.
  578. %% The server then sends its preface before checking the full client preface.
  579. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  580. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  581. %% We expect the server to close the connection when it receives a bad preface.
  582. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  583. ok.
  584. prior_knowledge_reject_missing_client_preface_settings(Config) ->
  585. doc("Servers must treat an invalid connection preface as a "
  586. "connection error of type PROTOCOL_ERROR. (RFC7540 3.4, RFC7540 3.5)"),
  587. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  588. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  589. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  590. %% Receive the server preface.
  591. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  592. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  593. %% We expect the server to close the connection when it receives a bad preface.
  594. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  595. ok.
  596. prior_knowledge_reject_invalid_client_preface_settings(Config) ->
  597. doc("Servers must treat an invalid connection preface as a "
  598. "connection error of type PROTOCOL_ERROR. (RFC7540 3.4, RFC7540 3.5)"),
  599. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  600. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  601. 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 >>]),
  602. %% Receive the server preface.
  603. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  604. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  605. %% We expect the server to close the connection when it receives a bad preface.
  606. {error, closed} = gen_tcp:recv(Socket, 9, 1000),
  607. ok.
  608. prior_knowledge_accept_client_preface_empty_settings(Config) ->
  609. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.4, RFC7540 3.5)"),
  610. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  611. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  612. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  613. %% Receive the server preface.
  614. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  615. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  616. %% Receive the SETTINGS ack.
  617. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  618. ok.
  619. prior_knowledge_client_preface_settings_ack_timeout(Config) ->
  620. doc("The SETTINGS frames sent by the client must be acknowledged. (RFC7540 3.5, RFC7540 6.5.3)"),
  621. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  622. %% Send a valid preface.
  623. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  624. %% Receive the server preface.
  625. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  626. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  627. %% Receive the SETTINGS ack.
  628. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  629. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  630. {ok, << _:24, 7:8, _:72, 4:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  631. ok.
  632. prior_knowledge(Config) ->
  633. doc("Streams can be initiated after a successful HTTP/2 connection "
  634. "with prior knowledge of server capabilities. (RFC7540 3.4)"),
  635. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  636. %% Send a valid preface.
  637. %% @todo Use non-empty SETTINGS here. Just because.
  638. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  639. %% Receive the server preface.
  640. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  641. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  642. %% Send the SETTINGS ack.
  643. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  644. %% Receive the SETTINGS ack.
  645. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  646. %% Wait until after the SETTINGS ack timeout was supposed to trigger.
  647. receive after 6000 -> ok end,
  648. %% Send a PING.
  649. ok = gen_tcp:send(Socket, cow_http2:ping(0)),
  650. %% Receive a PING ack back, indicating the connection is still up.
  651. {ok, << 8:24, 6:8, 0:7, 1:1, 0:96 >>} = gen_tcp:recv(Socket, 17, 1000),
  652. ok.
  653. %% Tests still need to be added for the following points:
  654. %% @todo how to test this?
  655. %3.2.1
  656. % A server decodes and interprets these values as it would any other
  657. % SETTINGS frame.
  658. %3.2
  659. % @todo (maybe an option to disable HTTP/2?)
  660. % A server that does not support HTTP/2 can respond to the request as
  661. % though the Upgrade header field were absent
  662. %% @todo Do we reject "http" requests over TLS and "https" requests over TCP?
  663. %% Yes, see 421 status code. But maybe some configuration is in order.
  664. %3.5
  665. %% @todo yeah idk
  666. %(if the upgrade failed and the connection send this before 101 then
  667. % we should 400 and close the connection, but not sure how it can fail)
  668. % @todo (maybe an option to disable HTTP/2?)
  669. % The client sends
  670. % the client connection preface immediately upon receipt of a 101
  671. % (Switching Protocols) response (indicating a successful upgrade)
  672. %% @todo
  673. %3.5
  674. %(big mess)
  675. % To avoid unnecessary latency, clients are permitted to send
  676. % additional frames to the server immediately after sending the client
  677. % connection preface, without waiting to receive the server connection
  678. % preface. It is important to note, however, that the server
  679. % connection preface SETTINGS frame might include parameters that
  680. % necessarily alter how a client is expected to communicate with the
  681. % server. Upon receiving the SETTINGS frame, the client is expected to
  682. % honor any parameters established. In some configurations, it is
  683. % possible for the server to transmit SETTINGS before the client sends
  684. % additional frames, providing an opportunity to avoid this issue.