sys_SUITE.erl 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  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. %% We use gen_server:call directly because the supervisor:count_children
  620. %% function has a timeout of infinity.
  621. %% @todo This can be changed to supervisor:count_children/1 once it is fixed.
  622. Counts = gen_server:call(Pid, count_children, 1000),
  623. 1 = proplists:get_value(specs, Counts),
  624. 0 = proplists:get_value(active, Counts),
  625. 0 = proplists:get_value(supervisors, Counts),
  626. 0 = proplists:get_value(workers, Counts),
  627. ok.
  628. %% supervisor:delete_child/2.
  629. supervisor_delete_child_not_found_h1(Config) ->
  630. doc("h1: The function supervisor:delete_child/2 must return {error, not_found}."),
  631. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  632. [{active, false}]),
  633. timer:sleep(100),
  634. Pid = do_get_remote_pid_tcp(Socket),
  635. %% When no children exist.
  636. {error, not_found} = supervisor:delete_child(Pid, cowboy_http),
  637. %% When a child exists.
  638. ok = gen_tcp:send(Socket,
  639. "GET /loop HTTP/1.1\r\n"
  640. "Host: localhost\r\n"
  641. "\r\n"),
  642. timer:sleep(100),
  643. {error, not_found} = supervisor:delete_child(Pid, cowboy_http),
  644. ok.
  645. supervisor_delete_child_not_found_h2(Config) ->
  646. doc("h2: The function supervisor:delete_child/2 must return {error, not_found}."),
  647. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  648. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  649. %% Do the handshake.
  650. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  651. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  652. ok = ssl:send(Socket, cow_http2:settings_ack()),
  653. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  654. timer:sleep(100),
  655. Pid = do_get_remote_pid_tls(Socket),
  656. %% When no children exist.
  657. {error, not_found} = supervisor:delete_child(Pid, cowboy_http2),
  658. %% When a child exists.
  659. {HeadersBlock, _} = cow_hpack:encode([
  660. {<<":method">>, <<"GET">>},
  661. {<<":scheme">>, <<"https">>},
  662. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  663. {<<":path">>, <<"/loop">>}
  664. ]),
  665. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  666. timer:sleep(100),
  667. {error, not_found} = supervisor:delete_child(Pid, cowboy_http2),
  668. ok.
  669. supervisor_delete_child_not_found_ws(Config) ->
  670. doc("ws: The function supervisor:delete_child/2 must return {error, not_found}."),
  671. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  672. [binary, {active, false}]),
  673. ok = gen_tcp:send(Socket,
  674. "GET /ws HTTP/1.1\r\n"
  675. "Host: localhost\r\n"
  676. "Connection: Upgrade\r\n"
  677. "Origin: http://localhost\r\n"
  678. "Sec-WebSocket-Version: 13\r\n"
  679. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  680. "Upgrade: websocket\r\n"
  681. "\r\n"),
  682. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  683. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  684. timer:sleep(100),
  685. Pid = do_get_remote_pid_tcp(Socket),
  686. %% We use gen_server:call directly because the supervisor:delete_child
  687. %% function has a timeout of infinity.
  688. %% @todo This can be changed to supervisor:delete_child/2 once it is fixed.
  689. {error, not_found} = gen_server:call(Pid, {delete_child, cowboy_websocket}, 1000),
  690. ok.
  691. %% supervisor:get_childspec/2.
  692. supervisor_get_childspec_not_found_h1(Config) ->
  693. doc("h1: The function supervisor:get_childspec/2 must return {error, not_found}."),
  694. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  695. [{active, false}]),
  696. timer:sleep(100),
  697. Pid = do_get_remote_pid_tcp(Socket),
  698. %% When no children exist.
  699. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http),
  700. %% When a child exists.
  701. ok = gen_tcp:send(Socket,
  702. "GET /loop HTTP/1.1\r\n"
  703. "Host: localhost\r\n"
  704. "\r\n"),
  705. timer:sleep(100),
  706. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http),
  707. ok.
  708. supervisor_get_childspec_not_found_h2(Config) ->
  709. doc("h2: The function supervisor:get_childspec/2 must return {error, not_found}."),
  710. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  711. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  712. %% Do the handshake.
  713. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  714. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  715. ok = ssl:send(Socket, cow_http2:settings_ack()),
  716. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  717. timer:sleep(100),
  718. Pid = do_get_remote_pid_tls(Socket),
  719. %% When no children exist.
  720. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http2),
  721. %% When a child exists.
  722. {HeadersBlock, _} = cow_hpack:encode([
  723. {<<":method">>, <<"GET">>},
  724. {<<":scheme">>, <<"https">>},
  725. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  726. {<<":path">>, <<"/loop">>}
  727. ]),
  728. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  729. timer:sleep(100),
  730. {error, not_found} = supervisor:get_childspec(Pid, cowboy_http2),
  731. ok.
  732. supervisor_get_childspec_not_found_ws(Config) ->
  733. doc("ws: The function supervisor:get_childspec/2 must return {error, not_found}."),
  734. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  735. [binary, {active, false}]),
  736. ok = gen_tcp:send(Socket,
  737. "GET /ws HTTP/1.1\r\n"
  738. "Host: localhost\r\n"
  739. "Connection: Upgrade\r\n"
  740. "Origin: http://localhost\r\n"
  741. "Sec-WebSocket-Version: 13\r\n"
  742. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  743. "Upgrade: websocket\r\n"
  744. "\r\n"),
  745. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  746. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  747. timer:sleep(100),
  748. Pid = do_get_remote_pid_tcp(Socket),
  749. %% We use gen_server:call directly because the supervisor:get_childspec
  750. %% function has a timeout of infinity.
  751. %% @todo This can be changed to supervisor:get_childspec/2 once it is fixed.
  752. {error, not_found} = gen_server:call(Pid, {get_childspec, cowboy_websocket}, 1000),
  753. ok.
  754. %% supervisor:restart_child/2.
  755. supervisor_restart_child_not_found_h1(Config) ->
  756. doc("h1: The function supervisor:restart_child/2 must return {error, not_found}."),
  757. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  758. [{active, false}]),
  759. timer:sleep(100),
  760. Pid = do_get_remote_pid_tcp(Socket),
  761. %% When no children exist.
  762. {error, not_found} = supervisor:restart_child(Pid, cowboy_http),
  763. %% When a child exists.
  764. ok = gen_tcp:send(Socket,
  765. "GET /loop HTTP/1.1\r\n"
  766. "Host: localhost\r\n"
  767. "\r\n"),
  768. timer:sleep(100),
  769. {error, not_found} = supervisor:restart_child(Pid, cowboy_http),
  770. ok.
  771. supervisor_restart_child_not_found_h2(Config) ->
  772. doc("h2: The function supervisor:restart_child/2 must return {error, not_found}."),
  773. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  774. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  775. %% Do the handshake.
  776. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  777. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  778. ok = ssl:send(Socket, cow_http2:settings_ack()),
  779. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  780. timer:sleep(100),
  781. Pid = do_get_remote_pid_tls(Socket),
  782. %% When no children exist.
  783. {error, not_found} = supervisor:restart_child(Pid, cowboy_http2),
  784. %% When a child exists.
  785. {HeadersBlock, _} = cow_hpack:encode([
  786. {<<":method">>, <<"GET">>},
  787. {<<":scheme">>, <<"https">>},
  788. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  789. {<<":path">>, <<"/loop">>}
  790. ]),
  791. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  792. timer:sleep(100),
  793. {error, not_found} = supervisor:restart_child(Pid, cowboy_http2),
  794. ok.
  795. supervisor_restart_child_not_found_ws(Config) ->
  796. doc("ws: The function supervisor:restart_child/2 must return {error, not_found}."),
  797. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  798. [binary, {active, false}]),
  799. ok = gen_tcp:send(Socket,
  800. "GET /ws HTTP/1.1\r\n"
  801. "Host: localhost\r\n"
  802. "Connection: Upgrade\r\n"
  803. "Origin: http://localhost\r\n"
  804. "Sec-WebSocket-Version: 13\r\n"
  805. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  806. "Upgrade: websocket\r\n"
  807. "\r\n"),
  808. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  809. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  810. timer:sleep(100),
  811. Pid = do_get_remote_pid_tcp(Socket),
  812. %% We use gen_server:call directly because the supervisor:restart_child
  813. %% function has a timeout of infinity.
  814. %% @todo This can be changed to supervisor:restart_child/2 once it is fixed.
  815. {error, not_found} = gen_server:call(Pid, {restart_child, cowboy_websocket}, 1000),
  816. ok.
  817. %% supervisor:start_child/2 must return {error, start_child_disabled}
  818. supervisor_start_child_not_found_h1(Config) ->
  819. doc("h1: The function supervisor:start_child/2 must return {error, start_child_disabled}."),
  820. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  821. [{active, false}]),
  822. timer:sleep(100),
  823. Pid = do_get_remote_pid_tcp(Socket),
  824. {error, start_child_disabled} = supervisor:start_child(Pid, #{
  825. id => error,
  826. start => {error, error, []}
  827. }),
  828. ok.
  829. supervisor_start_child_not_found_h2(Config) ->
  830. doc("h2: The function supervisor:start_child/2 must return {error, start_child_disabled}."),
  831. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  832. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  833. %% Do the handshake.
  834. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  835. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  836. ok = ssl:send(Socket, cow_http2:settings_ack()),
  837. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  838. timer:sleep(100),
  839. Pid = do_get_remote_pid_tls(Socket),
  840. {error, start_child_disabled} = supervisor:start_child(Pid, #{
  841. id => error,
  842. start => {error, error, []}
  843. }),
  844. ok.
  845. supervisor_start_child_not_found_ws(Config) ->
  846. doc("ws: The function supervisor:start_child/2 must return {error, start_child_disabled}."),
  847. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  848. [binary, {active, false}]),
  849. ok = gen_tcp:send(Socket,
  850. "GET /ws HTTP/1.1\r\n"
  851. "Host: localhost\r\n"
  852. "Connection: Upgrade\r\n"
  853. "Origin: http://localhost\r\n"
  854. "Sec-WebSocket-Version: 13\r\n"
  855. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  856. "Upgrade: websocket\r\n"
  857. "\r\n"),
  858. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  859. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  860. timer:sleep(100),
  861. Pid = do_get_remote_pid_tcp(Socket),
  862. %% We use gen_server:call directly because the supervisor:start_child
  863. %% function has a timeout of infinity.
  864. %% @todo This can be changed to supervisor:start_child/2 once it is fixed.
  865. {error, start_child_disabled} = gen_server:call(Pid, {start_child, #{
  866. id => error,
  867. start => {error, error, []}
  868. }}, 1000),
  869. ok.
  870. %% supervisor:terminate_child/2.
  871. supervisor_terminate_child_not_found_h1(Config) ->
  872. doc("h1: The function supervisor:terminate_child/2 must return {error, not_found}."),
  873. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  874. [{active, false}]),
  875. timer:sleep(100),
  876. Pid = do_get_remote_pid_tcp(Socket),
  877. %% When no children exist.
  878. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http),
  879. %% When a child exists.
  880. ok = gen_tcp:send(Socket,
  881. "GET /loop HTTP/1.1\r\n"
  882. "Host: localhost\r\n"
  883. "\r\n"),
  884. timer:sleep(100),
  885. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http),
  886. ok.
  887. supervisor_terminate_child_not_found_h2(Config) ->
  888. doc("h2: The function supervisor:terminate_child/2 must return {error, not_found}."),
  889. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  890. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  891. %% Do the handshake.
  892. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  893. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  894. ok = ssl:send(Socket, cow_http2:settings_ack()),
  895. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  896. timer:sleep(100),
  897. Pid = do_get_remote_pid_tls(Socket),
  898. %% When no children exist.
  899. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http2),
  900. %% When a child exists.
  901. {HeadersBlock, _} = cow_hpack:encode([
  902. {<<":method">>, <<"GET">>},
  903. {<<":scheme">>, <<"https">>},
  904. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  905. {<<":path">>, <<"/loop">>}
  906. ]),
  907. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  908. timer:sleep(100),
  909. {error, not_found} = supervisor:terminate_child(Pid, cowboy_http2),
  910. ok.
  911. supervisor_terminate_child_not_found_ws(Config) ->
  912. doc("ws: The function supervisor:terminate_child/2 must return {error, not_found}."),
  913. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  914. [binary, {active, false}]),
  915. ok = gen_tcp:send(Socket,
  916. "GET /ws HTTP/1.1\r\n"
  917. "Host: localhost\r\n"
  918. "Connection: Upgrade\r\n"
  919. "Origin: http://localhost\r\n"
  920. "Sec-WebSocket-Version: 13\r\n"
  921. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  922. "Upgrade: websocket\r\n"
  923. "\r\n"),
  924. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  925. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  926. timer:sleep(100),
  927. Pid = do_get_remote_pid_tcp(Socket),
  928. %% We use gen_server:call directly because the supervisor:terminate_child
  929. %% function has a timeout of infinity.
  930. %% @todo This can be changed to supervisor:terminate_child/2 once it is fixed.
  931. {error, not_found} = gen_server:call(Pid, {terminate_child, cowboy_websocket}, 1000),
  932. ok.
  933. %% supervisor:which_children/1.
  934. %%
  935. %% @todo The list of modules returned is probably wrong. This will
  936. %% need to be corrected when get_modules gets implemented.
  937. supervisor_which_children_h1(Config) ->
  938. doc("h1: The function supervisor:which_children/1 must work."),
  939. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  940. [{active, false}]),
  941. timer:sleep(100),
  942. Pid = do_get_remote_pid_tcp(Socket),
  943. %% No request was sent so there's no children.
  944. [] = supervisor:which_children(Pid),
  945. %% Send a request, observe that a children exists.
  946. ok = gen_tcp:send(Socket,
  947. "GET /loop HTTP/1.1\r\n"
  948. "Host: localhost\r\n"
  949. "\r\n"),
  950. timer:sleep(100),
  951. [{cowboy_http, Child, worker, [cowboy_http]}] = supervisor:which_children(Pid),
  952. true = is_pid(Child),
  953. ok.
  954. supervisor_which_children_h2(Config) ->
  955. doc("h2: The function supervisor:which_children/1 must work."),
  956. {ok, Socket} = ssl:connect("localhost", config(tls_port, Config),
  957. [{active, false}, binary, {alpn_advertised_protocols, [<<"h2">>]}]),
  958. %% Do the handshake.
  959. ok = ssl:send(Socket, ["PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", cow_http2:settings(#{})]),
  960. {ok, <<_,_,_,4,_/bits>>} = ssl:recv(Socket, 0, 1000),
  961. ok = ssl:send(Socket, cow_http2:settings_ack()),
  962. {ok, << 0:24, 4:8, 1:8, 0:32 >>} = ssl:recv(Socket, 9, 1000),
  963. timer:sleep(100),
  964. Pid = do_get_remote_pid_tls(Socket),
  965. %% No request was sent so there's no children.
  966. [] = supervisor:which_children(Pid),
  967. %% Send a request, observe that a children exists.
  968. {HeadersBlock, _} = cow_hpack:encode([
  969. {<<":method">>, <<"GET">>},
  970. {<<":scheme">>, <<"https">>},
  971. {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
  972. {<<":path">>, <<"/loop">>}
  973. ]),
  974. ok = ssl:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
  975. timer:sleep(100),
  976. [{cowboy_http2, Child, worker, [cowboy_http2]}] = supervisor:which_children(Pid),
  977. true = is_pid(Child),
  978. ok.
  979. supervisor_which_children_ws(Config) ->
  980. doc("ws: The function supervisor:which_children/1 must work. "
  981. "Websocket connections never have children."),
  982. {ok, Socket} = gen_tcp:connect("localhost", config(clear_port, Config),
  983. [binary, {active, false}]),
  984. ok = gen_tcp:send(Socket,
  985. "GET /ws HTTP/1.1\r\n"
  986. "Host: localhost\r\n"
  987. "Connection: Upgrade\r\n"
  988. "Origin: http://localhost\r\n"
  989. "Sec-WebSocket-Version: 13\r\n"
  990. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  991. "Upgrade: websocket\r\n"
  992. "\r\n"),
  993. {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000),
  994. {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
  995. timer:sleep(100),
  996. Pid = do_get_remote_pid_tcp(Socket),
  997. %% We use gen_server:call directly because the supervisor:which_children
  998. %% function has a timeout of infinity.
  999. %% @todo This can be changed to supervisor:which_children/1 once it is fixed.
  1000. [] = gen_server:call(Pid, which_children, 1000),
  1001. ok.