Browse Source

Delete local socket file when a listener closes

juhlig 5 years ago
parent
commit
9765f305e1
5 changed files with 49 additions and 24 deletions
  1. 4 1
      src/ranch.erl
  2. 14 9
      src/ranch_ssl.erl
  3. 14 9
      src/ranch_tcp.erl
  4. 1 0
      src/ranch_transport.erl
  5. 16 5
      test/acceptor_SUITE.erl

+ 4 - 1
src/ranch.erl

@@ -155,10 +155,13 @@ start_error(_, Error) -> Error.
 
 
 -spec stop_listener(ref()) -> ok | {error, not_found}.
 -spec stop_listener(ref()) -> ok | {error, not_found}.
 stop_listener(Ref) ->
 stop_listener(Ref) ->
+	[_, Transport, _, _, _] = ranch_server:get_listener_start_args(Ref),
+	TransOpts = get_transport_options(Ref),
 	case supervisor:terminate_child(ranch_sup, {ranch_listener_sup, Ref}) of
 	case supervisor:terminate_child(ranch_sup, {ranch_listener_sup, Ref}) of
 		ok ->
 		ok ->
 			_ = supervisor:delete_child(ranch_sup, {ranch_listener_sup, Ref}),
 			_ = supervisor:delete_child(ranch_sup, {ranch_listener_sup, Ref}),
-			ranch_server:cleanup_listener_opts(Ref);
+			ranch_server:cleanup_listener_opts(Ref),
+			Transport:cleanup(TransOpts);
 		{error, Reason} ->
 		{error, Reason} ->
 			{error, Reason}
 			{error, Reason}
 	end.
 	end.

+ 14 - 9
src/ranch_ssl.erl

@@ -43,6 +43,7 @@
 -export([sockname/1]).
 -export([sockname/1]).
 -export([shutdown/2]).
 -export([shutdown/2]).
 -export([close/1]).
 -export([close/1]).
+-export([cleanup/1]).
 
 
 -type ssl_opt() :: {alpn_preferred_protocols, [binary()]}
 -type ssl_opt() :: {alpn_preferred_protocols, [binary()]}
 	| {beast_mitigation, one_n_minus_one | zero_n | disabled}
 	| {beast_mitigation, one_n_minus_one | zero_n | disabled}
@@ -107,6 +108,7 @@ messages() -> {ssl, ssl_closed, ssl_error, ssl_passive}.
 
 
 -spec listen(ranch:transport_opts(opts())) -> {ok, ssl:sslsocket()} | {error, atom()}.
 -spec listen(ranch:transport_opts(opts())) -> {ok, ssl:sslsocket()} | {error, atom()}.
 listen(TransOpts) ->
 listen(TransOpts) ->
+	ok = cleanup(TransOpts),
 	SocketOpts = maps:get(socket_opts, TransOpts, []),
 	SocketOpts = maps:get(socket_opts, TransOpts, []),
 	case lists:keymember(cert, 1, SocketOpts)
 	case lists:keymember(cert, 1, SocketOpts)
 			orelse lists:keymember(certfile, 1, SocketOpts)
 			orelse lists:keymember(certfile, 1, SocketOpts)
@@ -124,15 +126,6 @@ do_listen(SocketOpts0, Logger) ->
 	SocketOpts2 = ranch:set_option_default(SocketOpts1, nodelay, true),
 	SocketOpts2 = ranch:set_option_default(SocketOpts1, nodelay, true),
 	SocketOpts3 = ranch:set_option_default(SocketOpts2, send_timeout, 30000),
 	SocketOpts3 = ranch:set_option_default(SocketOpts2, send_timeout, 30000),
 	SocketOpts = ranch:set_option_default(SocketOpts3, send_timeout_close, true),
 	SocketOpts = ranch:set_option_default(SocketOpts3, send_timeout_close, true),
-	%% In case of a local socket, we remove the socket file first.
-	%% It is possible to have multiple ip tuples in the socket options,
-	%% and the last one will be used (undocumented).
-	_ = case lists:keyfind(ip, 1, lists:reverse(SocketOpts0)) of
-		{ip, {local, SockFile}} ->
-			file:delete(SockFile);
-		_ ->
-			ok
-	end,
 	%% We set the port to 0 because it is given in the Opts directly.
 	%% We set the port to 0 because it is given in the Opts directly.
 	%% The port in the options takes precedence over the one in the
 	%% The port in the options takes precedence over the one in the
 	%% first argument.
 	%% first argument.
