sys_SUITE.erl 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444
  1. %% Copyright (c) 2018, 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(sys_SUITE).
  15. -compile(export_all).
  16. -compile(nowarn_export_all).
  17. -import(ct_helper, [config/2]).
  18. -import(ct_helper, [doc/1]).
  19. -import(ct_helper, [get_parent_pid/1]).
  20. -import(ct_helper, [get_remote_pid_tcp/1]).
  21. -import(ct_helper, [get_remote_pid_tls/1]).
  22. -import(ct_helper, [is_process_down/1]).
  23. -import(cowboy_test, [gun_open/1]).
  24. all() ->
  25. [{group, sys}].
  26. groups() ->
  27. [{sys, [parallel], ct_helper:all(?MODULE)}].
  28. init_per_suite(Config) ->
  29. ProtoOpts = #{
  30. env => #{dispatch => init_dispatch(Config)},
  31. logger => ?MODULE
  32. },
  33. %% Clear listener.
  34. {ok, _} = cowboy:start_clear(clear, [{port, 0}], ProtoOpts),
  35. ClearPort = ranch:get_port(clear),
  36. %% TLS listener.
  37. TLSOpts = ct_helper:get_certs_from_ets(),
  38. {ok, _} = cowboy:start_tls(tls, TLSOpts ++ [{port, 0}], ProtoOpts),
  39. TLSPort = ranch:get_port(tls),
  40. [
  41. {clear_port, ClearPort},
  42. %% @todo Add the h2 stuff to the opts.
  43. {tls_opts, TLSOpts},
  44. {tls_port, TLSPort}
  45. |Config].
  46. end_per_suite(_) ->
  47. ok = cowboy:stop_listener(clear),
  48. ok = cowboy:stop_listener(tls).
  49. init_dispatch(_) ->
  50. cowboy_router:compile([{"[...]", [
  51. {"/", hello_h, []},
  52. {"/loop", long_polling_sys_h, []},
  53. {"/ws", ws_echo, []}
  54. ]}]).
  55. %% Logger function silencing the expected warnings.
  56. error(Format, Args) ->
  57. error_logger:error_msg(Format, Args).
  58. warning("Received EXIT signal " ++ _, [{'EXIT', _, {shutdown, ?MODULE}}|_]) ->
  59. ok;
  60. warning(Format, Args) ->
  61. error_logger:warning_msg(Format, Args).
  62. %% proc_lib.
  63. proc_lib_initial_call_clear(Config) ->
  64. doc("Confirm that clear connection processes are started using proc_lib."),
  65. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), []),
  66. timer:sleep(100),
  67. Pid = get_remote_pid_tcp(Socket),
  68. {cowboy_clear, _, _} = proc_lib:initial_call(Pid),
  69. ok.
  70. proc_lib_initial_call_tls(Config) ->
  71. doc("Confirm that TLS connection processes are started using proc_lib."),
  72. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config), config(tls_opts, Config)),
  73. timer:sleep(100),
  74. Pid = get_remote_pid_tls(Socket),
  75. {cowboy_tls, _, _} = proc_lib:initial_call(Pid),
  76. ok.
  77. %% System messages.
  78. %%
  79. %% Plain system messages are received as {system, From, Msg}.
  80. %% The content and meaning of this message are not interpreted by
  81. %% the receiving process module. When a system message is received,
  82. %% function handle_system_msg/6 is called to handle the request.
  83. bad_system_from_h1(Config) ->
  84. doc("h1: Sending a system message with a bad From value results in a process crash."),
  85. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  86. timer:sleep(100),
  87. Pid = get_remote_pid_tcp(Socket),
  88. ct_helper_error_h:ignore(Pid, gen, reply, 2),
  89. Pid ! {system, bad, get_state},
  90. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  91. false = is_process_alive(Pid),
  92. ok.
  93. bad_system_from_h2(Config) ->
  94. doc("h2: Sending a system message with a bad From value results in a process crash."),
  95. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  96. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  97. %% Skip the SETTINGS frame.
  98. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  99. timer:sleep(100),
  100. Pid = get_remote_pid_tls(Socket),
  101. ct_helper_error_h:ignore(Pid, gen, reply, 2),
  102. Pid ! {system, bad, get_state},
  103. {error, closed} = ssl:recv(Socket, 0, 1000),
  104. false = is_process_alive(Pid),
  105. ok.
  106. bad_system_from_ws(Config) ->
  107. doc("ws: Sending a system message with a bad From value results in a process crash."),
  108. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  109. [binary, {active, false}]),
  110. ok = gen_tcp:send(Socket,
  111. "GET /ws HTTP/1.1\r\n"
  112. "Host: localhost\r\n"
  113. "Connection: Upgrade\r\n"
  114. "Origin: http://localhost\r\n"
  115. "Sec-WebSocket-Version: 13\r\n"
  116. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  117. "Upgrade: websocket\r\n"
  118. "\r\n"),
  119. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  120. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  121. timer:sleep(100),
  122. Pid = get_remote_pid_tcp(Socket),
  123. ct_helper_error_h:ignore(Pid, gen, reply, 2),
  124. Pid ! {system, bad, get_state},
  125. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  126. false = is_process_alive(Pid),
  127. ok.
  128. bad_system_from_loop(Config) ->
  129. doc("loop: Sending a system message with a bad From value results in a process crash."),
  130. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  131. ok = gen_tcp:send(Socket,
  132. "GET /loop HTTP/1.1\r\n"
  133. "Host: localhost\r\n"
  134. "\r\n"),
  135. timer:sleep(100),
  136. SupPid = get_remote_pid_tcp(Socket),
  137. [{_, Pid, _, _}] = supervisor:which_children(SupPid),
  138. ct_helper_error_h:ignore(Pid, gen, reply, 2),
  139. Pid ! {system, bad, get_state},
  140. {ok, "HTTP/1.1 500 "} = gen_tcp:recv(Socket, 13, 1000),
  141. false = is_process_alive(Pid),
  142. ok.
  143. bad_system_message_h1(Config) ->
  144. doc("h1: Sending a system message with a bad Request value results in an error."),
  145. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), []),
  146. timer:sleep(100),
  147. Pid = get_remote_pid_tcp(Socket),
  148. Ref = make_ref(),
  149. Pid ! {system, {self(), Ref}, hello},
  150. receive
  151. {Ref, {error, {unknown_system_msg, hello}}} ->
  152. ok
  153. after 1000 ->
  154. error(timeout)
  155. end.
  156. bad_system_message_h2(Config) ->
  157. doc("h2: Sending a system message with a bad Request value results in an error."),
  158. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  159. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  160. %% Skip the SETTINGS frame.
  161. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  162. timer:sleep(100),
  163. Pid = get_remote_pid_tls(Socket),
  164. Ref = make_ref(),
  165. Pid ! {system, {self(), Ref}, hello},
  166. receive
  167. {Ref, {error, {unknown_system_msg, hello}}} ->
  168. ok
  169. after 1000 ->
  170. error(timeout)
  171. end.
  172. bad_system_message_ws(Config) ->
  173. doc("ws: Sending a system message with a bad Request value results in an error."),
  174. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  175. [binary, {active, false}]),
  176. ok = gen_tcp:send(Socket,
  177. "GET /ws HTTP/1.1\r\n"
  178. "Host: localhost\r\n"
  179. "Connection: Upgrade\r\n"
  180. "Origin: http://localhost\r\n"
  181. "Sec-WebSocket-Version: 13\r\n"
  182. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  183. "Upgrade: websocket\r\n"
  184. "\r\n"),
  185. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  186. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  187. timer:sleep(100),
  188. Pid = get_remote_pid_tcp(Socket),
  189. Ref = make_ref(),
  190. Pid ! {system, {self(), Ref}, hello},
  191. receive
  192. {Ref, {error, {unknown_system_msg, hello}}} ->
  193. ok
  194. after 1000 ->
  195. error(timeout)
  196. end.
  197. bad_system_message_loop(Config) ->
  198. doc("loop: Sending a system message with a bad Request value results in an error."),
  199. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  200. ok = gen_tcp:send(Socket,
  201. "GET /loop HTTP/1.1\r\n"
  202. "Host: localhost\r\n"
  203. "\r\n"),
  204. timer:sleep(100),
  205. SupPid = get_remote_pid_tcp(Socket),
  206. [{_, Pid, _, _}] = supervisor:which_children(SupPid),
  207. Ref = make_ref(),
  208. Pid ! {system, {self(), Ref}, hello},
  209. receive
  210. {Ref, {error, {unknown_system_msg, hello}}} ->
  211. ok
  212. after 1000 ->
  213. error(timeout)
  214. end.
  215. good_system_message_h1(Config) ->
  216. doc("h1: System messages are handled properly."),
  217. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), []),
  218. timer:sleep(100),
  219. Pid = get_remote_pid_tcp(Socket),
  220. Ref = make_ref(),
  221. Pid ! {system, {self(), Ref}, get_state},
  222. receive
  223. {Ref, Result} when element(1, Result) =/= error ->
  224. ok
  225. after 1000 ->
  226. error(timeout)
  227. end.
  228. good_system_message_h2(Config) ->
  229. doc("h2: System messages are handled properly."),
  230. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  231. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  232. %% Skip the SETTINGS frame.
  233. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  234. timer:sleep(100),
  235. Pid = get_remote_pid_tls(Socket),
  236. Ref = make_ref(),
  237. Pid ! {system, {self(), Ref}, get_state},
  238. receive
  239. {Ref, Result} when element(1, Result) =/= error ->
  240. ok
  241. after 1000 ->
  242. error(timeout)
  243. end.
  244. good_system_message_ws(Config) ->
  245. doc("ws: System messages are handled properly."),
  246. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  247. [binary, {active, false}]),
  248. ok = gen_tcp:send(Socket,
  249. "GET /ws HTTP/1.1\r\n"
  250. "Host: localhost\r\n"
  251. "Connection: Upgrade\r\n"
  252. "Origin: http://localhost\r\n"
  253. "Sec-WebSocket-Version: 13\r\n"
  254. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  255. "Upgrade: websocket\r\n"
  256. "\r\n"),
  257. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  258. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  259. timer:sleep(100),
  260. Pid = get_remote_pid_tcp(Socket),
  261. Ref = make_ref(),
  262. Pid ! {system, {self(), Ref}, get_state},
  263. receive
  264. {Ref, Result} when element(1, Result) =/= error ->
  265. ok
  266. after 1000 ->
  267. error(timeout)
  268. end.
  269. good_system_message_loop(Config) ->
  270. doc("loop: System messages are handled properly."),
  271. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  272. ok = gen_tcp:send(Socket,
  273. "GET /loop HTTP/1.1\r\n"
  274. "Host: localhost\r\n"
  275. "\r\n"),
  276. timer:sleep(100),
  277. SupPid = get_remote_pid_tcp(Socket),
  278. [{_, Pid, _, _}] = supervisor:which_children(SupPid),
  279. Ref = make_ref(),
  280. Pid ! {system, {self(), Ref}, get_state},
  281. receive
  282. {Ref, Result} when element(1, Result) =/= error ->
  283. ok
  284. after 1000 ->
  285. error(timeout)
  286. end.
  287. %% 'EXIT'.
  288. %%
  289. %% Shutdown messages. If the process traps exits, it must be able
  290. %% to handle a shutdown request from its parent, the supervisor.
  291. %% The message {'EXIT', Parent, Reason} from the parent is an order
  292. %% to terminate. The process must terminate when this message is
  293. %% received, normally with the same Reason as Parent.
  294. trap_exit_parent_exit_h1(Config) ->
  295. doc("h1: A process trapping exits must stop when receiving "
  296. "an 'EXIT' message from its parent."),
  297. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  298. [{active, false}]),
  299. timer:sleep(100),
  300. Pid = get_remote_pid_tcp(Socket),
  301. Parent = get_parent_pid(Pid),
  302. Pid ! {'EXIT', Parent, {shutdown, ?MODULE}},
  303. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  304. true = is_process_down(Pid),
  305. ok.
  306. trap_exit_parent_exit_h2(Config) ->
  307. doc("h2: A process trapping exits must stop when receiving "
  308. "an 'EXIT' message from its parent."),
  309. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  310. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  311. %% Skip the SETTINGS frame.
  312. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  313. timer:sleep(100),
  314. Pid = get_remote_pid_tls(Socket),
  315. Parent = get_parent_pid(Pid),
  316. Pid ! {'EXIT', Parent, {shutdown, ?MODULE}},
  317. {error, closed} = ssl:recv(Socket, 0, 1000),
  318. true = is_process_down(Pid),
  319. ok.
  320. trap_exit_parent_exit_ws(Config) ->
  321. doc("ws: A process trapping exits must stop when receiving "
  322. "an 'EXIT' message from its parent."),
  323. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  324. [binary, {active, false}]),
  325. ok = gen_tcp:send(Socket,
  326. "GET /ws HTTP/1.1\r\n"
  327. "Host: localhost\r\n"
  328. "Connection: Upgrade\r\n"
  329. "Origin: http://localhost\r\n"
  330. "Sec-WebSocket-Version: 13\r\n"
  331. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  332. "Upgrade: websocket\r\n"
  333. "\r\n"),
  334. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  335. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  336. timer:sleep(100),
  337. Pid = get_remote_pid_tcp(Socket),
  338. Parent = get_parent_pid(Pid),
  339. Pid ! {'EXIT', Parent, {shutdown, ?MODULE}},
  340. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  341. true = is_process_down(Pid),
  342. ok.
  343. trap_exit_parent_exit_loop(Config) ->
  344. doc("loop: A process trapping exits must stop when receiving "
  345. "an 'EXIT' message from its parent."),
  346. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  347. ok = gen_tcp:send(Socket,
  348. "GET /loop HTTP/1.1\r\n"
  349. "Host: localhost\r\n"
  350. "\r\n"),
  351. timer:sleep(100),
  352. Parent = get_remote_pid_tcp(Socket),
  353. [{_, Pid, _, _}] = supervisor:which_children(Parent),
  354. Pid ! {'EXIT', Parent, {shutdown, ?MODULE}},
  355. %% We exit normally but didn't send a response.
  356. {ok, "HTTP/1.1 204 "} = gen_tcp:recv(Socket, 13, 1000),
  357. true = is_process_down(Pid),
  358. ok.
  359. trap_exit_other_exit_h1(Config) ->
  360. doc("h1: A process trapping exits must ignore "
  361. "'EXIT' messages from unknown processes."),
  362. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  363. [{active, false}]),
  364. timer:sleep(100),
  365. Pid = get_remote_pid_tcp(Socket),
  366. Pid ! {'EXIT', self(), {shutdown, ?MODULE}},
  367. ok = gen_tcp:send(Socket,
  368. "GET / HTTP/1.1\r\n"
  369. "Host: localhost\r\n"
  370. "\r\n"),
  371. {ok, "HTTP/1.1 200 "} = gen_tcp:recv(Socket, 13, 1000),
  372. true = is_process_alive(Pid),
  373. ok.
  374. trap_exit_other_exit_h2(Config) ->
  375. doc("h2: A process trapping exits must ignore "
  376. "'EXIT' messages from unknown processes."),
  377. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  378. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  379. %% Do the handshake.
  380. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  381. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  382. ok = ssl:send(Socket, cow_http2:settings_ack()),
  383. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  384. timer:sleep(100),
  385. Pid = get_remote_pid_tls(Socket),
  386. Pid ! {'EXIT', self(), {shutdown, ?MODULE}},
  387. %% Send a HEADERS frame as a request.
  388. {HeadersBlock, _} = cow_hpack:encode([
  389. {<<":method">>, <<"GET">>},
  390. {<<":scheme">>, <<"https">>},
  391. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  392. {<<":path">>, <<"/">>}
  393. ]),
  394. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  395. %% Receive a HEADERS frame as a response.
  396. {ok, << _:24, 1:8, _:40 >>} = ssl:recv(Socket, 9, 6000),
  397. true = is_process_alive(Pid),
  398. ok.
  399. trap_exit_other_exit_ws(Config) ->
  400. doc("ws: A process trapping exits must ignore "
  401. "'EXIT' messages from unknown processes."),
  402. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  403. [binary, {active, false}]),
  404. ok = gen_tcp:send(Socket,
  405. "GET /ws HTTP/1.1\r\n"
  406. "Host: localhost\r\n"
  407. "Connection: Upgrade\r\n"
  408. "Origin: http://localhost\r\n"
  409. "Sec-WebSocket-Version: 13\r\n"
  410. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  411. "Upgrade: websocket\r\n"
  412. "\r\n"),
  413. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  414. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  415. timer:sleep(100),
  416. Pid = get_remote_pid_tcp(Socket),
  417. Pid ! {'EXIT', self(), {shutdown, ?MODULE}},
  418. %% The process stays alive.
  419. {error, timeout} = gen_tcp:recv(Socket, 0, 1000),
  420. true = is_process_alive(Pid),
  421. ok.
  422. trap_exit_other_exit_loop(Config) ->
  423. doc("loop: A process trapping exits must ignore "
  424. "'EXIT' messages from unknown processes."),
  425. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  426. ok = gen_tcp:send(Socket,
  427. "GET /loop HTTP/1.1\r\n"
  428. "Host: localhost\r\n"
  429. "\r\n"),
  430. timer:sleep(100),
  431. Parent = get_remote_pid_tcp(Socket),
  432. [{_, Pid, _, _}] = supervisor:which_children(Parent),
  433. Pid ! {'EXIT', self(), {shutdown, ?MODULE}},
  434. %% The process stays alive.
  435. {ok, "HTTP/1.1 299 "} = gen_tcp:recv(Socket, 13, 1000),
  436. true = is_process_alive(Pid),
  437. ok.
  438. %% get_modules.
  439. %%
  440. %% If the modules used to implement the process change dynamically
  441. %% during runtime, the process must understand one more message.
  442. %% An example is the gen_event processes. The message is
  443. %% {_Label, {From, Ref}, get_modules}. The reply to this message is
  444. %% From ! {Ref, Modules}, where Modules is a list of the currently
  445. %% active modules in the process.
  446. %%
  447. %% For example:
  448. %%
  449. %% 1> application:start(sasl).
  450. %% ok
  451. %% 2> gen:call(alarm_handler, self(), get_modules).
  452. %% {ok,[alarm_handler]}
  453. %% 3> whereis(alarm_handler) ! {'$gen', {self(), make_ref()}, get_modules}.
  454. %% {'$gen',{<0.61.0>,#Ref<0.2900144977.374865921.142102>},
  455. %% get_modules}
  456. %% 4> flush().
  457. %% Shell got {#Ref<0.2900144977.374865921.142102>,[alarm_handler]}
  458. %%
  459. %% Cowboy's connection processes change dynamically: it starts with
  460. %% cowboy_clear or cowboy_tls, then becomes cowboy_http or cowboy_http2
  461. %% and may then become or involve cowboy_websocket. On top of that
  462. %% it has various callback modules in the form of stream handlers.
  463. %% @todo
  464. %get_modules_h1(Config) ->
  465. %get_modules_h2(Config) ->
  466. %get_modules_ws(Config) ->
  467. %get_modules_loop(Config) ->
  468. %% @todo On top of this we will want to make the supervisor calls
  469. %% in ranch_conns_sup return dynamic instead of a list of modules.
  470. %% sys:change_code/4,5.
  471. %%
  472. %% We do not actually change the module code, we just ensure that
  473. %% calling this function does not crash the process. The function
  474. %% Module:system_code_change/4 will be called within the process.
  475. sys_change_code_h1(Config) ->
  476. doc("h1: The sys:change_code/4 function works as expected."),
  477. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  478. timer:sleep(100),
  479. Pid = get_remote_pid_tcp(Socket),
  480. ok = sys:suspend(Pid),
  481. ok = gen_tcp:send(Socket,
  482. "GET / HTTP/1.1\r\n"
  483. "Host: localhost\r\n"
  484. "\r\n"),
  485. {error, timeout} = gen_tcp:recv(Socket, 13, 500),
  486. ok = sys:change_code(Pid, cowboy_http, undefined, undefined),
  487. ok = sys:resume(Pid),
  488. {ok, "HTTP/1.1 200 "} = gen_tcp:recv(Socket, 13, 500),
  489. ok.
  490. sys_change_code_h2(Config) ->
  491. doc("h2: The sys:change_code/4 function works as expected."),
  492. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  493. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  494. timer:sleep(100),
  495. Pid = get_remote_pid_tls(Socket),
  496. %% Send a valid preface.
  497. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  498. %% Receive the server preface.
  499. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  500. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  501. %% Send the SETTINGS ack.
  502. ok = ssl:send(Socket, cow_http2:settings_ack()),
  503. %% Receive the SETTINGS ack.
  504. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  505. %% Suspend the process and try to get a request in. The
  506. %% response will not come back until we resume the process.
  507. ok = sys:suspend(Pid),
  508. {HeadersBlock, _} = cow_hpack:encode([
  509. {<<":method">>, <<"GET">>},
  510. {<<":scheme">>, <<"http">>},
  511. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  512. {<<":path">>, <<"/">>}
  513. ]),
  514. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  515. %% Receive a HEADERS frame as a response.
  516. {error, timeout} = ssl:recv(Socket, 9, 500),
  517. ok = sys:change_code(Pid, cowboy_http2, undefined, undefined),
  518. ok = sys:resume(Pid),
  519. {ok, << _:24, 1:8, _:40 >>} = ssl:recv(Socket, 9, 6000),
  520. ok.
  521. sys_change_code_ws(Config) ->
  522. doc("ws: The sys:change_code/4 function works as expected."),
  523. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  524. [binary, {active, false}]),
  525. ok = gen_tcp:send(Socket,
  526. "GET /ws HTTP/1.1\r\n"
  527. "Host: localhost\r\n"
  528. "Connection: Upgrade\r\n"
  529. "Origin: http://localhost\r\n"
  530. "Sec-WebSocket-Version: 13\r\n"
  531. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  532. "Upgrade: websocket\r\n"
  533. "\r\n"),
  534. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  535. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  536. timer:sleep(100),
  537. Pid = get_remote_pid_tcp(Socket),
  538. ok = sys:suspend(Pid),
  539. Mask = 16#37fa213d,
  540. MaskedHello = ws_SUITE:do_mask(<<"Hello">>, Mask, <<>>),
  541. ok = gen_tcp:send(Socket, << 1:1, 0:3, 1:4, 1:1, 5:7, Mask:32, MaskedHello/binary >>),
  542. {error, timeout} = gen_tcp:recv(Socket, 0, 500),
  543. ok = sys:change_code(Pid, cowboy_websocket, undefined, undefined),
  544. ok = sys:resume(Pid),
  545. {ok, << 1:1, 0:3, 1:4, 0:1, 5:7, "Hello" >>} = gen_tcp:recv(Socket, 0, 6000),
  546. ok.
  547. sys_change_code_loop(Config) ->
  548. doc("loop: The sys:change_code/4 function works as expected."),
  549. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  550. ok = gen_tcp:send(Socket,
  551. "GET /loop HTTP/1.1\r\n"
  552. "Host: localhost\r\n"
  553. "\r\n"),
  554. timer:sleep(100),
  555. SupPid = get_remote_pid_tcp(Socket),
  556. [{_, Pid, _, _}] = supervisor:which_children(SupPid),
  557. %% The process sends a response 500ms after initializing.
  558. %% We expect to not receive it until we resume it.
  559. ok = sys:suspend(Pid),
  560. {error, timeout} = gen_tcp:recv(Socket, 13, 1000),
  561. ok = sys:change_code(Pid, cowboy_loop, undefined, undefined),
  562. ok = sys:resume(Pid),
  563. {ok, "HTTP/1.1 299 "} = gen_tcp:recv(Socket, 13, 500),
  564. ok.
  565. %% sys:get_state/1,2.
  566. %%
  567. %% None of the modules implement Module:system_get_state/1
  568. %% at this time so sys:get_state/1,2 returns the Misc value.
  569. sys_get_state_h1(Config) ->
  570. doc("h1: The sys:get_state/1 function works as expected."),
  571. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), []),
  572. timer:sleep(100),
  573. Pid = get_remote_pid_tcp(Socket),
  574. {State, Buffer} = sys:get_state(Pid),
  575. state = element(1, State),
  576. true = is_binary(Buffer),
  577. ok.
  578. sys_get_state_h2(Config) ->
  579. doc("h2: The sys:get_state/1 function works as expected."),
  580. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  581. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  582. %% Skip the SETTINGS frame.
  583. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  584. timer:sleep(100),
  585. Pid = get_remote_pid_tls(Socket),
  586. {State, Buffer} = sys:get_state(Pid),
  587. state = element(1, State),
  588. true = is_binary(Buffer),
  589. ok.
  590. sys_get_state_ws(Config) ->
  591. doc("ws: The sys:get_state/1 function works as expected."),
  592. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  593. [binary, {active, false}]),
  594. ok = gen_tcp:send(Socket,
  595. "GET /ws HTTP/1.1\r\n"
  596. "Host: localhost\r\n"
  597. "Connection: Upgrade\r\n"
  598. "Origin: http://localhost\r\n"
  599. "Sec-WebSocket-Version: 13\r\n"
  600. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  601. "Upgrade: websocket\r\n"
  602. "\r\n"),
  603. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  604. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  605. timer:sleep(100),
  606. Pid = get_remote_pid_tcp(Socket),
  607. {State, undefined, ParseState} = sys:get_state(Pid),
  608. state = element(1, State),
  609. case element(1, ParseState) of
  610. ps_header -> ok;
  611. ps_payload -> ok
  612. end.
  613. sys_get_state_loop(Config) ->
  614. doc("loop: The sys:get_state/1 function works as expected."),
  615. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  616. ok = gen_tcp:send(Socket,
  617. "GET /loop HTTP/1.1\r\n"
  618. "Host: localhost\r\n"
  619. "\r\n"),
  620. timer:sleep(100),
  621. SupPid = get_remote_pid_tcp(Socket),
  622. [{_, Pid, _, _}] = supervisor:which_children(SupPid),
  623. {Req, Env, long_polling_sys_h, undefined} = sys:get_state(Pid),
  624. #{pid := _, streamid := _} = Req,
  625. #{dispatch := _} = Env,
  626. ok.
  627. %% sys:get_status/1,2.
  628. sys_get_status_h1(Config) ->
  629. doc("h1: The sys:get_status/1 function works as expected."),
  630. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), []),
  631. timer:sleep(100),
  632. Pid = get_remote_pid_tcp(Socket),
  633. {status, Pid, {module, cowboy_http}, _} = sys:get_status(Pid),
  634. ok.
  635. sys_get_status_h2(Config) ->
  636. doc("h2: The sys:get_status/1 function works as expected."),
  637. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  638. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  639. %% Skip the SETTINGS frame.
  640. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  641. timer:sleep(100),
  642. Pid = get_remote_pid_tls(Socket),
  643. {status, Pid, {module, cowboy_http2}, _} = sys:get_status(Pid),
  644. ok.
  645. sys_get_status_ws(Config) ->
  646. doc("ws: The sys:get_status/1 function works as expected."),
  647. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  648. [binary, {active, false}]),
  649. ok = gen_tcp:send(Socket,
  650. "GET /ws HTTP/1.1\r\n"
  651. "Host: localhost\r\n"
  652. "Connection: Upgrade\r\n"
  653. "Origin: http://localhost\r\n"
  654. "Sec-WebSocket-Version: 13\r\n"
  655. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  656. "Upgrade: websocket\r\n"
  657. "\r\n"),
  658. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  659. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  660. timer:sleep(100),
  661. Pid = get_remote_pid_tcp(Socket),
  662. {status, Pid, {module, cowboy_websocket}, _} = sys:get_status(Pid),
  663. ok.
  664. sys_get_status_loop(Config) ->
  665. doc("loop: The sys:get_status/1 function works as expected."),
  666. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  667. ok = gen_tcp:send(Socket,
  668. "GET /loop HTTP/1.1\r\n"
  669. "Host: localhost\r\n"
  670. "\r\n"),
  671. timer:sleep(100),
  672. SupPid = get_remote_pid_tcp(Socket),
  673. [{_, Pid, _, _}] = supervisor:which_children(SupPid),
  674. {status, Pid, {module, cowboy_loop}, _} = sys:get_status(Pid),
  675. ok.
  676. %% sys:replace_state/2,3.
  677. %%
  678. %% None of the modules implement Module:system_replace_state/2
  679. %% at this time so sys:replace_state/2,3 handles the Misc value.
  680. %%
  681. %% We don't actually replace the state, we only care about
  682. %% whether the call executes as expected.
  683. sys_replace_state_h1(Config) ->
  684. doc("h1: The sys:replace_state/2 function works as expected."),
  685. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), []),
  686. timer:sleep(100),
  687. Pid = get_remote_pid_tcp(Socket),
  688. {State, Buffer} = sys:replace_state(Pid, fun(S) -> S end),
  689. state = element(1, State),
  690. true = is_binary(Buffer),
  691. ok.
  692. sys_replace_state_h2(Config) ->
  693. doc("h2: The sys:replace_state/2 function works as expected."),
  694. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  695. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  696. %% Skip the SETTINGS frame.
  697. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  698. timer:sleep(100),
  699. Pid = get_remote_pid_tls(Socket),
  700. {State, Buffer} = sys:replace_state(Pid, fun(S) -> S end),
  701. state = element(1, State),
  702. true = is_binary(Buffer),
  703. ok.
  704. sys_replace_state_ws(Config) ->
  705. doc("ws: The sys:replace_state/2 function works as expected."),
  706. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  707. [binary, {active, false}]),
  708. ok = gen_tcp:send(Socket,
  709. "GET /ws HTTP/1.1\r\n"
  710. "Host: localhost\r\n"
  711. "Connection: Upgrade\r\n"
  712. "Origin: http://localhost\r\n"
  713. "Sec-WebSocket-Version: 13\r\n"
  714. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  715. "Upgrade: websocket\r\n"
  716. "\r\n"),
  717. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  718. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  719. timer:sleep(100),
  720. Pid = get_remote_pid_tcp(Socket),
  721. {State, undefined, ParseState} = sys:replace_state(Pid, fun(S) -> S end),
  722. state = element(1, State),
  723. case element(1, ParseState) of
  724. ps_header -> ok;
  725. ps_payload -> ok
  726. end.
  727. sys_replace_state_loop(Config) ->
  728. doc("loop: The sys:replace_state/2 function works as expected."),
  729. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  730. ok = gen_tcp:send(Socket,
  731. "GET /loop HTTP/1.1\r\n"
  732. "Host: localhost\r\n"
  733. "\r\n"),
  734. timer:sleep(100),
  735. SupPid = get_remote_pid_tcp(Socket),
  736. [{_, Pid, _, _}] = supervisor:which_children(SupPid),
  737. {Req, Env, long_polling_sys_h, undefined} = sys:replace_state(Pid, fun(S) -> S end),
  738. #{pid := _, streamid := _} = Req,
  739. #{dispatch := _} = Env,
  740. ok.
  741. %% sys:suspend/1 and sys:resume/1.
  742. sys_suspend_and_resume_h1(Config) ->
  743. doc("h1: The sys:suspend/1 and sys:resume/1 functions work as expected."),
  744. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  745. timer:sleep(100),
  746. Pid = get_remote_pid_tcp(Socket),
  747. ok = sys:suspend(Pid),
  748. ok = gen_tcp:send(Socket,
  749. "GET / HTTP/1.1\r\n"
  750. "Host: localhost\r\n"
  751. "\r\n"),
  752. {error, timeout} = gen_tcp:recv(Socket, 13, 500),
  753. ok = sys:resume(Pid),
  754. {ok, "HTTP/1.1 200 "} = gen_tcp:recv(Socket, 13, 500),
  755. ok.
  756. sys_suspend_and_resume_h2(Config) ->
  757. doc("h2: The sys:suspend/1 and sys:resume/1 functions work as expected."),
  758. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  759. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  760. timer:sleep(100),
  761. Pid = get_remote_pid_tls(Socket),
  762. %% Send a valid preface.
  763. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  764. %% Receive the server preface.
  765. {ok, << Len:24 >>} = ssl:recv(Socket, 3, 1000),
  766. {ok, << 4:8, 0:40, _:Len/binary >>} = ssl:recv(Socket, 6 + Len, 1000),
  767. %% Send the SETTINGS ack.
  768. ok = ssl:send(Socket, cow_http2:settings_ack()),
  769. %% Receive the SETTINGS ack.
  770. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  771. %% Suspend the process and try to get a request in. The
  772. %% response will not come back until we resume the process.
  773. ok = sys:suspend(Pid),
  774. {HeadersBlock, _} = cow_hpack:encode([
  775. {<<":method">>, <<"GET">>},
  776. {<<":scheme">>, <<"http">>},
  777. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  778. {<<":path">>, <<"/">>}
  779. ]),
  780. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  781. %% Receive a HEADERS frame as a response.
  782. {error, timeout} = ssl:recv(Socket, 9, 500),
  783. ok = sys:resume(Pid),
  784. {ok, << _:24, 1:8, _:40 >>} = ssl:recv(Socket, 9, 6000),
  785. ok.
  786. sys_suspend_and_resume_ws(Config) ->
  787. doc("ws: The sys:suspend/1 and sys:resume/1 functions work as expected."),
  788. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  789. [binary, {active, false}]),
  790. ok = gen_tcp:send(Socket,
  791. "GET /ws HTTP/1.1\r\n"
  792. "Host: localhost\r\n"
  793. "Connection: Upgrade\r\n"
  794. "Origin: http://localhost\r\n"
  795. "Sec-WebSocket-Version: 13\r\n"
  796. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  797. "Upgrade: websocket\r\n"
  798. "\r\n"),
  799. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  800. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  801. timer:sleep(100),
  802. Pid = get_remote_pid_tcp(Socket),
  803. ok = sys:suspend(Pid),
  804. Mask = 16#37fa213d,
  805. MaskedHello = ws_SUITE:do_mask(<<"Hello">>, Mask, <<>>),
  806. ok = gen_tcp:send(Socket, << 1:1, 0:3, 1:4, 1:1, 5:7, Mask:32, MaskedHello/binary >>),
  807. {error, timeout} = gen_tcp:recv(Socket, 0, 500),
  808. ok = sys:resume(Pid),
  809. {ok, << 1:1, 0:3, 1:4, 0:1, 5:7, "Hello" >>} = gen_tcp:recv(Socket, 0, 6000),
  810. ok.
  811. sys_suspend_and_resume_loop(Config) ->
  812. doc("loop: The sys:suspend/1 and sys:resume/1 functions work as expected."),
  813. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  814. ok = gen_tcp:send(Socket,
  815. "GET /loop HTTP/1.1\r\n"
  816. "Host: localhost\r\n"
  817. "\r\n"),
  818. timer:sleep(100),
  819. SupPid = get_remote_pid_tcp(Socket),
  820. [{_, Pid, _, _}] = supervisor:which_children(SupPid),
  821. %% The process sends a response 500ms after initializing.
  822. %% We expect to not receive it until we resume it.
  823. ok = sys:suspend(Pid),
  824. {error, timeout} = gen_tcp:recv(Socket, 13, 1000),
  825. ok = sys:resume(Pid),
  826. {ok, "HTTP/1.1 299 "} = gen_tcp:recv(Socket, 13, 500),
  827. ok.
  828. %% sys:terminate/2,3.
  829. %%
  830. %% The callback Module:system_terminate/4 is used in all cases.
  831. sys_terminate_h1(Config) ->
  832. doc("h1: The sys:terminate/2,3 function works as expected."),
  833. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  834. timer:sleep(100),
  835. Pid = get_remote_pid_tcp(Socket),
  836. ok = sys:terminate(Pid, {shutdown, ?MODULE}),
  837. {error, closed} = gen_tcp:recv(Socket, 0, 500),
  838. ok.
  839. sys_terminate_h2(Config) ->
  840. doc("h2: The sys:terminate/2,3 function works as expected."),
  841. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  842. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  843. %% Skip the SETTINGS frame.
  844. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  845. timer:sleep(100),
  846. Pid = get_remote_pid_tls(Socket),
  847. ok = sys:terminate(Pid, {shutdown, ?MODULE}),
  848. {error, closed} = ssl:recv(Socket, 0, 500),
  849. ok.
  850. sys_terminate_ws(Config) ->
  851. doc("ws: The sys:terminate/2,3 function works as expected."),
  852. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  853. [binary, {active, false}]),
  854. ok = gen_tcp:send(Socket,
  855. "GET /ws HTTP/1.1\r\n"
  856. "Host: localhost\r\n"
  857. "Connection: Upgrade\r\n"
  858. "Origin: http://localhost\r\n"
  859. "Sec-WebSocket-Version: 13\r\n"
  860. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  861. "Upgrade: websocket\r\n"
  862. "\r\n"),
  863. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  864. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  865. timer:sleep(100),
  866. Pid = get_remote_pid_tcp(Socket),
  867. ok = sys:terminate(Pid, {shutdown, ?MODULE}),
  868. {error, closed} = gen_tcp:recv(Socket, 0, 500),
  869. ok.
  870. sys_terminate_loop(Config) ->
  871. doc("loop: The sys:terminate/2,3 function works as expected."),
  872. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  873. ok = gen_tcp:send(Socket,
  874. "GET /loop HTTP/1.1\r\n"
  875. "Host: localhost\r\n"
  876. "\r\n"),
  877. timer:sleep(100),
  878. SupPid = get_remote_pid_tcp(Socket),
  879. [{_, Pid, _, _}] = supervisor:which_children(SupPid),
  880. %% We stop the process normally and therefore get a 204.
  881. ok = sys:terminate(Pid, {shutdown, ?MODULE}),
  882. {ok, "HTTP/1.1 204 "} = gen_tcp:recv(Socket, 13, 500),
  883. ok.
  884. %% @todo Debugging functionality from sys.
  885. %%
  886. %% The functions make references to a debug structure.
  887. %% The debug structure is a list of dbg_opt(), which is
  888. %% an internal data type used by the function handle_system_msg/6.
  889. %% No debugging is performed if it is an empty list.
  890. %%
  891. %% Cowboy currently does not implement sys debugging.
  892. %%
  893. %% The following functions are concerned:
  894. %%
  895. %% * sys:install/2,3
  896. %% * sys:log/2,3
  897. %% * sys:log_to_file/2,3
  898. %% * sys:no_debug/1,2
  899. %% * sys:remove/2,3
  900. %% * sys:statistics/2,3
  901. %% * sys:trace/2,3
  902. %% * call debug_options/1
  903. %% * call get_debug/3
  904. %% * call handle_debug/4
  905. %% * call print_log/1
  906. %% supervisor.
  907. %%
  908. %% The connection processes act as supervisors by default
  909. %% so they must handle the supervisor messages.
  910. %% supervisor:count_children/1.
  911. supervisor_count_children_h1(Config) ->
  912. doc("h1: The function supervisor:count_children/1 must work."),
  913. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  914. [{active, false}]),
  915. timer:sleep(100),
  916. Pid = get_remote_pid_tcp(Socket),
  917. %% No request was sent so there's no children.
  918. Counts1 = supervisor:count_children(Pid),
  919. 1 = proplists:get_value(specs, Counts1),
  920. 0 = proplists:get_value(active, Counts1),
  921. 0 = proplists:get_value(supervisors, Counts1),
  922. 0 = proplists:get_value(workers, Counts1),
  923. %% Send a request, observe that a children exists.
  924. ok = gen_tcp:send(Socket,
  925. "GET /loop HTTP/1.1\r\n"
  926. "Host: localhost\r\n"
  927. "\r\n"),
  928. timer:sleep(100),
  929. Counts2 = supervisor:count_children(Pid),
  930. 1 = proplists:get_value(specs, Counts2),
  931. 1 = proplists:get_value(active, Counts2),
  932. 0 = proplists:get_value(supervisors, Counts2),
  933. 1 = proplists:get_value(workers, Counts2),
  934. ok.
  935. supervisor_count_children_h2(Config) ->
  936. doc("h2: The function supervisor:count_children/1 must work."),
  937. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  938. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  939. %% Do the handshake.
  940. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  941. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  942. ok = ssl:send(Socket, cow_http2:settings_ack()),
  943. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  944. timer:sleep(100),
  945. Pid = get_remote_pid_tls(Socket),
  946. %% No request was sent so there's no children.
  947. Counts1 = supervisor:count_children(Pid),
  948. 1 = proplists:get_value(specs, Counts1),
  949. 0 = proplists:get_value(active, Counts1),
  950. 0 = proplists:get_value(supervisors, Counts1),
  951. 0 = proplists:get_value(workers, Counts1),
  952. %% Send a request, observe that a children exists.
  953. {HeadersBlock, _} = cow_hpack:encode([
  954. {<<":method">>, <<"GET">>},
  955. {<<":scheme">>, <<"https">>},
  956. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  957. {<<":path">>, <<"/loop">>}
  958. ]),
  959. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  960. timer:sleep(100),
  961. Counts2 = supervisor:count_children(Pid),
  962. 1 = proplists:get_value(specs, Counts2),
  963. 1 = proplists:get_value(active, Counts2),
  964. 0 = proplists:get_value(supervisors, Counts2),
  965. 1 = proplists:get_value(workers, Counts2),
  966. ok.
  967. supervisor_count_children_ws(Config) ->
  968. doc("ws: The function supervisor:count_children/1 must work. "
  969. "Websocket connections never have children."),
  970. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  971. [binary, {active, false}]),
  972. ok = gen_tcp:send(Socket,
  973. "GET /ws HTTP/1.1\r\n"
  974. "Host: localhost\r\n"
  975. "Connection: Upgrade\r\n"
  976. "Origin: http://localhost\r\n"
  977. "Sec-WebSocket-Version: 13\r\n"
  978. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  979. "Upgrade: websocket\r\n"
  980. "\r\n"),
  981. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  982. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  983. timer:sleep(100),
  984. Pid = get_remote_pid_tcp(Socket),
  985. Counts = supervisor:count_children(Pid),
  986. 1 = proplists:get_value(specs, Counts),
  987. 0 = proplists:get_value(active, Counts),
  988. 0 = proplists:get_value(supervisors, Counts),
  989. 0 = proplists:get_value(workers, Counts),
  990. ok.
  991. %% supervisor:delete_child/2.
  992. supervisor_delete_child_not_found_h1(Config) ->
  993. doc("h1: The function supervisor:delete_child/2 must return {error, not_found}."),
  994. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  995. [{active, false}]),
  996. timer:sleep(100),
  997. Pid = get_remote_pid_tcp(Socket),
  998. %% When no children exist.
  999. {error, not_found} = supervisor:delete_child(Pid, cowboy_http),
  1000. %% When a child exists.
  1001. ok = gen_tcp:send(Socket,
  1002. "GET /loop HTTP/1.1\r\n"
  1003. "Host: localhost\r\n"
  1004. "\r\n"),
  1005. timer:sleep(100),
  1006. {error, not_found} = supervisor:delete_child(Pid, cowboy_http),
  1007. ok.
  1008. supervisor_delete_child_not_found_h2(Config) ->
  1009. doc("h2: The function supervisor:delete_child/2 must return {error, not_found}."),
  1010. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  1011. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  1012. %% Do the handshake.
  1013. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  1014. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  1015. ok = ssl:send(Socket, cow_http2:settings_ack()),
  1016. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  1017. timer:sleep(100),
  1018. Pid = get_remote_pid_tls(Socket),
  1019. %% When no children exist.
  1020. {error, not_found} = supervisor:delete_child(Pid, cowboy_http2),
  1021. %% When a child exists.
  1022. {HeadersBlock, _} = cow_hpack:encode([
  1023. {<<":method">>, <<"GET">>},
  1024. {<<":scheme">>, <<"https">>},
  1025. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1026. {<<":path">>, <<"/loop">>}
  1027. ]),
  1028. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1029. timer:sleep(100),
  1030. {error, not_found} = supervisor:delete_child(Pid, cowboy_http2),
  1031. ok.
  1032. supervisor_delete_child_not_found_ws(Config) ->
  1033. doc("ws: The function supervisor:delete_child/2 must return {error, not_found}."),
  1034. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  1035. [binary, {active, false}]),
  1036. ok = gen_tcp:send(Socket,
  1037. "GET /ws HTTP/1.1\r\n"
  1038. "Host: localhost\r\n"
  1039. "Connection: Upgrade\r\n"
  1040. "Origin: http://localhost\r\n"
  1041. "Sec-WebSocket-Version: 13\r\n"
  1042. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  1043. "Upgrade: websocket\r\n"
  1044. "\r\n"),
  1045. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  1046. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  1047. timer:sleep(100),
  1048. Pid = get_remote_pid_tcp(Socket),
  1049. {error, not_found} = supervisor:delete_child(Pid, cowboy_websocket),
  1050. ok.
  1051. %% supervisor:get_childspec/2.
  1052. supervisor_get_childspec_not_found_h1(Config) ->
  1053. doc("h1: The function supervisor:get_childspec/2 must return {error, not_found}."),
  1054. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  1055. [{active, false}]),
  1056. timer:sleep(100),
  1057. Pid = get_remote_pid_tcp(Socket),
  1058. %% When no children exist.
  1059. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http),
  1060. %% When a child exists.
  1061. ok = gen_tcp:send(Socket,
  1062. "GET /loop HTTP/1.1\r\n"
  1063. "Host: localhost\r\n"
  1064. "\r\n"),
  1065. timer:sleep(100),
  1066. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http),
  1067. ok.
  1068. supervisor_get_childspec_not_found_h2(Config) ->
  1069. doc("h2: The function supervisor:get_childspec/2 must return {error, not_found}."),
  1070. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  1071. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  1072. %% Do the handshake.
  1073. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  1074. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  1075. ok = ssl:send(Socket, cow_http2:settings_ack()),
  1076. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  1077. timer:sleep(100),
  1078. Pid = get_remote_pid_tls(Socket),
  1079. %% When no children exist.
  1080. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http2),
  1081. %% When a child exists.
  1082. {HeadersBlock, _} = cow_hpack:encode([
  1083. {<<":method">>, <<"GET">>},
  1084. {<<":scheme">>, <<"https">>},
  1085. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1086. {<<":path">>, <<"/loop">>}
  1087. ]),
  1088. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1089. timer:sleep(100),
  1090. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http2),
  1091. ok.
  1092. supervisor_get_childspec_not_found_ws(Config) ->
  1093. doc("ws: The function supervisor:get_childspec/2 must return {error, not_found}."),
  1094. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  1095. [binary, {active, false}]),
  1096. ok = gen_tcp:send(Socket,
  1097. "GET /ws HTTP/1.1\r\n"
  1098. "Host: localhost\r\n"
  1099. "Connection: Upgrade\r\n"
  1100. "Origin: http://localhost\r\n"
  1101. "Sec-WebSocket-Version: 13\r\n"
  1102. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  1103. "Upgrade: websocket\r\n"
  1104. "\r\n"),
  1105. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  1106. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  1107. timer:sleep(100),
  1108. Pid = get_remote_pid_tcp(Socket),
  1109. {error, not_found} = supervisor:get_childspec(Pid, cowboy_websocket),
  1110. ok.
  1111. %% supervisor:restart_child/2.
  1112. supervisor_restart_child_not_found_h1(Config) ->
  1113. doc("h1: The function supervisor:restart_child/2 must return {error, not_found}."),
  1114. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  1115. [{active, false}]),
  1116. timer:sleep(100),
  1117. Pid = get_remote_pid_tcp(Socket),
  1118. %% When no children exist.
  1119. {error, not_found} = supervisor:restart_child(Pid, cowboy_http),
  1120. %% When a child exists.
  1121. ok = gen_tcp:send(Socket,
  1122. "GET /loop HTTP/1.1\r\n"
  1123. "Host: localhost\r\n"
  1124. "\r\n"),
  1125. timer:sleep(100),
  1126. {error, not_found} = supervisor:restart_child(Pid, cowboy_http),
  1127. ok.
  1128. supervisor_restart_child_not_found_h2(Config) ->
  1129. doc("h2: The function supervisor:restart_child/2 must return {error, not_found}."),
  1130. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  1131. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  1132. %% Do the handshake.
  1133. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  1134. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  1135. ok = ssl:send(Socket, cow_http2:settings_ack()),
  1136. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  1137. timer:sleep(100),
  1138. Pid = get_remote_pid_tls(Socket),
  1139. %% When no children exist.
  1140. {error, not_found} = supervisor:restart_child(Pid, cowboy_http2),
  1141. %% When a child exists.
  1142. {HeadersBlock, _} = cow_hpack:encode([
  1143. {<<":method">>, <<"GET">>},
  1144. {<<":scheme">>, <<"https">>},
  1145. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1146. {<<":path">>, <<"/loop">>}
  1147. ]),
  1148. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1149. timer:sleep(100),
  1150. {error, not_found} = supervisor:restart_child(Pid, cowboy_http2),
  1151. ok.
  1152. supervisor_restart_child_not_found_ws(Config) ->
  1153. doc("ws: The function supervisor:restart_child/2 must return {error, not_found}."),
  1154. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  1155. [binary, {active, false}]),
  1156. ok = gen_tcp:send(Socket,
  1157. "GET /ws HTTP/1.1\r\n"
  1158. "Host: localhost\r\n"
  1159. "Connection: Upgrade\r\n"
  1160. "Origin: http://localhost\r\n"
  1161. "Sec-WebSocket-Version: 13\r\n"
  1162. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  1163. "Upgrade: websocket\r\n"
  1164. "\r\n"),
  1165. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  1166. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  1167. timer:sleep(100),
  1168. Pid = get_remote_pid_tcp(Socket),
  1169. {error, not_found} = supervisor:restart_child(Pid, cowboy_websocket),
  1170. ok.
  1171. %% supervisor:start_child/2 must return {error, start_child_disabled}
  1172. supervisor_start_child_not_found_h1(Config) ->
  1173. doc("h1: The function supervisor:start_child/2 must return {error, start_child_disabled}."),
  1174. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  1175. [{active, false}]),
  1176. timer:sleep(100),
  1177. Pid = get_remote_pid_tcp(Socket),
  1178. {error, start_child_disabled} = supervisor:start_child(Pid, #{
  1179. id => error,
  1180. start => {error, error, []}
  1181. }),
  1182. ok.
  1183. supervisor_start_child_not_found_h2(Config) ->
  1184. doc("h2: The function supervisor:start_child/2 must return {error, start_child_disabled}."),
  1185. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  1186. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  1187. %% Do the handshake.
  1188. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  1189. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  1190. ok = ssl:send(Socket, cow_http2:settings_ack()),
  1191. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  1192. timer:sleep(100),
  1193. Pid = get_remote_pid_tls(Socket),
  1194. {error, start_child_disabled} = supervisor:start_child(Pid, #{
  1195. id => error,
  1196. start => {error, error, []}
  1197. }),
  1198. ok.
  1199. supervisor_start_child_not_found_ws(Config) ->
  1200. doc("ws: The function supervisor:start_child/2 must return {error, start_child_disabled}."),
  1201. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  1202. [binary, {active, false}]),
  1203. ok = gen_tcp:send(Socket,
  1204. "GET /ws HTTP/1.1\r\n"
  1205. "Host: localhost\r\n"
  1206. "Connection: Upgrade\r\n"
  1207. "Origin: http://localhost\r\n"
  1208. "Sec-WebSocket-Version: 13\r\n"
  1209. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  1210. "Upgrade: websocket\r\n"
  1211. "\r\n"),
  1212. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  1213. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  1214. timer:sleep(100),
  1215. Pid = get_remote_pid_tcp(Socket),
  1216. {error, start_child_disabled} = supervisor:start_child(Pid, #{
  1217. id => error,
  1218. start => {error, error, []}
  1219. }),
  1220. ok.
  1221. %% supervisor:terminate_child/2.
  1222. supervisor_terminate_child_not_found_h1(Config) ->
  1223. doc("h1: The function supervisor:terminate_child/2 must return {error, not_found}."),
  1224. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  1225. [{active, false}]),
  1226. timer:sleep(100),
  1227. Pid = get_remote_pid_tcp(Socket),
  1228. %% When no children exist.
  1229. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http),
  1230. %% When a child exists.
  1231. ok = gen_tcp:send(Socket,
  1232. "GET /loop HTTP/1.1\r\n"
  1233. "Host: localhost\r\n"
  1234. "\r\n"),
  1235. timer:sleep(100),
  1236. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http),
  1237. ok.
  1238. supervisor_terminate_child_not_found_h2(Config) ->
  1239. doc("h2: The function supervisor:terminate_child/2 must return {error, not_found}."),
  1240. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  1241. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  1242. %% Do the handshake.
  1243. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  1244. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  1245. ok = ssl:send(Socket, cow_http2:settings_ack()),
  1246. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  1247. timer:sleep(100),
  1248. Pid = get_remote_pid_tls(Socket),
  1249. %% When no children exist.
  1250. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http2),
  1251. %% When a child exists.
  1252. {HeadersBlock, _} = cow_hpack:encode([
  1253. {<<":method">>, <<"GET">>},
  1254. {<<":scheme">>, <<"https">>},
  1255. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1256. {<<":path">>, <<"/loop">>}
  1257. ]),
  1258. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1259. timer:sleep(100),
  1260. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http2),
  1261. ok.
  1262. supervisor_terminate_child_not_found_ws(Config) ->
  1263. doc("ws: The function supervisor:terminate_child/2 must return {error, not_found}."),
  1264. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  1265. [binary, {active, false}]),
  1266. ok = gen_tcp:send(Socket,
  1267. "GET /ws HTTP/1.1\r\n"
  1268. "Host: localhost\r\n"
  1269. "Connection: Upgrade\r\n"
  1270. "Origin: http://localhost\r\n"
  1271. "Sec-WebSocket-Version: 13\r\n"
  1272. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  1273. "Upgrade: websocket\r\n"
  1274. "\r\n"),
  1275. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  1276. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  1277. timer:sleep(100),
  1278. Pid = get_remote_pid_tcp(Socket),
  1279. {error, not_found} = supervisor:terminate_child(Pid, cowboy_websocket),
  1280. ok.
  1281. %% supervisor:which_children/1.
  1282. %%
  1283. %% @todo The list of modules returned is probably wrong. This will
  1284. %% need to be corrected when get_modules gets implemented.
  1285. supervisor_which_children_h1(Config) ->
  1286. doc("h1: The function supervisor:which_children/1 must work."),
  1287. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  1288. [{active, false}]),
  1289. timer:sleep(100),
  1290. Pid = get_remote_pid_tcp(Socket),
  1291. %% No request was sent so there's no children.
  1292. [] = supervisor:which_children(Pid),
  1293. %% Send a request, observe that a children exists.
  1294. ok = gen_tcp:send(Socket,
  1295. "GET /loop HTTP/1.1\r\n"
  1296. "Host: localhost\r\n"
  1297. "\r\n"),
  1298. timer:sleep(100),
  1299. [{cowboy_http, Child, worker, [cowboy_http]}] = supervisor:which_children(Pid),
  1300. true = is_pid(Child),
  1301. ok.
  1302. supervisor_which_children_h2(Config) ->
  1303. doc("h2: The function supervisor:which_children/1 must work."),
  1304. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  1305. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  1306. %% Do the handshake.
  1307. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  1308. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  1309. ok = ssl:send(Socket, cow_http2:settings_ack()),
  1310. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  1311. timer:sleep(100),
  1312. Pid = get_remote_pid_tls(Socket),
  1313. %% No request was sent so there's no children.
  1314. [] = supervisor:which_children(Pid),
  1315. %% Send a request, observe that a children exists.
  1316. {HeadersBlock, _} = cow_hpack:encode([
  1317. {<<":method">>, <<"GET">>},
  1318. {<<":scheme">>, <<"https">>},
  1319. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  1320. {<<":path">>, <<"/loop">>}
  1321. ]),
  1322. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  1323. timer:sleep(100),
  1324. [{cowboy_http2, Child, worker, [cowboy_http2]}] = supervisor:which_children(Pid),
  1325. true = is_pid(Child),
  1326. ok.
  1327. supervisor_which_children_ws(Config) ->
  1328. doc("ws: The function supervisor:which_children/1 must work. "
  1329. "Websocket connections never have children."),
  1330. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  1331. [binary, {active, false}]),
  1332. ok = gen_tcp:send(Socket,
  1333. "GET /ws HTTP/1.1\r\n"
  1334. "Host: localhost\r\n"
  1335. "Connection: Upgrade\r\n"
  1336. "Origin: http://localhost\r\n"
  1337. "Sec-WebSocket-Version: 13\r\n"
  1338. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  1339. "Upgrade: websocket\r\n"
  1340. "\r\n"),
  1341. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  1342. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  1343. timer:sleep(100),
  1344. Pid = get_remote_pid_tcp(Socket),
  1345. [] = supervisor:which_children(Pid),
  1346. ok.