24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-06-24 22:25:47 +02:00

* src/ejabberd_c2s.erl: Hibernate inactive c2s processes (EJAB-356).

SVN Revision: 934
This commit is contained in:
Mickaël Rémond 2007-09-14 14:18:01 +00:00
parent f787a3ed71
commit 8d3d59535d

View File

@ -87,6 +87,7 @@
%% This is the timeout to apply between event when starting a new %% This is the timeout to apply between event when starting a new
%% session: %% session:
-define(C2S_OPEN_TIMEOUT, 5000). -define(C2S_OPEN_TIMEOUT, 5000).
-define(C2S_HIBERNATE_TIMEOUT, 90000).
-define(STREAM_HEADER, -define(STREAM_HEADER,
"<?xml version='1.0'?>" "<?xml version='1.0'?>"
@ -266,11 +267,11 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
c2s_stream_features, c2s_stream_features,
Server, Server,
[], [])}), [], [])}),
{next_state, wait_for_feature_request, fsm_next_state(wait_for_feature_request,
StateData#state{server = Server, StateData#state{
sasl_state = SASLState, server = Server,
lang = Lang}, sasl_state = SASLState,
?C2S_OPEN_TIMEOUT}; lang = Lang});
_ -> _ ->
case StateData#state.resource of case StateData#state.resource of
"" -> "" ->
@ -281,17 +282,18 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
[{"xmlns", ?NS_BIND}], []}, [{"xmlns", ?NS_BIND}], []},
{xmlelement, "session", {xmlelement, "session",
[{"xmlns", ?NS_SESSION}], []}]}), [{"xmlns", ?NS_SESSION}], []}]}),
{next_state, wait_for_bind, fsm_next_state(wait_for_bind,
StateData#state{server = Server, StateData#state{
lang = Lang}, server = Server,
?C2S_OPEN_TIMEOUT}; lang = Lang});
_ -> _ ->
send_element( send_element(
StateData, StateData,
{xmlelement, "stream:features", [], []}), {xmlelement, "stream:features", [], []}),
{next_state, wait_for_session, fsm_next_state(wait_for_session,
StateData#state{server = Server, StateData#state{
lang = Lang}} server = Server,
lang = Lang})
end end
end; end;
_ -> _ ->
@ -311,9 +313,10 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
{stop, normal, StateData}; {stop, normal, StateData};
true -> true ->
send_text(StateData, Header), send_text(StateData, Header),
{next_state, wait_for_auth, fsm_next_state(wait_for_auth,
StateData#state{server = Server, StateData#state{
lang = Lang}} server = Server,
lang = Lang})
end end
end; end;
_ -> _ ->
@ -385,13 +388,13 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
]}]} ]}]}
end, end,
send_element(StateData, Res), send_element(StateData, Res),
{next_state, wait_for_auth, StateData, ?C2S_OPEN_TIMEOUT}; fsm_next_state(wait_for_auth, StateData);
{auth, _ID, set, {_U, _P, _D, ""}} -> {auth, _ID, set, {_U, _P, _D, ""}} ->
Err = jlib:make_error_reply( Err = jlib:make_error_reply(
El, El,
?ERR_AUTH_NO_RESOURCE_PROVIDED(StateData#state.lang)), ?ERR_AUTH_NO_RESOURCE_PROVIDED(StateData#state.lang)),
send_element(StateData, Err), send_element(StateData, Err),
{next_state, wait_for_auth, StateData, ?C2S_OPEN_TIMEOUT}; fsm_next_state(wait_for_auth, StateData);
{auth, _ID, set, {U, P, D, R}} -> {auth, _ID, set, {U, P, D, R}} ->
JID = jlib:make_jid(U, StateData#state.server, R), JID = jlib:make_jid(U, StateData#state.server, R),
case (JID /= error) andalso case (JID /= error) andalso
@ -428,14 +431,15 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
privacy_get_user_list, StateData#state.server, privacy_get_user_list, StateData#state.server,
none, none,
[U, StateData#state.server]), [U, StateData#state.server]),
{next_state, session_established, fsm_next_state(session_established,
StateData#state{user = U, StateData#state{
user = U,
resource = R, resource = R,
jid = JID, jid = JID,
sid = SID, sid = SID,
pres_f = ?SETS:from_list(Fs1), pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1), pres_t = ?SETS:from_list(Ts1),
privacy_list = PrivList}}; privacy_list = PrivList});
_ -> _ ->
?INFO_MSG( ?INFO_MSG(
"(~w) Failed legacy authentication for ~s", "(~w) Failed legacy authentication for ~s",
@ -444,8 +448,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
Err = jlib:make_error_reply( Err = jlib:make_error_reply(
El, ?ERR_NOT_AUTHORIZED), El, ?ERR_NOT_AUTHORIZED),
send_element(StateData, Err), send_element(StateData, Err),
{next_state, wait_for_auth, StateData, fsm_next_state(wait_for_auth, StateData)
?C2S_OPEN_TIMEOUT}
end; end;
_ -> _ ->
if if
@ -456,8 +459,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
[StateData#state.socket, U, R]), [StateData#state.socket, U, R]),
Err = jlib:make_error_reply(El, ?ERR_JID_MALFORMED), Err = jlib:make_error_reply(El, ?ERR_JID_MALFORMED),
send_element(StateData, Err), send_element(StateData, Err),
{next_state, wait_for_auth, StateData, fsm_next_state(wait_for_auth, StateData);
?C2S_OPEN_TIMEOUT};
true -> true ->
?INFO_MSG( ?INFO_MSG(
"(~w) Forbidden legacy authentication for ~s", "(~w) Forbidden legacy authentication for ~s",
@ -465,13 +467,12 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
jlib:jid_to_string(JID)]), jlib:jid_to_string(JID)]),
Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED), Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
send_element(StateData, Err), send_element(StateData, Err),
{next_state, wait_for_auth, StateData, fsm_next_state(wait_for_auth, StateData)
?C2S_OPEN_TIMEOUT}
end end
end; end;
_ -> _ ->
process_unauthenticated_stanza(StateData, El), process_unauthenticated_stanza(StateData, El),
{next_state, wait_for_auth, StateData, ?C2S_OPEN_TIMEOUT} fsm_next_state(wait_for_auth, StateData)
end; end;
wait_for_auth(timeout, StateData) -> wait_for_auth(timeout, StateData) ->
@ -512,20 +513,20 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
U = xml:get_attr_s(username, Props), U = xml:get_attr_s(username, Props),
?INFO_MSG("(~w) Accepted authentication for ~s", ?INFO_MSG("(~w) Accepted authentication for ~s",
[StateData#state.socket, U]), [StateData#state.socket, U]),
{next_state, wait_for_stream, fsm_next_state(wait_for_stream,
StateData#state{streamid = new_id(), StateData#state{
streamid = new_id(),
authenticated = true, authenticated = true,
user = U user = U });
}, ?C2S_OPEN_TIMEOUT};
{continue, ServerOut, NewSASLState} -> {continue, ServerOut, NewSASLState} ->
send_element(StateData, send_element(StateData,
{xmlelement, "challenge", {xmlelement, "challenge",
[{"xmlns", ?NS_SASL}], [{"xmlns", ?NS_SASL}],
[{xmlcdata, [{xmlcdata,
jlib:encode_base64(ServerOut)}]}), jlib:encode_base64(ServerOut)}]}),
{next_state, wait_for_sasl_response, fsm_next_state(wait_for_sasl_response,
StateData#state{sasl_state = NewSASLState}, StateData#state{
?C2S_OPEN_TIMEOUT}; sasl_state = NewSASLState});
{error, Error, Username} -> {error, Error, Username} ->
?INFO_MSG( ?INFO_MSG(
"(~w) Failed authentication for ~s@~s", "(~w) Failed authentication for ~s@~s",
@ -542,8 +543,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
{xmlelement, "failure", {xmlelement, "failure",
[{"xmlns", ?NS_SASL}], [{"xmlns", ?NS_SASL}],
[{xmlelement, Error, [], []}]}), [{xmlelement, Error, [], []}]}),
{next_state, wait_for_feature_request, StateData, fsm_next_state(wait_for_feature_request, StateData)
?C2S_OPEN_TIMEOUT}
end; end;
{?NS_TLS, "starttls"} when TLS == true, {?NS_TLS, "starttls"} when TLS == true,
TLSEnabled == false, TLSEnabled == false,
@ -562,11 +562,11 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
Socket, TLSOpts, Socket, TLSOpts,
xml:element_to_string( xml:element_to_string(
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})), {xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []})),
{next_state, wait_for_stream, fsm_next_state(wait_for_stream,
StateData#state{socket = TLSSocket, StateData#state{socket = TLSSocket,
streamid = new_id(), streamid = new_id(),
tls_enabled = true tls_enabled = true
}, ?C2S_OPEN_TIMEOUT}; });
{?NS_COMPRESS, "compress"} when Zlib == true, {?NS_COMPRESS, "compress"} when Zlib == true,
SockMod == gen_tcp -> SockMod == gen_tcp ->
case xml:get_subtag(El, "method") of case xml:get_subtag(El, "method") of
@ -575,8 +575,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
{xmlelement, "failure", {xmlelement, "failure",
[{"xmlns", ?NS_COMPRESS}], [{"xmlns", ?NS_COMPRESS}],
[{xmlelement, "setup-failed", [], []}]}), [{xmlelement, "setup-failed", [], []}]}),
{next_state, wait_for_feature_request, StateData, fsm_next_state(wait_for_feature_request, StateData);
?C2S_OPEN_TIMEOUT};
Method -> Method ->
case xml:get_tag_cdata(Method) of case xml:get_tag_cdata(Method) of
"zlib" -> "zlib" ->
@ -586,18 +585,18 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
xml:element_to_string( xml:element_to_string(
{xmlelement, "compressed", {xmlelement, "compressed",
[{"xmlns", ?NS_COMPRESS}], []})), [{"xmlns", ?NS_COMPRESS}], []})),
{next_state, wait_for_stream, fsm_next_state(wait_for_stream,
StateData#state{socket = ZlibSocket, StateData#state{socket = ZlibSocket,
streamid = new_id() streamid = new_id()
}, ?C2S_OPEN_TIMEOUT}; });
_ -> _ ->
send_element(StateData, send_element(StateData,
{xmlelement, "failure", {xmlelement, "failure",
[{"xmlns", ?NS_COMPRESS}], [{"xmlns", ?NS_COMPRESS}],
[{xmlelement, "unsupported-method", [{xmlelement, "unsupported-method",
[], []}]}), [], []}]}),
{next_state, wait_for_feature_request, StateData, fsm_next_state(wait_for_feature_request,
?C2S_OPEN_TIMEOUT} StateData)
end end
end; end;
_ -> _ ->
@ -611,8 +610,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
{stop, normal, StateData}; {stop, normal, StateData};
true -> true ->
process_unauthenticated_stanza(StateData, El), process_unauthenticated_stanza(StateData, El),
{next_state, wait_for_feature_request, StateData, fsm_next_state(wait_for_feature_request, StateData)
?C2S_OPEN_TIMEOUT}
end end
end; end;
@ -647,19 +645,19 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
U = xml:get_attr_s(username, Props), U = xml:get_attr_s(username, Props),
?INFO_MSG("(~w) Accepted authentication for ~s", ?INFO_MSG("(~w) Accepted authentication for ~s",
[StateData#state.socket, U]), [StateData#state.socket, U]),
{next_state, wait_for_stream, fsm_next_state(wait_for_stream,
StateData#state{streamid = new_id(), StateData#state{
streamid = new_id(),
authenticated = true, authenticated = true,
user = U user = U});
}, ?C2S_OPEN_TIMEOUT};
{continue, ServerOut, NewSASLState} -> {continue, ServerOut, NewSASLState} ->
send_element(StateData, send_element(StateData,
{xmlelement, "challenge", {xmlelement, "challenge",
[{"xmlns", ?NS_SASL}], [{"xmlns", ?NS_SASL}],
[{xmlcdata, [{xmlcdata,
jlib:encode_base64(ServerOut)}]}), jlib:encode_base64(ServerOut)}]}),
{next_state, wait_for_sasl_response, fsm_next_state(wait_for_sasl_response,
StateData#state{sasl_state = NewSASLState}}; StateData#state{sasl_state = NewSASLState});
{error, Error, Username} -> {error, Error, Username} ->
?INFO_MSG( ?INFO_MSG(
"(~w) Failed authentication for ~s@~s", "(~w) Failed authentication for ~s@~s",
@ -669,20 +667,17 @@ wait_for_sasl_response({xmlstreamelement, El}, StateData) ->
{xmlelement, "failure", {xmlelement, "failure",
[{"xmlns", ?NS_SASL}], [{"xmlns", ?NS_SASL}],
[{xmlelement, Error, [], []}]}), [{xmlelement, Error, [], []}]}),
{next_state, wait_for_feature_request, StateData, fsm_next_state(wait_for_feature_request, StateData);
?C2S_OPEN_TIMEOUT};
{error, Error} -> {error, Error} ->
send_element(StateData, send_element(StateData,
{xmlelement, "failure", {xmlelement, "failure",
[{"xmlns", ?NS_SASL}], [{"xmlns", ?NS_SASL}],
[{xmlelement, Error, [], []}]}), [{xmlelement, Error, [], []}]}),
{next_state, wait_for_feature_request, StateData, fsm_next_state(wait_for_feature_request, StateData)
?C2S_OPEN_TIMEOUT}
end; end;
_ -> _ ->
process_unauthenticated_stanza(StateData, El), process_unauthenticated_stanza(StateData, El),
{next_state, wait_for_feature_request, StateData, fsm_next_state(wait_for_feature_request, StateData)
?C2S_OPEN_TIMEOUT}
end; end;
wait_for_sasl_response(timeout, StateData) -> wait_for_sasl_response(timeout, StateData) ->
@ -717,7 +712,7 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
error -> error ->
Err = jlib:make_error_reply(El, ?ERR_BAD_REQUEST), Err = jlib:make_error_reply(El, ?ERR_BAD_REQUEST),
send_element(StateData, Err), send_element(StateData, Err),
{next_state, wait_for_bind, StateData, ?C2S_OPEN_TIMEOUT}; fsm_next_state(wait_for_bind, StateData);
_ -> _ ->
JID = jlib:make_jid(U, StateData#state.server, R), JID = jlib:make_jid(U, StateData#state.server, R),
Res = IQ#iq{type = result, Res = IQ#iq{type = result,
@ -727,12 +722,11 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
[{xmlcdata, [{xmlcdata,
jlib:jid_to_string(JID)}]}]}]}, jlib:jid_to_string(JID)}]}]}]},
send_element(StateData, jlib:iq_to_xml(Res)), send_element(StateData, jlib:iq_to_xml(Res)),
{next_state, wait_for_session, fsm_next_state(wait_for_session,
StateData#state{resource = R, jid = JID}, StateData#state{resource = R, jid = JID})
?C2S_OPEN_TIMEOUT}
end; end;
_ -> _ ->
{next_state, wait_for_bind, StateData, ?C2S_OPEN_TIMEOUT} fsm_next_state(wait_for_bind, StateData)
end; end;
wait_for_bind(timeout, StateData) -> wait_for_bind(timeout, StateData) ->
@ -783,22 +777,22 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
privacy_get_user_list, StateData#state.server, privacy_get_user_list, StateData#state.server,
none, none,
[U, StateData#state.server]), [U, StateData#state.server]),
{next_state, session_established, fsm_next_state(session_established,
StateData#state{sid = SID, StateData#state{
sid = SID,
pres_f = ?SETS:from_list(Fs1), pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1), pres_t = ?SETS:from_list(Ts1),
privacy_list = PrivList}}; privacy_list = PrivList});
_ -> _ ->
?INFO_MSG("(~w) Forbidden session for ~s", ?INFO_MSG("(~w) Forbidden session for ~s",
[StateData#state.socket, [StateData#state.socket,
jlib:jid_to_string(JID)]), jlib:jid_to_string(JID)]),
Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED), Err = jlib:make_error_reply(El, ?ERR_NOT_ALLOWED),
send_element(StateData, Err), send_element(StateData, Err),
{next_state, wait_for_session, StateData, fsm_next_state(wait_for_session, StateData)
?C2S_OPEN_TIMEOUT}
end; end;
_ -> _ ->
{next_state, wait_for_session, StateData, ?C2S_OPEN_TIMEOUT} fsm_next_state(wait_for_session, StateData)
end; end;
wait_for_session(timeout, StateData) -> wait_for_session(timeout, StateData) ->
@ -903,7 +897,16 @@ session_established({xmlstreamelement, El}, StateData) ->
StateData StateData
end end
end, end,
{next_state, session_established, NewState}; fsm_next_state(session_established, NewState);
%% We hibernate the process to reduce memory consumption after a
%% configurable activity timeout
session_established(timeout, StateData) ->
%% TODO: Options must be stored in state:
Options = [],
proc_lib:hibernate(gen_fsm, enter_loop,
[?MODULE, Options, session_established, StateData]),
fsm_next_state(session_established, StateData);
session_established({xmlstreamend, _Name}, StateData) -> session_established({xmlstreamend, _Name}, StateData) ->
send_text(StateData, ?STREAM_TRAILER), send_text(StateData, ?STREAM_TRAILER),
@ -938,7 +941,7 @@ session_established(closed, StateData) ->
%% {stop, Reason, NewStateData} %% {stop, Reason, NewStateData}
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
handle_event(_Event, StateName, StateData) -> handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}. fsm_next_state(StateName, StateData).
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
%% Func: handle_sync_event/4 %% Func: handle_sync_event/4
@ -958,12 +961,11 @@ handle_sync_event({get_presence}, _From, StateName, StateData) ->
Resource = StateData#state.resource, Resource = StateData#state.resource,
Reply = {User, Resource, Show, Status}, Reply = {User, Resource, Show, Status},
{reply, Reply, StateName, StateData}; fsm_reply(Reply, StateName, StateData);
handle_sync_event(_Event, _From, StateName, StateData) -> handle_sync_event(_Event, _From, StateName, StateData) ->
Reply = ok, Reply = ok,
{reply, Reply, StateName, StateData}. fsm_reply(Reply, StateName, StateData).
code_change(_OldVsn, StateName, StateData, _Extra) -> code_change(_OldVsn, StateName, StateData, _Extra) ->
{ok, StateName, StateData}. {ok, StateName, StateData}.
@ -976,7 +978,7 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
handle_info({send_text, Text}, StateName, StateData) -> handle_info({send_text, Text}, StateName, StateData) ->
send_text(StateData, Text), send_text(StateData, Text),
{next_state, StateName, StateData}; fsm_next_state(StateName, StateData);
handle_info(replaced, _StateName, StateData) -> handle_info(replaced, _StateName, StateData) ->
Lang = StateData#state.lang, Lang = StateData#state.lang,
send_text(StateData, send_text(StateData,
@ -1187,16 +1189,16 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
ejabberd_hooks:run(user_receive_packet, ejabberd_hooks:run(user_receive_packet,
StateData#state.server, StateData#state.server,
[StateData#state.jid, From, To, FixedPacket]), [StateData#state.jid, From, To, FixedPacket]),
{next_state, StateName, NewState}; fsm_next_state(StateName, NewState);
true -> true ->
{next_state, StateName, NewState} fsm_next_state(StateName, NewState)
end; end;
handle_info({'DOWN', Monitor, _Type, _Object, _Info}, _StateName, StateData) handle_info({'DOWN', Monitor, _Type, _Object, _Info}, _StateName, StateData)
when Monitor == StateData#state.socket_monitor -> when Monitor == StateData#state.socket_monitor ->
{stop, normal, StateData}; {stop, normal, StateData};
handle_info(Info, StateName, StateData) -> handle_info(Info, StateName, StateData) ->
?ERROR_MSG("Unexpected info: ~p", [Info]), ?ERROR_MSG("Unexpected info: ~p", [Info]),
{next_state, StateName, StateData}. fsm_next_state(StateName, StateData).
%%---------------------------------------------------------------------- %%----------------------------------------------------------------------
%% Func: terminate/3 %% Func: terminate/3
@ -1858,3 +1860,17 @@ peerip(SockMod, Socket) ->
{ok, IPOK} -> IPOK; {ok, IPOK} -> IPOK;
_ -> undefined _ -> undefined
end. end.
%% fsm_next_state: Generate the next_state FSM tuple with different
%% timeout, depending on the future state
fsm_next_state(session_established, StateData) ->
{next_state, session_established, StateData, ?C2S_HIBERNATE_TIMEOUT};
fsm_next_state(StateName, StateData) ->
{next_state, StateName, StateData, ?C2S_OPEN_TIMEOUT}.
%% fsm_reply: Generate the reply FSM tuple with different timeout,
%% depending on the future state
fsm_reply(Reply, session_established, StateData) ->
{reply, Reply, session_established, StateData, ?C2S_HIBERNATE_TIMEOUT};
fsm_reply(Reply, StateName, StateData) ->
{reply, Reply, StateName, StateData, ?C2S_OPEN_TIMEOUT}.