acceptor_SUITE.erl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. %% Copyright (c) 2011-2012, 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. -include_lib("common_test/include/ct.hrl").
  16. %% ct.
  17. -export([all/0]).
  18. -export([groups/0]).
  19. -export([init_per_suite/1]).
  20. -export([end_per_suite/1]).
  21. -export([init_per_group/2]).
  22. -export([end_per_group/2]).
  23. %% misc.
  24. -export([misc_bad_transport/1]).
  25. %% ssl.
  26. -export([ssl_accept_error/1]).
  27. -export([ssl_accept_socket/1]).
  28. -export([ssl_active_echo/1]).
  29. -export([ssl_echo/1]).
  30. %% tcp.
  31. -export([tcp_accept_socket/1]).
  32. -export([tcp_active_echo/1]).
  33. -export([tcp_echo/1]).
  34. -export([tcp_max_connections/1]).
  35. -export([tcp_max_connections_and_beyond/1]).
  36. -export([tcp_set_max_connections/1]).
  37. -export([tcp_upgrade/1]).
  38. %% supervisor.
  39. -export([supervisor_clean_restart/1]).
  40. -export([supervisor_clean_child_restart/1]).
  41. -export([supervisor_conns_alive/1]).
  42. %% ct.
  43. all() ->
  44. [{group, tcp}, {group, ssl}, {group, misc}, {group, supervisor}].
  45. groups() ->
  46. [{tcp, [
  47. tcp_accept_socket,
  48. tcp_active_echo,
  49. tcp_echo,
  50. tcp_max_connections,
  51. tcp_max_connections_and_beyond,
  52. tcp_set_max_connections,
  53. tcp_upgrade
  54. ]}, {ssl, [
  55. ssl_accept_error,
  56. ssl_accept_socket,
  57. ssl_active_echo,
  58. ssl_echo
  59. ]}, {misc, [
  60. misc_bad_transport
  61. ]}, {supervisor, [
  62. supervisor_clean_restart,
  63. supervisor_clean_child_restart,
  64. supervisor_conns_alive
  65. ]}].
  66. init_per_suite(Config) ->
  67. ok = application:start(ranch),
  68. Config.
  69. end_per_suite(_) ->
  70. application:stop(ranch),
  71. ok.
  72. init_per_group(ssl, Config) ->
  73. application:start(crypto),
  74. application:start(public_key),
  75. application:start(ssl),
  76. Config;
  77. init_per_group(_, Config) ->
  78. Config.
  79. end_per_group(ssl, _) ->
  80. application:stop(ssl),
  81. application:stop(public_key),
  82. application:stop(crypto),
  83. ok;
  84. end_per_group(_, _) ->
  85. ok.
  86. %% misc.
  87. misc_bad_transport(_) ->
  88. {error, badarg} = ranch:start_listener(misc_bad_transport, 1,
  89. bad_transport, [{port, 0}],
  90. echo_protocol, []),
  91. ok.
  92. %% ssl.
  93. ssl_accept_error(Config) ->
  94. {ok, _} = ranch:start_listener(ssl_accept_error, 1,
  95. ranch_ssl, [{port, 0},
  96. {certfile, ?config(data_dir, Config) ++ "cert.pem"}],
  97. echo_protocol, []),
  98. Port = ranch:get_port(ssl_accept_error),
  99. [AcceptorPid] = ets:lookup_element(ranch_server,
  100. {acceptors, ssl_accept_error}, 2),
  101. true = is_process_alive(AcceptorPid),
  102. {ok, Socket} = gen_tcp:connect("localhost", Port,
  103. [binary, {active, false}, {packet, raw}]),
  104. ok = gen_tcp:close(Socket),
  105. receive after 500 -> ok end,
  106. true = is_process_alive(AcceptorPid).
  107. ssl_accept_socket(Config) ->
  108. %%% XXX we can't do the spawn to test the controlling process change
  109. %%% because of the bug in ssl
  110. {ok, S} = ssl:listen(0,
  111. [{certfile, ?config(data_dir, Config) ++ "cert.pem"}, binary,
  112. {active, false}, {packet, raw}, {reuseaddr, true}]),
  113. {ok, _} = ranch:start_listener(ssl_accept_socket, 1,
  114. ranch_ssl, [{socket, S}], echo_protocol, []),
  115. Port = ranch:get_port(ssl_accept_socket),
  116. {ok, Socket} = ssl:connect("localhost", Port,
  117. [binary, {active, false}, {packet, raw},
  118. {certfile, ?config(data_dir, Config) ++ "cert.pem"}]),
  119. ok = ssl:send(Socket, <<"TCP Ranch is working!">>),
  120. {ok, <<"TCP Ranch is working!">>} = ssl:recv(Socket, 21, 1000),
  121. ok = ranch:stop_listener(ssl_accept_socket),
  122. {error, closed} = ssl:recv(Socket, 0, 1000),
  123. %% Make sure the listener stopped.
  124. {'EXIT', _} = begin catch ranch:get_port(ssl_accept_socket) end,
  125. ok.
  126. ssl_active_echo(Config) ->
  127. {ok, _} = ranch:start_listener(ssl_active_echo, 1,
  128. ranch_ssl, [{port, 0},
  129. {certfile, ?config(data_dir, Config) ++ "cert.pem"}],
  130. active_echo_protocol, []),
  131. Port = ranch:get_port(ssl_active_echo),
  132. {ok, Socket} = ssl:connect("localhost", Port,
  133. [binary, {active, false}, {packet, raw},
  134. {certfile, ?config(data_dir, Config) ++ "cert.pem"}]),
  135. ok = ssl:send(Socket, <<"SSL Ranch is working!">>),
  136. {ok, <<"SSL Ranch is working!">>} = ssl:recv(Socket, 21, 1000),
  137. ok = ranch:stop_listener(ssl_active_echo),
  138. {error, closed} = ssl:recv(Socket, 0, 1000),
  139. %% Make sure the listener stopped.
  140. {'EXIT', _} = begin catch ranch:get_port(ssl_active_echo) end,
  141. ok.
  142. ssl_echo(Config) ->
  143. {ok, _} = ranch:start_listener(ssl_echo, 1,
  144. ranch_ssl, [{port, 0},
  145. {certfile, ?config(data_dir, Config) ++ "cert.pem"}],
  146. echo_protocol, []),
  147. Port = ranch:get_port(ssl_echo),
  148. {ok, Socket} = ssl:connect("localhost", Port,
  149. [binary, {active, false}, {packet, raw},
  150. {certfile, ?config(data_dir, Config) ++ "cert.pem"}]),
  151. ok = ssl:send(Socket, <<"SSL Ranch is working!">>),
  152. {ok, <<"SSL Ranch is working!">>} = ssl:recv(Socket, 21, 1000),
  153. ok = ranch:stop_listener(ssl_echo),
  154. {error, closed} = ssl:recv(Socket, 0, 1000),
  155. %% Make sure the listener stopped.
  156. {'EXIT', _} = begin catch ranch:get_port(ssl_echo) end,
  157. ok.
  158. %% tcp.
  159. tcp_accept_socket(_) ->
  160. Ref = make_ref(),
  161. Parent = self(),
  162. spawn(fun() ->
  163. {ok, S} = gen_tcp:listen(0, [binary, {active, false}, {packet, raw},
  164. {reuseaddr, true}]),
  165. {ok, _} = ranch:start_listener(tcp_accept_socket, 1,
  166. ranch_tcp, [{socket, S}], echo_protocol, []),
  167. Parent ! Ref
  168. end),
  169. receive
  170. Ref -> ok
  171. end,
  172. Port = ranch:get_port(tcp_accept_socket),
  173. {ok, Socket} = gen_tcp:connect("localhost", Port,
  174. [binary, {active, false}, {packet, raw}]),
  175. ok = gen_tcp:send(Socket, <<"TCP Ranch is working!">>),
  176. {ok, <<"TCP Ranch is working!">>} = gen_tcp:recv(Socket, 21, 1000),
  177. ok = ranch:stop_listener(tcp_accept_socket),
  178. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  179. %% Make sure the listener stopped.
  180. {'EXIT', _} = begin catch ranch:get_port(tcp_accept_socket) end,
  181. ok.
  182. tcp_active_echo(_) ->
  183. {ok, _} = ranch:start_listener(tcp_active_echo, 1,
  184. ranch_tcp, [{port, 0}], active_echo_protocol, []),
  185. Port = ranch:get_port(tcp_active_echo),
  186. {ok, Socket} = gen_tcp:connect("localhost", Port,
  187. [binary, {active, false}, {packet, raw}]),
  188. ok = gen_tcp:send(Socket, <<"TCP Ranch is working!">>),
  189. {ok, <<"TCP Ranch is working!">>} = gen_tcp:recv(Socket, 21, 1000),
  190. ok = ranch:stop_listener(tcp_active_echo),
  191. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  192. %% Make sure the listener stopped.
  193. {'EXIT', _} = begin catch ranch:get_port(tcp_active_echo) end,
  194. ok.
  195. tcp_echo(_) ->
  196. {ok, _} = ranch:start_listener(tcp_echo, 1,
  197. ranch_tcp, [{port, 0}], echo_protocol, []),
  198. Port = ranch:get_port(tcp_echo),
  199. {ok, Socket} = gen_tcp:connect("localhost", Port,
  200. [binary, {active, false}, {packet, raw}]),
  201. ok = gen_tcp:send(Socket, <<"TCP Ranch is working!">>),
  202. {ok, <<"TCP Ranch is working!">>} = gen_tcp:recv(Socket, 21, 1000),
  203. ok = ranch:stop_listener(tcp_echo),
  204. {error, closed} = gen_tcp:recv(Socket, 0, 1000),
  205. %% Make sure the listener stopped.
  206. {'EXIT', _} = begin catch ranch:get_port(tcp_echo) end,
  207. ok.
  208. tcp_max_connections(_) ->
  209. {ok, _} = ranch:start_listener(tcp_max_connections, 1,
  210. ranch_tcp, [{port, 0}, {max_connections, 10}],
  211. notify_and_wait_protocol, [{msg, connected}, {pid, self()}]),
  212. Port = ranch:get_port(tcp_max_connections),
  213. %% @todo We'll probably want a more direct interface to count_connections.
  214. ListenerPid = ranch_server:lookup_listener(tcp_max_connections),
  215. ok = connect_loop(Port, 11, 150),
  216. 10 = ranch_server:count_connections(ListenerPid),
  217. 10 = receive_loop(connected, 400),
  218. 1 = receive_loop(connected, 1000).
  219. tcp_max_connections_and_beyond(_) ->
  220. {ok, _} = ranch:start_listener(tcp_max_connections_and_beyond, 1,
  221. ranch_tcp, [{port, 0}, {max_connections, 10}],
  222. remove_conn_and_wait_protocol, [{remove, true}]),
  223. Port = ranch:get_port(tcp_max_connections_and_beyond),
  224. %% @todo We'll probably want a more direct interface to count_connections.
  225. ListenerPid = ranch_server:lookup_listener(tcp_max_connections_and_beyond),
  226. ok = connect_loop(Port, 10, 0),
  227. 0 = ranch_server:count_connections(ListenerPid),
  228. ranch:set_protocol_options(tcp_max_connections_and_beyond,
  229. [{remove, false}]),
  230. receive after 500 -> ok end,
  231. ok = connect_loop(Port, 10, 0),
  232. receive after 500 -> ok end,
  233. 10 = ranch_server:count_connections(ListenerPid).
  234. tcp_set_max_connections(_) ->
  235. {ok, _} = ranch:start_listener(tcp_set_max_connections, 1,
  236. ranch_tcp, [{port, 0}, {max_connections, 10}],
  237. notify_and_wait_protocol, [{msg, connected}, {pid, self()}]),
  238. Port = ranch:get_port(tcp_set_max_connections),
  239. %% @todo We'll probably want a more direct interface to count_connections.
  240. ListenerPid = ranch_server:lookup_listener(tcp_set_max_connections),
  241. ok = connect_loop(Port, 20, 0),
  242. 10 = ranch_server:count_connections(ListenerPid),
  243. 10 = receive_loop(connected, 1000),
  244. 10 = ranch:get_max_connections(tcp_set_max_connections),
  245. ranch:set_max_connections(tcp_set_max_connections, 20),
  246. 10 = receive_loop(connected, 1000),
  247. 20 = ranch:get_max_connections(tcp_set_max_connections).
  248. tcp_upgrade(_) ->
  249. receive after 20000 -> ok end,
  250. {ok, _} = ranch:start_listener(tcp_upgrade, 1,
  251. ranch_tcp, [{port, 0}],
  252. notify_and_wait_protocol, [{msg, connected}, {pid, self()}]),
  253. Port = ranch:get_port(tcp_upgrade),
  254. ok = connect_loop(Port, 1, 0),
  255. receive connected -> ok after 1000 -> error(timeout) end,
  256. ranch:set_protocol_options(tcp_upgrade, [{msg, upgraded}, {pid, self()}]),
  257. ok = connect_loop(Port, 1, 0),
  258. receive upgraded -> ok after 1000 -> error(timeout) end.
  259. %% Supervisor tests
  260. supervisor_clean_restart(_) ->
  261. %% There we verify that mature listener death will not let
  262. %% whole supervisor down and also the supervisor itself will
  263. %% restart everything properly.
  264. Ref = supervisor_clean_restart,
  265. NbAcc = 4,
  266. {ok, Pid} = ranch:start_listener(Ref,
  267. NbAcc, ranch_tcp, [{port, 0}], echo_protocol, []),
  268. %% Trace supervisor spawns.
  269. 1 = erlang:trace(Pid, true, [procs, set_on_spawn]),
  270. ListenerPid0 = ranch_server:lookup_listener(Ref),
  271. erlang:exit(ListenerPid0, kill),
  272. receive after 1000 -> ok end,
  273. %% Verify that supervisor is alive
  274. true = is_process_alive(Pid),
  275. %% ...but children are dead.
  276. false = is_process_alive(ListenerPid0),
  277. %% Receive traces from newly started children
  278. ListenerPid = receive {trace, Pid, spawn, Pid1, _} -> Pid1 end,
  279. _ConnSupPid = receive {trace, Pid, spawn, Pid2, _} -> Pid2 end,
  280. AccSupPid = receive {trace, Pid, spawn, Pid3, _} -> Pid3 end,
  281. %% ...and its acceptors.
  282. [receive {trace, AccSupPid, spawn, _Pid, _} -> ok end ||
  283. _ <- lists:seq(1, NbAcc)],
  284. %% No more traces then.
  285. receive
  286. {trace, EPid, spawn, _, _} when EPid == Pid; EPid == AccSupPid ->
  287. error(invalid_restart)
  288. after 1000 -> ok end,
  289. %% Verify that new children registered themselves properly.
  290. ListenerPid = ranch_server:lookup_listener(Ref),
  291. _ = erlang:trace(all, false, [all]),
  292. ok = clean_traces().
  293. supervisor_clean_child_restart(_) ->
  294. %% Then we verify that only parts of the supervision tree
  295. %% restarted in the case of failure.
  296. Ref = supervisor_clean_child_restart,
  297. %% Trace socket allocations.
  298. _ = erlang:trace(new, true, [call]),
  299. 1 = erlang:trace_pattern({ranch_tcp, listen, 1}, [{'_', [], [{return_trace}]}], [global]),
  300. {ok, Pid} = ranch:start_listener(Ref,
  301. 1, ranch_tcp, [{port, 0}], echo_protocol, []),
  302. %% Trace supervisor spawns.
  303. 1 = erlang:trace(Pid, true, [procs, set_on_spawn]),
  304. ListenerPid0 = ranch_server:lookup_listener(Ref),
  305. %% Manually shut the listening socket down.
  306. LSocket = receive
  307. {trace, _, return_from, {ranch_tcp, listen, 1}, {ok, Socket}} ->
  308. Socket
  309. after 0 ->
  310. error(lsocket_unknown)
  311. end,
  312. ok = gen_tcp:close(LSocket),
  313. receive after 1000 -> ok end,
  314. %% Verify that supervisor and its first two children are alive.
  315. true = is_process_alive(Pid),
  316. true = is_process_alive(ListenerPid0),
  317. %% Check that acceptors_sup is restarted properly.
  318. AccSupPid = receive {trace, Pid, spawn, Pid1, _} -> Pid1 end,
  319. AccPid = receive {trace, AccSupPid, spawn, Pid2, _} -> Pid2 end,
  320. receive {trace, AccPid, spawn, _, _} -> ok end,
  321. %% No more traces then.
  322. receive
  323. {trace, _, spawn, _, _} -> error(invalid_restart)
  324. after 1000 -> ok end,
  325. %% Verify that children still registered right.
  326. ListenerPid0 = ranch_server:lookup_listener(Ref),
  327. _ = erlang:trace_pattern({ranch_tcp, listen, 1}, false, []),
  328. _ = erlang:trace(all, false, [all]),
  329. ok = clean_traces(),
  330. ok.
  331. supervisor_conns_alive(_) ->
  332. %% And finally we make sure that in the case of partial failure
  333. %% live connections are not being killed.
  334. Ref = supervisor_conns_alive,
  335. _ = erlang:trace(new, true, [call]),
  336. 1 = erlang:trace_pattern({ranch_tcp, listen, 1}, [{'_', [], [{return_trace}]}], [global]),
  337. {ok, _} = ranch:start_listener(Ref,
  338. 1, ranch_tcp, [{port, 0}], remove_conn_and_wait_protocol, [{remove, false}]),
  339. ok,
  340. %% Get the listener socket
  341. LSocket = receive
  342. {trace, _, return_from, {ranch_tcp, listen, 1}, {ok, S}} ->
  343. S
  344. after 0 ->
  345. error(lsocket_unknown)
  346. end,
  347. TcpPort = ranch:get_port(Ref),
  348. {ok, Socket} = gen_tcp:connect("localhost", TcpPort,
  349. [binary, {active, true}, {packet, raw}]),
  350. %% Shut the socket down
  351. ok = gen_tcp:close(LSocket),
  352. %% Assert that client is still viable.
  353. receive {tcp_closed, _} -> error(closed) after 1500 -> ok end,
  354. ok = gen_tcp:send(Socket, <<"poke">>),
  355. receive {tcp_closed, _} -> ok end,
  356. _ = erlang:trace(all, false, [all]),
  357. ok = clean_traces().
  358. clean_traces() ->
  359. receive
  360. {trace, _, _, _} ->
  361. clean_traces();
  362. {trace, _, _, _, _} ->
  363. clean_traces()
  364. after 0 ->
  365. ok
  366. end.
  367. %% Utility functions.
  368. connect_loop(_, 0, _) ->
  369. ok;
  370. connect_loop(Port, N, Sleep) ->
  371. {ok, _} = gen_tcp:connect("localhost", Port,
  372. [binary, {active, false}, {packet, raw}]),
  373. receive after Sleep -> ok end,
  374. connect_loop(Port, N - 1, Sleep).
  375. receive_loop(Message, Timeout) ->
  376. receive_loop(Message, Timeout, 0).
  377. receive_loop(Message, Timeout, N) ->
  378. receive Message ->
  379. receive_loop(Message, Timeout, N + 1)
  380. after Timeout ->
  381. N
  382. end.