diff options
author | Rory Byrne <[email protected]> | 2015-04-16 18:01:23 +0100 |
---|---|---|
committer | Rory Byrne <[email protected]> | 2015-06-09 21:51:29 +0100 |
commit | eed21b4fd99c8ab71dcefd3802104186af1cb6b0 (patch) | |
tree | f4191a382894c67ee002c2087749a251e5316b05 /lib/kernel | |
parent | 0098eed847516cc6760d961421c83527816e35ae (diff) | |
download | otp-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.erl | 37 |
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. |