sys_SUITE.erl 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058
  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. ProtoOpts = #{
  26. env => #{dispatch => init_dispatch(Config)}
  27. },
  28. %% Clear listener.
  29. {ok, _} = cowboy:start_clear(clear, [{port, 0}], ProtoOpts),
  30. ClearPort = ranch:get_port(clear),
  31. %% TLS listener.
  32. TLSOpts = ct_helper:get_certs_from_ets(),
  33. {ok, _} = cowboy:start_tls(tls, TLSOpts ++ [{port, 0}], ProtoOpts),
  34. TLSPort = ranch:get_port(tls),
  35. [
  36. {clear_port, ClearPort},
  37. %% @todo Add the h2 stuff to the opts.
  38. {tls_opts, TLSOpts},
  39. {tls_port, TLSPort}
  40. |Config].
  41. end_per_suite(_) ->
  42. ok = cowboy:stop_listener(clear),
  43. ok = cowboy:stop_listener(tls).
  44. init_dispatch(_) ->
  45. cowboy_router:compile([{"[...]", [
  46. {"/", hello_h, []},
  47. {"/loop", long_polling_sys_h, []},
  48. {"/ws", ws_echo, []}
  49. ]}]).
  50. do_get_remote_pid_tcp(Socket) when is_port(Socket) ->
  51. do_get_remote_pid_tcp(inet:sockname(Socket));
  52. do_get_remote_pid_tcp(SockName) ->
  53. AllPorts = [{P, erlang:port_info(P)} || P <- erlang:ports()],
  54. [Pid] = [
  55. proplists:get_value(connected, I)
  56. || {P, I} <- AllPorts,
  57. I =/= undefined,
  58. proplists:get_value(name, I) =:= "tcp_inet",
  59. inet:peername(P) =:= SockName],
  60. Pid.
  61. -include_lib("ssl/src/ssl_connection.hrl").
  62. do_get_remote_pid_tls(Socket) ->
  63. %% This gives us the pid of the sslsocket process.
  64. %% We must introspect this process in order to retrieve the connection pid.
  65. TLSPid = do_get_remote_pid_tcp(ssl:sockname(Socket)),
  66. {_, #state{user_application={_, UserPid}}} = sys:get_state(TLSPid),
  67. UserPid.
  68. do_get_parent_pid(Pid) ->
  69. {_, ProcDict} = process_info(Pid, dictionary),
  70. {_, [Parent|_]} = lists:keyfind('$ancestors', 1, ProcDict),
  71. Parent.
  72. %% proc_lib.
  73. proc_lib_initial_call_clear(Config) ->
  74. doc("Confirm that clear connection processes are started using proc_lib."),
  75. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), []),
  76. timer:sleep(100),
  77. Pid = do_get_remote_pid_tcp(Socket),
  78. {cowboy_clear, _, _} = proc_lib:initial_call(Pid),
  79. ok.
  80. proc_lib_initial_call_tls(Config) ->
  81. doc("Confirm that TLS connection processes are started using proc_lib."),
  82. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config), config(tls_opts, Config)),
  83. timer:sleep(100),
  84. Pid = do_get_remote_pid_tls(Socket),
  85. {cowboy_tls, _, _} = proc_lib:initial_call(Pid),
  86. ok.
  87. %% System messages.
  88. %%
  89. %% Plain system messages are received as {system, From, Msg}.
  90. %% The content and meaning of this message are not interpreted by
  91. %% the receiving process module. When a system message is received,
  92. %% function handle_system_msg/6 is called to handle the request.
  93. %% @todo The flush/0 function in cowboy_http needs to be fixed
  94. %% so that it doesn't eat up system messages. It should only
  95. %% flush messages that are specific to cowboy_http.
  96. %% @todo The cowboy_websocket module needs to have the functions
  97. %% handler_loop and websocket_payload_loop merged into one.
  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're getting a 500 because the process exited in an unexpected way
  371. %% from Cowboy's point of view.
  372. {ok, "HTTP/1.1 500 "} = gen_tcp:recv(Socket, 13, 1000),
  373. false = is_process_alive(Pid),
  374. ok.
  375. trap_exit_other_exit_h1(Config) ->
  376. doc("h1: A process trapping exits must ignore "
  377. "'EXIT' messages from unknown processes."),
  378. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  379. [{active, false}]),
  380. timer:sleep(100),
  381. Pid = do_get_remote_pid_tcp(Socket),
  382. Pid ! {'EXIT', self(), shutdown},
  383. ok = gen_tcp:send(Socket,
  384. "GET / HTTP/1.1\r\n"
  385. "Host: localhost\r\n"
  386. "\r\n"),
  387. {ok, "HTTP/1.1 200 "} = gen_tcp:recv(Socket, 13, 1000),
  388. true = is_process_alive(Pid),
  389. ok.
  390. trap_exit_other_exit_h2(Config) ->
  391. doc("h2: A process trapping exits must ignore "
  392. "'EXIT' messages from unknown processes."),
  393. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  394. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  395. %% Do the handshake.
  396. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  397. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  398. ok = ssl:send(Socket, cow_http2:settings_ack()),
  399. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  400. timer:sleep(100),
  401. Pid = do_get_remote_pid_tls(Socket),
  402. Pid ! {'EXIT', self(), shutdown},
  403. %% Send a HEADERS frame as a request.
  404. {HeadersBlock, _} = cow_hpack:encode([
  405. {<<":method">>, <<"GET">>},
  406. {<<":scheme">>, <<"https">>},
  407. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  408. {<<":path">>, <<"/">>}
  409. ]),
  410. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  411. %% Receive a HEADERS frame as a response.
  412. {ok, << _:24, 1:8, _:40 >>} = ssl:recv(Socket, 9, 6000),
  413. true = is_process_alive(Pid),
  414. ok.
  415. trap_exit_other_exit_ws(Config) ->
  416. doc("ws: A process trapping exits must ignore "
  417. "'EXIT' messages from unknown processes."),
  418. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  419. [binary, {active, false}]),
  420. ok = gen_tcp:send(Socket,
  421. "GET /ws HTTP/1.1\r\n"
  422. "Host: localhost\r\n"
  423. "Connection: Upgrade\r\n"
  424. "Origin: http://localhost\r\n"
  425. "Sec-WebSocket-Version: 13\r\n"
  426. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  427. "Upgrade: websocket\r\n"
  428. "\r\n"),
  429. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  430. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  431. timer:sleep(100),
  432. Pid = do_get_remote_pid_tcp(Socket),
  433. Parent = do_get_parent_pid(Pid),
  434. Pid ! {'EXIT', Parent, shutdown},
  435. %% The process stays alive.
  436. {error, timeout} = gen_tcp:recv(Socket, 0, 1000),
  437. true = is_process_alive(Pid),
  438. ok.
  439. trap_exit_other_exit_loop(Config) ->
  440. doc("loop: A process trapping exits must ignore "
  441. "'EXIT' messages from unknown processes."),
  442. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config), [{active, false}]),
  443. ok = gen_tcp:send(Socket,
  444. "GET /loop HTTP/1.1\r\n"
  445. "Host: localhost\r\n"
  446. "\r\n"),
  447. timer:sleep(100),
  448. Parent = do_get_remote_pid_tcp(Socket),
  449. [{_, Pid, _, _}] = supervisor:which_children(Parent),
  450. Pid ! {'EXIT', Parent, shutdown},
  451. %% The process stays alive.
  452. {ok, "HTTP/1.1 299 "} = gen_tcp:recv(Socket, 13, 1000),
  453. true = is_process_alive(Pid),
  454. ok.
  455. %% get_modules.
  456. %%
  457. %% If the modules used to implement the process change dynamically
  458. %% during runtime, the process must understand one more message.
  459. %% An example is the gen_event processes. The message is
  460. %% {_Label, {From, Ref}, get_modules}. The reply to this message is
  461. %% From ! {Ref, Modules}, where Modules is a list of the currently
  462. %% active modules in the process.
  463. %%
  464. %% For example:
  465. %%
  466. %% 1> application:start(sasl).
  467. %% ok
  468. %% 2> gen:call(alarm_handler, self(), get_modules).
  469. %% {ok,[alarm_handler]}
  470. %% 3> whereis(alarm_handler) ! {'$gen', {self(), make_ref()}, get_modules}.
  471. %% {'$gen',{<0.61.0>,#Ref<0.2900144977.374865921.142102>},
  472. %% get_modules}
  473. %% 4> flush().
  474. %% Shell got {#Ref<0.2900144977.374865921.142102>,[alarm_handler]}
  475. %%
  476. %% Cowboy's connection processes change dynamically: it starts with
  477. %% cowboy_clear or cowboy_tls, then becomes cowboy_http or cowboy_http2
  478. %% and may then become or involve cowboy_websocket. On top of that
  479. %% it has various callback modules in the form of stream handlers.
  480. %% @todo
  481. %get_modules_h1(Config) ->
  482. %get_modules_h2(Config) ->
  483. %get_modules_ws(Config) ->
  484. %get_modules_loop(Config) ->
  485. %% @todo On top of this we will want to make the supervisor calls
  486. %% in ranch_conns_sup return dynamic instead of a list of modules.
  487. %% sys.
  488. %% @todo sys:change_code/4,5 and Module:system_code_change/4
  489. %sys_change_code_h1(Config) ->
  490. %sys_change_code_h2(Config) ->
  491. %sys_change_code_ws(Config) ->
  492. %sys_change_code_loop(Config) ->
  493. %% @todo sys:get_state/1,2 and Module:system_get_state/1
  494. %sys_get_state_h1(Config) ->
  495. %sys_get_state_h2(Config) ->
  496. %sys_get_state_ws(Config) ->
  497. %sys_get_state_loop(Config) ->
  498. %% @todo sys:get_status/1,2
  499. %sys_get_status_h1(Config) ->
  500. %sys_get_status_h2(Config) ->
  501. %sys_get_status_ws(Config) ->
  502. %sys_get_status_loop(Config) ->
  503. %% @todo sys:replace_state/2,3 and Module:replace_state/2
  504. %sys_replace_state_h1(Config) ->
  505. %sys_replace_state_h2(Config) ->
  506. %sys_replace_state_ws(Config) ->
  507. %sys_replace_state_loop(Config) ->
  508. %% @todo sys:resume/1,2 and sys:suspend/1,2 and Module:system_continue/3
  509. %sys_suspend_and_resume_h1(Config) ->
  510. %sys_suspend_and_resume_h2(Config) ->
  511. %sys_suspend_and_resume_ws(Config) ->
  512. %sys_suspend_and_resume_loop(Config) ->
  513. %% @todo sys:terminate/2,3 and Module:system_terminate/4
  514. %sys_terminate_h1(Config) ->
  515. %sys_terminate_h2(Config) ->
  516. %sys_terminate_ws(Config) ->
  517. %sys_terminate_loop(Config) ->
  518. %% @todo Debugging functionality from sys.
  519. %%
  520. %% The functions make references to a debug structure.
  521. %% The debug structure is a list of dbg_opt(), which is
  522. %% an internal data type used by function handle_system_msg/6.
  523. %% No debugging is performed if it is an empty list.
  524. %%
  525. %% Cowboy currently does not implement sys debugging.
  526. %%
  527. %% The following functions are concerned:
  528. %%
  529. %% * sys:install/2,3
  530. %% * sys:log/2,3
  531. %% * sys:log_to_file/2,3
  532. %% * sys:no_debug/1,2
  533. %% * sys:remove/2,3
  534. %% * sys:statistics/2,3
  535. %% * sys:trace/2,3
  536. %% * call debug_options/1
  537. %% * call get_debug/3
  538. %% * call handle_debug/4
  539. %% * call print_log/1
  540. %% supervisor.
  541. %%
  542. %% The connection processes act as supervisors by default
  543. %% so they must handle the supervisor messages.
  544. %% supervisor:count_children/1.
  545. supervisor_count_children_h1(Config) ->
  546. doc("h1: The function supervisor:count_children/1 must work."),
  547. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  548. [{active, false}]),
  549. timer:sleep(100),
  550. Pid = do_get_remote_pid_tcp(Socket),
  551. %% No request was sent so there's no children.
  552. Counts1 = supervisor:count_children(Pid),
  553. 1 = proplists:get_value(specs, Counts1),
  554. 0 = proplists:get_value(active, Counts1),
  555. 0 = proplists:get_value(supervisors, Counts1),
  556. 0 = proplists:get_value(workers, Counts1),
  557. %% Send a request, observe that a children exists.
  558. ok = gen_tcp:send(Socket,
  559. "GET /loop HTTP/1.1\r\n"
  560. "Host: localhost\r\n"
  561. "\r\n"),
  562. timer:sleep(100),
  563. Counts2 = supervisor:count_children(Pid),
  564. 1 = proplists:get_value(specs, Counts2),
  565. 1 = proplists:get_value(active, Counts2),
  566. 0 = proplists:get_value(supervisors, Counts2),
  567. 1 = proplists:get_value(workers, Counts2),
  568. ok.
  569. supervisor_count_children_h2(Config) ->
  570. doc("h2: The function supervisor:count_children/1 must work."),
  571. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  572. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  573. %% Do the handshake.
  574. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  575. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  576. ok = ssl:send(Socket, cow_http2:settings_ack()),
  577. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  578. timer:sleep(100),
  579. Pid = do_get_remote_pid_tls(Socket),
  580. %% No request was sent so there's no children.
  581. Counts1 = supervisor:count_children(Pid),
  582. 1 = proplists:get_value(specs, Counts1),
  583. 0 = proplists:get_value(active, Counts1),
  584. 0 = proplists:get_value(supervisors, Counts1),
  585. 0 = proplists:get_value(workers, Counts1),
  586. %% Send a request, observe that a children exists.
  587. {HeadersBlock, _} = cow_hpack:encode([
  588. {<<":method">>, <<"GET">>},
  589. {<<":scheme">>, <<"https">>},
  590. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  591. {<<":path">>, <<"/loop">>}
  592. ]),
  593. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  594. timer:sleep(100),
  595. Counts2 = supervisor:count_children(Pid),
  596. 1 = proplists:get_value(specs, Counts2),
  597. 1 = proplists:get_value(active, Counts2),
  598. 0 = proplists:get_value(supervisors, Counts2),
  599. 1 = proplists:get_value(workers, Counts2),
  600. ok.
  601. supervisor_count_children_ws(Config) ->
  602. doc("ws: The function supervisor:count_children/1 must work. "
  603. "Websocket connections never have children."),
  604. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  605. [binary, {active, false}]),
  606. ok = gen_tcp:send(Socket,
  607. "GET /ws HTTP/1.1\r\n"
  608. "Host: localhost\r\n"
  609. "Connection: Upgrade\r\n"
  610. "Origin: http://localhost\r\n"
  611. "Sec-WebSocket-Version: 13\r\n"
  612. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  613. "Upgrade: websocket\r\n"
  614. "\r\n"),
  615. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  616. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  617. timer:sleep(100),
  618. Pid = do_get_remote_pid_tcp(Socket),
  619. Counts = supervisor:count_children(Pid),
  620. 1 = proplists:get_value(specs, Counts),
  621. 0 = proplists:get_value(active, Counts),
  622. 0 = proplists:get_value(supervisors, Counts),
  623. 0 = proplists:get_value(workers, Counts),
  624. ok.
  625. %% supervisor:delete_child/2.
  626. supervisor_delete_child_not_found_h1(Config) ->
  627. doc("h1: The function supervisor:delete_child/2 must return {error, not_found}."),
  628. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  629. [{active, false}]),
  630. timer:sleep(100),
  631. Pid = do_get_remote_pid_tcp(Socket),
  632. %% When no children exist.
  633. {error, not_found} = supervisor:delete_child(Pid, cowboy_http),
  634. %% When a child exists.
  635. ok = gen_tcp:send(Socket,
  636. "GET /loop HTTP/1.1\r\n"
  637. "Host: localhost\r\n"
  638. "\r\n"),
  639. timer:sleep(100),
  640. {error, not_found} = supervisor:delete_child(Pid, cowboy_http),
  641. ok.
  642. supervisor_delete_child_not_found_h2(Config) ->
  643. doc("h2: The function supervisor:delete_child/2 must return {error, not_found}."),
  644. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  645. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  646. %% Do the handshake.
  647. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  648. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  649. ok = ssl:send(Socket, cow_http2:settings_ack()),
  650. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  651. timer:sleep(100),
  652. Pid = do_get_remote_pid_tls(Socket),
  653. %% When no children exist.
  654. {error, not_found} = supervisor:delete_child(Pid, cowboy_http2),
  655. %% When a child exists.
  656. {HeadersBlock, _} = cow_hpack:encode([
  657. {<<":method">>, <<"GET">>},
  658. {<<":scheme">>, <<"https">>},
  659. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  660. {<<":path">>, <<"/loop">>}
  661. ]),
  662. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  663. timer:sleep(100),
  664. {error, not_found} = supervisor:delete_child(Pid, cowboy_http2),
  665. ok.
  666. supervisor_delete_child_not_found_ws(Config) ->
  667. doc("ws: The function supervisor:delete_child/2 must return {error, not_found}."),
  668. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  669. [binary, {active, false}]),
  670. ok = gen_tcp:send(Socket,
  671. "GET /ws HTTP/1.1\r\n"
  672. "Host: localhost\r\n"
  673. "Connection: Upgrade\r\n"
  674. "Origin: http://localhost\r\n"
  675. "Sec-WebSocket-Version: 13\r\n"
  676. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  677. "Upgrade: websocket\r\n"
  678. "\r\n"),
  679. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  680. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  681. timer:sleep(100),
  682. Pid = do_get_remote_pid_tcp(Socket),
  683. {error, not_found} = supervisor:delete_child(Pid, cowboy_websocket),
  684. ok.
  685. %% supervisor:get_childspec/2.
  686. supervisor_get_childspec_not_found_h1(Config) ->
  687. doc("h1: The function supervisor:get_childspec/2 must return {error, not_found}."),
  688. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  689. [{active, false}]),
  690. timer:sleep(100),
  691. Pid = do_get_remote_pid_tcp(Socket),
  692. %% When no children exist.
  693. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http),
  694. %% When a child exists.
  695. ok = gen_tcp:send(Socket,
  696. "GET /loop HTTP/1.1\r\n"
  697. "Host: localhost\r\n"
  698. "\r\n"),
  699. timer:sleep(100),
  700. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http),
  701. ok.
  702. supervisor_get_childspec_not_found_h2(Config) ->
  703. doc("h2: The function supervisor:get_childspec/2 must return {error, not_found}."),
  704. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  705. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  706. %% Do the handshake.
  707. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  708. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  709. ok = ssl:send(Socket, cow_http2:settings_ack()),
  710. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  711. timer:sleep(100),
  712. Pid = do_get_remote_pid_tls(Socket),
  713. %% When no children exist.
  714. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http2),
  715. %% When a child exists.
  716. {HeadersBlock, _} = cow_hpack:encode([
  717. {<<":method">>, <<"GET">>},
  718. {<<":scheme">>, <<"https">>},
  719. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  720. {<<":path">>, <<"/loop">>}
  721. ]),
  722. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  723. timer:sleep(100),
  724. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http2),
  725. ok.
  726. supervisor_get_childspec_not_found_ws(Config) ->
  727. doc("ws: The function supervisor:get_childspec/2 must return {error, not_found}."),
  728. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  729. [binary, {active, false}]),
  730. ok = gen_tcp:send(Socket,
  731. "GET /ws HTTP/1.1\r\n"
  732. "Host: localhost\r\n"
  733. "Connection: Upgrade\r\n"
  734. "Origin: http://localhost\r\n"
  735. "Sec-WebSocket-Version: 13\r\n"
  736. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  737. "Upgrade: websocket\r\n"
  738. "\r\n"),
  739. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  740. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  741. timer:sleep(100),
  742. Pid = do_get_remote_pid_tcp(Socket),
  743. {error, not_found} = supervisor:get_childspec(Pid, cowboy_websocket),
  744. ok.
  745. %% supervisor:restart_child/2.
  746. supervisor_restart_child_not_found_h1(Config) ->
  747. doc("h1: The function supervisor:restart_child/2 must return {error, not_found}."),
  748. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  749. [{active, false}]),
  750. timer:sleep(100),
  751. Pid = do_get_remote_pid_tcp(Socket),
  752. %% When no children exist.
  753. {error, not_found} = supervisor:restart_child(Pid, cowboy_http),
  754. %% When a child exists.
  755. ok = gen_tcp:send(Socket,
  756. "GET /loop HTTP/1.1\r\n"
  757. "Host: localhost\r\n"
  758. "\r\n"),
  759. timer:sleep(100),
  760. {error, not_found} = supervisor:restart_child(Pid, cowboy_http),
  761. ok.
  762. supervisor_restart_child_not_found_h2(Config) ->
  763. doc("h2: The function supervisor:restart_child/2 must return {error, not_found}."),
  764. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  765. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  766. %% Do the handshake.
  767. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  768. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  769. ok = ssl:send(Socket, cow_http2:settings_ack()),
  770. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  771. timer:sleep(100),
  772. Pid = do_get_remote_pid_tls(Socket),
  773. %% When no children exist.
  774. {error, not_found} = supervisor:restart_child(Pid, cowboy_http2),
  775. %% When a child exists.
  776. {HeadersBlock, _} = cow_hpack:encode([
  777. {<<":method">>, <<"GET">>},
  778. {<<":scheme">>, <<"https">>},
  779. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  780. {<<":path">>, <<"/loop">>}
  781. ]),
  782. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  783. timer:sleep(100),
  784. {error, not_found} = supervisor:restart_child(Pid, cowboy_http2),
  785. ok.
  786. supervisor_restart_child_not_found_ws(Config) ->
  787. doc("ws: The function supervisor:restart_child/2 must return {error, not_found}."),
  788. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  789. [binary, {active, false}]),
  790. ok = gen_tcp:send(Socket,
  791. "GET /ws HTTP/1.1\r\n"
  792. "Host: localhost\r\n"
  793. "Connection: Upgrade\r\n"
  794. "Origin: http://localhost\r\n"
  795. "Sec-WebSocket-Version: 13\r\n"
  796. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  797. "Upgrade: websocket\r\n"
  798. "\r\n"),
  799. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  800. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  801. timer:sleep(100),
  802. Pid = do_get_remote_pid_tcp(Socket),
  803. {error, not_found} = supervisor:restart_child(Pid, cowboy_websocket),
  804. ok.
  805. %% supervisor:start_child/2 must return {error, start_child_disabled}
  806. supervisor_start_child_not_found_h1(Config) ->
  807. doc("h1: The function supervisor:start_child/2 must return {error, start_child_disabled}."),
  808. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  809. [{active, false}]),
  810. timer:sleep(100),
  811. Pid = do_get_remote_pid_tcp(Socket),
  812. {error, start_child_disabled} = supervisor:start_child(Pid, #{
  813. id => error,
  814. start => {error, error, []}
  815. }),
  816. ok.
  817. supervisor_start_child_not_found_h2(Config) ->
  818. doc("h2: The function supervisor:start_child/2 must return {error, start_child_disabled}."),
  819. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  820. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  821. %% Do the handshake.
  822. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  823. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  824. ok = ssl:send(Socket, cow_http2:settings_ack()),
  825. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  826. timer:sleep(100),
  827. Pid = do_get_remote_pid_tls(Socket),
  828. {error, start_child_disabled} = supervisor:start_child(Pid, #{
  829. id => error,
  830. start => {error, error, []}
  831. }),
  832. ok.
  833. supervisor_start_child_not_found_ws(Config) ->
  834. doc("ws: The function supervisor:start_child/2 must return {error, start_child_disabled}."),
  835. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  836. [binary, {active, false}]),
  837. ok = gen_tcp:send(Socket,
  838. "GET /ws HTTP/1.1\r\n"
  839. "Host: localhost\r\n"
  840. "Connection: Upgrade\r\n"
  841. "Origin: http://localhost\r\n"
  842. "Sec-WebSocket-Version: 13\r\n"
  843. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  844. "Upgrade: websocket\r\n"
  845. "\r\n"),
  846. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  847. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  848. timer:sleep(100),
  849. Pid = do_get_remote_pid_tcp(Socket),
  850. {error, start_child_disabled} = supervisor:start_child(Pid, #{
  851. id => error,
  852. start => {error, error, []}
  853. }),
  854. ok.
  855. %% supervisor:terminate_child/2.
  856. supervisor_terminate_child_not_found_h1(Config) ->
  857. doc("h1: The function supervisor:terminate_child/2 must return {error, not_found}."),
  858. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  859. [{active, false}]),
  860. timer:sleep(100),
  861. Pid = do_get_remote_pid_tcp(Socket),
  862. %% When no children exist.
  863. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http),
  864. %% When a child exists.
  865. ok = gen_tcp:send(Socket,
  866. "GET /loop HTTP/1.1\r\n"
  867. "Host: localhost\r\n"
  868. "\r\n"),
  869. timer:sleep(100),
  870. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http),
  871. ok.
  872. supervisor_terminate_child_not_found_h2(Config) ->
  873. doc("h2: The function supervisor:terminate_child/2 must return {error, not_found}."),
  874. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  875. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  876. %% Do the handshake.
  877. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  878. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  879. ok = ssl:send(Socket, cow_http2:settings_ack()),
  880. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  881. timer:sleep(100),
  882. Pid = do_get_remote_pid_tls(Socket),
  883. %% When no children exist.
  884. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http2),
  885. %% When a child exists.
  886. {HeadersBlock, _} = cow_hpack:encode([
  887. {<<":method">>, <<"GET">>},
  888. {<<":scheme">>, <<"https">>},
  889. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  890. {<<":path">>, <<"/loop">>}
  891. ]),
  892. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  893. timer:sleep(100),
  894. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http2),
  895. ok.
  896. supervisor_terminate_child_not_found_ws(Config) ->
  897. doc("ws: The function supervisor:terminate_child/2 must return {error, not_found}."),
  898. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  899. [binary, {active, false}]),
  900. ok = gen_tcp:send(Socket,
  901. "GET /ws HTTP/1.1\r\n"
  902. "Host: localhost\r\n"
  903. "Connection: Upgrade\r\n"
  904. "Origin: http://localhost\r\n"
  905. "Sec-WebSocket-Version: 13\r\n"
  906. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  907. "Upgrade: websocket\r\n"
  908. "\r\n"),
  909. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  910. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  911. timer:sleep(100),
  912. Pid = do_get_remote_pid_tcp(Socket),
  913. {error, not_found} = supervisor:terminate_child(Pid, cowboy_websocket),
  914. ok.
  915. %% supervisor:which_children/1.
  916. %%
  917. %% @todo The list of modules returned is probably wrong. This will
  918. %% need to be corrected when get_modules gets implemented.
  919. supervisor_which_children_h1(Config) ->
  920. doc("h1: The function supervisor:which_children/1 must work."),
  921. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  922. [{active, false}]),
  923. timer:sleep(100),
  924. Pid = do_get_remote_pid_tcp(Socket),
  925. %% No request was sent so there's no children.
  926. [] = supervisor:which_children(Pid),
  927. %% Send a request, observe that a children exists.
  928. ok = gen_tcp:send(Socket,
  929. "GET /loop HTTP/1.1\r\n"
  930. "Host: localhost\r\n"
  931. "\r\n"),
  932. timer:sleep(100),
  933. [{cowboy_http, Child, worker, [cowboy_http]}] = supervisor:which_children(Pid),
  934. true = is_pid(Child),
  935. ok.
  936. supervisor_which_children_h2(Config) ->
  937. doc("h2: The function supervisor:which_children/1 must work."),
  938. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  939. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  940. %% Do the handshake.
  941. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  942. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  943. ok = ssl:send(Socket, cow_http2:settings_ack()),
  944. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  945. timer:sleep(100),
  946. Pid = do_get_remote_pid_tls(Socket),
  947. %% No request was sent so there's no children.
  948. [] = supervisor:which_children(Pid),
  949. %% Send a request, observe that a children exists.
  950. {HeadersBlock, _} = cow_hpack:encode([
  951. {<<":method">>, <<"GET">>},
  952. {<<":scheme">>, <<"https">>},
  953. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  954. {<<":path">>, <<"/loop">>}
  955. ]),
  956. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  957. timer:sleep(100),
  958. [{cowboy_http2, Child, worker, [cowboy_http2]}] = supervisor:which_children(Pid),
  959. true = is_pid(Child),
  960. ok.
  961. supervisor_which_children_ws(Config) ->
  962. doc("ws: The function supervisor:which_children/1 must work. "
  963. "Websocket connections never have children."),
  964. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  965. [binary, {active, false}]),
  966. ok = gen_tcp:send(Socket,
  967. "GET /ws HTTP/1.1\r\n"
  968. "Host: localhost\r\n"
  969. "Connection: Upgrade\r\n"
  970. "Origin: http://localhost\r\n"
  971. "Sec-WebSocket-Version: 13\r\n"
  972. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  973. "Upgrade: websocket\r\n"
  974. "\r\n"),
  975. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  976. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  977. timer:sleep(100),
  978. Pid = do_get_remote_pid_tcp(Socket),
  979. [] = supervisor:which_children(Pid),
  980. ok.