comparison mod_pubsub_serverinfo/mod_pubsub_serverinfo.lua @ 5808:2465d5191ae9

mod_pubsub_serverinfo: Consider sibling vhosts 'connected' Prosody does not have s2s connections between vhosts. Multiple domains will therefor not show up as each-other 'remote domains'. With this commit, the module considers vhosts permanently s2s-connected. Additional debug logging has been added.
author Guus der Kinderen <guus.der.kinderen@gmail.com>
date Thu, 04 Jan 2024 15:14:19 +0100
parents 55b99f593c3a
children a8cae8322b7c
comparison
equal deleted inserted replaced
5807:f08818b8b87d 5808:2465d5191ae9
120 -- mapped to a collection of remote domain names. De-duplicate all remote domain names by using 120 -- mapped to a collection of remote domain names. De-duplicate all remote domain names by using
121 -- them as an index in a table. 121 -- them as an index in a table.
122 local domains_by_host = {} 122 local domains_by_host = {}
123 for session, _ in pairs(prosody.incoming_s2s) do 123 for session, _ in pairs(prosody.incoming_s2s) do
124 if session ~= nil and session.from_host ~= nil and local_domain == session.to_host then 124 if session ~= nil and session.from_host ~= nil and local_domain == session.to_host then
125 module:log("debug", "Local host '%s' has remote '%s' (inbound)", session.to_host, session.from_host);
125 local sessions = domains_by_host[session.to_host] 126 local sessions = domains_by_host[session.to_host]
126 if sessions == nil then sessions = {} end; -- instantiate a new entry if none existed 127 if sessions == nil then sessions = {} end; -- instantiate a new entry if none existed
127 sessions[session.from_host] = true 128 sessions[session.from_host] = true
128 domains_by_host[session.to_host] = sessions 129 domains_by_host[session.to_host] = sessions
129 end 130 end
130 end 131 end
131 132
132 -- At an earlier stage, the code iterated voer all prosody.hosts - but that turned out to be to noisy. 133 -- At an earlier stage, the code iterated over all prosody.hosts, trying to generate one pubsub item for all local hosts. That turned out to be
134 -- to noisy. Instead, this code now creates an item that includes the local vhost only. It is assumed that this module will also be loaded for
135 -- other vhosts. Their data should then be published to distinct pub/sub services and nodes.
136
133 -- for host, data in pairs(prosody.hosts) do 137 -- for host, data in pairs(prosody.hosts) do
134 local host = local_domain 138 local host = local_domain
135 local data = prosody.hosts[host] 139 local data = prosody.hosts[host]
136 if data ~= nil then 140 if data ~= nil then
137 local sessions = domains_by_host[host] 141 local sessions = domains_by_host[host]
138 if sessions == nil then sessions = {} end; -- instantiate a new entry if none existed 142 if sessions == nil then sessions = {} end; -- instantiate a new entry if none existed
139 if data.s2sout ~= nil then 143 if data.s2sout ~= nil then
140 for _, session in pairs(data.s2sout) do 144 for _, session in pairs(data.s2sout) do
141 if session.to_host ~= nil then 145 if session.to_host ~= nil then
146 module:log("debug", "Local host '%s' has remote '%s' (outbound)", host, session.to_host);
142 sessions[session.to_host] = true 147 sessions[session.to_host] = true
143 domains_by_host[host] = sessions 148 domains_by_host[host] = sessions
144 end 149 end
150 end
151 end
152
153 -- When the instance of Prosody hosts more than one host, the other hosts can be thought of as having a 'permanent' s2s connection.
154 for host_name, host_info in pairs(prosody.hosts) do
155 if host ~= host_name and host_info.type ~= "component" then
156 module:log("debug", "Local host '%s' has remote '%s' (vhost)", host, host_name);
157 sessions[host_name] = true;
158 domains_by_host[host] = sessions
145 end 159 end
146 end 160 end
147 end 161 end
148 162
149 -- Build the publication stanza. 163 -- Build the publication stanza.
194 function does_opt_in(remoteDomain) 208 function does_opt_in(remoteDomain)
195 209
196 -- try to read answer from cache. 210 -- try to read answer from cache.
197 local cached_value = opt_in_cache[remoteDomain] 211 local cached_value = opt_in_cache[remoteDomain]
198 if cached_value ~= nil and os.difftime(cached_value.expires, os.time()) > 0 then 212 if cached_value ~= nil and os.difftime(cached_value.expires, os.time()) > 0 then
213 module:log("debug", "Opt-in status (from cache) for '%s': %s", remoteDomain, cached_value.opt_in)
199 return cached_value.opt_in; 214 return cached_value.opt_in;
200 end 215 end
201 216
202 -- TODO worry about not having multiple requests in flight to the same domain.cached_value 217 -- TODO worry about not having multiple requests in flight to the same domain.cached_value
203 218
204 -- Cache could not provide an answer. Perform service discovery. 219 -- Cache could not provide an answer. Perform service discovery.
220 module:log("debug", "No cached opt-in status for '%s': performing disco/info to determine opt-in.", remoteDomain)
205 local discoRequest = st.iq({ type = "get", to = remoteDomain, from = actor, id = new_id() }) 221 local discoRequest = st.iq({ type = "get", to = remoteDomain, from = actor, id = new_id() })
206 :tag("query", { xmlns = "http://jabber.org/protocol/disco#info" }) 222 :tag("query", { xmlns = "http://jabber.org/protocol/disco#info" })
207 223
208 module:send_iq(discoRequest):next( 224 module:send_iq(discoRequest):next(
209 function(response) 225 function(response)
210 if response.stanza ~= nil and response.stanza.attr.type == "result" then 226 if response.stanza ~= nil and response.stanza.attr.type == "result" then
211 local query = response.stanza:get_child("query", "http://jabber.org/protocol/disco#info") 227 local query = response.stanza:get_child("query", "http://jabber.org/protocol/disco#info")
212 if query ~= nil then 228 if query ~= nil then
213 for feature in query:childtags("feature", "http://jabber.org/protocol/disco#info") do 229 for feature in query:childtags("feature", "http://jabber.org/protocol/disco#info") do
230 module:log("debug", "Disco/info feature for '%s': %s", remoteDomain, feature)
214 if feature.attr.var == 'urn:xmpp:serverinfo:0' then 231 if feature.attr.var == 'urn:xmpp:serverinfo:0' then
232 module:log("debug", "Disco/info response included opt-in for '%s'", remoteDomain)
215 opt_in_cache[remoteDomain] = { 233 opt_in_cache[remoteDomain] = {
216 opt_in = true; 234 opt_in = true;
217 expires = os.time() + cache_ttl; 235 expires = os.time() + cache_ttl;
218 } 236 }
219 return; -- prevent 'false' to be cached, down below. 237 return; -- prevent 'false' to be cached, down below.
220 end 238 end
221 end 239 end
222 end 240 end
223 end 241 end
242 module:log("debug", "Disco/info response did not include opt-in for '%s'", remoteDomain)
224 opt_in_cache[remoteDomain] = { 243 opt_in_cache[remoteDomain] = {
225 opt_in = false; 244 opt_in = false;
226 expires = os.time() + cache_ttl; 245 expires = os.time() + cache_ttl;
227 } 246 }
228 end, 247 end,
229 function(response) 248 function(response)
249 module:log("debug", "An error occurred while performing a disco/info request to determine opt-in for '%s'", remoteDomain, response)
230 opt_in_cache[remoteDomain] = { 250 opt_in_cache[remoteDomain] = {
231 opt_in = false; 251 opt_in = false;
232 expires = os.time() + cache_ttl; 252 expires = os.time() + cache_ttl;
233 } 253 }
234 end 254 end