sys_SUITE.erl 41 KB

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