Merge branch 'stable'

Conflicts:
	src/rabbit_stomp_reader.erl
This commit is contained in:
Michael Klishin 2017-05-03 12:50:39 +02:00
commit fcdd8ef3d5
3 changed files with 40 additions and 27 deletions

View File

@ -58,13 +58,13 @@ adapter_name(State) ->
PeerAddr :: inet:ip_address(). PeerAddr :: inet:ip_address().
-type process_frame_result() :: -type process_frame_result() ::
{ok, #proc_state{}} | {ok, term(), #proc_state{}} |
{stop, term(), #proc_state{}}. {stop, term(), #proc_state{}}.
-spec process_frame(#stomp_frame{}, #proc_state{}) -> -spec process_frame(#stomp_frame{}, #proc_state{}) ->
process_frame_result(). process_frame_result().
-spec flush_and_die(#proc_state{}) -> ok. -spec flush_and_die(#proc_state{}) -> #proc_state{}.
-spec command({Command, Frame}, State) -> process_frame_result() -spec command({Command, Frame}, State) -> process_frame_result()
when Command :: string(), when Command :: string(),
@ -212,7 +212,7 @@ handle_exit(Conn, {shutdown, {connection_closing,
State = #proc_state{connection = Conn}) -> State = #proc_state{connection = Conn}) ->
amqp_death(Code, Explanation, State); amqp_death(Code, Explanation, State);
handle_exit(Conn, Reason, State = #proc_state{connection = Conn}) -> handle_exit(Conn, Reason, State = #proc_state{connection = Conn}) ->
send_error("AMQP connection died", "Reason: ~p", [Reason], State), _ = send_error("AMQP connection died", "Reason: ~p", [Reason], State),
{stop, {conn_died, Reason}, State}; {stop, {conn_died, Reason}, State};
handle_exit(Ch, {shutdown, {server_initiated_close, Code, Explanation}}, handle_exit(Ch, {shutdown, {server_initiated_close, Code, Explanation}},
@ -220,7 +220,7 @@ handle_exit(Ch, {shutdown, {server_initiated_close, Code, Explanation}},
amqp_death(Code, Explanation, State); amqp_death(Code, Explanation, State);
handle_exit(Ch, Reason, State = #proc_state{channel = Ch}) -> handle_exit(Ch, Reason, State = #proc_state{channel = Ch}) ->
send_error("AMQP channel died", "Reason: ~p", [Reason], State), _ = send_error("AMQP channel died", "Reason: ~p", [Reason], State),
{stop, {channel_died, Reason}, State}; {stop, {channel_died, Reason}, State};
handle_exit(Ch, {shutdown, {server_initiated_close, Code, Explanation}}, handle_exit(Ch, {shutdown, {server_initiated_close, Code, Explanation}},
State = #proc_state{channel = Ch}) -> State = #proc_state{channel = Ch}) ->
@ -248,7 +248,7 @@ process_request(ProcessFun, SuccessFun, State) ->
end, end,
case Res of case Res of
{ok, Frame, NewState = #proc_state{connection = Conn}} -> {ok, Frame, NewState = #proc_state{connection = Conn}} ->
case Frame of _ = case Frame of
none -> ok; none -> ok;
_ -> send_frame(Frame, NewState) _ -> send_frame(Frame, NewState)
end, end,
@ -449,14 +449,14 @@ server_cancel_consumer(ConsumerTag, State = #proc_state{subscriptions = Subs}) -
{ok, {_, Id1}} -> Id1; {ok, {_, Id1}} -> Id1;
{error, {_, Id1}} -> "Unknown[" ++ Id1 ++ "]" {error, {_, Id1}} -> "Unknown[" ++ Id1 ++ "]"
end, end,
send_error_frame("Server cancelled subscription", _ = send_error_frame("Server cancelled subscription",
[{?HEADER_SUBSCRIPTION, Id}], [{?HEADER_SUBSCRIPTION, Id}],
"The server has canceled a subscription.~n" "The server has canceled a subscription.~n"
"No more messages will be delivered for ~p.~n", "No more messages will be delivered for ~p.~n",
[Description], [Description],
State), State),
tidy_canceled_subscription(ConsumerTag, Subscription, tidy_canceled_subscription(ConsumerTag, Subscription,
#stomp_frame{}, State) undefined, State)
end. end.
cancel_subscription({error, invalid_prefix}, _Frame, State) -> cancel_subscription({error, invalid_prefix}, _Frame, State) ->
@ -495,6 +495,15 @@ cancel_subscription({ok, ConsumerTag, Description}, Frame,
end end
end. end.
%% Server-initiated cancelations will pass an undefined instead of a
%% STOMP frame. In this case we know that the queue was deleted and
%% thus we don't have to clean it up.
tidy_canceled_subscription(ConsumerTag, _Subscription,
undefined, State = #proc_state{subscriptions = Subs}) ->
Subs1 = dict:erase(ConsumerTag, Subs),
ok(State#proc_state{subscriptions = Subs1});
%% Client-initiated cancelations will pass an actual frame
tidy_canceled_subscription(ConsumerTag, #subscription{dest_hdr = DestHdr}, tidy_canceled_subscription(ConsumerTag, #subscription{dest_hdr = DestHdr},
Frame, State = #proc_state{subscriptions = Subs}) -> Frame, State = #proc_state{subscriptions = Subs}) ->
Subs1 = maps:remove(ConsumerTag, Subs), Subs1 = maps:remove(ConsumerTag, Subs),
@ -663,8 +672,8 @@ do_subscribe(Destination, DestHdr, Frame,
{ok, _} -> {ok, _} ->
Message = "Duplicated subscription identifier", Message = "Duplicated subscription identifier",
Detail = "A subscription identified by '~s' alredy exists.", Detail = "A subscription identified by '~s' alredy exists.",
error(Message, Detail, [ConsumerTag], State), _ = error(Message, Detail, [ConsumerTag], State),
send_error(Message, Detail, [ConsumerTag], State), _ = send_error(Message, Detail, [ConsumerTag], State),
{stop, normal, close_connection(State)}; {stop, normal, close_connection(State)};
error -> error ->
ExchangeAndKey = ExchangeAndKey =
@ -1049,7 +1058,7 @@ ensure_heartbeats(Heartbeats) ->
{SendTimeout, ReceiveTimeout} = {SendTimeout, ReceiveTimeout} =
{millis_to_seconds(CY), millis_to_seconds(CX)}, {millis_to_seconds(CY), millis_to_seconds(CX)},
rabbit_stomp_reader:start_heartbeats(self(), {SendTimeout, ReceiveTimeout}), _ = rabbit_stomp_reader:start_heartbeats(self(), {SendTimeout, ReceiveTimeout}),
{SendTimeout * 1000 , ReceiveTimeout * 1000}. {SendTimeout * 1000 , ReceiveTimeout * 1000}.
millis_to_seconds(M) when M =< 0 -> 0; millis_to_seconds(M) when M =< 0 -> 0;

View File

@ -52,7 +52,7 @@ start_link(SupHelperPid, Ref, Sock, Configuration) ->
%% meaningless synchronous call to the underlying gen_event %% meaningless synchronous call to the underlying gen_event
%% mechanism. When it returns the mailbox is drained, and we %% mechanism. When it returns the mailbox is drained, and we
%% return to our caller to accept more connections. %% return to our caller to accept more connections.
gen_event:which_handlers(error_logger), _ = gen_event:which_handlers(error_logger),
{ok, Pid}. {ok, Pid}.
@ -80,7 +80,7 @@ init([SupHelperPid, Ref, Sock, Configuration]) ->
[self(), ConnStr]), [self(), ConnStr]),
ParseState = rabbit_stomp_frame:initial_state(), ParseState = rabbit_stomp_frame:initial_state(),
register_resource_alarm(), _ = register_resource_alarm(),
gen_server2:enter_loop(?MODULE, [], gen_server2:enter_loop(?MODULE, [],
rabbit_event:init_stats_timer( rabbit_event:init_stats_timer(
run_socket(control_throttle( run_socket(control_throttle(
@ -275,15 +275,18 @@ run_socket(State = #reader_state{state = blocked}) ->
run_socket(State = #reader_state{recv_outstanding = true}) -> run_socket(State = #reader_state{recv_outstanding = true}) ->
State; State;
run_socket(State = #reader_state{socket = Sock}) -> run_socket(State = #reader_state{socket = Sock}) ->
rabbit_net:async_recv(Sock, 0, infinity), _ = rabbit_net:async_recv(Sock, 0, infinity),
State#reader_state{recv_outstanding = true}. State#reader_state{recv_outstanding = true}.
terminate(Reason, undefined) ->
log_reason(Reason, undefined),
{stop, Reason};
terminate(Reason, State = #reader_state{ processor_state = ProcState }) -> terminate(Reason, State = #reader_state{ processor_state = ProcState }) ->
maybe_emit_stats(State), maybe_emit_stats(State),
log_reason(Reason, State), log_reason(Reason, State),
rabbit_stomp_processor:flush_and_die(ProcState), _ = rabbit_stomp_processor:flush_and_die(ProcState),
ok. {stop, Reason}.
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
@ -331,12 +334,14 @@ log_reason({shutdown, client_heartbeat_timeout},
log_reason(normal, #reader_state{ conn_name = ConnName}) -> log_reason(normal, #reader_state{ conn_name = ConnName}) ->
log(info, "closing STOMP connection ~p (~s)~n", [self(), ConnName]); log(info, "closing STOMP connection ~p (~s)~n", [self(), ConnName]);
log_reason(shutdown, undefined) ->
log(error, "closing STOMP connection that never completed connection handshake (negotiation)~n", []);
log_reason(Reason, #reader_state{ processor_state = ProcState }) -> log_reason(Reason, #reader_state{ processor_state = ProcState }) ->
AdapterName = rabbit_stomp_processor:adapter_name(ProcState), AdapterName = rabbit_stomp_processor:adapter_name(ProcState),
rabbit_log:warning("STOMP connection ~s terminated" rabbit_log:warning("STOMP connection ~s terminated"
" with reason ~p, closing it~n", [AdapterName, Reason]). " with reason ~p, closing it~n", [AdapterName, Reason]).
%%---------------------------------------------------------------------------- %%----------------------------------------------------------------------------
processor_args(Configuration, Sock) -> processor_args(Configuration, Sock) ->
@ -417,8 +422,8 @@ info_internal(SockStat, #reader_state{socket = Sock}) when SockStat =:= recv_oct
SockStat =:= send_cnt; SockStat =:= send_cnt;
SockStat =:= send_pend -> SockStat =:= send_pend ->
case rabbit_net:getstat(Sock, [SockStat]) of case rabbit_net:getstat(Sock, [SockStat]) of
{ok, [{_, I}]} -> I; {ok, [{_, N}]} when is_number(N) -> N;
{error, _} -> '' _ -> 0
end; end;
info_internal(state, State) -> info_internal(connection_state, State); info_internal(state, State) -> info_internal(connection_state, State);
info_internal(garbage_collection, _State) -> info_internal(garbage_collection, _State) ->

View File

@ -383,7 +383,6 @@ subscription_queue_name(Destination, SubscriptionId, Frame) ->
%% ---- Helpers ---- %% ---- Helpers ----
split([], _Splitter) -> []; split([], _Splitter) -> [];
split(Content, []) -> Content;
split(Content, Splitter) -> split(Content, [], [], Splitter). split(Content, Splitter) -> split(Content, [], [], Splitter).
split([], RPart, RParts, _Splitter) -> split([], RPart, RParts, _Splitter) ->