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