diff options
author | Simon MacMullen <simon@rabbitmq.com> | 2012-09-04 13:06:57 +0100 |
---|---|---|
committer | Simon MacMullen <simon@rabbitmq.com> | 2012-09-04 13:06:57 +0100 |
commit | 0a2bffe30242bbabf750c772a39b5ac4b9b614a3 (patch) | |
tree | 1a02b19cf2d098ede786ac6a7be20ca6fc375b27 | |
parent | 84f207a4c7326d33db8f854c48279fb9c5e22702 (diff) | |
parent | 05a8e69c70a290a3b27c48abac1f4c069ad5a92e (diff) | |
download | rabbitmq-server-0a2bffe30242bbabf750c772a39b5ac4b9b614a3.tar.gz |
Merge bug 25120.
-rw-r--r-- | src/rabbit_net.erl | 29 | ||||
-rw-r--r-- | src/rabbit_networking.erl | 12 | ||||
-rw-r--r-- | src/rabbit_reader.erl | 5 |
3 files changed, 42 insertions, 4 deletions
diff --git a/src/rabbit_net.erl b/src/rabbit_net.erl index 698a7c9a..038154c3 100644 --- a/src/rabbit_net.erl +++ b/src/rabbit_net.erl @@ -77,6 +77,8 @@ %%--------------------------------------------------------------------------- +-define(SSL_CLOSE_TIMEOUT, 5000). + -define(IS_SSL(Sock), is_record(Sock, ssl_socket)). is_ssl(Sock) -> ?IS_SSL(Sock). @@ -148,8 +150,31 @@ send(Sock, Data) when is_port(Sock) -> gen_tcp:send(Sock, Data). close(Sock) when ?IS_SSL(Sock) -> ssl:close(Sock#ssl_socket.ssl); close(Sock) when is_port(Sock) -> gen_tcp:close(Sock). -fast_close(Sock) when ?IS_SSL(Sock) -> ok; -fast_close(Sock) when is_port(Sock) -> erlang:port_close(Sock), ok. +fast_close(Sock) when ?IS_SSL(Sock) -> + %% We cannot simply port_close the underlying tcp socket since the + %% TLS protocol is quite insistent that a proper closing handshake + %% should take place (see RFC 5245 s7.2.1). So we call ssl:close + %% instead, but that can block for a very long time, e.g. when + %% there is lots of pending output and there is tcp backpressure, + %% or the ssl_connection process has entered the the + %% workaround_transport_delivery_problems function during + %% termination, which, inexplicably, does a gen_tcp:recv(Socket, + %% 0), which may never return if the client doesn't send a FIN or + %% that gets swallowed by the network. Since there is no timeout + %% variant of ssl:close, we construct our own. + {Pid, MRef} = spawn_monitor(fun () -> ssl:close(Sock#ssl_socket.ssl) end), + erlang:send_after(?SSL_CLOSE_TIMEOUT, self(), {Pid, ssl_close_timeout}), + receive + {Pid, ssl_close_timeout} -> + erlang:demonitor(MRef, [flush]), + exit(Pid, kill); + {'DOWN', MRef, process, Pid, _Reason} -> + ok + end, + catch port_close(Sock#ssl_socket.tcp), + ok; +fast_close(Sock) when is_port(Sock) -> + catch port_close(Sock), ok. sockname(Sock) when ?IS_SSL(Sock) -> ssl:sockname(Sock#ssl_socket.ssl); sockname(Sock) when is_port(Sock) -> inet:sockname(Sock). diff --git a/src/rabbit_networking.erl b/src/rabbit_networking.erl index 94a5a2b7..2d0ded12 100644 --- a/src/rabbit_networking.erl +++ b/src/rabbit_networking.erl @@ -160,7 +160,19 @@ ssl_transform_fun(SslOpts) -> case catch ssl:ssl_accept(Sock, SslOpts, ?SSL_TIMEOUT * 1000) of {ok, SslSock} -> {ok, #ssl_socket{tcp = Sock, ssl = SslSock}}; + {error, timeout} -> + {error, {ssl_upgrade_error, timeout}}; {error, Reason} -> + %% We have no idea what state the ssl_connection + %% process is in - it could still be happily + %% going, it might be stuck, or it could be just + %% about to fail. There is little that our caller + %% can do but close the TCP socket, but this could + %% cause ssl alerts to get dropped (which is bad + %% form, according to the TLS spec). So we give + %% the ssl_connection a little bit of time to send + %% such alerts. + timer:sleep(?SSL_TIMEOUT * 1000), {error, {ssl_upgrade_error, Reason}}; {'EXIT', Reason} -> {error, {ssl_upgrade_failure, Reason}} diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl index bd20deb2..aef48b20 100644 --- a/src/rabbit_reader.erl +++ b/src/rabbit_reader.erl @@ -184,6 +184,8 @@ socket_op(Sock, Fun) -> {ok, Res} -> Res; {error, Reason} -> log(error, "error on AMQP connection ~p: ~p~n", [self(), Reason]), + %% NB: this is tcp socket, even in case of ssl + rabbit_net:fast_close(Sock), exit(normal) end. @@ -242,8 +244,7 @@ start_connection(Parent, ChannelSupSupPid, Collector, StartHeartbeatFun, Deb, %% controlling process and hence its termination will close %% the socket. However, to keep the file_handle_cache %% accounting as accurate as possible we ought to close the - %% socket w/o delay before termination. fast_close does that, - %% though only for non-ssl sockets. + %% socket w/o delay before termination. rabbit_net:fast_close(ClientSock), rabbit_event:notify(connection_closed, [{pid, self()}]) end, |