1717% %TODO check where to best 'hibernate' when returning from callback
1818% %TODO use rabbit_global_counters for MQTT protocol
1919
20- -export ([conserve_resources /3 , start_keepalive / 2 ,
20+ -export ([conserve_resources /3 ,
2121 close_connection /2 ]).
2222
2323-export ([info /2 ]).
@@ -166,61 +166,14 @@ handle_info({bump_credit, Msg}, State) ->
166166 credit_flow :handle_bump_msg (Msg ),
167167 maybe_process_deferred_recv (control_throttle (State ));
168168
169- handle_info ({start_keepalive , KeepaliveSec },
170- State = # state {socket = Sock ,
171- keepalive = undefined })
172- when is_number (KeepaliveSec ), KeepaliveSec > 0 ->
173- case rabbit_net :getstat (Sock , [recv_oct ]) of
174- {ok , [{recv_oct , RecvOct }]} ->
175- % % "If the Keep Alive value is non-zero and the Server does not receive a Control
176- % % Packet from the Client within one and a half times the Keep Alive time period,
177- % % it MUST disconnect the Network Connection to the Client as if the network had
178- % % failed" [MQTT-3.1.2-24]
179- % % 0.75 * 2 = 1.5
180- IntervalMs = timer :seconds (round (0.75 * KeepaliveSec )),
181- Ref = start_keepalive_timer (# keepalive {interval_ms = IntervalMs }),
182- {noreply , State # state {keepalive = # keepalive {timer = Ref ,
183- interval_ms = IntervalMs ,
184- recv_oct = RecvOct ,
185- received = true }}};
186- {error , einval } ->
187- % % the socket is dead, most likely because the connection is being shut down
188- {stop , {shutdown , cannot_get_socket_stats }, State };
189- {error , Reason } ->
190- {stop , Reason , State }
191- end ;
192-
193- handle_info ({timeout , Ref , keepalive },
194- State = # state {socket = Sock ,
195- conn_name = ConnStr ,
196- proc_state = PState ,
197- keepalive = # keepalive {timer = Ref ,
198- recv_oct = SameRecvOct ,
199- received = ReceivedPreviously } = KeepAlive }) ->
200- case rabbit_net :getstat (Sock , [recv_oct ]) of
201- {ok , [{recv_oct , SameRecvOct }]}
202- when ReceivedPreviously ->
203- % % Did not receive from socket for the 1st time.
204- Ref1 = start_keepalive_timer (KeepAlive ),
205- {noreply ,
206- State # state {keepalive = KeepAlive # keepalive {timer = Ref1 ,
207- received = false }},
208- hibernate };
209- {ok , [{recv_oct , SameRecvOct }]} ->
210- % % Did not receive from socket for 2nd time successively.
211- rabbit_log_connection :error (" closing MQTT connection ~p (keepalive timeout)" , [ConnStr ]),
212- send_will_and_terminate (PState , {shutdown , keepalive_timeout }, State );
213- {ok , [{recv_oct , RecvOct }]} ->
214- % % Received from socket.
215- Ref1 = start_keepalive_timer (KeepAlive ),
216- {noreply ,
217- State # state {keepalive = KeepAlive # keepalive {timer = Ref1 ,
218- recv_oct = RecvOct ,
219- received = true }},
220- hibernate };
221- {error , einval } ->
222- % % the socket is dead, most likely because the connection is being shut down
223- {stop , {shutdown , cannot_get_socket_stats }, State };
169+ handle_info ({keepalive , Req }, State = # state {keepalive = KState0 ,
170+ conn_name = ConnName }) ->
171+ case rabbit_mqtt_keepalive :handle (Req , KState0 ) of
172+ {ok , KState } ->
173+ {noreply , State # state {keepalive = KState }, hibernate };
174+ {error , timeout } ->
175+ rabbit_log_connection :error (" closing MQTT connection ~p (keepalive timeout)" , [ConnName ]),
176+ send_will_and_terminate ({shutdown , keepalive_timeout }, State );
224177 {error , Reason } ->
225178 {stop , Reason , State }
226179 end ;
@@ -254,14 +207,9 @@ handle_info({'DOWN', _MRef, process, _Pid, _Reason} = Evt,
254207handle_info (Msg , State ) ->
255208 {stop , {mqtt_unexpected_msg , Msg }, State }.
256209
257- start_keepalive_timer (# keepalive {interval_ms = Time }) ->
258- erlang :start_timer (Time , self (), keepalive ).
259-
260- cancel_keepalive_timer (# keepalive {timer = Ref }) ->
261- erlang :cancel_timer (Ref , [{async , true }, {info , false }]).
262-
263- terminate (Reason , State ) ->
264- maybe_emit_stats (State ),
210+ terminate (Reason , State = # state {keepalive = KState0 }) ->
211+ KState = rabbit_mqtt_keepalive :cancel_timer (KState0 ),
212+ maybe_emit_stats (State # state {keepalive = KState }),
265213 do_terminate (Reason , State ).
266214
267215handle_pre_hibernate (State ) ->
@@ -300,7 +248,7 @@ do_terminate({network_error, Reason}, _State) ->
300248 rabbit_log_connection :error (" MQTT detected network error: ~p " , [Reason ]);
301249
302250do_terminate (normal , # state {proc_state = ProcState ,
303- conn_name = ConnName }) ->
251+ conn_name = ConnName }) ->
304252 rabbit_mqtt_processor :terminate (ProcState ),
305253 rabbit_log_connection :info (" closing MQTT connection ~p (~s )" , [self (), ConnName ]),
306254 ok ;
@@ -395,9 +343,6 @@ callback_reply(State, {ok, ProcState}) ->
395343callback_reply (State , {error , Reason , ProcState }) ->
396344 {stop , Reason , pstate (State , ProcState )}.
397345
398- start_keepalive (_ , 0 ) -> ok ;
399- start_keepalive (Pid , Keepalive ) -> Pid ! {start_keepalive , Keepalive }.
400-
401346pstate (State = # state {}, PState = # proc_state {}) ->
402347 State # state { proc_state = PState }.
403348
@@ -415,32 +360,31 @@ parse(Bytes, ParseState) ->
415360% % "The Will Message MUST be published when the Network Connection is subsequently
416361% % closed unless the Will Message has been deleted by the Server on receipt of a
417362% % DISCONNECT Packet [MQTT-3.1.2-8]."
418- send_will_and_terminate (PState , State ) ->
419- send_will_and_terminate (PState , {shutdown , conn_closed }, State ).
363+ send_will_and_terminate (State ) ->
364+ send_will_and_terminate ({shutdown , conn_closed }, State ).
420365
421- send_will_and_terminate (PState , Reason , State = # state {conn_name = ConnStr }) ->
366+ send_will_and_terminate (Reason , State = # state {conn_name = ConnStr ,
367+ proc_state = PState }) ->
422368 rabbit_log_connection :debug (" MQTT: about to send will message (if any) on connection ~p " , [ConnStr ]),
423369 rabbit_mqtt_processor :send_will (PState ),
424370 {stop , Reason , State }.
425371
426372network_error (closed ,
427373 State = # state {conn_name = ConnStr ,
428- proc_state = PState ,
429374 received_connect_frame = Connected }) ->
430375 Fmt = " MQTT connection ~p will terminate because peer closed TCP connection" ,
431376 Args = [ConnStr ],
432377 case Connected of
433378 true -> rabbit_log_connection :info (Fmt , Args );
434379 false -> rabbit_log_connection :debug (Fmt , Args )
435380 end ,
436- send_will_and_terminate (PState , State );
381+ send_will_and_terminate (State );
437382
438383network_error (Reason ,
439- State = # state {conn_name = ConnStr ,
440- proc_state = PState }) ->
384+ State = # state {conn_name = ConnStr }) ->
441385 rabbit_log_connection :info (" MQTT detected network error for ~p : ~p " ,
442386 [ConnStr , Reason ]),
443- send_will_and_terminate (PState , State ).
387+ send_will_and_terminate (State ).
444388
445389run_socket (State = # state { connection_state = blocked }) ->
446390 State ;
@@ -454,24 +398,14 @@ run_socket(State = #state{ socket = Sock }) ->
454398
455399control_throttle (State = # state {connection_state = Flow ,
456400 conserve = Conserve ,
457- keepalive = KeepAlive }) ->
401+ keepalive = KState }) ->
458402 case {Flow , Conserve orelse credit_flow :blocked ()} of
459- {running , true }
460- when KeepAlive =:= undefined ->
461- State # state {connection_state = blocked };
462403 {running , true } ->
463- % %TODO Instead of cancelling / setting the timer every time the connection
464- % % gets blocked / unblocked, restart the timer when it expires and
465- % % the connection_state is blocked.
466- ok = cancel_keepalive_timer (KeepAlive ),
467- State # state {connection_state = blocked };
468- {blocked , false }
469- when KeepAlive =:= undefined ->
470- run_socket (State # state {connection_state = running });
404+ State # state {connection_state = blocked ,
405+ keepalive = rabbit_mqtt_keepalive :cancel_timer (KState )};
471406 {blocked , false } ->
472- Ref = start_keepalive_timer (KeepAlive ),
473- run_socket (State # state {connection_state = running ,
474- keepalive = KeepAlive # keepalive {timer = Ref }});
407+ run_socket (State # state {connection_state = running ,
408+ keepalive = rabbit_mqtt_keepalive :start_timer (KState )});
475409 {_ , _ } ->
476410 run_socket (State )
477411 end .
0 commit comments