acceptor_SUITE.erl 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832
  1. %% Copyright (c) 2011-2017, 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(acceptor_SUITE).
  15. -compile(export_all).
  16. -import(ct_helper, [doc/1]).
  17. -import(ct_helper, [name/0]).
  18. %% ct.
  19. all() ->
  20. [{group, tcp}, {group, ssl}, {group, misc}, {group, supervisor}].
  21. groups() ->
  22. [{tcp, [
  23. tcp_accept_socket,
  24. tcp_active_echo,
  25. tcp_echo,
  26. tcp_inherit_options,
  27. tcp_max_connections,
  28. tcp_max_connections_and_beyond,
  29. tcp_max_connections_infinity,
  30. tcp_remove_connections,
  31. tcp_set_max_connections,
  32. tcp_set_max_connections_clean,
  33. tcp_getopts_capability,
  34. tcp_getstat_capability,
  35. tcp_upgrade,
  36. tcp_error_eaddrinuse,
  37. tcp_error_eacces
  38. ]}, {ssl, [
  39. ssl_accept_error,
  40. ssl_accept_socket,
  41. ssl_active_echo,
  42. ssl_echo,
  43. ssl_sni_echo,
  44. ssl_sni_fail,
  45. ssl_getopts_capability,
  46. ssl_getstat_capability,
  47. ssl_error_eaddrinuse,
  48. ssl_error_no_cert,
  49. ssl_error_eacces
  50. ]}, {misc, [
  51. misc_bad_transport,
  52. misc_bad_transport_options,
  53. misc_info
  54. ]}, {supervisor, [
  55. connection_type_supervisor,
  56. connection_type_supervisor_separate_from_connection,
  57. supervisor_clean_child_restart,
  58. supervisor_clean_conns_sup_restart,
  59. supervisor_clean_restart,
  60. supervisor_conns_alive,
  61. supervisor_protocol_start_link_crash,
  62. supervisor_server_recover_state
  63. ]}].
  64. %% misc.
  65. misc_bad_transport(_) ->
  66. doc("Reject invalid transport modules."),
  67. {error, badarg} = ranch:start_listener(misc_bad_transport,
  68. bad_transport, [], echo_protocol, []),
  69. ok.
  70. misc_bad_transport_options(_) ->
  71. doc("Ignore invalid transport options."),
  72. {ok, _} = ranch:start_listener(misc_bad_transport,
  73. ranch_tcp, [binary, {packet, 4}, <<"garbage">>, raw, backlog], echo_protocol, []),
  74. ok.
  75. misc_info(_) ->
  76. doc("Information about listeners."),
  77. %% Open a listener with a few connections.
  78. {ok, Pid1} = ranch:start_listener({misc_info, tcp},
  79. ranch_tcp, [{num_acceptors, 1}],
  80. remove_conn_and_wait_protocol, [{remove, true, 2500}]),
  81. Port1 = ranch:get_port({misc_info, tcp}),
  82. %% Open a few more listeners with different arguments.
  83. {ok, Pid2} = ranch:start_listener({misc_info, act},
  84. ranch_tcp, [{num_acceptors, 2}], active_echo_protocol, {}),
  85. Port2 = ranch:get_port({misc_info, act}),
  86. ranch:set_max_connections({misc_info, act}, infinity),
  87. Opts = ct_helper:get_certs_from_ets(),
  88. {ok, Pid3} = ranch:start_listener({misc_info, ssl},
  89. ranch_ssl, [{num_acceptors, 3}|Opts], echo_protocol, [{}]),
  90. Port3 = ranch:get_port({misc_info, ssl}),
  91. %% Open 5 connections, 3 removed from the count.
  92. {ok, _} = gen_tcp:connect("localhost", Port1, [binary, {active, false}, {packet, raw}]),
  93. {ok, _} = gen_tcp:connect("localhost", Port1, [binary, {active, false}, {packet, raw}]),
  94. {ok, _} = gen_tcp:connect("localhost", Port1, [binary, {active, false}, {packet, raw}]),
  95. receive after 250 -> ok end,
  96. ranch:set_protocol_options({misc_info, tcp}, [{remove, false, 2500}]),
  97. receive after 250 -> ok end,
  98. {ok, _} = gen_tcp:connect("localhost", Port1, [binary, {active, false}, {packet, raw}]),
  99. {ok, _} = gen_tcp:connect("localhost", Port1, [binary, {active, false}, {packet, raw}]),
  100. receive after 250 -> ok end,
  101. %% Confirm the info returned by Ranch is correct.
  102. [
  103. {{misc_info, act}, [
  104. {pid, Pid2},
  105. {ip, _},
  106. {port, Port2},
  107. {num_acceptors, 2},
  108. {max_connections, infinity}, %% Option was modified.
  109. {active_connections, 0},
  110. {all_connections, 0},
  111. {transport, ranch_tcp},
  112. {transport_options, [{num_acceptors, 2}]},
  113. {protocol, active_echo_protocol},
  114. {protocol_options, {}}
  115. ]},
  116. {{misc_info, ssl}, [
  117. {pid, Pid3},
  118. {ip, _},
  119. {port, Port3},
  120. {num_acceptors, 3},
  121. {max_connections, 1024},
  122. {active_connections, 0},
  123. {all_connections, 0},
  124. {transport, ranch_ssl},
  125. {transport_options, [{num_acceptors, 3}|Opts]},
  126. {protocol, echo_protocol},
  127. {protocol_options, [{}]}
  128. ]},
  129. {{misc_info, tcp}, [
  130. {pid, Pid1},
  131. {ip, _},
  132. {port, Port1},
  133. {num_acceptors, 1},
  134. {max_connections, 1024},
  135. {active_connections, 2},
  136. {all_connections, 5},
  137. {transport, ranch_tcp},
  138. {transport_options, [{num_acceptors, 1}]},
  139. {protocol, remove_conn_and_wait_protocol},
  140. {protocol_options, [{remove, false, 2500}]} %% Option was modified.
  141. ]}
  142. ] = lists:sort([L || L={{misc_info, _}, _} <- ranch:info()]),
  143. %% Get acceptors.
  144. [_] = ranch:procs({misc_info, tcp}, acceptors),
  145. [_, _] = ranch:procs({misc_info, act}, acceptors),
  146. [_, _, _] = ranch:procs({misc_info, ssl}, acceptors),
  147. %% Get connections.
  148. [_, _, _, _, _] = ranch:procs({misc_info, tcp}, connections),
  149. [] = ranch:procs({misc_info, act}, connections),
  150. [] = ranch:procs({misc_info, ssl}, connections),
  151. ok.
  152. %% ssl.
  153. ssl_accept_error(_) ->
  154. doc("Acceptor must not crash if client disconnects in the middle of SSL handshake."),
  155. Name = name(),
  156. Opts = ct_helper:get_certs_from_ets(),
  157. {ok, ListenerSup} = ranch:start_listener(Name,
  158. ranch_ssl, [{num_acceptors, 1}|Opts], echo_protocol, []),
  159. Port = ranch:get_port(Name),
  160. ListenerSupChildren = supervisor:which_children(ListenerSup),
  161. {_, AcceptorsSup, _, _} = lists:keyfind(ranch_acceptors_sup, 1, ListenerSupChildren),
  162. [{{acceptor, _, _}, AcceptorPid, _, _}] = supervisor:which_children(AcceptorsSup),
  163. true = is_process_alive(AcceptorPid),
  164. {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  165. ok = gen_tcp:close(Socket),
  166. receive after 500 -> ok end,
  167. true = is_process_alive(AcceptorPid),
  168. ok = ranch:stop_listener(Name).
  169. ssl_accept_socket(_) ->
  170. doc("Ensure that listener can use an externally opened SSL listen socket."),
  171. Name = name(),
  172. Opts = ct_helper:get_certs_from_ets(),
  173. {ok, S} = ssl:listen(0, [binary, {active, false}, {packet, raw}, {reuseaddr, true}|Opts]),
  174. {ok, _} = ranch:start_listener(Name, ranch_ssl, [{socket, S}], echo_protocol, []),
  175. Port = ranch:get_port(Name),
  176. {ok, Socket} = ssl:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  177. ok = ssl:send(Socket, <<"TCP Ranch is working!">>),
  178. {ok, <<"TCP Ranch is working!">>} = ssl:recv(Socket, 21, 1000),
  179. ok = ranch:stop_listener(Name),
  180. {error, closed} = ssl:recv(Socket, 0, 1000),
  181. %% Make sure the listener stopped.
  182. {'EXIT', _} = begin catch ranch:get_port(Name) end,
  183. ok.
  184. ssl_active_echo(_) ->
  185. doc("Ensure that active mode works with SSL transport."),
  186. Name = name(),
  187. Opts = ct_helper:get_certs_from_ets(),
  188. {ok, _} = ranch:start_listener(Name, ranch_ssl, Opts, active_echo_protocol, []),
  189. Port = ranch:get_port(Name),
  190. {ok, Socket} = ssl:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  191. ok = ssl:send(Socket, <<"SSL Ranch is working!">>),
  192. {ok, <<"SSL Ranch is working!">>} = ssl:recv(Socket, 21, 1000),
  193. ok = ranch:stop_listener(Name),
  194. {error, closed} = ssl:recv(Socket, 0, 1000),
  195. %% Make sure the listener stopped.
  196. {'EXIT', _} = begin catch ranch:get_port(Name) end,
  197. ok.
  198. ssl_echo(_) ->
  199. doc("Ensure that passive mode works with SSL transport."),
  200. Name = name(),
  201. Opts = ct_helper:get_certs_from_ets(),
  202. {ok, _} = ranch:start_listener(Name, ranch_ssl, Opts, echo_protocol, []),
  203. Port = ranch:get_port(Name),
  204. {ok, Socket} = ssl:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  205. ok = ssl:send(Socket, <<"SSL Ranch is working!">>),
  206. {ok, <<"SSL Ranch is working!">>} = ssl:recv(Socket, 21, 1000),
  207. ok = ranch:stop_listener(Name),
  208. {error, closed} = ssl:recv(Socket, 0, 1000),
  209. %% Make sure the listener stopped.
  210. {'EXIT', _} = begin catch ranch:get_port(Name) end,
  211. ok.
  212. ssl_sni_echo(_) ->
  213. case application:get_key(ssl, vsn) of
  214. {ok, Vsn} when Vsn >= "7.0" ->
  215. do_ssl_sni_echo();
  216. _ ->
  217. {skip, "No SNI support."}
  218. end.
  219. do_ssl_sni_echo() ->
  220. doc("Ensure that SNI works with SSL transport."),
  221. Name = name(),
  222. Opts = ct_helper:get_certs_from_ets(),
  223. {ok, _} = ranch:start_listener(Name, ranch_ssl, [{sni_hosts, [{"localhost", Opts}]}], echo_protocol, []),
  224. Port = ranch:get_port(Name),
  225. {ok, Socket} = ssl:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  226. ok = ssl:send(Socket, <<"SSL Ranch is working!">>),
  227. {ok, <<"SSL Ranch is working!">>} = ssl:recv(Socket, 21, 1000),
  228. ok = ranch:stop_listener(Name),
  229. {error, closed} = ssl:recv(Socket, 0, 1000),
  230. %% Make sure the listener stopped.
  231. {'EXIT', _} = begin catch ranch:get_port(Name) end,
  232. ok.
  233. ssl_sni_fail(_) ->
  234. case application:get_key(ssl, vsn) of
  235. {ok, Vsn} when Vsn >= "7.0" ->
  236. do_ssl_sni_fail();
  237. _ ->
  238. {skip, "No SNI support."}
  239. end.
  240. do_ssl_sni_fail() ->
  241. doc("Ensure that connection fails when host is not in SNI list."),
  242. Name = name(),
  243. Opts = ct_helper:get_certs_from_ets(),
  244. {ok, _} = ranch:start_listener(Name, ranch_ssl, [{sni_hosts, [{"pouet", Opts}]}], echo_protocol, []),
  245. Port = ranch:get_port(Name),
  246. {error, _} = ssl:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  247. ok = ranch:stop_listener(Name),
  248. %% Make sure the listener stopped.
  249. {'EXIT', _} = begin catch ranch:get_port(Name) end,
  250. ok.
  251. ssl_getopts_capability(_) ->
  252. doc("Ensure getopts/2 capability."),
  253. Name=name(),
  254. Opts=ct_helper:get_certs_from_ets(),
  255. {ok, _}=ranch:start_listener(Name, ranch_ssl, Opts, transport_capabilities_protocol, []),
  256. Port=ranch:get_port(Name),
  257. {ok, Socket}=ssl:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  258. ok=ssl:send(Socket, <<"getopts/2">>),
  259. {ok, <<"OK">>}=ssl:recv(Socket, 0, 1000),
  260. ok=ranch:stop_listener(Name),
  261. {error, closed}=ssl:recv(Socket, 0, 1000),
  262. {'EXIT', _}=begin catch ranch:get_port(Name) end,
  263. ok.
  264. ssl_getstat_capability(_) ->
  265. case application:get_key(ssl, vsn) of
  266. {ok, Vsn} when Vsn>="8.0" ->
  267. do_ssl_getstat_capability();
  268. _ ->
  269. {skip, "No getstat/{1,2} support."}
  270. end.
  271. do_ssl_getstat_capability() ->
  272. doc("Ensure getstat/{1,2} capability."),
  273. Name=name(),
  274. Opts=ct_helper:get_certs_from_ets(),
  275. {ok, _}=ranch:start_listener(Name, ranch_ssl, Opts, transport_capabilities_protocol, []),
  276. Port=ranch:get_port(Name),
  277. {ok, Socket}=ssl:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  278. ok=ssl:send(Socket, <<"getstat/1">>),
  279. {ok, <<"OK">>}=ssl:recv(Socket, 0, 1000),
  280. ok=ssl:send(Socket, <<"getstat/2">>),
  281. {ok, <<"OK">>}=ssl:recv(Socket, 0, 1000),
  282. ok=ranch:stop_listener(Name),
  283. {error, closed}=ssl:recv(Socket, 0, 1000),
  284. {'EXIT', _}=begin catch ranch:get_port(Name) end,
  285. ok.
  286. ssl_error_eaddrinuse(_) ->
  287. doc("Ensure that failure due to an eaddrinuse returns a compact readable error."),
  288. Name = name(),
  289. Opts = ct_helper:get_certs_from_ets(),
  290. {ok, _} = ranch:start_listener(Name, ranch_ssl, Opts, active_echo_protocol, []),
  291. Port = ranch:get_port(Name),
  292. {error, eaddrinuse} = ranch:start_listener({Name, fails},
  293. ranch_ssl, [{port, Port}|Opts], active_echo_protocol, []),
  294. ok = ranch:stop_listener(Name),
  295. %% Make sure the listener stopped.
  296. {'EXIT', _} = begin catch ranch:get_port(Name) end,
  297. ok.
  298. ssl_error_no_cert(_) ->
  299. doc("Ensure that failure due to missing certificate returns a compact readable error."),
  300. {error, no_cert} = ranch:start_listener(name(), ranch_ssl, [], active_echo_protocol, []),
  301. ok.
  302. ssl_error_eacces(_) ->
  303. case os:type() of
  304. {win32, nt} ->
  305. doc("There are no privileged ports on Windows.");
  306. _ ->
  307. doc("Ensure that failure due to an eacces returns a compact readable error."),
  308. Name = name(),
  309. Opts = ct_helper:get_certs_from_ets(),
  310. {error, eacces} = ranch:start_listener(Name,
  311. ranch_ssl, [{port, 283}|Opts], active_echo_protocol, []),
  312. ok
  313. end.
  314. %% tcp.
  315. tcp_accept_socket(_) ->
  316. doc("Ensure that listener can use an externally opened TCP listen socket."),
  317. Name = name(),
  318. {ok, S} = gen_tcp:listen(0, [binary, {active, false}, {packet, raw}, {reuseaddr, true}]),
  319. {ok, _} = ranch:start_listener(Name, ranch_tcp, [{socket, S}], echo_protocol, []),
  320. Port = ranch:get_port(Name),
  321. {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  322. ok = gen_tcp:send(Socket, <<"TCP Ranch is working!">>),
  323. {ok, <<"TCP Ranch is working!">>} = gen_tcp:recv(Socket, 21, 1000),
  324. ok = ranch:stop_listener(Name),
  325. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  326. %% Make sure the listener stopped.
  327. {'EXIT', _} = begin catch ranch:get_port(Name) end,
  328. ok.
  329. tcp_active_echo(_) ->
  330. doc("Ensure that active mode works with TCP transport."),
  331. Name = name(),
  332. {ok, _} = ranch:start_listener(Name, ranch_tcp, [], active_echo_protocol, []),
  333. Port = ranch:get_port(Name),
  334. {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  335. ok = gen_tcp:send(Socket, <<"TCP Ranch is working!">>),
  336. {ok, <<"TCP Ranch is working!">>} = gen_tcp:recv(Socket, 21, 1000),
  337. ok = ranch:stop_listener(Name),
  338. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  339. %% Make sure the listener stopped.
  340. {'EXIT', _} = begin catch ranch:get_port(Name) end,
  341. ok.
  342. tcp_echo(_) ->
  343. doc("Ensure that passive mode works with TCP transport."),
  344. Name = name(),
  345. {ok, _} = ranch:start_listener(Name, ranch_tcp, [], echo_protocol, []),
  346. Port = ranch:get_port(Name),
  347. {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  348. ok = gen_tcp:send(Socket, <<"TCP Ranch is working!">>),
  349. {ok, <<"TCP Ranch is working!">>} = gen_tcp:recv(Socket, 21, 1000),
  350. ok = ranch:stop_listener(Name),
  351. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  352. %% Make sure the listener stopped.
  353. {'EXIT', _} = begin catch ranch:get_port(Name) end,
  354. ok.
  355. tcp_inherit_options(_) ->
  356. doc("Ensure TCP options are inherited in the protocol."),
  357. Name = name(),
  358. Opts = [{nodelay, false}, {send_timeout_close, false}],
  359. {ok, _} = ranch:start_listener(Name, ranch_tcp, Opts, check_tcp_options, [{pid, self()} | Opts]),
  360. Port = ranch:get_port(Name),
  361. {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, true}, {packet, raw}]),
  362. receive checked -> ok after 1000 -> error(timeout) end,
  363. ok = gen_tcp:close(Socket),
  364. ok = ranch:stop_listener(Name).
  365. tcp_max_connections(_) ->
  366. doc("Ensure the max_connections option actually limits connections."),
  367. Name = name(),
  368. {ok, _} = ranch:start_listener(Name,
  369. ranch_tcp, [{max_connections, 10}, {num_acceptors, 1}],
  370. notify_and_wait_protocol, [{msg, connected}, {pid, self()}]),
  371. Port = ranch:get_port(Name),
  372. ok = connect_loop(Port, 11, 150),
  373. 10 = ranch_server:count_connections(Name),
  374. 10 = receive_loop(connected, 400),
  375. 1 = receive_loop(connected, 1000),
  376. ok = ranch:stop_listener(Name).
  377. tcp_max_connections_and_beyond(_) ->
  378. doc("Ensure the max_connections option works when connections are removed from the count."),
  379. Name = name(),
  380. {ok, _} = ranch:start_listener(Name,
  381. ranch_tcp, [{max_connections, 10}, {num_acceptors, 1}],
  382. remove_conn_and_wait_protocol, [{remove, true, 2500}]),
  383. Port = ranch:get_port(Name),
  384. ok = connect_loop(Port, 10, 0),
  385. receive after 250 -> ok end,
  386. 0 = ranch_server:count_connections(Name),
  387. 10 = length(supervisor:which_children(ranch_server:get_connections_sup(Name))),
  388. Counts = supervisor:count_children(ranch_server:get_connections_sup(Name)),
  389. {_, 1} = lists:keyfind(specs, 1, Counts),
  390. {_, 0} = lists:keyfind(supervisors, 1, Counts),
  391. {_, 10} = lists:keyfind(active, 1, Counts),
  392. {_, 10} = lists:keyfind(workers, 1, Counts),
  393. ranch:set_protocol_options(Name, [{remove, false, 2500}]),
  394. receive after 250 -> ok end,
  395. ok = connect_loop(Port, 10, 0),
  396. receive after 250 -> ok end,
  397. 10 = ranch_server:count_connections(Name),
  398. 20 = length(supervisor:which_children(ranch_server:get_connections_sup(Name))),
  399. Counts2 = supervisor:count_children(ranch_server:get_connections_sup(Name)),
  400. {_, 20} = lists:keyfind(active, 1, Counts2),
  401. {_, 20} = lists:keyfind(workers, 1, Counts2),
  402. ok = ranch:stop_listener(Name).
  403. tcp_max_connections_infinity(_) ->
  404. doc("Set the max_connections option from 10 to infinity and back to 10."),
  405. Name = name(),
  406. {ok, _} = ranch:start_listener(Name,
  407. ranch_tcp, [{max_connections, 10}, {num_acceptors, 1}],
  408. notify_and_wait_protocol, [{msg, connected}, {pid, self()}]),
  409. Port = ranch:get_port(Name),
  410. ok = connect_loop(Port, 20, 0),
  411. 10 = ranch_server:count_connections(Name),
  412. 10 = receive_loop(connected, 1000),
  413. 10 = ranch_server:count_connections(Name),
  414. 10 = ranch:get_max_connections(Name),
  415. ranch:set_max_connections(Name, infinity),
  416. receive after 250 -> ok end,
  417. 20 = ranch_server:count_connections(Name),
  418. infinity = ranch:get_max_connections(Name),
  419. ranch:set_max_connections(Name, 10),
  420. 20 = ranch_server:count_connections(Name),
  421. 10 = receive_loop(connected, 1000),
  422. ok = ranch:stop_listener(Name).
  423. tcp_remove_connections(_) ->
  424. doc("Ensure that removed connections are only removed once."),
  425. Name = name(),
  426. {ok, _} = ranch:start_listener(Name,
  427. ranch_tcp, [],
  428. remove_conn_and_wait_protocol, [{remove, true, 0}]),
  429. Port = ranch:get_port(Name),
  430. ok = connect_loop(Port, 10, 0),
  431. receive after 250 -> ok end,
  432. 0 = ranch_server:count_connections(Name),
  433. ok = ranch:stop_listener(Name).
  434. tcp_set_max_connections(_) ->
  435. doc("Ensure that changing the max_connections option to a larger value allows for more connections."),
  436. Name = name(),
  437. {ok, _} = ranch:start_listener(Name,
  438. ranch_tcp, [{max_connections, 10}, {num_acceptors, 1}],
  439. notify_and_wait_protocol, [{msg, connected}, {pid, self()}]),
  440. Port = ranch:get_port(Name),
  441. ok = connect_loop(Port, 20, 0),
  442. 10 = ranch_server:count_connections(Name),
  443. 10 = receive_loop(connected, 1000),
  444. 10 = ranch:get_max_connections(Name),
  445. ranch:set_max_connections(Name, 20),
  446. 10 = receive_loop(connected, 1000),
  447. 20 = ranch:get_max_connections(Name),
  448. ok = ranch:stop_listener(Name).
  449. tcp_set_max_connections_clean(Config) ->
  450. case code:is_module_native(?MODULE) of
  451. true -> doc("This test uses tracing and is not compatible with native code.");
  452. false -> do_tcp_set_max_connections_clean(Config)
  453. end.
  454. do_tcp_set_max_connections_clean(_) ->
  455. doc("Ensure that setting max_connections does not crash any process."),
  456. Name = name(),
  457. {ok, ListSupPid} = ranch:start_listener(Name, ranch_tcp,
  458. [{max_connections, 4}],
  459. notify_and_wait_protocol, [{msg, connected}, {pid, self()}]),
  460. Children = supervisor:which_children(ListSupPid),
  461. {_, AccSupPid, _, _} = lists:keyfind(ranch_acceptors_sup, 1, Children),
  462. 1 = erlang:trace(ListSupPid, true, [procs]),
  463. 1 = erlang:trace(AccSupPid, true, [procs]),
  464. Port = ranch:get_port(Name),
  465. N = 20,
  466. ok = connect_loop(Port, N*5, 0),
  467. %% Randomly set max_connections.
  468. [spawn(ranch, set_max_connections, [Name, Max]) ||
  469. Max <- lists:flatten(lists:duplicate(N, [6, 4, 8, infinity]))],
  470. receive
  471. {trace, _, spawn, _, _} ->
  472. error(dirty_set_max_connections)
  473. after
  474. 2000 -> ok
  475. end,
  476. _ = erlang:trace(all, false, [all]),
  477. ok = clean_traces(),
  478. ok = ranch:stop_listener(Name).
  479. tcp_getopts_capability(_) ->
  480. doc("Ensure getopts/2 capability."),
  481. Name=name(),
  482. {ok, _}=ranch:start_listener(Name, ranch_tcp, [], transport_capabilities_protocol, []),
  483. Port=ranch:get_port(Name),
  484. {ok, Socket}=gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  485. ok=gen_tcp:send(Socket, <<"getopts/2">>),
  486. {ok, <<"OK">>}=gen_tcp:recv(Socket, 0, 1000),
  487. ok=ranch:stop_listener(Name),
  488. {error, closed}=gen_tcp:recv(Socket, 0, 1000),
  489. {'EXIT', _}=begin catch ranch:get_port(Name) end,
  490. ok.
  491. tcp_getstat_capability(_) ->
  492. doc("Ensure getstat/{1,2} capability."),
  493. Name=name(),
  494. {ok, _}=ranch:start_listener(Name, ranch_tcp, [], transport_capabilities_protocol, []),
  495. Port=ranch:get_port(Name),
  496. {ok, Socket}=gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  497. ok=gen_tcp:send(Socket, <<"getstat/1">>),
  498. {ok, <<"OK">>}=gen_tcp:recv(Socket, 0, 1000),
  499. ok=gen_tcp:send(Socket, <<"getstat/2">>),
  500. {ok, <<"OK">>}=gen_tcp:recv(Socket, 0, 1000),
  501. ok=ranch:stop_listener(Name),
  502. {error, closed}=gen_tcp:recv(Socket, 0, 1000),
  503. {'EXIT', _}=begin catch ranch:get_port(Name) end,
  504. ok.
  505. tcp_upgrade(_) ->
  506. doc("Ensure that protocol options can be updated."),
  507. Name = name(),
  508. {ok, _} = ranch:start_listener(Name,
  509. ranch_tcp, [],
  510. notify_and_wait_protocol, [{msg, connected}, {pid, self()}]),
  511. Port = ranch:get_port(Name),
  512. ok = connect_loop(Port, 1, 0),
  513. receive connected -> ok after 1000 -> error(timeout) end,
  514. ranch:set_protocol_options(Name, [{msg, upgraded}, {pid, self()}]),
  515. ok = connect_loop(Port, 1, 0),
  516. receive upgraded -> ok after 1000 -> error(timeout) end,
  517. ok = ranch:stop_listener(Name).
  518. tcp_error_eaddrinuse(_) ->
  519. doc("Ensure that failure due to an eaddrinuse returns a compact readable error."),
  520. Name = name(),
  521. {ok, _} = ranch:start_listener(Name, ranch_tcp, [], active_echo_protocol, []),
  522. Port = ranch:get_port(Name),
  523. {error, eaddrinuse} = ranch:start_listener({Name, fails},
  524. ranch_tcp, [{port, Port}], active_echo_protocol, []),
  525. ok = ranch:stop_listener(Name),
  526. %% Make sure the listener stopped.
  527. {'EXIT', _} = begin catch ranch:get_port(Name) end,
  528. ok.
  529. tcp_error_eacces(_) ->
  530. case os:type() of
  531. {win32, nt} ->
  532. doc("There are no privileged ports on Windows.");
  533. _ ->
  534. doc("Ensure that failure due to an eacces returns a compact readable error."),
  535. Name = name(),
  536. {error, eacces} = ranch:start_listener(Name,
  537. ranch_tcp, [{port, 283}], active_echo_protocol, []),
  538. ok
  539. end.
  540. %% Supervisor tests
  541. connection_type_supervisor(_) ->
  542. doc("The supervisor connection type must be reflected in the specifications."),
  543. Name = name(),
  544. {ok, _} = ranch:start_listener(Name,
  545. ranch_tcp, [{connection_type, supervisor}],
  546. echo_protocol, []),
  547. Port = ranch:get_port(Name),
  548. {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  549. ok = gen_tcp:send(Socket, <<"TCP Ranch is working!">>),
  550. {ok, <<"TCP Ranch is working!">>} = gen_tcp:recv(Socket, 21, 1000),
  551. ConnsSup = ranch_server:get_connections_sup(Name),
  552. [{echo_protocol, _, supervisor, [echo_protocol]}] = supervisor:which_children(ConnsSup),
  553. ok = ranch:stop_listener(Name),
  554. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  555. %% Make sure the listener stopped.
  556. {'EXIT', _} = begin catch ranch:get_port(Name) end,
  557. ok.
  558. connection_type_supervisor_separate_from_connection(_) ->
  559. doc("The supervisor connection type allows separate supervised and connection processes."),
  560. Name = name(),
  561. {ok, _} = ranch:start_listener(Name,
  562. ranch_tcp, [{connection_type, supervisor}],
  563. supervisor_separate, []),
  564. Port = ranch:get_port(Name),
  565. {ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {active, false}, {packet, raw}]),
  566. ok = gen_tcp:send(Socket, <<"TCP Ranch is working!">>),
  567. {ok, <<"TCP Ranch is working!">>} = gen_tcp:recv(Socket, 21, 1000),
  568. ConnsSup = ranch_server:get_connections_sup(Name),
  569. [{supervisor_separate, _, supervisor, [supervisor_separate]}] = supervisor:which_children(ConnsSup),
  570. ok = ranch:stop_listener(Name),
  571. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  572. %% Make sure the listener stopped.
  573. {'EXIT', _} = begin catch ranch:get_port(Name) end,
  574. ok.
  575. supervisor_clean_child_restart(Config) ->
  576. case code:is_module_native(?MODULE) of
  577. true -> doc("This test uses tracing and is not compatible with native code.");
  578. false -> do_supervisor_clean_child_restart(Config)
  579. end.
  580. do_supervisor_clean_child_restart(_) ->
  581. doc("Verify that only the relevant parts of the supervision tree restarted "
  582. "when the listening socket is closed."),
  583. Name = name(),
  584. %% Trace socket allocations.
  585. _ = erlang:trace(new, true, [call]),
  586. 1 = erlang:trace_pattern({ranch_tcp, listen, 1},
  587. [{'_', [], [{return_trace}]}], [global]),
  588. {ok, Pid} = ranch:start_listener(Name,
  589. ranch_tcp, [{num_acceptors, 1}], echo_protocol, []),
  590. %% Trace supervisor spawns.
  591. 1 = erlang:trace(Pid, true, [procs, set_on_spawn]),
  592. ConnsSup = ranch_server:get_connections_sup(Name),
  593. %% Manually shut the listening socket down.
  594. LSocket = receive
  595. {trace, _, return_from, {ranch_tcp, listen, 1}, {ok, Socket}} ->
  596. Socket
  597. after 0 ->
  598. error(lsocket_unknown)
  599. end,
  600. ok = gen_tcp:close(LSocket),
  601. receive after 1000 -> ok end,
  602. %% Verify that supervisor and its first two children are alive.
  603. true = is_process_alive(Pid),
  604. true = is_process_alive(ConnsSup),
  605. %% Check that acceptors_sup is restarted properly.
  606. AccSupPid = receive {trace, Pid, spawn, Pid1, _} -> Pid1 end,
  607. receive {trace, AccSupPid, spawn, _, _} -> ok end,
  608. %% No more traces then.
  609. receive
  610. {trace, _, spawn, _, _} -> error(invalid_restart)
  611. after 1000 -> ok end,
  612. %% Verify that children still registered right.
  613. ConnsSup = ranch_server:get_connections_sup(Name),
  614. _ = erlang:trace_pattern({ranch_tcp, listen, 1}, false, []),
  615. _ = erlang:trace(all, false, [all]),
  616. ok = clean_traces(),
  617. ok = ranch:stop_listener(Name).
  618. supervisor_clean_conns_sup_restart(_) ->
  619. doc("Verify that a conns_sup can not register with the same name as an already "
  620. "registered ranch_conns_sup that is still alive. Make sure this does not crash "
  621. "the ranch_server process."),
  622. Name = name(),
  623. {ok, _} = ranch:start_listener(Name,
  624. ranch_tcp, [], echo_protocol, []),
  625. Server = erlang:whereis(ranch_server),
  626. ServerMonRef = erlang:monitor(process, Server),
  627. %% Exit because Name already registered and is alive.
  628. {'EXIT', _} = (catch ranch_server:set_connections_sup(Name, self())),
  629. receive
  630. {'DOWN', ServerMonRef, process, Server, _} ->
  631. error(ranch_server_down)
  632. after
  633. 1000 ->
  634. ok
  635. end,
  636. ok = ranch:stop_listener(Name).
  637. supervisor_clean_restart(Config) ->
  638. case code:is_module_native(?MODULE) of
  639. true -> doc("This test uses tracing and is not compatible with native code.");
  640. false -> do_supervisor_clean_restart(Config)
  641. end.
  642. do_supervisor_clean_restart(_) ->
  643. doc("Verify that killing ranch_conns_sup does not crash everything "
  644. "and that it restarts properly."),
  645. Name = name(),
  646. NumAcc = 4,
  647. {ok, Pid} = ranch:start_listener(Name,
  648. ranch_tcp, [{num_acceptors, NumAcc}], echo_protocol, []),
  649. %% Trace supervisor spawns.
  650. 1 = erlang:trace(Pid, true, [procs, set_on_spawn]),
  651. ConnsSup0 = ranch_server:get_connections_sup(Name),
  652. erlang:exit(ConnsSup0, kill),
  653. receive after 1000 -> ok end,
  654. %% Verify that supervisor is alive
  655. true = is_process_alive(Pid),
  656. %% ...but children are dead.
  657. false = is_process_alive(ConnsSup0),
  658. %% Receive traces from newly started children
  659. ConnsSup = receive {trace, Pid, spawn, Pid2, _} -> Pid2 end,
  660. AccSupPid = receive {trace, Pid, spawn, Pid3, _} -> Pid3 end,
  661. %% ...and its acceptors.
  662. [receive {trace, AccSupPid, spawn, _Pid, _} -> ok end ||
  663. _ <- lists:seq(1, NumAcc)],
  664. %% No more traces then.
  665. receive
  666. {trace, EPid, spawn, _, _} when EPid == Pid; EPid == AccSupPid ->
  667. error(invalid_restart)
  668. after 1000 -> ok end,
  669. %% Verify that new children registered themselves properly.
  670. ConnsSup = ranch_server:get_connections_sup(Name),
  671. _ = erlang:trace(all, false, [all]),
  672. ok = clean_traces(),
  673. ok = ranch:stop_listener(Name).
  674. supervisor_conns_alive(Config) ->
  675. case code:is_module_native(?MODULE) of
  676. true -> doc("This test uses tracing and is not compatible with native code.");
  677. false -> do_supervisor_conns_alive(Config)
  678. end.
  679. do_supervisor_conns_alive(_) ->
  680. doc("Ensure that active connections stay open when the listening socket gets closed."),
  681. Name = name(),
  682. _ = erlang:trace(new, true, [call]),
  683. 1 = erlang:trace_pattern({ranch_tcp, listen, 1},
  684. [{'_', [], [{return_trace}]}], [global]),
  685. {ok, _} = ranch:start_listener(Name,
  686. ranch_tcp, [],
  687. remove_conn_and_wait_protocol, [{remove, false, 2500}]),
  688. %% Get the listener socket
  689. LSocket = receive
  690. {trace, _, return_from, {ranch_tcp, listen, 1}, {ok, S}} ->
  691. S
  692. after 500 ->
  693. error(lsocket_unknown)
  694. end,
  695. TcpPort = ranch:get_port(Name),
  696. {ok, Socket} = gen_tcp:connect("localhost", TcpPort,
  697. [binary, {active, true}, {packet, raw}]),
  698. receive after 500 -> ok end,
  699. %% Shut the socket down
  700. ok = gen_tcp:close(LSocket),
  701. %% Assert that client is still viable.
  702. receive {tcp_closed, _} -> error(closed) after 1500 -> ok end,
  703. ok = gen_tcp:send(Socket, <<"poke">>),
  704. receive {tcp_closed, _} -> ok end,
  705. _ = erlang:trace(all, false, [all]),
  706. ok = clean_traces(),
  707. ok = ranch:stop_listener(Name).
  708. supervisor_protocol_start_link_crash(_) ->
  709. doc("Ensure a protocol start crash does not kill all connections."),
  710. Name = name(),
  711. {ok, _} = ranch:start_listener(Name, ranch_tcp, [], crash_protocol, []),
  712. ConnsSup = ranch_server:get_connections_sup(Name),
  713. Port = ranch:get_port(Name),
  714. {ok, _} = gen_tcp:connect("localhost", Port, [binary, {active, true}, {packet, raw}]),
  715. receive after 500 -> ok end,
  716. ConnsSup = ranch_server:get_connections_sup(Name),
  717. ok = ranch:stop_listener(Name).
  718. supervisor_server_recover_state(Config) ->
  719. case code:is_module_native(?MODULE) of
  720. true -> doc("This test uses tracing and is not compatible with native code.");
  721. false -> do_supervisor_server_recover_state(Config)
  722. end.
  723. do_supervisor_server_recover_state(_) ->
  724. doc("Ensure that when ranch_server crashes and restarts, it recovers "
  725. "its state and continues monitoring the same processes."),
  726. Name = name(),
  727. _ = erlang:trace(new, true, [call]),
  728. 1 = erlang:trace_pattern({ranch_server, init, 1},
  729. [{'_', [], [{return_trace}]}], [global]),
  730. {ok, _} = ranch:start_listener(Name, ranch_tcp, [], echo_protocol, []),
  731. ConnsSup = ranch_server:get_connections_sup(Name),
  732. ServerPid = erlang:whereis(ranch_server),
  733. {monitors, Monitors} = erlang:process_info(ServerPid, monitors),
  734. erlang:exit(ServerPid, kill),
  735. receive
  736. {trace, ServerPid2, return_from, {ranch_server, init, 1}, _Result} ->
  737. {monitors, Monitors2} = erlang:process_info(ServerPid2, monitors),
  738. %% Check that ranch_server is monitoring the same processes.
  739. true = (lists:usort(Monitors) == lists:usort(Monitors2))
  740. after
  741. 1000 ->
  742. error(timeout)
  743. end,
  744. ConnsSup = ranch_server:get_connections_sup(Name),
  745. ok = ranch:stop_listener(Name),
  746. %% Check ranch_server has removed the ranch_conns_sup.
  747. {'EXIT', {badarg, _}} = (catch ranch_server:get_connections_sup(Name)),
  748. _ = erlang:trace(all, false, [all]),
  749. ok = clean_traces().
  750. %% Utility functions.
  751. connect_loop(_, 0, _) ->
  752. ok;
  753. connect_loop(Port, N, Sleep) ->
  754. {ok, _} = gen_tcp:connect("localhost", Port,
  755. [binary, {active, false}, {packet, raw}]),
  756. receive after Sleep -> ok end,
  757. connect_loop(Port, N - 1, Sleep).
  758. receive_loop(Message, Timeout) ->
  759. receive_loop(Message, Timeout, 0).
  760. receive_loop(Message, Timeout, N) ->
  761. receive Message ->
  762. receive_loop(Message, Timeout, N + 1)
  763. after Timeout ->
  764. N
  765. end.
  766. clean_traces() ->
  767. receive
  768. {trace, _, _, _} ->
  769. clean_traces();
  770. {trace, _, _, _, _} ->
  771. clean_traces()
  772. after 0 ->
  773. ok
  774. end.