2222-export ([websocket_info /3 ]).
2323-export ([websocket_terminate /3 ]).
2424
25+ -export ([conserve_resources /3 ]).
26+
2527-include_lib (" amqp_client/include/amqp_client.hrl" ).
2628
2729-record (state , {
2830 conn_name ,
31+ keepalive ,
32+ keepalive_sup ,
2933 parse_state ,
3034 proc_state
3135}).
3236
3337init (_ , _ , _ ) ->
3438 {upgrade , protocol , cowboy_websocket }.
3539
36- websocket_init (_ , Req , _ ) ->
37- % io:format(user, "~p~n", [Req]),
38-
40+ websocket_init (_ , Req , Opts ) ->
3941 process_flag (trap_exit , true ),
42+ {_ , KeepaliveSup } = lists :keyfind (keepalive_sup , 1 , Opts ),
4043 Sock = cowboy_req :get (socket , Req ),
4144 case rabbit_net :connection_string (Sock , inbound ) of
4245 {ok , ConnStr } ->
4346 rabbit_log :log (connection , info , " accepting WEB-MQTT connection ~p (~s )~n " , [self (), ConnStr ]),
44- % rabbit_alarm:register(
45- % self(), {?MODULE, conserve_resources, []}),
47+ rabbit_alarm :register (
48+ self (), {? MODULE , conserve_resources , []}),
4649 ProcessorState = rabbit_mqtt_processor :initial_state (Sock ,
4750 rabbit_mqtt_reader :ssl_login_name (Sock ),
4851 fun send_reply /2 ),
4952 {SecWsProtocol , Req1 } = cowboy_req :header (<<" sec-websocket-protocol" >>, Req ),
5053 Req2 = cowboy_req :set_resp_header (<<" sec-websocket-protocol" >>, SecWsProtocol , Req1 ),
51- % % @todo compact?
52- % % control_throttle
53- {ok , Req2 , # state {
54- % % keepalive/keepalive_sup
55- conn_name = ConnStr ,
56- parse_state = rabbit_mqtt_frame :initial_state (),
57- proc_state = ProcessorState
58- }};
54+ Req3 = cowboy_req :compact (Req2 ),
55+ % % @todo control_throttle
56+ {ok , Req3 , # state {
57+ conn_name = ConnStr ,
58+ keepalive = {none , none },
59+ keepalive_sup = KeepaliveSup ,
60+ parse_state = rabbit_mqtt_frame :initial_state (),
61+ proc_state = ProcessorState
62+ }, hibernate };
5963 _ ->
6064 {shutdown , Req }
6165 end .
6266
63- % % @todo hibernate everywhere?
6467websocket_handle ({binary , Data }, Req , State ) ->
6568 handle_data (Data , Req , State );
6669websocket_handle (Frame , Req , State ) ->
67- rabbit_log :info ( " rabbit_web_mqtt : unexpected Websocket frame ~p~n " ,
70+ rabbit_log :log ( connection , info , " WEB-MQTT : unexpected Websocket frame ~p~n " ,
6871 [Frame ]),
69- {ok , Req , State }.
72+ {ok , Req , State , hibernate }.
7073
7174websocket_info ({# 'basic.deliver' {}, # amqp_msg {}, _DeliveryCtx } = Delivery ,
7275 Req , State = # state { proc_state = ProcState0 }) ->
7376 case rabbit_mqtt_processor :amqp_callback (Delivery , ProcState0 ) of
7477 {ok , ProcState } ->
75- {ok , Req , State # state { proc_state = ProcState }};
78+ {ok , Req , State # state { proc_state = ProcState }, hibernate };
7679 {error , _ , _ } ->
7780 {shutdown , Req , State }
7881 end ;
7982websocket_info (# 'basic.ack' {} = Ack , Req , State = # state { proc_state = ProcState0 }) ->
8083 case rabbit_mqtt_processor :amqp_callback (Ack , ProcState0 ) of
8184 {ok , ProcState } ->
82- {ok , Req , State # state { proc_state = ProcState }};
85+ {ok , Req , State # state { proc_state = ProcState }, hibernate };
8386 {error , _ , _ } ->
8487 {shutdown , Req , State }
8588 end ;
8689websocket_info (# 'basic.consume_ok' {}, Req , State ) ->
87- {ok , Req , State };
90+ {ok , Req , State , hibernate };
8891websocket_info (# 'basic.cancel' {}, Req , State ) ->
8992 {shutdown , Req , State };
9093websocket_info ({reply , Data }, Req , State ) ->
91- {reply , {binary , Data }, Req , State };
92- websocket_info ({'EXIT' , _ , _ }, State ) ->
94+ {reply , {binary , Data }, Req , State , hibernate };
95+ websocket_info ({'EXIT' , _ , _ }, Req , State ) ->
9396 {shutdown , Req , State };
9497websocket_info ({'$gen_cast' , duplicate_id }, Req , State = # state { proc_state = ProcState ,
9598 conn_name = ConnName }) ->
96- rabbit_log :warning ( " MQTT disconnecting duplicate client id ~p (~p )~n " ,
99+ rabbit_log :log ( connection , warning , " WEB- MQTT disconnecting duplicate client id ~p (~p )~n " ,
97100 [rabbit_mqtt_processor :info (client_id , ProcState ), ConnName ]),
98101 {shutdown , Req , State };
102+ websocket_info ({start_keepalives , Keepalive }, Req ,
103+ State = # state { keepalive_sup = KeepaliveSup }) ->
104+ Sock = cowboy_req :get (socket , Req ),
105+ % % Only the client has the responsibility for sending keepalives
106+ SendFun = fun () -> ok end ,
107+ Parent = self (),
108+ ReceiveFun = fun () -> Parent ! keepalive_timeout end ,
109+ Heartbeater = rabbit_heartbeat :start (
110+ KeepaliveSup , Sock , 0 , SendFun , Keepalive , ReceiveFun ),
111+ {ok , Req , State # state { keepalive = Heartbeater }};
112+
113+ websocket_info (keepalive_timeout , Req , State = # state {conn_name = ConnStr ,
114+ proc_state = PState }) ->
115+ rabbit_log :log (connection , error , " closing WEB-MQTT connection ~p (keepalive timeout)~n " , [ConnStr ]),
116+ rabbit_mqtt_processor :send_will (PState ),
117+ {shutdown , Req , State };
118+
119+ % % @todo conserve_resources
120+ % % @todo bump_credit
121+
99122websocket_info (Msg , Req , State ) ->
100- rabbit_log :info ( " rabbit_web_mqtt : unexpected message ~p~n " ,
123+ rabbit_log :log ( connection , info , " WEB-MQTT : unexpected message ~p~n " ,
101124 [Msg ]),
102- {ok , Req , State }.
103-
125+ {ok , Req , State , hibernate }.
104126
105- websocket_terminate (_ , _ , # state {proc_state = ProcState }) ->
127+ websocket_terminate (_ , _ , # state { proc_state = ProcState ,
128+ conn_name = ConnName }) ->
129+ rabbit_log :log (connection , info , " closing WEB-MQTT connection ~p (~s )~n " , [self (), ConnName ]),
106130 rabbit_mqtt_processor :close_connection (ProcState ),
107131 ok ;
108132websocket_terminate (_ , _ , _ ) ->
109133 ok .
110134
135+ conserve_resources (Pid , _ , Conserve ) ->
136+ Pid ! {conserve_resources , Conserve },
137+ ok .
138+
111139% % Internal.
112140
113141handle_data (<<>>, Req , State ) ->
114- {ok , Req , State };
142+ {ok , Req , State , hibernate };
115143handle_data (Data , Req , State = # state { parse_state = ParseState ,
116144 proc_state = ProcState ,
117145 conn_name = ConnStr }) ->
118146 case rabbit_mqtt_frame :parse (Data , ParseState ) of
119147 {more , ParseState1 } ->
120148 % % @todo control_throttle
121- {ok , Req , State # state { parse_state = ParseState1 }};
149+ {ok , Req , State # state { parse_state = ParseState1 }, hibernate };
122150 {ok , Frame , Rest } ->
123151 case rabbit_mqtt_processor :process_frame (Frame , ProcState ) of
124152 {ok , ProcState1 } ->
@@ -145,5 +173,4 @@ handle_data(Data, Req, State = #state{ parse_state = ParseState,
145173 end .
146174
147175send_reply (Frame , _ ) ->
148- rabbit_log :info (" MQTT sending frame ~p ~n " , [Frame ]),
149176 self () ! {reply , rabbit_mqtt_frame :serialise (Frame )}.
0 commit comments