sys_SUITE.erl 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055
  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(cowboy_test, [gun_open/1]).
  20. all() ->
  21. [{group, sys}].
  22. groups() ->
  23. [{sys, [parallel], ct_helper:all(?MODULE)}].
  24. init_per_suite(Config) ->
  25. ct:print("This test suite will produce error reports about "
  26. "EXIT signals for unknown processes."),
  27. ProtoOpts = #{
  28. env => #{dispatch => init_dispatch(Config)}
  29. },
  30. %% Clear listener.
  31. {ok, _} = cowboy:start_clear(clear, [{port, 0}], ProtoOpts),
  32. ClearPort = ranch:get_port(clear),
  33. %% TLS listener.
  34. TLSOpts = ct_helper:get_certs_from_ets(),
  35. {ok, _} = cowboy:start_tls(tls, TLSOpts ++ [{port, 0}], ProtoOpts),
  36. TLSPort = ranch:get_port(tls),
  37. [
  38. {clear_port, ClearPort},
  39. %% @todo Add the h2 stuff to the opts.
  40. {tls_opts, TLSOpts},
  41. {tls_port, TLSPort}
  42. |Config].
  43. end_per_suite(_) ->
  44. ok = cowboy:stop_listener(clear),
  45. ok = cowboy:stop_listener(tls).
  46. init_dispatch(_) ->
  47. cowboy_router:compile([{"[...]", [
  48. {"/", hello_h, []},
  49. {"/loop", long_polling_sys_h, []},
  50. {"/ws", ws_echo, []}
  51. ]}]).
  52. do_get_remote_pid_tcp(Socket) when is_port(Socket) ->
  53. do_get_remote_pid_tcp(inet:sockname(Socket));
  54. do_get_remote_pid_tcp(SockName) ->
  55. AllPorts = [{P, erlang:port_info(P)} || P <- erlang:ports()],
  56. [Pid] = [
  57. proplists:get_value(connected, I)
  58. || {P, I} <- AllPorts,
  59. I =/= undefined,
  60. proplists:get_value(name, I) =:= "tcp_inet",
  61. inet:peername(P) =:= SockName],
  62. Pid.
  63. -include_lib("ssl/src/ssl_connection.hrl").
  64. do_get_remote_pid_tls(Socket) ->
  65. %% This gives us the pid of the sslsocket process.
  66. %% We must introspect this process in order to retrieve the connection pid.
  67. TLSPid = do_get_remote_pid_tcp(ssl:sockname(Socket)),
  68. {_, #state{user_application={_, UserPid}}} = sys:get_state(TLSPid),
  69. UserPid.
  70. do_get_parent_pid(Pid) ->
  71. {_, ProcDict} = process_info(Pid, dictionary),
  72. {_, [Parent|_]} = lists:keyfind('$ancestors', 1, ProcDict),
  73. Parent.
  74. %% proc_lib.
  75. proc_lib_initial_call_clear(Config) ->
  76. doc("Confirm that clear connection processes are started using proc_lib."),
  77. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), []),
  78. timer:sleep(100),
  79. Pid = do_get_remote_pid_tcp(Socket),
  80. {cowboy_clear, _, _} = proc_lib:initial_call(Pid),
  81. ok.
  82. proc_lib_initial_call_tls(Config) ->
  83. doc("Confirm that TLS connection processes are started using proc_lib."),
  84. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config), config(tls_opts, Config)),
  85. timer:sleep(100),
  86. Pid = do_get_remote_pid_tls(Socket),
  87. {cowboy_tls, _, _} = proc_lib:initial_call(Pid),
  88. ok.
  89. %% System messages.
  90. %%
  91. %% Plain system messages are received as {system, From, Msg}.
  92. %% The content and meaning of this message are not interpreted by
  93. %% the receiving process module. When a system message is received,
  94. %% function handle_system_msg/6 is called to handle the request.
  95. %% @todo The flush/0 function in cowboy_http needs to be fixed
  96. %% so that it doesn't eat up system messages. It should only
  97. %% flush messages that are specific to cowboy_http.
  98. bad_system_from_h1(Config) ->
  99. doc("h1: Sending a system message with a bad From value results in a process crash."),
  100. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  101. timer:sleep(100),
  102. Pid = do_get_remote_pid_tcp(Socket),
  103. ct_helper_error_h:ignore(Pid, gen, reply, 2),
  104. Pid ! {system, bad, get_state},
  105. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  106. false = is_process_alive(Pid),
  107. ok.
  108. bad_system_from_h2(Config) ->
  109. doc("h2: Sending a system message with a bad From value results in a process crash."),
  110. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  111. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  112. %% Skip the SETTINGS frame.
  113. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  114. timer:sleep(100),
  115. Pid = do_get_remote_pid_tls(Socket),
  116. ct_helper_error_h:ignore(Pid, gen, reply, 2),
  117. Pid ! {system, bad, get_state},
  118. {error, closed} = ssl:recv(Socket, 0, 1000),
  119. false = is_process_alive(Pid),
  120. ok.
  121. bad_system_from_ws(Config) ->
  122. doc("ws: Sending a system message with a bad From value results in a process crash."),
  123. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  124. [binary, {active, false}]),
  125. ok = gen_tcp:send(Socket,
  126. "GET /ws HTTP/1.1\r\n"
  127. "Host: localhost\r\n"
  128. "Connection: Upgrade\r\n"
  129. "Origin: http://localhost\r\n"
  130. "Sec-WebSocket-Version: 13\r\n"
  131. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  132. "Upgrade: websocket\r\n"
  133. "\r\n"),
  134. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  135. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  136. timer:sleep(100),
  137. Pid = do_get_remote_pid_tcp(Socket),
  138. ct_helper_error_h:ignore(Pid, gen, reply, 2),
  139. Pid ! {system, bad, get_state},
  140. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  141. false = is_process_alive(Pid),
  142. ok.
  143. bad_system_from_loop(Config) ->
  144. doc("loop: Sending a system message with a bad From value results in a process crash."),
  145. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  146. ok = gen_tcp:send(Socket,
  147. "GET /loop HTTP/1.1\r\n"
  148. "Host: localhost\r\n"
  149. "\r\n"),
  150. timer:sleep(100),
  151. SupPid = do_get_remote_pid_tcp(Socket),
  152. [{_, Pid, _, _}] = supervisor:which_children(SupPid),
  153. ct_helper_error_h:ignore(Pid, gen, reply, 2),
  154. Pid ! {system, bad, get_state},
  155. {ok, "HTTP/1.1 500 "} = gen_tcp:recv(Socket, 13, 1000),
  156. false = is_process_alive(Pid),
  157. ok.
  158. bad_system_message_h1(Config) ->
  159. doc("h1: Sending a system message with a bad Request value results in an error."),
  160. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), []),
  161. timer:sleep(100),
  162. Pid = do_get_remote_pid_tcp(Socket),
  163. Ref = make_ref(),
  164. Pid ! {system, {self(), Ref}, hello},
  165. receive
  166. {Ref, {error, {unknown_system_msg, hello}}} ->
  167. ok
  168. after 1000 ->
  169. error(timeout)
  170. end.
  171. bad_system_message_h2(Config) ->
  172. doc("h2: Sending a system message with a bad Request value results in an error."),
  173. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  174. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  175. %% Skip the SETTINGS frame.
  176. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  177. timer:sleep(100),
  178. Pid = do_get_remote_pid_tls(Socket),
  179. Ref = make_ref(),
  180. Pid ! {system, {self(), Ref}, hello},
  181. receive
  182. {Ref, {error, {unknown_system_msg, hello}}} ->
  183. ok
  184. after 1000 ->
  185. error(timeout)
  186. end.
  187. bad_system_message_ws(Config) ->
  188. doc("ws: Sending a system message with a bad Request value results in an error."),
  189. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  190. [binary, {active, false}]),
  191. ok = gen_tcp:send(Socket,
  192. "GET /ws HTTP/1.1\r\n"
  193. "Host: localhost\r\n"
  194. "Connection: Upgrade\r\n"
  195. "Origin: http://localhost\r\n"
  196. "Sec-WebSocket-Version: 13\r\n"
  197. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  198. "Upgrade: websocket\r\n"
  199. "\r\n"),
  200. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  201. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  202. timer:sleep(100),
  203. Pid = do_get_remote_pid_tcp(Socket),
  204. Ref = make_ref(),
  205. Pid ! {system, {self(), Ref}, hello},
  206. receive
  207. {Ref, {error, {unknown_system_msg, hello}}} ->
  208. ok
  209. after 1000 ->
  210. error(timeout)
  211. end.
  212. bad_system_message_loop(Config) ->
  213. doc("loop: Sending a system message with a bad Request value results in an error."),
  214. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  215. ok = gen_tcp:send(Socket,
  216. "GET /loop HTTP/1.1\r\n"
  217. "Host: localhost\r\n"
  218. "\r\n"),
  219. timer:sleep(100),
  220. SupPid = do_get_remote_pid_tcp(Socket),
  221. [{_, Pid, _, _}] = supervisor:which_children(SupPid),
  222. Ref = make_ref(),
  223. Pid ! {system, {self(), Ref}, hello},
  224. receive
  225. {Ref, {error, {unknown_system_msg, hello}}} ->
  226. ok
  227. after 1000 ->
  228. error(timeout)
  229. end.
  230. good_system_message_h1(Config) ->
  231. doc("h1: System messages are handled properly."),
  232. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), []),
  233. timer:sleep(100),
  234. Pid = do_get_remote_pid_tcp(Socket),
  235. Ref = make_ref(),
  236. Pid ! {system, {self(), Ref}, get_state},
  237. receive
  238. {Ref, Result} when element(1, Result) =/= error ->
  239. ok
  240. after 1000 ->
  241. error(timeout)
  242. end.
  243. good_system_message_h2(Config) ->
  244. doc("h2: System messages are handled properly."),
  245. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  246. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  247. %% Skip the SETTINGS frame.
  248. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  249. timer:sleep(100),
  250. Pid = do_get_remote_pid_tls(Socket),
  251. Ref = make_ref(),
  252. Pid ! {system, {self(), Ref}, get_state},
  253. receive
  254. {Ref, Result} when element(1, Result) =/= error ->
  255. ok
  256. after 1000 ->
  257. error(timeout)
  258. end.
  259. good_system_message_ws(Config) ->
  260. doc("ws: Sending a system message with a bad Request value results in an error."),
  261. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  262. [binary, {active, false}]),
  263. ok = gen_tcp:send(Socket,
  264. "GET /ws HTTP/1.1\r\n"
  265. "Host: localhost\r\n"
  266. "Connection: Upgrade\r\n"
  267. "Origin: http://localhost\r\n"
  268. "Sec-WebSocket-Version: 13\r\n"
  269. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  270. "Upgrade: websocket\r\n"
  271. "\r\n"),
  272. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  273. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  274. timer:sleep(100),
  275. Pid = do_get_remote_pid_tcp(Socket),
  276. Ref = make_ref(),
  277. Pid ! {system, {self(), Ref}, get_state},
  278. receive
  279. {Ref, Result} when element(1, Result) =/= error ->
  280. ok
  281. after 1000 ->
  282. error(timeout)
  283. end.
  284. good_system_message_loop(Config) ->
  285. doc("loop: Sending a system message with a bad Request value results in an error."),
  286. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  287. ok = gen_tcp:send(Socket,
  288. "GET /loop HTTP/1.1\r\n"
  289. "Host: localhost\r\n"
  290. "\r\n"),
  291. timer:sleep(100),
  292. SupPid = do_get_remote_pid_tcp(Socket),
  293. [{_, Pid, _, _}] = supervisor:which_children(SupPid),
  294. Ref = make_ref(),
  295. Pid ! {system, {self(), Ref}, get_state},
  296. receive
  297. {Ref, Result} when element(1, Result) =/= error ->
  298. ok
  299. after 1000 ->
  300. error(timeout)
  301. end.
  302. %% 'EXIT'.
  303. %%
  304. %% Shutdown messages. If the process traps exits, it must be able
  305. %% to handle a shutdown request from its parent, the supervisor.
  306. %% The message {'EXIT', Parent, Reason} from the parent is an order
  307. %% to terminate. The process must terminate when this message is
  308. %% received, normally with the same Reason as Parent.
  309. trap_exit_parent_exit_h1(Config) ->
  310. doc("h1: A process trapping exits must stop when receiving "
  311. "an 'EXIT' message from its parent."),
  312. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  313. [{active, false}]),
  314. timer:sleep(100),
  315. Pid = do_get_remote_pid_tcp(Socket),
  316. Parent = do_get_parent_pid(Pid),
  317. Pid ! {'EXIT', Parent, shutdown},
  318. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  319. false = is_process_alive(Pid),
  320. ok.
  321. trap_exit_parent_exit_h2(Config) ->
  322. doc("h2: A process trapping exits must stop when receiving "
  323. "an 'EXIT' message from its parent."),
  324. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  325. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  326. %% Skip the SETTINGS frame.
  327. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  328. timer:sleep(100),
  329. Pid = do_get_remote_pid_tls(Socket),
  330. Parent = do_get_parent_pid(Pid),
  331. Pid ! {'EXIT', Parent, shutdown},
  332. {error, closed} = ssl:recv(Socket, 0, 1000),
  333. false = is_process_alive(Pid),
  334. ok.
  335. trap_exit_parent_exit_ws(Config) ->
  336. doc("ws: A process trapping exits must stop when receiving "
  337. "an 'EXIT' message from its parent."),
  338. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  339. [binary, {active, false}]),
  340. ok = gen_tcp:send(Socket,
  341. "GET /ws HTTP/1.1\r\n"
  342. "Host: localhost\r\n"
  343. "Connection: Upgrade\r\n"
  344. "Origin: http://localhost\r\n"
  345. "Sec-WebSocket-Version: 13\r\n"
  346. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  347. "Upgrade: websocket\r\n"
  348. "\r\n"),
  349. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  350. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  351. timer:sleep(100),
  352. Pid = do_get_remote_pid_tcp(Socket),
  353. Parent = do_get_parent_pid(Pid),
  354. Pid ! {'EXIT', Parent, shutdown},
  355. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  356. false = is_process_alive(Pid),
  357. ok.
  358. trap_exit_parent_exit_loop(Config) ->
  359. doc("loop: A process trapping exits must stop when receiving "
  360. "an 'EXIT' message from its parent."),
  361. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  362. ok = gen_tcp:send(Socket,
  363. "GET /loop HTTP/1.1\r\n"
  364. "Host: localhost\r\n"
  365. "\r\n"),
  366. timer:sleep(100),
  367. Parent = do_get_remote_pid_tcp(Socket),
  368. [{_, Pid, _, _}] = supervisor:which_children(Parent),
  369. Pid ! {'EXIT', Parent, shutdown},
  370. %% We exit normally but didn't send a response.
  371. {ok, "HTTP/1.1 204 "} = gen_tcp:recv(Socket, 13, 1000),
  372. false = is_process_alive(Pid),
  373. ok.
  374. trap_exit_other_exit_h1(Config) ->
  375. doc("h1: A process trapping exits must ignore "
  376. "'EXIT' messages from unknown processes."),
  377. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  378. [{active, false}]),
  379. timer:sleep(100),
  380. Pid = do_get_remote_pid_tcp(Socket),
  381. Pid ! {'EXIT', self(), shutdown},
  382. ok = gen_tcp:send(Socket,
  383. "GET / HTTP/1.1\r\n"
  384. "Host: localhost\r\n"
  385. "\r\n"),
  386. {ok, "HTTP/1.1 200 "} = gen_tcp:recv(Socket, 13, 1000),
  387. true = is_process_alive(Pid),
  388. ok.
  389. trap_exit_other_exit_h2(Config) ->
  390. doc("h2: A process trapping exits must ignore "
  391. "'EXIT' messages from unknown processes."),
  392. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  393. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  394. %% Do the handshake.
  395. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  396. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  397. ok = ssl:send(Socket, cow_http2:settings_ack()),
  398. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  399. timer:sleep(100),
  400. Pid = do_get_remote_pid_tls(Socket),
  401. Pid ! {'EXIT', self(), shutdown},
  402. %% Send a HEADERS frame as a request.
  403. {HeadersBlock, _} = cow_hpack:encode([
  404. {<<":method">>, <<"GET">>},
  405. {<<":scheme">>, <<"https">>},
  406. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  407. {<<":path">>, <<"/">>}
  408. ]),
  409. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  410. %% Receive a HEADERS frame as a response.
  411. {ok, << _:24, 1:8, _:40 >>} = ssl:recv(Socket, 9, 6000),
  412. true = is_process_alive(Pid),
  413. ok.
  414. trap_exit_other_exit_ws(Config) ->
  415. doc("ws: A process trapping exits must ignore "
  416. "'EXIT' messages from unknown processes."),
  417. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  418. [binary, {active, false}]),
  419. ok = gen_tcp:send(Socket,
  420. "GET /ws HTTP/1.1\r\n"
  421. "Host: localhost\r\n"
  422. "Connection: Upgrade\r\n"
  423. "Origin: http://localhost\r\n"
  424. "Sec-WebSocket-Version: 13\r\n"
  425. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  426. "Upgrade: websocket\r\n"
  427. "\r\n"),
  428. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  429. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  430. timer:sleep(100),
  431. Pid = do_get_remote_pid_tcp(Socket),
  432. Pid ! {'EXIT', self(), shutdown},
  433. %% The process stays alive.
  434. {error, timeout} = gen_tcp:recv(Socket, 0, 1000),
  435. true = is_process_alive(Pid),
  436. ok.
  437. trap_exit_other_exit_loop(Config) ->
  438. doc("loop: A process trapping exits must ignore "
  439. "'EXIT' messages from unknown processes."),
  440. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  441. ok = gen_tcp:send(Socket,
  442. "GET /loop HTTP/1.1\r\n"
  443. "Host: localhost\r\n"
  444. "\r\n"),
  445. timer:sleep(100),
  446. Parent = do_get_remote_pid_tcp(Socket),
  447. [{_, Pid, _, _}] = supervisor:which_children(Parent),
  448. Pid ! {'EXIT', self(), shutdown},
  449. %% The process stays alive.
  450. {ok, "HTTP/1.1 299 "} = gen_tcp:recv(Socket, 13, 1000),
  451. true = is_process_alive(Pid),
  452. ok.
  453. %% get_modules.
  454. %%
  455. %% If the modules used to implement the process change dynamically
  456. %% during runtime, the process must understand one more message.
  457. %% An example is the gen_event processes. The message is
  458. %% {_Label, {From, Ref}, get_modules}. The reply to this message is
  459. %% From ! {Ref, Modules}, where Modules is a list of the currently
  460. %% active modules in the process.
  461. %%
  462. %% For example:
  463. %%
  464. %% 1> application:start(sasl).
  465. %% ok
  466. %% 2> gen:call(alarm_handler, self(), get_modules).
  467. %% {ok,[alarm_handler]}
  468. %% 3> whereis(alarm_handler) ! {'$gen', {self(), make_ref()}, get_modules}.
  469. %% {'$gen',{<0.61.0>,#Ref<0.2900144977.374865921.142102>},
  470. %% get_modules}
  471. %% 4> flush().
  472. %% Shell got {#Ref<0.2900144977.374865921.142102>,[alarm_handler]}
  473. %%
  474. %% Cowboy's connection processes change dynamically: it starts with
  475. %% cowboy_clear or cowboy_tls, then becomes cowboy_http or cowboy_http2
  476. %% and may then become or involve cowboy_websocket. On top of that
  477. %% it has various callback modules in the form of stream handlers.
  478. %% @todo
  479. %get_modules_h1(Config) ->
  480. %get_modules_h2(Config) ->
  481. %get_modules_ws(Config) ->
  482. %get_modules_loop(Config) ->
  483. %% @todo On top of this we will want to make the supervisor calls
  484. %% in ranch_conns_sup return dynamic instead of a list of modules.
  485. %% sys.
  486. %% @todo sys:change_code/4,5 and Module:system_code_change/4
  487. %sys_change_code_h1(Config) ->
  488. %sys_change_code_h2(Config) ->
  489. %sys_change_code_ws(Config) ->
  490. %sys_change_code_loop(Config) ->
  491. %% @todo sys:get_state/1,2 and Module:system_get_state/1
  492. %sys_get_state_h1(Config) ->
  493. %sys_get_state_h2(Config) ->
  494. %sys_get_state_ws(Config) ->
  495. %sys_get_state_loop(Config) ->
  496. %% @todo sys:get_status/1,2
  497. %sys_get_status_h1(Config) ->
  498. %sys_get_status_h2(Config) ->
  499. %sys_get_status_ws(Config) ->
  500. %sys_get_status_loop(Config) ->
  501. %% @todo sys:replace_state/2,3 and Module:replace_state/2
  502. %sys_replace_state_h1(Config) ->
  503. %sys_replace_state_h2(Config) ->
  504. %sys_replace_state_ws(Config) ->
  505. %sys_replace_state_loop(Config) ->
  506. %% @todo sys:resume/1,2 and sys:suspend/1,2 and Module:system_continue/3
  507. %sys_suspend_and_resume_h1(Config) ->
  508. %sys_suspend_and_resume_h2(Config) ->
  509. %sys_suspend_and_resume_ws(Config) ->
  510. %sys_suspend_and_resume_loop(Config) ->
  511. %% @todo sys:terminate/2,3 and Module:system_terminate/4
  512. %sys_terminate_h1(Config) ->
  513. %sys_terminate_h2(Config) ->
  514. %sys_terminate_ws(Config) ->
  515. %sys_terminate_loop(Config) ->
  516. %% @todo Debugging functionality from sys.
  517. %%
  518. %% The functions make references to a debug structure.
  519. %% The debug structure is a list of dbg_opt(), which is
  520. %% an internal data type used by function handle_system_msg/6.
  521. %% No debugging is performed if it is an empty list.
  522. %%
  523. %% Cowboy currently does not implement sys debugging.
  524. %%
  525. %% The following functions are concerned:
  526. %%
  527. %% * sys:install/2,3
  528. %% * sys:log/2,3
  529. %% * sys:log_to_file/2,3
  530. %% * sys:no_debug/1,2
  531. %% * sys:remove/2,3
  532. %% * sys:statistics/2,3
  533. %% * sys:trace/2,3
  534. %% * call debug_options/1
  535. %% * call get_debug/3
  536. %% * call handle_debug/4
  537. %% * call print_log/1
  538. %% supervisor.
  539. %%
  540. %% The connection processes act as supervisors by default
  541. %% so they must handle the supervisor messages.
  542. %% supervisor:count_children/1.
  543. supervisor_count_children_h1(Config) ->
  544. doc("h1: The function supervisor:count_children/1 must work."),
  545. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  546. [{active, false}]),
  547. timer:sleep(100),
  548. Pid = do_get_remote_pid_tcp(Socket),
  549. %% No request was sent so there's no children.
  550. Counts1 = supervisor:count_children(Pid),
  551. 1 = proplists:get_value(specs, Counts1),
  552. 0 = proplists:get_value(active, Counts1),
  553. 0 = proplists:get_value(supervisors, Counts1),
  554. 0 = proplists:get_value(workers, Counts1),
  555. %% Send a request, observe that a children exists.
  556. ok = gen_tcp:send(Socket,
  557. "GET /loop HTTP/1.1\r\n"
  558. "Host: localhost\r\n"
  559. "\r\n"),
  560. timer:sleep(100),
  561. Counts2 = supervisor:count_children(Pid),
  562. 1 = proplists:get_value(specs, Counts2),
  563. 1 = proplists:get_value(active, Counts2),
  564. 0 = proplists:get_value(supervisors, Counts2),
  565. 1 = proplists:get_value(workers, Counts2),
  566. ok.
  567. supervisor_count_children_h2(Config) ->
  568. doc("h2: The function supervisor:count_children/1 must work."),
  569. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  570. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  571. %% Do the handshake.
  572. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  573. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  574. ok = ssl:send(Socket, cow_http2:settings_ack()),
  575. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  576. timer:sleep(100),
  577. Pid = do_get_remote_pid_tls(Socket),
  578. %% No request was sent so there's no children.
  579. Counts1 = supervisor:count_children(Pid),
  580. 1 = proplists:get_value(specs, Counts1),
  581. 0 = proplists:get_value(active, Counts1),
  582. 0 = proplists:get_value(supervisors, Counts1),
  583. 0 = proplists:get_value(workers, Counts1),
  584. %% Send a request, observe that a children exists.
  585. {HeadersBlock, _} = cow_hpack:encode([
  586. {<<":method">>, <<"GET">>},
  587. {<<":scheme">>, <<"https">>},
  588. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  589. {<<":path">>, <<"/loop">>}
  590. ]),
  591. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  592. timer:sleep(100),
  593. Counts2 = supervisor:count_children(Pid),
  594. 1 = proplists:get_value(specs, Counts2),
  595. 1 = proplists:get_value(active, Counts2),
  596. 0 = proplists:get_value(supervisors, Counts2),
  597. 1 = proplists:get_value(workers, Counts2),
  598. ok.
  599. supervisor_count_children_ws(Config) ->
  600. doc("ws: The function supervisor:count_children/1 must work. "
  601. "Websocket connections never have children."),
  602. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  603. [binary, {active, false}]),
  604. ok = gen_tcp:send(Socket,
  605. "GET /ws HTTP/1.1\r\n"
  606. "Host: localhost\r\n"
  607. "Connection: Upgrade\r\n"
  608. "Origin: http://localhost\r\n"
  609. "Sec-WebSocket-Version: 13\r\n"
  610. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  611. "Upgrade: websocket\r\n"
  612. "\r\n"),
  613. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  614. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  615. timer:sleep(100),
  616. Pid = do_get_remote_pid_tcp(Socket),
  617. Counts = supervisor:count_children(Pid),
  618. 1 = proplists:get_value(specs, Counts),
  619. 0 = proplists:get_value(active, Counts),
  620. 0 = proplists:get_value(supervisors, Counts),
  621. 0 = proplists:get_value(workers, Counts),
  622. ok.
  623. %% supervisor:delete_child/2.
  624. supervisor_delete_child_not_found_h1(Config) ->
  625. doc("h1: The function supervisor:delete_child/2 must return {error, not_found}."),
  626. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  627. [{active, false}]),
  628. timer:sleep(100),
  629. Pid = do_get_remote_pid_tcp(Socket),
  630. %% When no children exist.
  631. {error, not_found} = supervisor:delete_child(Pid, cowboy_http),
  632. %% When a child exists.
  633. ok = gen_tcp:send(Socket,
  634. "GET /loop HTTP/1.1\r\n"
  635. "Host: localhost\r\n"
  636. "\r\n"),
  637. timer:sleep(100),
  638. {error, not_found} = supervisor:delete_child(Pid, cowboy_http),
  639. ok.
  640. supervisor_delete_child_not_found_h2(Config) ->
  641. doc("h2: The function supervisor:delete_child/2 must return {error, not_found}."),
  642. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  643. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  644. %% Do the handshake.
  645. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  646. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  647. ok = ssl:send(Socket, cow_http2:settings_ack()),
  648. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  649. timer:sleep(100),
  650. Pid = do_get_remote_pid_tls(Socket),
  651. %% When no children exist.
  652. {error, not_found} = supervisor:delete_child(Pid, cowboy_http2),
  653. %% When a child exists.
  654. {HeadersBlock, _} = cow_hpack:encode([
  655. {<<":method">>, <<"GET">>},
  656. {<<":scheme">>, <<"https">>},
  657. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  658. {<<":path">>, <<"/loop">>}
  659. ]),
  660. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  661. timer:sleep(100),
  662. {error, not_found} = supervisor:delete_child(Pid, cowboy_http2),
  663. ok.
  664. supervisor_delete_child_not_found_ws(Config) ->
  665. doc("ws: The function supervisor:delete_child/2 must return {error, not_found}."),
  666. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  667. [binary, {active, false}]),
  668. ok = gen_tcp:send(Socket,
  669. "GET /ws HTTP/1.1\r\n"
  670. "Host: localhost\r\n"
  671. "Connection: Upgrade\r\n"
  672. "Origin: http://localhost\r\n"
  673. "Sec-WebSocket-Version: 13\r\n"
  674. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  675. "Upgrade: websocket\r\n"
  676. "\r\n"),
  677. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  678. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  679. timer:sleep(100),
  680. Pid = do_get_remote_pid_tcp(Socket),
  681. {error, not_found} = supervisor:delete_child(Pid, cowboy_websocket),
  682. ok.
  683. %% supervisor:get_childspec/2.
  684. supervisor_get_childspec_not_found_h1(Config) ->
  685. doc("h1: The function supervisor:get_childspec/2 must return {error, not_found}."),
  686. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  687. [{active, false}]),
  688. timer:sleep(100),
  689. Pid = do_get_remote_pid_tcp(Socket),
  690. %% When no children exist.
  691. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http),
  692. %% When a child exists.
  693. ok = gen_tcp:send(Socket,
  694. "GET /loop HTTP/1.1\r\n"
  695. "Host: localhost\r\n"
  696. "\r\n"),
  697. timer:sleep(100),
  698. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http),
  699. ok.
  700. supervisor_get_childspec_not_found_h2(Config) ->
  701. doc("h2: The function supervisor:get_childspec/2 must return {error, not_found}."),
  702. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  703. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  704. %% Do the handshake.
  705. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  706. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  707. ok = ssl:send(Socket, cow_http2:settings_ack()),
  708. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  709. timer:sleep(100),
  710. Pid = do_get_remote_pid_tls(Socket),
  711. %% When no children exist.
  712. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http2),
  713. %% When a child exists.
  714. {HeadersBlock, _} = cow_hpack:encode([
  715. {<<":method">>, <<"GET">>},
  716. {<<":scheme">>, <<"https">>},
  717. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  718. {<<":path">>, <<"/loop">>}
  719. ]),
  720. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  721. timer:sleep(100),
  722. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http2),
  723. ok.
  724. supervisor_get_childspec_not_found_ws(Config) ->
  725. doc("ws: The function supervisor:get_childspec/2 must return {error, not_found}."),
  726. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  727. [binary, {active, false}]),
  728. ok = gen_tcp:send(Socket,
  729. "GET /ws HTTP/1.1\r\n"
  730. "Host: localhost\r\n"
  731. "Connection: Upgrade\r\n"
  732. "Origin: http://localhost\r\n"
  733. "Sec-WebSocket-Version: 13\r\n"
  734. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  735. "Upgrade: websocket\r\n"
  736. "\r\n"),
  737. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  738. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  739. timer:sleep(100),
  740. Pid = do_get_remote_pid_tcp(Socket),
  741. {error, not_found} = supervisor:get_childspec(Pid, cowboy_websocket),
  742. ok.
  743. %% supervisor:restart_child/2.
  744. supervisor_restart_child_not_found_h1(Config) ->
  745. doc("h1: The function supervisor:restart_child/2 must return {error, not_found}."),
  746. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  747. [{active, false}]),
  748. timer:sleep(100),
  749. Pid = do_get_remote_pid_tcp(Socket),
  750. %% When no children exist.
  751. {error, not_found} = supervisor:restart_child(Pid, cowboy_http),
  752. %% When a child exists.
  753. ok = gen_tcp:send(Socket,
  754. "GET /loop HTTP/1.1\r\n"
  755. "Host: localhost\r\n"
  756. "\r\n"),
  757. timer:sleep(100),
  758. {error, not_found} = supervisor:restart_child(Pid, cowboy_http),
  759. ok.
  760. supervisor_restart_child_not_found_h2(Config) ->
  761. doc("h2: The function supervisor:restart_child/2 must return {error, not_found}."),
  762. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  763. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  764. %% Do the handshake.
  765. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  766. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  767. ok = ssl:send(Socket, cow_http2:settings_ack()),
  768. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  769. timer:sleep(100),
  770. Pid = do_get_remote_pid_tls(Socket),
  771. %% When no children exist.
  772. {error, not_found} = supervisor:restart_child(Pid, cowboy_http2),
  773. %% When a child exists.
  774. {HeadersBlock, _} = cow_hpack:encode([
  775. {<<":method">>, <<"GET">>},
  776. {<<":scheme">>, <<"https">>},
  777. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  778. {<<":path">>, <<"/loop">>}
  779. ]),
  780. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  781. timer:sleep(100),
  782. {error, not_found} = supervisor:restart_child(Pid, cowboy_http2),
  783. ok.
  784. supervisor_restart_child_not_found_ws(Config) ->
  785. doc("ws: The function supervisor:restart_child/2 must return {error, not_found}."),
  786. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  787. [binary, {active, false}]),
  788. ok = gen_tcp:send(Socket,
  789. "GET /ws HTTP/1.1\r\n"
  790. "Host: localhost\r\n"
  791. "Connection: Upgrade\r\n"
  792. "Origin: http://localhost\r\n"
  793. "Sec-WebSocket-Version: 13\r\n"
  794. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  795. "Upgrade: websocket\r\n"
  796. "\r\n"),
  797. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  798. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  799. timer:sleep(100),
  800. Pid = do_get_remote_pid_tcp(Socket),
  801. {error, not_found} = supervisor:restart_child(Pid, cowboy_websocket),
  802. ok.
  803. %% supervisor:start_child/2 must return {error, start_child_disabled}
  804. supervisor_start_child_not_found_h1(Config) ->
  805. doc("h1: The function supervisor:start_child/2 must return {error, start_child_disabled}."),
  806. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  807. [{active, false}]),
  808. timer:sleep(100),
  809. Pid = do_get_remote_pid_tcp(Socket),
  810. {error, start_child_disabled} = supervisor:start_child(Pid, #{
  811. id => error,
  812. start => {error, error, []}
  813. }),
  814. ok.
  815. supervisor_start_child_not_found_h2(Config) ->
  816. doc("h2: The function supervisor:start_child/2 must return {error, start_child_disabled}."),
  817. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  818. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  819. %% Do the handshake.
  820. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  821. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  822. ok = ssl:send(Socket, cow_http2:settings_ack()),
  823. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  824. timer:sleep(100),
  825. Pid = do_get_remote_pid_tls(Socket),
  826. {error, start_child_disabled} = supervisor:start_child(Pid, #{
  827. id => error,
  828. start => {error, error, []}
  829. }),
  830. ok.
  831. supervisor_start_child_not_found_ws(Config) ->
  832. doc("ws: The function supervisor:start_child/2 must return {error, start_child_disabled}."),
  833. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  834. [binary, {active, false}]),
  835. ok = gen_tcp:send(Socket,
  836. "GET /ws HTTP/1.1\r\n"
  837. "Host: localhost\r\n"
  838. "Connection: Upgrade\r\n"
  839. "Origin: http://localhost\r\n"
  840. "Sec-WebSocket-Version: 13\r\n"
  841. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  842. "Upgrade: websocket\r\n"
  843. "\r\n"),
  844. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  845. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  846. timer:sleep(100),
  847. Pid = do_get_remote_pid_tcp(Socket),
  848. {error, start_child_disabled} = supervisor:start_child(Pid, #{
  849. id => error,
  850. start => {error, error, []}
  851. }),
  852. ok.
  853. %% supervisor:terminate_child/2.
  854. supervisor_terminate_child_not_found_h1(Config) ->
  855. doc("h1: The function supervisor:terminate_child/2 must return {error, not_found}."),
  856. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  857. [{active, false}]),
  858. timer:sleep(100),
  859. Pid = do_get_remote_pid_tcp(Socket),
  860. %% When no children exist.
  861. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http),
  862. %% When a child exists.
  863. ok = gen_tcp:send(Socket,
  864. "GET /loop HTTP/1.1\r\n"
  865. "Host: localhost\r\n"
  866. "\r\n"),
  867. timer:sleep(100),
  868. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http),
  869. ok.
  870. supervisor_terminate_child_not_found_h2(Config) ->
  871. doc("h2: The function supervisor:terminate_child/2 must return {error, not_found}."),
  872. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  873. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  874. %% Do the handshake.
  875. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  876. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  877. ok = ssl:send(Socket, cow_http2:settings_ack()),
  878. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  879. timer:sleep(100),
  880. Pid = do_get_remote_pid_tls(Socket),
  881. %% When no children exist.
  882. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http2),
  883. %% When a child exists.
  884. {HeadersBlock, _} = cow_hpack:encode([
  885. {<<":method">>, <<"GET">>},
  886. {<<":scheme">>, <<"https">>},
  887. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  888. {<<":path">>, <<"/loop">>}
  889. ]),
  890. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  891. timer:sleep(100),
  892. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http2),
  893. ok.
  894. supervisor_terminate_child_not_found_ws(Config) ->
  895. doc("ws: The function supervisor:terminate_child/2 must return {error, not_found}."),
  896. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  897. [binary, {active, false}]),
  898. ok = gen_tcp:send(Socket,
  899. "GET /ws HTTP/1.1\r\n"
  900. "Host: localhost\r\n"
  901. "Connection: Upgrade\r\n"
  902. "Origin: http://localhost\r\n"
  903. "Sec-WebSocket-Version: 13\r\n"
  904. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  905. "Upgrade: websocket\r\n"
  906. "\r\n"),
  907. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  908. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  909. timer:sleep(100),
  910. Pid = do_get_remote_pid_tcp(Socket),
  911. {error, not_found} = supervisor:terminate_child(Pid, cowboy_websocket),
  912. ok.
  913. %% supervisor:which_children/1.
  914. %%
  915. %% @todo The list of modules returned is probably wrong. This will
  916. %% need to be corrected when get_modules gets implemented.
  917. supervisor_which_children_h1(Config) ->
  918. doc("h1: The function supervisor:which_children/1 must work."),
  919. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  920. [{active, false}]),
  921. timer:sleep(100),
  922. Pid = do_get_remote_pid_tcp(Socket),
  923. %% No request was sent so there's no children.
  924. [] = supervisor:which_children(Pid),
  925. %% Send a request, observe that a children exists.
  926. ok = gen_tcp:send(Socket,
  927. "GET /loop HTTP/1.1\r\n"
  928. "Host: localhost\r\n"
  929. "\r\n"),
  930. timer:sleep(100),
  931. [{cowboy_http, Child, worker, [cowboy_http]}] = supervisor:which_children(Pid),
  932. true = is_pid(Child),
  933. ok.
  934. supervisor_which_children_h2(Config) ->
  935. doc("h2: The function supervisor:which_children/1 must work."),
  936. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  937. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  938. %% Do the handshake.
  939. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  940. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  941. ok = ssl:send(Socket, cow_http2:settings_ack()),
  942. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  943. timer:sleep(100),
  944. Pid = do_get_remote_pid_tls(Socket),
  945. %% No request was sent so there's no children.
  946. [] = supervisor:which_children(Pid),
  947. %% Send a request, observe that a children exists.
  948. {HeadersBlock, _} = cow_hpack:encode([
  949. {<<":method">>, <<"GET">>},
  950. {<<":scheme">>, <<"https">>},
  951. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  952. {<<":path">>, <<"/loop">>}
  953. ]),
  954. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  955. timer:sleep(100),
  956. [{cowboy_http2, Child, worker, [cowboy_http2]}] = supervisor:which_children(Pid),
  957. true = is_pid(Child),
  958. ok.
  959. supervisor_which_children_ws(Config) ->
  960. doc("ws: The function supervisor:which_children/1 must work. "
  961. "Websocket connections never have children."),
  962. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  963. [binary, {active, false}]),
  964. ok = gen_tcp:send(Socket,
  965. "GET /ws HTTP/1.1\r\n"
  966. "Host: localhost\r\n"
  967. "Connection: Upgrade\r\n"
  968. "Origin: http://localhost\r\n"
  969. "Sec-WebSocket-Version: 13\r\n"
  970. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  971. "Upgrade: websocket\r\n"
  972. "\r\n"),
  973. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  974. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  975. timer:sleep(100),
  976. Pid = do_get_remote_pid_tcp(Socket),
  977. [] = supervisor:which_children(Pid),
  978. ok.