acceptor_SUITE.erl 12 KB

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