@@ -287,3 +280,15 @@ shutdown(Socket, How) ->
 -spec close(ssl:sslsocket()) -> ok.
 -spec close(ssl:sslsocket()) -> ok.
 close(Socket) ->
 close(Socket) ->
 	ssl:close(Socket).
 	ssl:close(Socket).
+
+-spec cleanup(ranch:transport_opts(opts())) -> ok.
+cleanup(#{socket_opts:=SocketOpts}) ->
+	case lists:keyfind(ip, 1, lists:reverse(SocketOpts)) of
+		{ip, {local, SockFile}} ->
+			_ = file:delete(SockFile),
+			ok;
+		_ ->
+			ok
+	end;
+cleanup(_) ->
+	ok.

+ 14 - 9
src/ranch_tcp.erl

@@ -43,6 +43,7 @@
 -export([sockname/1]).
 -export([sockname/1]).
 -export([shutdown/2]).
 -export([shutdown/2]).
 -export([close/1]).
 -export([close/1]).
+-export([cleanup/1]).
 
 
 -type opt() :: {backlog, non_neg_integer()}
 -type opt() :: {backlog, non_neg_integer()}
 	| {buffer, non_neg_integer()}
 	| {buffer, non_neg_integer()}
@@ -86,21 +87,13 @@ messages() -> {tcp, tcp_closed, tcp_error, tcp_passive}.
 
 
 -spec listen(ranch:transport_opts(opts())) -> {ok, inet:socket()} | {error, atom()}.
 -spec listen(ranch:transport_opts(opts())) -> {ok, inet:socket()} | {error, atom()}.
 listen(TransOpts) ->
 listen(TransOpts) ->
+	ok = cleanup(TransOpts),
 	Logger = maps:get(logger, TransOpts, logger),
 	Logger = maps:get(logger, TransOpts, logger),
 	SocketOpts0 = maps:get(socket_opts, TransOpts, []),
 	SocketOpts0 = maps:get(socket_opts, TransOpts, []),
 	SocketOpts1 = ranch:set_option_default(SocketOpts0, backlog, 1024),
 	SocketOpts1 = ranch:set_option_default(SocketOpts0, backlog, 1024),
 	SocketOpts2 = ranch:set_option_default(SocketOpts1, nodelay, true),
 	SocketOpts2 = ranch:set_option_default(SocketOpts1, nodelay, true),
 	SocketOpts3 = ranch:set_option_default(SocketOpts2, send_timeout, 30000),
 	SocketOpts3 = ranch:set_option_default(SocketOpts2, send_timeout, 30000),
 	SocketOpts4 = ranch:set_option_default(SocketOpts3, send_timeout_close, true),
 	SocketOpts4 = ranch:set_option_default(SocketOpts3, send_timeout_close, true),
-	%% In case of a local socket, we remove the socket file first.
-	%% It is possible to have multiple ip tuples in the socket options,
-	%% and the last one will be used (undocumented).
-	_ = case lists:keyfind(ip, 1, lists:reverse(SocketOpts0)) of
-		{ip, {local, SockFile}} ->
-			file:delete(SockFile);
-		_ ->
-			ok
-	end,
 	%% We set the port to 0 because it is given in the Opts directly.
 	%% We set the port to 0 because it is given in the Opts directly.
 	%% The port in the options takes precedence over the one in the
 	%% The port in the options takes precedence over the one in the
 	%% first argument.
 	%% first argument.
@@ -271,3 +264,15 @@ shutdown(Socket, How) ->
 -spec close(inet:socket()) -> ok.
 -spec close(inet:socket()) -> ok.
 close(Socket) ->
 close(Socket) ->
 	gen_tcp:close(Socket).
 	gen_tcp:close(Socket).
+
+-spec cleanup(ranch:transport_opts(opts())) -> ok.
+cleanup(#{socket_opts:=SocketOpts}) ->
+	case lists:keyfind(ip, 1, lists:reverse(SocketOpts)) of
+		{ip, {local, SockFile}} ->
+			_ = file:delete(SockFile),
+			ok;
+		_ ->
+			ok
+	end;
+cleanup(_) ->
+	ok.

+ 1 - 0
src/ranch_transport.erl

@@ -66,6 +66,7 @@
 -callback shutdown(socket(), read | write | read_write)
 -callback shutdown(socket(), read | write | read_write)
 	-> ok | {error, atom()}.
 	-> ok | {error, atom()}.
 -callback close(socket()) -> ok.
 -callback close(socket()) -> ok.
+-callback cleanup(ranch:transport_opts(any())) -> ok.
 
 
 %% A fallback for transports that don't have a native sendfile implementation.
 %% A fallback for transports that don't have a native sendfile implementation.
 %% Note that the ordering of arguments is different from file:sendfile/5 and
 %% Note that the ordering of arguments is different from file:sendfile/5 and

+ 16 - 5
test/acceptor_SUITE.erl

@@ -77,7 +77,7 @@ groups() ->
 		misc_opts_logger,
 		misc_opts_logger,
 		misc_set_transport_options,
 		misc_set_transport_options,
 		misc_wait_for_connections,
 		misc_wait_for_connections,
-		misc_multiple_ips_in_listen_opts
+		misc_multiple_ip_local_socket_opts
 	]}, {supervisor, [
 	]}, {supervisor, [
 		connection_type_supervisor,
 		connection_type_supervisor,
 		connection_type_supervisor_separate_from_connection,
 		connection_type_supervisor_separate_from_connection,
@@ -405,15 +405,15 @@ do_expect_waiter(WaiterPid) ->
 			end
 			end
 	end.
 	end.
 
 
-misc_multiple_ips_in_listen_opts(_) ->
+misc_multiple_ip_local_socket_opts(_) ->
 	case do_os_supports_local_sockets() of
 	case do_os_supports_local_sockets() of
 		true ->
 		true ->
-			do_misc_multiple_ips_in_listen_opts();
+			do_misc_multiple_ip_local_socket_opts();
 		false ->
 		false ->
 			{skip, "No local socket support."}
 			{skip, "No local socket support."}
 	end.
 	end.
 
 
-do_misc_multiple_ips_in_listen_opts() ->
+do_misc_multiple_ip_local_socket_opts() ->
 	doc("Ensure that a listener uses the expected ip option if multiple are given."),
 	doc("Ensure that a listener uses the expected ip option if multiple are given."),
 	Name = name(),
 	Name = name(),
 	SockFile1 = do_tempname(),
 	SockFile1 = do_tempname(),
@@ -421,7 +421,14 @@ do_misc_multiple_ips_in_listen_opts() ->
 	Opts = [{ip, {local, SockFile1}}, {ip, {local, SockFile2}}],
 	Opts = [{ip, {local, SockFile1}}, {ip, {local, SockFile2}}],
 	{ok, _} = ranch:start_listener(Name, ranch_tcp, #{socket_opts => Opts}, echo_protocol, []),
 	{ok, _} = ranch:start_listener(Name, ranch_tcp, #{socket_opts => Opts}, echo_protocol, []),
 	{local, SockFile2} = ranch:get_addr(Name),
 	{local, SockFile2} = ranch:get_addr(Name),
-	ok = ranch:stop_listener(Name).
+	%% Make sure the socket file from the ignored ip option
+	%% has not been created.
+	{error, enoent} = file:read_file_info(SockFile1),
+	ok = ranch:stop_listener(Name),
+	%% Make sure the socket file from the accepted ip option
+	%% is removed.
+	{error, enoent} = file:read_file_info(SockFile2),
+	ok.
 
 
 %% ssl.
 %% ssl.
 
 
@@ -607,6 +614,8 @@ do_ssl_local_echo() ->
 		{error, closed} = ssl:recv(Socket, 0, 1000),
 		{error, closed} = ssl:recv(Socket, 0, 1000),
 		%% Make sure the listener stopped.
 		%% Make sure the listener stopped.
 		{'EXIT', _} = begin catch ranch:get_port(Name) end,
 		{'EXIT', _} = begin catch ranch:get_port(Name) end,
+		%% Make sure the socket file is removed.
+		{error, enoent} = file:read_file_info(SockFile),
 		ok
 		ok
 	after
 	after
 		file:delete(SockFile)
 		file:delete(SockFile)
@@ -900,6 +909,8 @@ do_tcp_local_echo() ->
 		{error, closed} = gen_tcp:recv(Socket, 0, 1000),
 		{error, closed} = gen_tcp:recv(Socket, 0, 1000),
 		%% Make sure the listener stopped.
 		%% Make sure the listener stopped.
 		{'EXIT', _} = begin catch ranch:get_port(Name) end,
 		{'EXIT', _} = begin catch ranch:get_port(Name) end,
+		%% Make sure the socket file is removed.
+		{error, enoent} = file:read_file_info(SockFile),
 		ok
 		ok
 	after
 	after
 		file:delete(SockFile)
 		file:delete(SockFile)