aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
authorRory Byrne <rory@nybek.com>2015-04-16 18:01:23 +0100
committerRory Byrne <rory@nybek.com>2015-06-09 21:51:29 +0100
commiteed21b4fd99c8ab71dcefd3802104186af1cb6b0 (patch)
treef4191a382894c67ee002c2087749a251e5316b05 /lib/kernel
parent0098eed847516cc6760d961421c83527816e35ae (diff)
downloadotp-eed21b4fd99c8ab71dcefd3802104186af1cb6b0.tar.gz
otp-eed21b4fd99c8ab71dcefd3802104186af1cb6b0.tar.bz2
otp-eed21b4fd99c8ab71dcefd3802104186af1cb6b0.zip
Fix socket option {linger, {true, 0}} to abort TCP connections
Up until now, if {linger, {true, 0}} is set on the socket and there is data in the port driver queue, the connection is not aborted until the port queue is empty and close() is called on the underlying file descriptor. This bug allows an idle TCP client to prevent a server from terminating the connection and freeing resources. This patch fixes the problem by discarding the port queue if the socket is closed when {linger, {true, 0}} is set.
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl37
1 files changed, 35 insertions, 2 deletions
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index ddd9d356ee..83bfd1f69d 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -35,7 +35,7 @@
show_econnreset_passive/1, econnreset_after_sync_send/1,
econnreset_after_async_send_active/1,
econnreset_after_async_send_active_once/1,
- econnreset_after_async_send_passive/1,
+ econnreset_after_async_send_passive/1, linger_zero/1,
default_options/1, http_bad_packet/1,
busy_send/1, busy_disconnect_passive/1, busy_disconnect_active/1,
fill_sendq/1, partial_recv_and_close/1,
@@ -101,7 +101,7 @@ all() ->
show_econnreset_passive, econnreset_after_sync_send,
econnreset_after_async_send_active,
econnreset_after_async_send_active_once,
- econnreset_after_async_send_passive,
+ econnreset_after_async_send_passive, linger_zero,
default_options, http_bad_packet, busy_send,
busy_disconnect_passive, busy_disconnect_active,
fill_sendq, partial_recv_and_close,
@@ -1358,6 +1358,39 @@ econnreset_after_async_send_passive(Config) when is_list(Config) ->
ok = ?t:sleep(20),
{error, econnreset} = gen_tcp:recv(Client1, 0).
+%%
+%% Test {linger {true, 0}} aborts a connection
+%%
+
+linger_zero(Config) when is_list(Config) ->
+ %% All the econnreset tests will prove that {linger, {true, 0}} aborts
+ %% a connection when the driver queue is empty. We will test here
+ %% that it also works when the driver queue is not empty.
+ {OS, _} = os:type(),
+ {ok, L} = gen_tcp:listen(0, [{active, false},
+ {recbuf, 4096},
+ {show_econnreset, true}]),
+ {ok, Port} = inet:port(L),
+ {ok, Client} = gen_tcp:connect(localhost, Port,
+ [{active, false},
+ {sndbuf, 4096}]),
+ {ok, S} = gen_tcp:accept(L),
+ ok = gen_tcp:close(L),
+ PayloadSize = 1024 * 1024,
+ Payload = lists:duplicate(PayloadSize, $.),
+ ok = gen_tcp:send(Client, Payload),
+ case erlang:port_info(Client, queue_size) of
+ {queue_size, N} when N > 0 -> ok;
+ {queue_size, 0} when OS =:= win32 -> ok;
+ {queue_size, 0} = T -> ?t:fail(T)
+ end,
+ ok = inet:setopts(Client, [{linger, {true, 0}}]),
+ ok = gen_tcp:close(Client),
+ ok = ?t:sleep(1),
+ undefined = erlang:port_info(Client, connected),
+ {error, econnreset} = gen_tcp:recv(S, PayloadSize).
+
+
%% Thanks to Luke Gorrie. Tests for a very specific problem with
%% corrupt data. The testcase will be killed by the timetrap timeout
%% if the bug is present.