Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
cqueues/src/dns.resolvers.lua
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
232 lines (172 sloc)
4.68 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| local loader = function(loader, ...) | |
| local resolver = require"cqueues.dns.resolver" | |
| local config = require"cqueues.dns.config" | |
| local condition = require"cqueues.condition" | |
| local monotime = require"cqueues".monotime | |
| local random = require"cqueues.dns".random | |
| local errno = require"cqueues.errno" | |
| local ETIMEDOUT = errno.ETIMEDOUT | |
| local function todeadline(timeout) | |
| return (timeout and (monotime() + timeout)) or nil | |
| end -- todeadline | |
| local function totimeout(deadline) | |
| return (deadline and math.max(0, deadline - monotime())) or nil | |
| end -- totimeout | |
| -- | |
| -- NOTE: Keep track of an unordered collection of objects, and in | |
| -- particular a count of objects in the collection. If an object is | |
| -- garbage collected automatically decrement the count and signal | |
| -- the condition variable. | |
| -- | |
| local new_hook | |
| if _G._VERSION == "Lua 5.1" then | |
| -- Lua 5.1 does not support __gc on tables, so we need to use newproxy | |
| new_hook = function(mt) | |
| local u = newproxy(false) | |
| debug.setmetatable(u, mt) | |
| return u | |
| end | |
| else | |
| new_hook = function(mt) | |
| return setmetatable({}, mt) | |
| end | |
| end | |
| local alive = {} | |
| function alive.new(condvar) | |
| local self = setmetatable({}, { __index = alive }) | |
| self.n = 0 | |
| self.table = setmetatable({}, { __mode = "k" }) | |
| self.condvar = condvar | |
| self.hooks = {} | |
| self.hookmt = { __gc = function (hook) | |
| self.n = self.n - 1 | |
| self.condvar:signal() | |
| end } | |
| return self | |
| end -- alive.new | |
| function alive:add(x) | |
| if not self.table[x] then | |
| local hook = self.hooks[#self.hooks] | |
| if hook then | |
| self.hooks[#self.hooks] = nil | |
| else | |
| hook = new_hook(self.hookmt) | |
| end | |
| self.table[x] = hook | |
| self.n = self.n + 1 | |
| end | |
| end -- alive:add | |
| function alive:delete(x) | |
| if self.table[x] then | |
| self.hooks[#self.hooks + 1] = self.table[x] | |
| self.table[x] = nil | |
| self.n = self.n - 1 | |
| self.condvar:signal() | |
| end | |
| end -- alive:delete | |
| function alive:check() | |
| local n = 0 | |
| for _ in pairs(self.table) do | |
| n = n + 1 | |
| end | |
| return assert(n == self.n, "resolver registry corrupt") | |
| end -- alive:check | |
| local pool = {} | |
| local function tryget(self) | |
| local res, why | |
| local cache_len = #self.cache | |
| if cache_len > 1 then | |
| res = self.cache[cache_len] | |
| self.cache[cache_len] = nil | |
| elseif self.alive.n < self.hiwat then | |
| res, why = resolver.new(self.resconf, self.hosts, self.hints) | |
| if not res then | |
| return nil, why | |
| end | |
| end | |
| self.alive:add(res) | |
| return res | |
| end -- tryget | |
| local function getby(self, deadline) | |
| local res, why = tryget(self) | |
| while not res and not why do | |
| if deadline and deadline <= monotime() then | |
| return nil, ETIMEDOUT | |
| else | |
| self.condvar:wait(totimeout(deadline)) | |
| res, why = tryget(self) | |
| end | |
| end | |
| return res, why | |
| end -- getby | |
| function pool:get(timeout) | |
| return getby(self, todeadline(timeout)) | |
| end -- pool:get | |
| function pool:put(res) | |
| self.alive:delete(res) | |
| local cache_len = #self.cache | |
| if cache_len < self.lowat and res:stat().queries < self.querymax then | |
| if not self.lifo and cache_len > 0 then | |
| local i = random(cache_len+1) + 1 | |
| self.cache[cache_len+1] = self.cache[i] | |
| self.cache[i] = res | |
| else | |
| self.cache[cache_len+1] = res | |
| end | |
| else | |
| res:close() | |
| end | |
| end -- pool:put | |
| function pool:signal() | |
| self.condvar:signal() | |
| end -- pool:signal | |
| function pool:query(name, type, class, timeout) | |
| local deadline = todeadline(timeout or self.timeout) | |
| local res, why = getby(self, deadline) | |
| if not res then | |
| return nil, why | |
| end | |
| local pkt, why = res:query(name, type, class, totimeout(deadline)) | |
| if not self.debug then | |
| self:put(res) | |
| end | |
| return pkt, why | |
| end -- pool:query | |
| local resolvers = {} | |
| resolvers.lowat = 1 | |
| resolvers.hiwat = 32 | |
| resolvers.querymax = 2048 | |
| resolvers.debug = nil | |
| resolvers.lifo = false | |
| function resolvers.new(resconf, hosts, hints) | |
| local self = {} | |
| self.resconf = (type(resconf) == "table" and config.new(resconf)) or resconf | |
| self.hosts = hosts | |
| self.hints = hints | |
| self.condvar = condition.new() | |
| self.lowat = resolvers.lowat | |
| self.hiwat = resolvers.hiwat | |
| self.timeout = resolvers.timeout | |
| self.querymax = resolvers.querymax | |
| self.debug = resolvers.debug | |
| self.lifo = resolvers.lifo | |
| self.cache = {} | |
| self.alive = alive.new(self.condvar) | |
| return setmetatable(self, { __index = pool }) | |
| end -- resolvers.new | |
| function resolvers.stub(cfg) | |
| return resolvers.new(config.stub(cfg)) | |
| end -- resolvers.stub | |
| function resolvers.root(cfg) | |
| return resolvers.new(config.root(cfg)) | |
| end -- resolvers.root | |
| function resolvers.type(o) | |
| local mt = getmetatable(o) | |
| if mt and mt.__index == pool then | |
| return "dns resolver pool" | |
| end | |
| end -- resolvers.type | |
| resolvers.loader = loader | |
| return resolvers | |
| end | |
| return loader(loader, ...) |