Software /
code /
prosody
Changeset
13765:7c57fb2ffbb0 13.0
mod_websocket: Merge session close handling changes from mod_c2s (bug fixes)
This should bring some fixes and general robustness that mod_websocket had
missed out on. The duplicated code here is not at all ideal. To prevent this
happening again, we should figure out how to have the common logic in a single
place, while still being able to do the websocket-specific parts that we need.
The main known bug that this fixes is that it's possible for a session to get
into a non-destroyable state. For example, if we try to session:close() a
hibernating session, then session.conn is nil and the function will simply
return without doing anything. In the mod_c2s code we already handle this, and
just destroy the session. But if a hibernating websocket session is never
resumed or becomes non-resumable, it will become immortal!
By merging the fix from mod_c2s, the session should now be correctly
destroyed.
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Tue, 11 Mar 2025 18:44:40 +0000 |
parents | 13764:667a58e74fd9 |
children | 13766:b11242656300 13767:56fad06904b1 |
files | plugins/mod_websocket.lua |
diffstat | 1 files changed, 17 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- a/plugins/mod_websocket.lua Tue Mar 11 18:37:16 2025 +0000 +++ b/plugins/mod_websocket.lua Tue Mar 11 18:44:40 2025 +0000 @@ -87,6 +87,7 @@ end end end + stream_error = tostring(stream_error); log("debug", "Disconnecting client, <stream:error> is: %s", stream_error); session.send(stream_error); end @@ -94,28 +95,33 @@ session.send(st.stanza("close", { xmlns = xmlns_framing })); function session.send() return false; end - -- luacheck: ignore 422/reason - -- FIXME reason should be handled in common place - local reason = (reason and (reason.name or reason.text or reason.condition)) or reason; - session.log("debug", "c2s stream for %s closed: %s", session.full_jid or ("<"..session.ip..">"), reason or "session closed"); + local reason_text = (reason and (reason.name or reason.text or reason.condition)) or reason; + session.log("debug", "c2s stream for %s closed: %s", session.full_jid or session.ip or "<unknown>", reason_text or "session closed"); -- Authenticated incoming stream may still be sending us stanzas, so wait for </stream:stream> from remote local conn = session.conn; - if reason == nil and not session.notopen and session.type == "c2s" then + if reason_text == nil and not session.notopen and session.type == "c2s" then -- Grace time to process data from authenticated cleanly-closed stream add_task(stream_close_timeout, function () if not session.destroyed then session.log("warn", "Failed to receive a stream close response, closing connection anyway..."); - sm_destroy_session(session, reason); - conn:write(build_close(1000, "Stream closed")); - conn:close(); + sm_destroy_session(session, reason_text); + if conn then + conn:write(build_close(1000, "Stream closed")); + conn:close(); + end end end); else - sm_destroy_session(session, reason); - conn:write(build_close(1000, "Stream closed")); - conn:close(); + sm_destroy_session(session, reason_text); + if conn then + conn:write(build_close(1000, "Stream closed")); + conn:close(); + end end + else + local reason_text = (reason and (reason.name or reason.text or reason.condition)) or reason; + sm_destroy_session(session, reason_text); end end