rfc7540_SUITE.erl 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  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: h2\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: h2\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_reject_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: h2\r\n"
  90. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  91. "\r\n"]),
  92. {ok, <<"HTTP/1.1 400">>} = 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: h2\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: h2\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: h2\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. http_upgrade_101(Config) ->
  135. doc("A 101 response must be sent on successful upgrade "
  136. "to HTTP/2 when using the HTTP Upgrade mechanism. (RFC7540 3.2, RFC7230 6.7)"),
  137. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  138. ok = gen_tcp:send(Socket, [
  139. "GET / HTTP/1.1\r\n"
  140. "Host: localhost\r\n"
  141. "Connection: Upgrade, HTTP2-Settings\r\n"
  142. "Upgrade: h2\r\n"
  143. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  144. "\r\n"]),
  145. {ok, <<"HTTP/1.1 101 Switching Protocols\r\n">>} = gen_tcp:recv(Socket, 36, 1000),
  146. ok.
  147. http_upgrade_server_preface(Config) ->
  148. doc("The first frame after the upgrade must be a "
  149. "SETTINGS frame for the server connection preface. (RFC7540 3.2, RFC7540 3.5, RFC7540 6.5)"),
  150. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  151. ok = gen_tcp:send(Socket, [
  152. "GET / HTTP/1.1\r\n"
  153. "Host: localhost\r\n"
  154. "Connection: Upgrade, HTTP2-Settings\r\n"
  155. "Upgrade: h2\r\n"
  156. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  157. "\r\n"]),
  158. {ok, <<"HTTP/1.1 101 Switching Protocols\r\n">>} = gen_tcp:recv(Socket, 36, 1000),
  159. %% Receive the server preface.
  160. {ok, << _:24, 4:8, 0:40 >>} = gen_tcp:recv(Socket, 9, 1000),
  161. ok.
  162. http_upgrade_client_preface_timeout(Config) ->
  163. doc("Clients negotiating HTTP/2 and not sending a preface in "
  164. "a timely manner must be disconnected."),
  165. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  166. ok = gen_tcp:send(Socket, [
  167. "GET / HTTP/1.1\r\n"
  168. "Host: localhost\r\n"
  169. "Connection: Upgrade, HTTP2-Settings\r\n"
  170. "Upgrade: h2\r\n"
  171. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  172. "\r\n"]),
  173. {ok, <<"HTTP/1.1 101 Switching Protocols\r\n">>} = gen_tcp:recv(Socket, 36, 1000),
  174. %% Receive the server preface.
  175. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  176. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  177. %% Do not send the preface. Wait for the server to disconnect us.
  178. {error, closed} = gen_tcp:recv(Socket, 3, 6000),
  179. ok.
  180. http_upgrade_reject_missing_client_preface(Config) ->
  181. doc("Servers must treat an invalid connection preface as a "
  182. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  183. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  184. ok = gen_tcp:send(Socket, [
  185. "GET / HTTP/1.1\r\n"
  186. "Host: localhost\r\n"
  187. "Connection: Upgrade, HTTP2-Settings\r\n"
  188. "Upgrade: h2\r\n"
  189. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  190. "\r\n"]),
  191. {ok, <<"HTTP/1.1 101 Switching Protocols\r\n">>} = gen_tcp:recv(Socket, 36, 1000),
  192. %% Send a SETTINGS frame directly instead of the proper preface.
  193. ok = gen_tcp:send(Socket, cow_http2:settings(#{})),
  194. %% Receive the server preface.
  195. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  196. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  197. %% We expect the server to close the connection when it receives a bad preface.
  198. {error, closed} = gen_tcp:recv(Socket, 3, 1000),
  199. ok.
  200. http_upgrade_reject_invalid_client_preface(Config) ->
  201. doc("Servers must treat an invalid connection preface as a "
  202. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  203. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  204. ok = gen_tcp:send(Socket, [
  205. "GET / HTTP/1.1\r\n"
  206. "Host: localhost\r\n"
  207. "Connection: Upgrade, HTTP2-Settings\r\n"
  208. "Upgrade: h2\r\n"
  209. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  210. "\r\n"]),
  211. {ok, <<"HTTP/1.1 101 Switching Protocols\r\n">>} = gen_tcp:recv(Socket, 36, 1000),
  212. %% Send a slightly incorrect preface.
  213. ok = gen_tcp:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  214. %% Receive the server preface.
  215. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  216. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  217. %% We expect the server to close the connection when it receives a bad preface.
  218. {error, closed} = gen_tcp:recv(Socket, 3, 1000),
  219. ok.
  220. http_upgrade_reject_missing_client_preface_settings(Config) ->
  221. doc("Servers must treat an invalid connection preface as a "
  222. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  223. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  224. ok = gen_tcp:send(Socket, [
  225. "GET / HTTP/1.1\r\n"
  226. "Host: localhost\r\n"
  227. "Connection: Upgrade, HTTP2-Settings\r\n"
  228. "Upgrade: h2\r\n"
  229. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  230. "\r\n"]),
  231. {ok, <<"HTTP/1.1 101 Switching Protocols\r\n">>} = gen_tcp:recv(Socket, 36, 1000),
  232. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  233. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  234. %% Receive the server preface.
  235. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  236. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  237. %% We expect the server to close the connection when it receives a bad preface.
  238. {error, closed} = gen_tcp:recv(Socket, 3, 1000),
  239. ok.
  240. http_upgrade_reject_invalid_client_preface_settings(Config) ->
  241. doc("Servers must treat an invalid connection preface as a "
  242. "connection error of type PROTOCOL_ERROR. (RFC7540 3.2, RFC7540 3.5)"),
  243. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  244. ok = gen_tcp:send(Socket, [
  245. "GET / HTTP/1.1\r\n"
  246. "Host: localhost\r\n"
  247. "Connection: Upgrade, HTTP2-Settings\r\n"
  248. "Upgrade: h2\r\n"
  249. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  250. "\r\n"]),
  251. {ok, <<"HTTP/1.1 101 Switching Protocols\r\n">>} = gen_tcp:recv(Socket, 36, 1000),
  252. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  253. 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 >>]),
  254. %% Receive the server preface.
  255. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  256. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  257. %% We expect the server to close the connection when it receives a bad preface.
  258. {error, closed} = gen_tcp:recv(Socket, 3, 1000),
  259. ok.
  260. http_upgrade_accept_client_preface_empty_settings(Config) ->
  261. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.2, RFC7540 3.5)"),
  262. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  263. ok = gen_tcp:send(Socket, [
  264. "GET / HTTP/1.1\r\n"
  265. "Host: localhost\r\n"
  266. "Connection: Upgrade, HTTP2-Settings\r\n"
  267. "Upgrade: h2\r\n"
  268. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  269. "\r\n"]),
  270. {ok, <<"HTTP/1.1 101 Switching Protocols\r\n">>} = gen_tcp:recv(Socket, 36, 1000),
  271. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  272. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  273. %% Receive the server preface.
  274. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  275. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  276. %% Receive the SETTINGS ack.
  277. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  278. ok.
  279. http_upgrade_client_preface_settings_ack_timeout(Config) ->
  280. doc("The SETTINGS frames sent by the client must be acknowledged. (RFC7540 3.5, RFC7540 6.5.3)"),
  281. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  282. ok = gen_tcp:send(Socket, [
  283. "GET / HTTP/1.1\r\n"
  284. "Host: localhost\r\n"
  285. "Connection: Upgrade, HTTP2-Settings\r\n"
  286. "Upgrade: h2\r\n"
  287. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  288. "\r\n"]),
  289. {ok, <<"HTTP/1.1 101 Switching Protocols\r\n">>} = gen_tcp:recv(Socket, 36, 1000),
  290. %% Send a valid preface.
  291. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  292. %% Receive the server preface.
  293. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  294. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  295. %% Receive the SETTINGS ack.
  296. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  297. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  298. {ok, << _:24, 7:8, _:72, 4:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  299. ok.
  300. %% @todo We need a successful test with actual options in HTTP2-Settings.
  301. %% @todo Also assigned default priority values but not sure how to test that.
  302. http_upgrade_response(Config) ->
  303. doc("A response must be sent to the initial HTTP/1.1 request "
  304. "after switching to HTTP/2. The response must use "
  305. "the stream identifier 1. (RFC7540 3.2)"),
  306. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  307. ok = gen_tcp:send(Socket, [
  308. "GET / HTTP/1.1\r\n"
  309. "Host: localhost\r\n"
  310. "Connection: Upgrade, HTTP2-Settings\r\n"
  311. "Upgrade: h2\r\n"
  312. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  313. "\r\n"]),
  314. {ok, <<"HTTP/1.1 101 Switching Protocols\r\n">>} = gen_tcp:recv(Socket, 36, 1000),
  315. %% Send a valid preface.
  316. %% @todo Use non-empty SETTINGS here. Just because.
  317. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  318. %% Receive the server preface.
  319. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  320. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  321. %% Send the SETTINGS ack.
  322. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  323. %% Receive the SETTINGS ack.
  324. %% @todo It's possible that we receive the response before the SETTINGS ack.
  325. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  326. %% Receive the response to the original request. It uses streamid 1.
  327. {ok, << _:24, 1:8, _:8, 1:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  328. ok.
  329. http_upgrade_response_half_closed(Config) ->
  330. doc("The stream for the initial HTTP/1.1 request is half-closed. (RFC7540 3.2)"),
  331. %% Try sending more data after the upgrade and get an error.
  332. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  333. ok = gen_tcp:send(Socket, [
  334. "GET / HTTP/1.1\r\n"
  335. "Host: localhost\r\n"
  336. "Connection: Upgrade, HTTP2-Settings\r\n"
  337. "Upgrade: h2\r\n"
  338. "HTTP2-Settings: ", base64:encode(cow_http2:settings_payload(#{})), "\r\n",
  339. "\r\n"]),
  340. {ok, <<"HTTP/1.1 101 Switching Protocols\r\n">>} = gen_tcp:recv(Socket, 36, 1000),
  341. %% Send a valid preface.
  342. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  343. %% Send more data on the stream to trigger an error.
  344. ok = gen_tcp:send(Socket, cow_http2:data(1, fin, <<>>)),
  345. %% Receive the server preface.
  346. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  347. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  348. %% Receive the SETTINGS ack.
  349. %% @todo It's possible that we receive the response before the SETTINGS ack or RST_STREAM.
  350. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  351. %% The server resets the stream with reason STREAM_CLOSED.
  352. {ok, << 4:24, 3:8, 0:8, 1:32, 5:32 >>} = gen_tcp:recv(Socket, 13, 1000),
  353. ok.
  354. %% Starting HTTP/2 for "https" URIs.
  355. alpn_ignore_h2c(Config) ->
  356. doc("An h2c ALPN protocol identifier must be ignored. (RFC7540 3.3)"),
  357. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  358. [{alpn_advertised_protocols, [<<"h2c">>, <<"http/1.1">>]}, binary, {active, false}]),
  359. {ok, <<"http/1.1">>} = ssl:negotiated_protocol(Socket),
  360. ok.
  361. alpn_server_preface(Config) ->
  362. doc("The first frame must be a SETTINGS frame "
  363. "for the server connection preface. (RFC7540 3.3, RFC7540 3.5, RFC7540 6.5)"),
  364. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  365. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  366. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  367. %% Receive the server preface.
  368. {ok, << _:24, 4:8, 0:40 >>} = ssl:recv(Socket, 9, 1000),
  369. ok.
  370. alpn_client_preface_timeout(Config) ->
  371. doc("Clients negotiating HTTP/2 and not sending a preface in "
  372. "a timely manner must be disconnected."),
  373. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  374. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  375. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  376. %% Receive the server preface.
  377. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  378. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  379. %% Do not send the preface. Wait for the server to disconnect us.
  380. {error, closed} = ssl:recv(Socket, 3, 6000),
  381. ok.
  382. alpn_reject_missing_client_preface(Config) ->
  383. doc("Servers must treat an invalid connection preface as a "
  384. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  385. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  386. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  387. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  388. %% Send a SETTINGS frame directly instead of the proper preface.
  389. ok = ssl:send(Socket, cow_http2:settings(#{})),
  390. %% Receive the server preface.
  391. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  392. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  393. %% We expect the server to close the connection when it receives a bad preface.
  394. {error, closed} = ssl:recv(Socket, 3, 1000),
  395. ok.
  396. alpn_reject_invalid_client_preface(Config) ->
  397. doc("Servers must treat an invalid connection preface as a "
  398. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  399. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  400. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  401. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  402. %% Send a slightly incorrect preface.
  403. ok = ssl:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  404. %% Receive the server preface.
  405. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  406. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  407. %% We expect the server to close the connection when it receives a bad preface.
  408. {error, closed} = ssl:recv(Socket, 3, 1000),
  409. ok.
  410. alpn_reject_missing_client_preface_settings(Config) ->
  411. doc("Servers must treat an invalid connection preface as a "
  412. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  413. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  414. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  415. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  416. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  417. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:ping(0)]),
  418. %% Receive the server preface.
  419. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  420. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  421. %% We expect the server to close the connection when it receives a bad preface.
  422. {error, closed} = ssl:recv(Socket, 3, 1000),
  423. ok.
  424. alpn_reject_invalid_client_preface_settings(Config) ->
  425. doc("Servers must treat an invalid connection preface as a "
  426. "connection error of type PROTOCOL_ERROR. (RFC7540 3.3, RFC7540 3.5)"),
  427. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  428. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  429. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  430. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  431. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", << 0:24, 4:8, 0:9, 1:31 >>]),
  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. %% We expect the server to close the connection when it receives a bad preface.
  436. {error, closed} = ssl:recv(Socket, 3, 1000),
  437. ok.
  438. alpn_accept_client_preface_empty_settings(Config) ->
  439. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.3, RFC7540 3.5)"),
  440. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  441. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  442. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  443. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  444. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  445. %% Receive the server preface.
  446. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  447. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  448. %% Receive the SETTINGS ack.
  449. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  450. ok.
  451. alpn_client_preface_settings_ack_timeout(Config) ->
  452. doc("Failure to acknowledge the server's SETTINGS frame "
  453. "results in a SETTINGS_TIMEOUT connection error. (RFC7540 3.5, RFC7540 6.5.3)"),
  454. {ok, Socket} = ssl:connect("localhost", config(port, Config),
  455. [{alpn_advertised_protocols, [<<"h2">>]}, binary, {active, false}]),
  456. {ok, <<"h2">>} = ssl:negotiated_protocol(Socket),
  457. %% Send a valid preface.
  458. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  459. %% Receive the server preface.
  460. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  461. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  462. %% Receive the SETTINGS ack.
  463. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  464. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  465. {ok, << _:24, 7:8, _:72, 4:32 >>} = ssl:recv(Socket, 17, 6000),
  466. ok.
  467. alpn(Config) ->
  468. doc("Successful ALPN negotiation. (RFC7540 3.3)"),
  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.
  473. %% @todo Use non-empty SETTINGS here. Just because.
  474. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  475. %% Receive the server preface.
  476. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  477. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  478. %% Send the SETTINGS ack.
  479. ok = ssl:send(Socket, cow_http2:settings_ack()),
  480. %% Receive the SETTINGS ack.
  481. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  482. %% Wait until after the SETTINGS ack timeout was supposed to trigger.
  483. receive after 6000 -> ok end,
  484. %% Send a PING.
  485. ok = ssl:send(Socket, cow_http2:ping(0)),
  486. %% Receive a PING ack back, indicating the connection is still up.
  487. {ok, << 8:24, 6:8, 0:7, 1:1, 0:96 >>} = ssl:recv(Socket, 17, 1000),
  488. ok.
  489. %% Starting HTTP/2 with prior knowledge.
  490. prior_knowledge_reject_tls(Config) ->
  491. doc("Implementations that support HTTP/2 over TLS must use ALPN. (RFC7540 3.4)"),
  492. {ok, Socket} = ssl:connect("localhost", config(port, Config), [binary, {active, false}]),
  493. %% Send a valid preface.
  494. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  495. %% We expect the server to send an HTTP 400 error
  496. %% when trying to use HTTP/2 without going through ALPN negotiation.
  497. {ok, <<"HTTP/1.1 400">>} = ssl:recv(Socket, 12, 1000),
  498. ok.
  499. prior_knowledge_server_preface(Config) ->
  500. doc("The first frame must be a SETTINGS frame "
  501. "for the server connection preface. (RFC7540 3.4, RFC7540 3.5, RFC7540 6.5)"),
  502. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  503. %% Send a valid preface.
  504. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  505. %% Receive the server preface.
  506. {ok, << _:24, 4:8, 0:40 >>} = gen_tcp:recv(Socket, 9, 1000),
  507. ok.
  508. %% Note: the client preface timeout doesn't apply in this case,
  509. %% so we don't test it. An HTTP/1.1 client that does not send
  510. %% a request in a timely manner will get disconnected by the
  511. %% HTTP protocol code, not by HTTP/2's.
  512. %% Note: the test that starts by sending a SETTINGS frame is
  513. %% redundant with tests sending garbage on the connection.
  514. %% From the point of view of an HTTP/1.1 connection, a
  515. %% SETTINGS frame is indistinguishable from garbage.
  516. prior_knowledge_reject_invalid_client_preface(Config) ->
  517. doc("An incorrect preface is an invalid HTTP/1.1 request. (RFC7540 3.4)"),
  518. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  519. %% Send a slightly incorrect preface.
  520. ok = gen_tcp:send(Socket, "PRI * HTTP/2.0\r\n\r\nSM: Value\r\n\r\n"),
  521. %% We propagate to HTTP/2 after checking only the request-line.
  522. %% The server then sends its preface before checking the full client preface.
  523. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  524. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  525. %% We expect the server to close the connection when it receives a bad preface.
  526. {error, closed} = gen_tcp:recv(Socket, 3, 1000),
  527. ok.
  528. prior_knowledge_reject_missing_client_preface_settings(Config) ->
  529. doc("Servers must treat an invalid connection preface as a "
  530. "connection error of type PROTOCOL_ERROR. (RFC7540 3.4, RFC7540 3.5)"),
  531. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  532. %% Send a valid preface sequence except followed by a PING instead of a SETTINGS frame.
  533. ok = gen_tcp: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 >>} = gen_tcp:recv(Socket, 3, 1000),
  536. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  537. %% We expect the server to close the connection when it receives a bad preface.
  538. {error, closed} = gen_tcp:recv(Socket, 3, 1000),
  539. ok.
  540. prior_knowledge_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.4, RFC7540 3.5)"),
  543. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  544. %% Send a valid preface sequence except followed by a badly formed SETTINGS frame.
  545. 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 >>]),
  546. %% Receive the server preface.
  547. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  548. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  549. %% We expect the server to close the connection when it receives a bad preface.
  550. {error, closed} = gen_tcp:recv(Socket, 3, 1000),
  551. ok.
  552. prior_knowledge_accept_client_preface_empty_settings(Config) ->
  553. doc("The SETTINGS frame in the client preface may be empty. (RFC7540 3.4, RFC7540 3.5)"),
  554. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  555. %% Send a valid preface sequence except followed by an empty SETTINGS frame.
  556. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  557. %% Receive the server preface.
  558. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  559. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  560. %% Receive the SETTINGS ack.
  561. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  562. ok.
  563. prior_knowledge_client_preface_settings_ack_timeout(Config) ->
  564. doc("The SETTINGS frames sent by the client must be acknowledged. (RFC7540 3.5, RFC7540 6.5.3)"),
  565. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  566. %% Send a valid preface.
  567. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  568. %% Receive the server preface.
  569. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  570. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  571. %% Receive the SETTINGS ack.
  572. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  573. %% Do not ack the server preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
  574. {ok, << _:24, 7:8, _:72, 4:32 >>} = gen_tcp:recv(Socket, 17, 6000),
  575. ok.
  576. prior_knowledge(Config) ->
  577. doc("Streams can be initiated after a successful HTTP/2 connection "
  578. "with prior knowledge of server capabilities. (RFC7540 3.4)"),
  579. {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),
  580. %% Send a valid preface.
  581. %% @todo Use non-empty SETTINGS here. Just because.
  582. ok = gen_tcp:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  583. %% Receive the server preface.
  584. {ok, << Len:24 >>} = gen_tcp:recv(Socket, 3, 1000),
  585. {ok, << 4:8, 0:40, _:Len/binary >>} = gen_tcp:recv(Socket, 6 + Len, 1000),
  586. %% Send the SETTINGS ack.
  587. ok = gen_tcp:send(Socket, cow_http2:settings_ack()),
  588. %% Receive the SETTINGS ack.
  589. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = gen_tcp:recv(Socket, 9, 1000),
  590. %% Wait until after the SETTINGS ack timeout was supposed to trigger.
  591. receive after 6000 -> ok end,
  592. %% Send a PING.
  593. ok = gen_tcp:send(Socket, cow_http2:ping(0)),
  594. %% Receive a PING ack back, indicating the connection is still up.
  595. {ok, << 8:24, 6:8, 0:7, 1:1, 0:96 >>} = gen_tcp:recv(Socket, 17, 1000),
  596. ok.
  597. %% Tests still need to be added for the following points:
  598. %% @todo how to test this?
  599. %3.2.1
  600. % A server decodes and interprets these values as it would any other
  601. % SETTINGS frame.
  602. %3.2
  603. % @todo (maybe an option to disable HTTP/2?)
  604. % A server that does not support HTTP/2 can respond to the request as
  605. % though the Upgrade header field were absent
  606. %% @todo Do we reject "http" requests over TLS and "https" requests over TCP?
  607. %% Yes, see 421 status code. But maybe some configuration is in order.
  608. %3.5
  609. %% @todo yeah idk
  610. %(if the upgrade failed and the connection send this before 101 then
  611. % we should 400 and close the connection, but not sure how it can fail)
  612. % @todo (maybe an option to disable HTTP/2?)
  613. % The client sends
  614. % the client connection preface immediately upon receipt of a 101
  615. % (Switching Protocols) response (indicating a successful upgrade)
  616. %% @todo
  617. %3.5
  618. %(big mess)
  619. % To avoid unnecessary latency, clients are permitted to send
  620. % additional frames to the server immediately after sending the client
  621. % connection preface, without waiting to receive the server connection
  622. % preface. It is important to note, however, that the server
  623. % connection preface SETTINGS frame might include parameters that
  624. % necessarily alter how a client is expected to communicate with the
  625. % server. Upon receiving the SETTINGS frame, the client is expected to
  626. % honor any parameters established. In some configurations, it is
  627. % possible for the server to transmit SETTINGS before the client sends
  628. % additional frames, providing an opportunity to avoid this issue.