* middleware/metrics: add more metrics middleware/cache: Add metrics for number of elements in the cache. Also export the total size. Update README to detail the new metrics. middleware/metrics Move metrics into subpackage called "vars". This breaks the import cycle and is cleaner. This allows vars.Report to be used in the the dnsserver to log refused queries. middleware/metrics: tests Add tests to the metrics framework. The metrics/test subpackage allows scraping of the local server. Do a few test scrape of the metrics that are defined in the metrics middleware. This also allows metrics integration tests to check if the caching and dnssec middleware export their metrics correctly. * update README * typos * fix tests
108 lines
3.1 KiB
Go
108 lines
3.1 KiB
Go
// Package proxy is middleware that proxies requests.
|
|
package proxy
|
|
|
|
import (
|
|
"errors"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/miekg/coredns/middleware"
|
|
|
|
"github.com/miekg/dns"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
var errUnreachable = errors.New("unreachable backend")
|
|
|
|
// Proxy represents a middleware instance that can proxy requests to another DNS server.
|
|
type Proxy struct {
|
|
Next middleware.Handler
|
|
Client *client
|
|
Upstreams []Upstream
|
|
}
|
|
|
|
// Upstream manages a pool of proxy upstream hosts. Select should return a
|
|
// suitable upstream host, or nil if no such hosts are available.
|
|
type Upstream interface {
|
|
// The domain name this upstream host should be routed on.
|
|
From() string
|
|
// Selects an upstream host to be routed to.
|
|
Select() *UpstreamHost
|
|
// Checks if subpdomain is not an ignored.
|
|
IsAllowedPath(string) bool
|
|
// Options returns the options set for this upstream
|
|
Options() Options
|
|
}
|
|
|
|
// UpstreamHostDownFunc can be used to customize how Down behaves.
|
|
type UpstreamHostDownFunc func(*UpstreamHost) bool
|
|
|
|
// UpstreamHost represents a single proxy upstream
|
|
type UpstreamHost struct {
|
|
Conns int64 // must be first field to be 64-bit aligned on 32-bit systems
|
|
Name string // IP address (and port) of this upstream host
|
|
Fails int32
|
|
FailTimeout time.Duration
|
|
Unhealthy bool
|
|
CheckDown UpstreamHostDownFunc
|
|
WithoutPathPrefix string
|
|
}
|
|
|
|
// Down checks whether the upstream host is down or not.
|
|
// Down will try to use uh.CheckDown first, and will fall
|
|
// back to some default criteria if necessary.
|
|
func (uh *UpstreamHost) Down() bool {
|
|
if uh.CheckDown == nil {
|
|
// Default settings
|
|
fails := atomic.LoadInt32(&uh.Fails)
|
|
return uh.Unhealthy || fails > 0
|
|
}
|
|
return uh.CheckDown(uh)
|
|
}
|
|
|
|
// tryDuration is how long to try upstream hosts; failures result in
|
|
// immediate retries until this duration ends or we get a nil host.
|
|
var tryDuration = 60 * time.Second
|
|
|
|
// ServeDNS satisfies the middleware.Handler interface.
|
|
func (p Proxy) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
|
for _, upstream := range p.Upstreams {
|
|
start := time.Now()
|
|
|
|
// Since Select() should give us "up" hosts, keep retrying
|
|
// hosts until timeout (or until we get a nil host).
|
|
for time.Now().Sub(start) < tryDuration {
|
|
host := upstream.Select()
|
|
if host == nil {
|
|
return dns.RcodeServerFailure, errUnreachable
|
|
}
|
|
|
|
atomic.AddInt64(&host.Conns, 1)
|
|
|
|
reply, backendErr := p.Client.ServeDNS(w, r, host)
|
|
|
|
atomic.AddInt64(&host.Conns, -1)
|
|
|
|
if backendErr == nil {
|
|
w.WriteMsg(reply)
|
|
return 0, nil
|
|
}
|
|
timeout := host.FailTimeout
|
|
if timeout == 0 {
|
|
timeout = 10 * time.Second
|
|
}
|
|
atomic.AddInt32(&host.Fails, 1)
|
|
go func(host *UpstreamHost, timeout time.Duration) {
|
|
time.Sleep(timeout)
|
|
atomic.AddInt32(&host.Fails, -1)
|
|
}(host, timeout)
|
|
}
|
|
return dns.RcodeServerFailure, errUnreachable
|
|
}
|
|
return p.Next.ServeDNS(ctx, w, r)
|
|
}
|
|
|
|
func (p Proxy) Name() string { return "proxy" }
|
|
|
|
// defaultTimeout is the default networking timeout for DNS requests.
|
|
const defaultTimeout = 5 * time.Second
|