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