Files
coredns/plugin/pkg/proxy/proxy.go

170 lines
4.0 KiB
Go

package proxy
import (
"crypto/tls"
"net"
"net/http"
"runtime"
"sync/atomic"
"time"
"github.com/coredns/coredns/plugin/pkg/log"
"github.com/coredns/coredns/plugin/pkg/up"
)
// Proxy defines an upstream host.
type Proxy struct {
fails uint32
addr string
proxyName string
transport *Transport
protocol string
dohMethod string
dohHost string
readTimeout time.Duration
// health checking
probe *up.Probe
health HealthChecker
}
// NewProxy returns a new proxy.
func NewProxy(proxyName, addr, protocol string) *Proxy {
p := &Proxy{
addr: addr,
fails: 0,
probe: up.New(),
readTimeout: 2 * time.Second,
transport: newTransport(proxyName, addr),
protocol: protocol,
dohMethod: http.MethodPost,
dohHost: "",
health: NewHealthChecker(proxyName, protocol, true, "."),
proxyName: proxyName,
}
runtime.SetFinalizer(p, (*Proxy).finalizer)
return p
}
func (p *Proxy) Addr() string { return p.addr }
// SetTLSConfig sets the TLS config in the lower p.transport and in the healthchecking client.
func (p *Proxy) SetTLSConfig(cfg *tls.Config) {
p.transport.SetTLSConfig(cfg)
p.health.SetTLSConfig(cfg)
if p.transport.httpClient != nil {
p.transport.httpClient.Transport.(*http.Transport).TLSClientConfig = cfg
}
}
// SetExpire sets the expire duration in the lower p.transport.
func (p *Proxy) SetExpire(expire time.Duration) { p.transport.SetExpire(expire) }
// SetMaxAge sets the maximum connection lifetime in the lower p.transport.
// A value of 0 (default) disables max-age.
func (p *Proxy) SetMaxAge(maxAge time.Duration) { p.transport.SetMaxAge(maxAge) }
// SetMaxIdleConns sets the maximum idle connections per transport type.
// A value of 0 means unlimited (default).
func (p *Proxy) SetMaxIdleConns(n int) {
p.transport.SetMaxIdleConns(n)
if p.transport.httpClient != nil {
p.transport.httpClient.Transport.(*http.Transport).MaxIdleConns = n
p.transport.httpClient.Transport.(*http.Transport).MaxIdleConnsPerHost = n
}
}
func (p *Proxy) SetHTTPClient(client *http.Client) {
p.transport.httpClient = client
}
func (p *Proxy) SetDOHRequestOptions(method string) {
p.dohMethod = method
}
func (p *Proxy) SetDOHHost(host string) {
p.dohHost = host
}
func (p *Proxy) DoHHost() string { return p.dohHost }
func (p *Proxy) GetHealthchecker() HealthChecker {
return p.health
}
func (p *Proxy) GetTransport() *Transport {
return p.transport
}
func (p *Proxy) Fails() uint32 {
return atomic.LoadUint32(&p.fails)
}
// Healthcheck kicks of a round of health checks for this proxy.
func (p *Proxy) Healthcheck() {
if p.health == nil {
log.Warning("No healthchecker")
return
}
p.probe.Do(func() error {
return p.health.Check(p)
})
}
// Down returns true if this proxy is down, i.e. has *more* fails than maxfails.
func (p *Proxy) Down(maxfails uint32) bool {
if maxfails == 0 {
return false
}
fails := atomic.LoadUint32(&p.fails)
return fails > maxfails
}
// Stop close stops the health checking goroutine.
func (p *Proxy) Stop() { p.probe.Stop() }
func (p *Proxy) finalizer() { p.transport.Stop() }
// Start starts the proxy's healthchecking.
func (p *Proxy) Start(duration time.Duration) {
p.probe.Start(duration)
p.transport.Start()
}
func (p *Proxy) SetReadTimeout(duration time.Duration) {
p.readTimeout = duration
}
// incrementFails increments the number of fails safely.
func (p *Proxy) incrementFails() {
curVal := atomic.LoadUint32(&p.fails)
if curVal > curVal+1 {
// overflow occurred, do not update the counter again
return
}
atomic.AddUint32(&p.fails, 1)
}
// SetLocalAddress sets the local address for the proxy, used as the source address for outbound connections.
func (p *Proxy) SetLocalAddress(addr net.IP) {
p.transport.SetLocalAddress(addr)
if p.transport.httpClient != nil {
httpTransport := p.transport.httpClient.Transport.(*http.Transport)
if addr == nil {
httpTransport.DialContext = nil
return
}
dialer := &net.Dialer{LocalAddr: &net.TCPAddr{IP: addr}}
httpTransport.DialContext = dialer.DialContext
}
}
const (
maxTimeout = 2 * time.Second
)