Software /
code /
prosody
File
util/error.lua @ 12468:353836684009
net.connect: Fix accumulation of connection attempt references
Connection attempts that failed the Happy Eyeballs race were not
unreferenced and would accumulate.
Tested by inspecting the 'pending_connections_map' after establishing
s2s with a s2s target where the IPv6 port has a -j DROP rule causing it
to time out and the IPv4 attempt wins the race.
Expected is that the losing connection stays around until net.server
timeouts kick in where it should be removed. The map table should tend
towards being empty during idle times.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Wed, 20 Apr 2022 22:41:54 +0200 |
parent | 11224:8143fd2f138b |
child | 12975:d10957394a3c |
child | 13078:6da83deb8d7f |
line wrap: on
line source
local id = require "util.id"; local util_debug; -- only imported on-demand -- Library configuration (see configure()) local auto_inject_traceback = false; local error_mt = { __name = "error" }; function error_mt:__tostring() return ("error<%s:%s:%s>"):format(self.type, self.condition, self.text or ""); end local function is_error(e) return getmetatable(e) == error_mt; end local function configure(opt) if opt.auto_inject_traceback ~= nil then auto_inject_traceback = opt.auto_inject_traceback; if auto_inject_traceback then util_debug = require "util.debug"; end end end -- Do we want any more well-known fields? -- Or could we just copy all fields from `e`? -- Sometimes you want variable details in the `text`, how to handle that? -- Translations? -- Should the `type` be restricted to the stanza error types or free-form? -- What to set `type` to for stream errors or SASL errors? Those don't have a 'type' attr. local function new(e, context, registry, source) if is_error(e) then return e; end local template = registry and registry[e]; if not template then if type(e) == "table" then template = { code = e.code; type = e.type; condition = e.condition; text = e.text; extra = e.extra; }; else template = {}; end end context = context or {}; if auto_inject_traceback then context.traceback = util_debug.get_traceback_table(nil, 2); end local error_instance = setmetatable({ instance_id = id.short(); type = template.type or "cancel"; condition = template.condition or "undefined-condition"; text = template.text; code = template.code; extra = template.extra; context = context; source = source; }, error_mt); return error_instance; end -- compact --> normal form local function expand_registry(namespace, registry) local mapped = {} for err,template in pairs(registry) do local e = { type = template[1]; condition = template[2]; text = template[3]; }; if namespace and template[4] then e.extra = { namespace = namespace, condition = template[4] }; end mapped[err] = e; end return mapped; end local function init(source, namespace, registry) if type(namespace) == "table" then -- registry can be given as second argument if namespace is not used registry, namespace = namespace, nil; end local _, protoerr = next(registry, nil); if protoerr and type(next(protoerr)) == "number" then registry = expand_registry(namespace, registry); end local function wrap(e, context) if is_error(e) then return e; end local err = new(registry[e] or { type = "cancel", condition = "undefined-condition" }, context, registry, source); err.context.wrapped_error = e; return err; end return { source = source; registry = registry; new = function (e, context) return new(e, context, registry, source); end; coerce = function (ok, err, ...) if ok then return ok, err, ...; end return nil, wrap(err); end; wrap = wrap; is_error = is_error; }; end local function coerce(ok, err, ...) if ok or is_error(err) then return ok, err, ...; end local new_err = new({ type = "cancel", condition = "undefined-condition" }, { wrapped_error = err }); return ok, new_err, ...; end local function from_stanza(stanza, context, source) local error_type, condition, text, extra_tag = stanza:get_error(); local error_tag = stanza:get_child("error"); context = context or {}; context.stanza = stanza; context.by = error_tag.attr.by or stanza.attr.from; local uri; if condition == "gone" or condition == "redirect" then uri = error_tag:get_child_text(condition, "urn:ietf:params:xml:ns:xmpp-stanzas"); end return new({ type = error_type or "cancel"; condition = condition or "undefined-condition"; text = text; extra = (extra_tag or uri) and { uri = uri; tag = extra_tag; } or nil; }, context, nil, source); end return { new = new; init = init; coerce = coerce; is_error = is_error; is_err = is_error; -- COMPAT w/ older 0.12 trunk from_stanza = from_stanza; configure = configure; }