comparison core/moduleapi.lua @ 11200:bf8f2da84007

Merge 0.11->trunk
author Kim Alvefur <zash@zash.se>
date Thu, 05 Nov 2020 22:31:25 +0100
parents 1dc49accb58e
children 5f15ab7c6ae5
comparison
equal deleted inserted replaced
11199:6c7c50a4de32 11200:bf8f2da84007
12 local logger = require "util.logger"; 12 local logger = require "util.logger";
13 local pluginloader = require "util.pluginloader"; 13 local pluginloader = require "util.pluginloader";
14 local timer = require "util.timer"; 14 local timer = require "util.timer";
15 local resolve_relative_path = require"util.paths".resolve_relative_path; 15 local resolve_relative_path = require"util.paths".resolve_relative_path;
16 local st = require "util.stanza"; 16 local st = require "util.stanza";
17 local cache = require "util.cache";
18 local errors = require "util.error";
19 local promise = require "util.promise";
20 local time_now = require "util.time".now;
21 local format = require "util.format".format;
22 local jid_node = require "util.jid".node;
17 23
18 local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; 24 local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat;
19 local error, setmetatable, type = error, setmetatable, type; 25 local error, setmetatable, type = error, setmetatable, type;
20 local ipairs, pairs, select = ipairs, pairs, select; 26 local ipairs, pairs, select = ipairs, pairs, select;
21 local tonumber, tostring = tonumber, tostring; 27 local tonumber, tostring = tonumber, tostring;
22 local require = require; 28 local require = require;
23 local pack = table.pack or function(...) return {n=select("#",...), ...}; end -- table.pack is only in 5.2 29 local pack = table.pack or require "util.table".pack; -- table.pack is only in 5.2
24 local unpack = table.unpack or unpack; --luacheck: ignore 113 -- renamed in 5.2 30 local unpack = table.unpack or unpack; --luacheck: ignore 113 -- renamed in 5.2
25 31
26 local prosody = prosody; 32 local prosody = prosody;
27 local hosts = prosody.hosts; 33 local hosts = prosody.hosts;
28 34
359 365
360 function api:send(stanza, origin) 366 function api:send(stanza, origin)
361 return core_post_stanza(origin or hosts[self.host], stanza); 367 return core_post_stanza(origin or hosts[self.host], stanza);
362 end 368 end
363 369
370 function api:send_iq(stanza, origin, timeout)
371 local iq_cache = self._iq_cache;
372 if not iq_cache then
373 iq_cache = cache.new(256, function (_, iq)
374 iq.reject(errors.new({
375 type = "wait", condition = "resource-constraint",
376 text = "evicted from iq tracking cache"
377 }));
378 end);
379 self._iq_cache = iq_cache;
380 end
381
382 local event_type;
383 if not jid_node(stanza.attr.from) then
384 event_type = "host";
385 else -- assume bare since we can't hook full jids
386 event_type = "bare";
387 end
388 local result_event = "iq-result/"..event_type.."/"..stanza.attr.id;
389 local error_event = "iq-error/"..event_type.."/"..stanza.attr.id;
390 local cache_key = event_type.."/"..stanza.attr.id;
391
392 local p = promise.new(function (resolve, reject)
393 local function result_handler(event)
394 if event.stanza.attr.from == stanza.attr.to then
395 resolve(event);
396 return true;
397 end
398 end
399
400 local function error_handler(event)
401 if event.stanza.attr.from == stanza.attr.to then
402 reject(errors.from_stanza(event.stanza, event));
403 return true;
404 end
405 end
406
407 if iq_cache:get(cache_key) then
408 reject(errors.new({
409 type = "modify", condition = "conflict",
410 text = "IQ stanza id attribute already used",
411 }));
412 return;
413 end
414
415 self:hook(result_event, result_handler);
416 self:hook(error_event, error_handler);
417
418 local timeout_handle = self:add_timer(timeout or 120, function ()
419 reject(errors.new({
420 type = "wait", condition = "remote-server-timeout",
421 text = "IQ stanza timed out",
422 }));
423 end);
424
425 local ok = iq_cache:set(cache_key, {
426 reject = reject, resolve = resolve,
427 timeout_handle = timeout_handle,
428 result_handler = result_handler, error_handler = error_handler;
429 });
430
431 if not ok then
432 reject(errors.new({
433 type = "wait", condition = "internal-server-error",
434 text = "Could not store IQ tracking data"
435 }));
436 return;
437 end
438
439 local wrapped_origin = setmetatable({
440 -- XXX Needed in some cases for replies to work correctly when sending queries internally.
441 send = function (reply)
442 resolve({ stanza = reply });
443 end;
444 }, {
445 __index = origin or hosts[self.host];
446 });
447
448 self:send(stanza, wrapped_origin);
449 end);
450
451 p:finally(function ()
452 local iq = iq_cache:get(cache_key);
453 if iq then
454 self:unhook(result_event, iq.result_handler);
455 self:unhook(error_event, iq.error_handler);
456 iq.timeout_handle:stop();
457 iq_cache:set(cache_key, nil);
458 end
459 end);
460
461 return p;
462 end
463
364 function api:broadcast(jids, stanza, iter) 464 function api:broadcast(jids, stanza, iter)
365 for jid in (iter or it.values)(jids) do 465 for jid in (iter or it.values)(jids) do
366 local new_stanza = st.clone(stanza); 466 local new_stanza = st.clone(stanza);
367 new_stanza.attr.to = jid; 467 new_stanza.attr.to = jid;
368 self:send(new_stanza); 468 self:send(new_stanza);
394 return setmetatable(t, timer_mt); 494 return setmetatable(t, timer_mt);
395 end 495 end
396 496
397 local path_sep = package.config:sub(1,1); 497 local path_sep = package.config:sub(1,1);
398 function api:get_directory() 498 function api:get_directory()
399 return self.path and (self.path:gsub("%"..path_sep.."[^"..path_sep.."]*$", "")) or nil; 499 return self.resource_path or self.path and (self.path:gsub("%"..path_sep.."[^"..path_sep.."]*$", "")) or nil;
400 end 500 end
401 501
402 function api:load_resource(path, mode) 502 function api:load_resource(path, mode)
403 path = resolve_relative_path(self:get_directory(), path); 503 path = resolve_relative_path(self:get_directory(), path);
404 return io.open(path, mode); 504 return io.open(path, mode);
406 506
407 function api:open_store(name, store_type) 507 function api:open_store(name, store_type)
408 return require"core.storagemanager".open(self.host, name or self.name, store_type); 508 return require"core.storagemanager".open(self.host, name or self.name, store_type);
409 end 509 end
410 510
411 function api:measure(name, stat_type) 511 function api:measure(name, stat_type, conf)
412 local measure = require "core.statsmanager".measure; 512 local measure = require "core.statsmanager".measure;
413 return measure(stat_type, "/"..self.host.."/mod_"..self.name.."/"..name); 513 return measure(stat_type, "/"..self.host.."/mod_"..self.name.."/"..name, conf);
414 end 514 end
415 515
416 function api:measure_object_event(events_object, event_name, stat_name) 516 function api:measure_object_event(events_object, event_name, stat_name)
417 local m = self:measure(stat_name or event_name, "times"); 517 local m = self:measure(stat_name or event_name, "times");
418 local function handler(handlers, _event_name, _event_data) 518 local function handler(handlers, _event_name, _event_data)
430 530
431 function api:measure_global_event(event_name, stat_name) 531 function api:measure_global_event(event_name, stat_name)
432 return self:measure_object_event(prosody.events.wrappers, event_name, stat_name); 532 return self:measure_object_event(prosody.events.wrappers, event_name, stat_name);
433 end 533 end
434 534
535 local status_priorities = { error = 3, warn = 2, info = 1, core = 0 };
536
537 function api:set_status(status_type, status_message, override)
538 local priority = status_priorities[status_type];
539 if not priority then
540 self:log("error", "set_status: Invalid status type '%s', assuming 'info'");
541 status_type, priority = "info", status_priorities.info;
542 end
543 local current_priority = status_priorities[self.status_type] or 0;
544 -- By default an 'error' status can only be overwritten by another 'error' status
545 if (current_priority >= status_priorities.error and priority < current_priority and override ~= true)
546 or (override == false and current_priority > priority) then
547 self:log("debug", "moduleapi: ignoring status [prio %d override %s]: %s", priority, override, status_message);
548 return;
549 end
550 self.status_type, self.status_message, self.status_time = status_type, status_message, time_now();
551 self:fire_event("module-status/updated", { name = self.name });
552 end
553
554 function api:log_status(level, msg, ...)
555 self:set_status(level, format(msg, ...));
556 return self:log(level, msg, ...);
557 end
558
559 function api:get_status()
560 return self.status_type, self.status_message, self.status_time;
561 end
562
435 return api; 563 return api;