blob: dc25a319986b75b6644863fc56d56c770a3442ed [file] [log] [blame]
Jan Schär75ea9f42024-07-29 17:01:41 +02001package proxy
2
3// Taken and modified from CoreDNS, under Apache 2.0.
4
5import (
6 "crypto/tls"
7 "runtime"
8 "sync/atomic"
9 "time"
10
11 "source.monogon.dev/osbase/net/dns/forward/up"
12)
13
14// Proxy defines an upstream host.
15type Proxy struct {
16 fails uint32
17 addr string
18
19 transport *Transport
20
21 writeTimeout time.Duration
22 readTimeout time.Duration
23
24 // health checking
25 probe *up.Probe
26 health HealthChecker
27}
28
29// NewProxy returns a new proxy.
30func NewProxy(addr string) *Proxy {
31 p := &Proxy{
32 addr: addr,
33 fails: 0,
34 probe: up.New(),
35 writeTimeout: 2 * time.Second,
36 readTimeout: 2 * time.Second,
37 transport: newTransport(addr),
38 health: NewHealthChecker(true, "."),
39 }
40
41 runtime.SetFinalizer(p, (*Proxy).finalizer)
42 return p
43}
44
45func (p *Proxy) Addr() string { return p.addr }
46
47// SetTLSConfig sets the TLS config in the lower p.transport
48// and in the healthchecking client.
49func (p *Proxy) SetTLSConfig(cfg *tls.Config) {
50 p.transport.SetTLSConfig(cfg)
51 p.health.SetTLSConfig(cfg)
52}
53
54// SetExpire sets the expire duration in the lower p.transport.
55func (p *Proxy) SetExpire(expire time.Duration) { p.transport.SetExpire(expire) }
56
57func (p *Proxy) GetHealthchecker() HealthChecker {
58 return p.health
59}
60
61func (p *Proxy) Fails() uint32 {
62 return atomic.LoadUint32(&p.fails)
63}
64
65// Healthcheck kicks of a round of health checks for this proxy.
66func (p *Proxy) Healthcheck() {
67 if p.health == nil {
68 return
69 }
70
71 p.probe.Do(func() error {
72 return p.health.Check(p)
73 })
74}
75
76// Down returns true if this proxy is down, i.e. has *more* fails than maxfails.
77func (p *Proxy) Down(maxfails uint32) bool {
78 if maxfails == 0 {
79 return false
80 }
81
82 fails := atomic.LoadUint32(&p.fails)
83 return fails > maxfails
84}
85
86// Stop close stops the health checking goroutine.
87func (p *Proxy) Stop() { p.probe.Stop() }
88func (p *Proxy) finalizer() { p.transport.Stop() }
89
90// Start starts the proxy's healthchecking.
91func (p *Proxy) Start(duration time.Duration) {
92 p.probe.Start(duration)
93 p.transport.Start()
94}
95
96func (p *Proxy) SetReadTimeout(duration time.Duration) {
97 p.readTimeout = duration
98}
99
100// incrementFails increments the number of fails safely.
101func (p *Proxy) incrementFails() {
102 curVal := atomic.LoadUint32(&p.fails)
103 if curVal > curVal+1 {
104 // overflow occurred, do not update the counter again
105 return
106 }
107 atomic.AddUint32(&p.fails, 1)
108}