sys_SUITE.erl 49 KB

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