Skip to content

Changing the way timers work #48

Merged
merged 5 commits into from Jun 17, 2011
View
4 src/socketio.app.src
@@ -10,5 +10,7 @@
misultin
]},
{mod, { socketio_app, []}},
- {env, [ {heartbeat_interval, 10000}, {close_timeout, 8000} ]}
+ {env, [{heartbeat_interval, 10000},
+ {close_timeout, 8000},
+ {polling_duration, 20000}]}
]}.
View
86 src/socketio_transport_htmlfile.erl
@@ -63,9 +63,9 @@ init([Sup, SessionId, ServerModule, {'htmlfile', {Req, Caller}}]) ->
_ ->
error_logger:warning_report(
"Could not load default heartbeat_interval value from "
- "the application file. Setting the default value to infinity."
+ "the application file. Setting the default value to 10000."
),
- infinity
+ 10000
end,
CloseTimeout =
case application:get_env(close_timeout) of
@@ -85,7 +85,7 @@ init([Sup, SessionId, ServerModule, {'htmlfile', {Req, Caller}}]) ->
req = Req,
caller = Caller,
close_timeout = CloseTimeout,
- heartbeat_interval = HeartbeatInterval,
+ heartbeat_interval = {make_ref(), HeartbeatInterval},
event_manager = EventMgr,
sup = Sup
}}.
@@ -105,21 +105,24 @@ init([Sup, SessionId, ServerModule, {'htmlfile', {Req, Caller}}]) ->
%% @end
%%--------------------------------------------------------------------
%% Incoming data
-handle_call({'htmlfile', data, Req}, _From, #state{ heartbeat_interval = Interval,
+handle_call({'htmlfile', data, Req}, _From, #state{ heartbeat_interval = Interval,
server_module = ServerModule,
event_manager = EventManager } = State) ->
- Data = apply(ServerModule, parse_post, [Req]),
- Self = self(),
- lists:foreach(fun({"data", M}) ->
- spawn(fun () ->
- F = fun(#heartbeat{}) -> ignore;
- (M0) -> gen_event:notify(EventManager, {message, Self, M0})
- end,
- [F(Msg) || Msg <- socketio_data:decode(#msg{content=M})]
- end)
- end, Data),
- apply(ServerModule, respond, [Req, 200, [{"Content-Type","text/plain"}],"ok"]),
- {reply, ok, State, Interval};
+ Msgs = [socketio_data:decode(#msg{content=Data}) || {"data", Data} <- ServerModule:parse_post(Req)],
+ F = fun(#heartbeat{}, _Acc) ->
+ {timer, reset_heartbeat(Interval)};
+ (M, Acc) ->
+ gen_event:notify(EventManager, {message, self(), M}),
+ Acc
+ end,
+ NewState = case lists:foldl(F, undefined, lists:flatten(Msgs)) of
+ {timer, NewInterval} ->
+ State#state{ heartbeat_interval = NewInterval};
+ undefined ->
+ State
+ end,
+ ServerModule:respond(Req, 200, [{"Content-Type", "text/plain"}], "ok"),
+ {reply, ok, NewState};
%% Event management
handle_call(event_manager, _From, #state{ event_manager = EventMgr } = State) ->
@@ -149,26 +152,29 @@ handle_call(stop, _From, State) ->
%% @end
%%--------------------------------------------------------------------
handle_cast({initialize, Req}, #state{ heartbeat_interval = Interval, server_module = ServerModule } = State) ->
- apply(ServerModule, headers, [Req, [{"Content-Type", "text/html"},
- {"Connection", "Keep-Alive"},
- {"Transfer-Encoding", "chunked"}]]),
- H = "<html><body>" ++ lists:duplicate(254,$\s),
- link(apply(ServerModule, socket, [Req])),
- apply(ServerModule, chunk, [Req, H]),
- {noreply, State#state{ connection_reference = {'htmlfile', connected} }, Interval};
+ ServerModule:headers(Req, [{"Content-Type", "text/html"},
+ {"Connection", "Keep-Alive"},
+ {"Transfer-Encoding", "chunked"}]),
+ H = "<html><body>" ++ lists:duplicate(254, $\s),
+ link(ServerModule:socket(Req)),
+ ServerModule:chunk(Req, H),
+ {noreply, State#state{ connection_reference = {htmlfile, connected},
+ heartbeat_interval = reset_heartbeat(Interval) }};
handle_cast(heartbeat, #state{ heartbeats = Beats,
heartbeat_interval = Interval } = State) ->
Beats1 = Beats + 1,
socketio_client:send(self(), #heartbeat{ index = Beats1 }),
- {noreply, State#state { heartbeats = Beats1 }, Interval};
+ {noreply, State#state{ heartbeats = Beats1,
+ heartbeat_interval = reset_heartbeat(Interval) }};
%% Send
handle_cast({send, Message}, #state{ req = Req,
server_module = ServerModule,
- connection_reference = {'htmlfile', connected }, heartbeat_interval = Interval } = State) ->
+ connection_reference = {'htmlfile', connected },
+ heartbeat_interval = Interval } = State) ->
send_message(Message, ServerModule, Req),
- {noreply, State, Interval};
+ {noreply, State#state{ heartbeat_interval = reset_heartbeat(Interval) }};
handle_cast(_, #state{} = State) ->
{noreply, State}.
@@ -184,15 +190,34 @@ handle_cast(_, #state{} = State) ->
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
+%% CLient disconnected. We fire a timer (ServerTimeout)!
handle_info({'EXIT',_Port,_Reason}, #state{ close_timeout = ServerTimeout} = State) when is_port(_Port) ->
{noreply, State#state { connection_reference = {'htmlfile', none}}, ServerTimeout};
+%% This branch handles two purposes: 1. handling the close_timeout,
+%% 2. handling the heartbeat timeout that might comes first. The thing is,
+%% when the client connection dies, we need to wait for the close_timeout
+%% to be fired. That one can be cancelled at any time by God knows what for now,
+%% and that might be desirable. However, it can also be cancelled because we
+%% happen to receive the heartbeat timeout. Given the right setting, this will
+%% happen every time a disconnection happens at the point where the delay left to
+%% the current heartbeat is longer than the delay left to the total value of
+%% close_timout. Funny, eh?
+%% For this reason, the heartbeat timeout when we have no htmlfile
+%% connection reference has to be seen as the same as the close_timeout
+%% timer firing off. This is the safest way to guarantee everything will run
+%% okay.
handle_info(timeout, #state{ server_module = ServerModule,
connection_reference = {'htmlfile', none}, caller = Caller, req = Req } = State) ->
- gen_server:reply(Caller, apply(ServerModule, respond, [Req, 200])),
+ gen_server:reply(Caller, ServerModule:respond(Req, 200)),
{stop, shutdown, State};
-handle_info(timeout, State) ->
+%% See previous clauses' comments
+handle_info({timeout, _Ref, heartbeat}, #state{ connection_reference = {'htmlfile', none} } = State) ->
+ handle_info(timeout, State);
+
+%% Regular heartbeat
+handle_info({timeout, _Ref, heartbeat}, State) ->
gen_server:cast(self(), heartbeat),
{noreply, State};
@@ -237,3 +262,8 @@ send_message(Message, ServerModule, Req) ->
Message0 = binary_to_list(jsx:term_to_json(list_to_binary(Message), [{strict, false}])),
M = "<script>parent.s._(" ++ Message0 ++ ", document);</script>",
apply(ServerModule, chunk, [Req, M]).
+
+reset_heartbeat({TimerRef, Time}) ->
@jannschu
jannschu added a note Jun 16, 2011

Is this function duplicated with a small change in the transports? (reset_duration, reset_interval)

@ferd
Collaborator
ferd added a note Jun 16, 2011

Most of them yes. I could likely generalize it by making the message to send a third parameter in a triple. I guess it would make more sense, although I'm not sure where the function would sit.

@jannschu
jannschu added a note Jun 16, 2011

Maybe adding a socketio_transport module, but it would be a bit empty. I did not look closer, eventually there is more to generalize.

@ferd
Collaborator
ferd added a note Jun 16, 2011

Agreed. A lot of the handling of 'heartbeat vs. message' is surprisingly similar and could likely be abstracted away too. I'll look into that once we get it to work (I've seen your fix, I'll do it in a few minutes after lunch)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ erlang:cancel_timer(TimerRef),
+ NewRef = erlang:start_timer(Time, self(), heartbeat),
+ {NewRef, Time}.
View
83 src/socketio_transport_polling.erl
@@ -62,7 +62,7 @@ init([Sup, SessionId, ServerModule, {TransportType, {Req, Index}}]) ->
Time;
_ ->
error_logger:warning_report(
- "Could not load default heartbeat_interval value from "
+ "Could not load default polling_duration value from "
"the application file. Setting the default value to 20000 ms."
),
20000
@@ -82,7 +82,7 @@ init([Sup, SessionId, ServerModule, {TransportType, {Req, Index}}]) ->
connection_reference = {TransportType, none},
req = Req,
index = Index,
- polling_duration = PollingDuration,
+ polling_duration = {make_ref(), PollingDuration},
close_timeout = CloseTimeout,
event_manager = EventMgr,
sup = Sup
@@ -108,26 +108,32 @@ init([Sup, SessionId, ServerModule, {TransportType, Req}]) ->
%%--------------------------------------------------------------------
%% Incoming data
handle_call({_TransportType, data, Req}, From, #state{ server_module = ServerModule,
- event_manager = EventManager, sup = Sup } = State) ->
- Data = apply(ServerModule, parse_post, [Req]),
- Self = self(),
- Response =
- case cors_headers(apply(ServerModule, get_headers, [Req]), Sup) of
- {false, _Headers} ->
- gen_server:reply(From, apply(ServerModule, respond, [Req, 405, "unauthorized"]));
- {_, Headers0} ->
- Self = self(),
- lists:foreach(fun({"data", M}) ->
- spawn(fun () ->
- F = fun(#heartbeat{}) -> ignore;
- (M0) -> gen_event:notify(EventManager, {message, Self, M0})
- end,
- [F(Msg) || Msg <- socketio_data:decode(#msg{content=M})]
- end)
- end, Data),
- gen_server:reply(From, apply(ServerModule, respond,[Req, 200, [Headers0|[{"Content-Type", "text/plain"}]], "ok"]))
- end,
- {reply, Response, State};
+ event_manager = EventManager,
+ polling_duration = Interval,
+ sup = Sup } = State) ->
+ Msgs = [socketio_data:decode(#msg{content=Data}) || {"data", Data} <- ServerModule:parse_post(Req)],
+ {Response, NewState} =
+ case cors_headers(ServerModule:get_headers(Req), Sup) of
+ {false, _Headers} ->
+ Reply = gen_server:reply(From, ServerModule:respond(Req, 405, "unauthorized")),
+ {Reply, State};
+ {_, Headers} ->
+ F = fun(#heartbeat{}, _Acc) ->
+ {timer, reset_duration(Interval)};
+ (M, Acc) ->
+ gen_event:notify(EventManager, {message, self(), M}),
+ Acc
+ end,
+ TmpState = case lists:foldl(F, undefined, lists:flatten(Msgs)) of
+ {timer, NewInterval} ->
+ State#state{ polling_duration = NewInterval};
+ undefined ->
+ State
+ end,
+ Reply = gen_server:reply(From, ServerModule:respond(Req, 200, [Headers | [{"Content-Type", "text/plain"}]], "ok")),
+ {Reply, TmpState}
+ end,
+ {reply, Response, NewState};
%% Event management
handle_call(event_manager, _From, #state{ event_manager = EventMgr } = State) ->
@@ -160,10 +166,13 @@ handle_call(stop, _From, State) ->
handle_cast({TransportType, polling_request, {Req, Index}, Server}, State) ->
handle_cast({TransportType, polling_request, Req, Server}, State#state{ index = Index});
handle_cast({TransportType, polling_request, Req, Server}, #state { server_module = ServerModule,
- polling_duration = Interval, message_buffer = [] } = State) ->
- apply(ServerModule, ensure_longpolling_request, [Req]),
- link(apply(ServerModule, socket, [Req])),
- {noreply, State#state{ connection_reference = {TransportType, connected}, req = Req, caller = Server }, Interval};
+ polling_duration = Interval,
+ message_buffer = [] } = State) ->
+ ServerModule:ensure_longpolling_request(Req),
+ link(ServerModule:socket(Req)),
+ {noreply, State#state{ connection_reference = {TransportType, connected}, req = Req,
+ caller = Server,
+ polling_duration = reset_duration(Interval) }};
handle_cast({TransportType, polling_request, Req, Server}, #state { server_module = ServerModule,
message_buffer = Buffer } = State) ->
@@ -179,7 +188,8 @@ handle_cast({send, Message}, #state{ server_module = ServerModule,
connection_reference = {TransportType, connected }, req = Req, caller = Caller,
index = Index, sup = Sup, polling_duration = Interval} = State) ->
gen_server:reply(Caller, send_message(Message, Req, Index, ServerModule, Sup)),
- {noreply, State#state{ connection_reference = {TransportType, none}}, Interval};
+ {noreply, State#state{ connection_reference = {TransportType, none},
+ polling_duration = reset_duration(Interval) }};
handle_cast(_, State) ->
{noreply, State}.
@@ -195,21 +205,29 @@ handle_cast(_, State) ->
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
+%% A client has disconnected. We fire a timer (CloseTimeout)!
handle_info({'EXIT',Port,_Reason}, #state{ connection_reference = {TransportType, _ }, close_timeout = CloseTimeout} = State) when is_port(Port) ->
{noreply, State#state { connection_reference = {TransportType, none}}, CloseTimeout};
-%% Connection has timed out
-handle_info(timeout, #state{ server_module = ServerModule,
+%% Connections has timed out, but is technically still active. This is like a
+%% heartbeat, but for polling connections.
+handle_info({timeout, _Ref, polling}, #state{ server_module = ServerModule,
connection_reference = {_TransportType, connected},
caller = Caller, req = Req, index = Index, sup = Sup } = State) ->
gen_server:reply(Caller, send_message("", Req, Index, ServerModule, Sup)),
{noreply, State};
-%% Client has timed out
+%% Client has timed out, no active connection found. (connection_reference = none)
handle_info(timeout, #state{ server_module = ServerModule, caller = Caller, req = Req } = State) ->
gen_server:reply(Caller, apply(ServerModule, respond, [Req, 200,""])),
{stop, shutdown, State};
+%% client has timed out, no active connection found, but the normal close_timeout
+%% is being interrupted by the polling timeout timer interacting in here.
+%% We defer to the preceding clause.
+handle_info({timeout, _Ref, polling}, #state{ } = State) ->
+ handle_info(timeout, State);
+
handle_info(_Info, State) ->
{noreply, State}.
@@ -301,3 +319,8 @@ cors_headers(Headers, Sup) ->
{false, Headers}
end
end.
+
+reset_duration({TimerRef, Time}) ->
+ erlang:cancel_timer(TimerRef),
+ NewRef = erlang:start_timer(Time, self(), polling),
+ {NewRef, Time}.
View
39 src/socketio_transport_websocket.erl
@@ -70,7 +70,7 @@ init([Sup, SessionId, ServerModule, ConnectionReference]) ->
session_id = SessionId,
server_module = ServerModule,
connection_reference = ConnectionReference,
- heartbeat_interval = HeartbeatInterval,
+ heartbeat_interval = {make_ref(), HeartbeatInterval},
event_manager = EventMgr,
sup = Sup
}}.
@@ -92,14 +92,18 @@ init([Sup, SessionId, ServerModule, ConnectionReference]) ->
%% Websockets
handle_call({websocket, Data, _Ws}, _From, #state{ heartbeat_interval = Interval, event_manager = EventManager } = State) ->
- Self = self(),
- spawn_link(fun() ->
- F = fun (#heartbeat{}) -> ignore; %% FIXME: we should actually reply
- (M) -> gen_event:notify(EventManager, {message, Self, M})
- end,
- [F(Msg) || Msg <- socketio_data:decode(#msg{content=Data})]
- end),
- {reply, ok, State, Interval};
+ F = fun(#heartbeat{}, _Acc) ->
+ {timer, reset_interval(Interval)};
+ (M, Acc) ->
+ gen_event:notify(EventManager, {message, self(), M}),
+ Acc
+ end,
+ case lists:foldl(F, undefined, socketio_data:decode(#msg{content=Data})) of
+ {timer, NewInterval} ->
+ {reply, ok, State#state{ heartbeat_interval = NewInterval }};
+ undefined ->
+ {reply, ok, State}
+ end;
handle_call({websocket, _}, _From, State) ->
{reply, ok, State};
@@ -133,17 +137,18 @@ handle_call(stop, _From, State) ->
%%--------------------------------------------------------------------
%% Send
handle_cast({send, Message}, #state{ server_module = ServerModule,
- connection_reference = ConnectionReference, heartbeat_interval = Interval } = State) ->
+ connection_reference = ConnectionReference,
+ heartbeat_interval = Interval } = State) ->
handle_send(ConnectionReference, Message, ServerModule),
- {noreply, State, Interval};
+ {noreply, State#state{ heartbeat_interval = reset_interval(Interval) }};
handle_cast(heartbeat, #state{
server_module = ServerModule,
connection_reference = ConnectionReference, heartbeats = Beats,
heartbeat_interval = Interval } = State) ->
Beats1 = Beats + 1,
handle_send(ConnectionReference, #heartbeat{ index = Beats1 }, ServerModule),
- {noreply, State#state { heartbeats = Beats1 }, Interval}.
+ {noreply, State#state { heartbeats = Beats1, heartbeat_interval = reset_interval(Interval) }}.
%%--------------------------------------------------------------------
%% @private
@@ -155,9 +160,8 @@ handle_cast(heartbeat, #state{
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
-handle_info(timeout, State) ->
- gen_server:cast(self(), heartbeat),
- {noreply, State};
+handle_info({timeout, _Ref, heartbeat}, State) ->
+ handle_cast(heartbeat, State);
handle_info(_Info, State) ->
{noreply, State}.
@@ -192,3 +196,8 @@ code_change(_OldVsn, State, _Extra) ->
%%%===================================================================
handle_send({websocket, Ws}, Message, ServerModule) ->
apply(ServerModule, websocket_send, [Ws, socketio_data:encode(Message)]).
+
+reset_interval({TimerRef, Time}) ->
+ erlang:cancel_timer(TimerRef),
+ NewRef = erlang:start_timer(Time, self(), heartbeat),
+ {NewRef, Time}.
View
89 src/socketio_transport_xhr_multipart.erl
@@ -63,9 +63,9 @@ init([Sup, SessionId, ServerModule, {'xhr-multipart', {Req, Caller}}]) ->
_ ->
error_logger:warning_report(
"Could not load default heartbeat_interval value from "
- "the application file. Setting the default value to infinity."
+ "the application file. Setting the default value to 10000."
),
- infinity
+ 10000
end,
CloseTimeout =
case application:get_env(close_timeout) of
@@ -89,7 +89,7 @@ init([Sup, SessionId, ServerModule, {'xhr-multipart', {Req, Caller}}]) ->
req = Req,
caller = Caller,
close_timeout = CloseTimeout,
- heartbeat_interval = HeartbeatInterval,
+ heartbeat_interval = {make_ref(), HeartbeatInterval},
event_manager = EventMgr,
sup = Sup
}}.
@@ -110,19 +110,23 @@ init([Sup, SessionId, ServerModule, {'xhr-multipart', {Req, Caller}}]) ->
%%--------------------------------------------------------------------
%% Incoming data
handle_call({'xhr-multipart', data, Req}, _From, #state{ server_module = ServerModule,
- heartbeat_interval = Interval, event_manager = EventManager } = State) ->
- Data = apply(ServerModule, parse_post, [Req]),
- Self = self(),
- lists:foreach(fun({"data", M}) ->
- spawn(fun () ->
- F = fun(#heartbeat{}) -> ignore;
- (M0) -> gen_event:notify(EventManager, {message, Self, M0})
- end,
- [F(Msg) || Msg <- socketio_data:decode(#msg{content=M})]
- end)
- end, Data),
- apply(ServerModule, respond, [Req, 200,[{"Content-Type","text/plain"}],"ok"]),
- {reply, ok, State, Interval};
+ heartbeat_interval = Interval,
+ event_manager = EventManager } = State) ->
+ Msgs = [socketio_data:decode(#msg{content=Data}) || {"data", Data} <- ServerModule:parse_post(Req)],
+ F = fun(#heartbeat{}, _Acc) ->
+ {timer, reset_heartbeat(Interval)};
+ (M, Acc) ->
+ gen_event:notify(EventManager, {message, self(), M}),
+ Acc
+ end,
+ NewState = case lists:foldl(F, undefined, lists:flatten(Msgs)) of
+ {timer, NewInterval} ->
+ State#state{ heartbeat_interval = NewInterval};
+ undefined ->
+ State
+ end,
+ ServerModule:respond(Req, 200, [{"Content-Type", "text/plain"}], "ok"),
+ {reply, ok, NewState};
%% Event management
handle_call(event_manager, _From, #state{ event_manager = EventMgr } = State) ->
@@ -152,38 +156,41 @@ handle_call(stop, _From, State) ->
%% @end
%%--------------------------------------------------------------------
handle_cast({initialize, Req}, #state{ server_module = ServerModule, heartbeat_interval = Interval } = State) ->
- Headers = apply(ServerModule, get_headers, [Req]),
- Headers1 =
+ Headers = ServerModule:get_headers(Req),
+ Headers1 =
case proplists:get_value('Origin', Headers) of
undefined ->
Headers;
Origin ->
case socketio_listener:verify_origin(Origin, socketio_listener:origins(listener(State))) of
true ->
[{"Access-Control-Allow-Origin", "*"},
- {"Access-Control-Allow-Credentials", "true"}|Headers];
+ {"Access-Control-Allow-Credentials", "true"} | Headers];
false ->
Headers
end
end,
- link(apply(ServerModule, socket, [Req])),
- apply(ServerModule, headers, [Req, [{"Content-Type", "multipart/x-mixed-replace;boundary=\"socketio\""},
- {"Connection", "Keep-Alive"}|Headers1]]),
- apply(ServerModule, stream, [Req, "--socketio\n"]),
- {noreply, State#state{ connection_reference = {'xhr-multipart', connected} }, Interval};
+ link(ServerModule:socket(Req)),
+ ServerModule:headers(Req, [{"Content-Type", "multipart/x-mixed-replace;boundary=\"socketio\""},
+ {"Connection", "Keep-Alive"} | Headers1]),
+ ServerModule:stream(Req, "--socketio\n"),
+ {noreply, State#state{ connection_reference = {'xhr-multipart', connected},
+ heartbeat_interval = reset_heartbeat(Interval) }};
handle_cast(heartbeat, #state{ heartbeats = Beats,
heartbeat_interval = Interval } = State) ->
Beats1 = Beats + 1,
socketio_client:send(self(), #heartbeat{ index = Beats1 }),
- {noreply, State#state { heartbeats = Beats1 }, Interval};
+ {noreply, State#state { heartbeats = Beats1,
+ heartbeat_interval = reset_heartbeat(Interval) }};
%% Send
handle_cast({send, Message}, #state{ req = Req,
server_module = ServerModule,
- connection_reference = {'xhr-multipart', connected }, heartbeat_interval = Interval } = State) ->
+ connection_reference = {'xhr-multipart', connected },
+ heartbeat_interval = Interval } = State) ->
send_message(Message, Req, ServerModule),
- {noreply, State, Interval};
+ {noreply, State#state{ heartbeat_interval = reset_heartbeat(Interval) }};
handle_cast(_, #state{} = State) ->
{noreply, State}.
@@ -199,15 +206,34 @@ handle_cast(_, #state{} = State) ->
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
+%% A client has disconnected. We fire a timer (ServerTimeout)!
handle_info({'EXIT',_Port,_Reason}, #state{ close_timeout = ServerTimeout} = State) when is_port(_Port) ->
{noreply, State#state { connection_reference = {'xhr-multipart', none}}, ServerTimeout};
+%% This branch handles two purposes: 1. handling the close_timeout,
+%% 2. handling the heartbeat timeout that might comes first. The thing is,
+%% when the client connection dies, we need to wait for the close_timeout
+%% to be fired. That one can be cancelled at any time by God knows what for now,
+%% and that might be desirable. However, it can also be cancelled because we
+%% happen to receive the heartbeat timeout. Given the right setting, this will
+%% happen every time a disconnection happens at the point where the delay left to
+%% the current heartbeat is longer than the delay left to the total value of
+%% close_timout. Funny, eh?
+%% For this reason, the heartbeat timeout when we have no xhr-multipart
+%% connection reference has to be seen as the same as the close_timeout
+%% timer firing off. This is the safest way to guarantee everything will run
+%% okay.
handle_info(timeout, #state{ server_module = ServerModule,
connection_reference = {'xhr-multipart', none}, req = Req, caller = Caller } = State) ->
- gen_server:reply(Caller, apply(ServerModule, respond, [Req, 200, ""])),
+ gen_server:reply(Caller, ServerModule:respond(Req, 200, "")),
{stop, shutdown, State};
-handle_info(timeout, State) ->
+%% See the previous clauses' comments, please.
+handle_info({timeout, _Ref, heartbeat}, #state{ connection_reference = {'xhr-multipart', none} } = State) ->
+ handle_info(timeout, State);
+
+%% Good old regular heartbeat. I admire your simplicity.
+handle_info({timeout, _Ref, heartbeat}, State) ->
gen_server:cast(self(), heartbeat),
{noreply, State};
@@ -255,3 +281,8 @@ send_message(Message, Req, ServerModule) ->
listener(#state{ sup = Sup }) ->
socketio_listener:server(Sup).
+
+reset_heartbeat({TimerRef, Time}) ->
+ erlang:cancel_timer(TimerRef),
+ NewRef = erlang:start_timer(Time, self(), heartbeat),
+ {NewRef, Time}.
Something went wrong with that request. Please try again.