Skip to content

Commit

Permalink
reverseproxy: Expand SRV/A addrs for cache key
Browse files Browse the repository at this point in the history
Hopefully fix #4645
  • Loading branch information
mholt committed Mar 18, 2022
1 parent 93c99f6 commit dc4d147
Showing 1 changed file with 39 additions and 19 deletions.
58 changes: 39 additions & 19 deletions modules/caddyhttp/reverseproxy/upstreams.go
Expand Up @@ -72,11 +72,6 @@ func (SRVUpstreams) CaddyModule() caddy.ModuleInfo {
}
}

// String returns the RFC 2782 representation of the SRV domain.
func (su SRVUpstreams) String() string {
return fmt.Sprintf("_%s._%s.%s", su.Service, su.Proto, su.Name)
}

func (su *SRVUpstreams) Provision(ctx caddy.Context) error {
su.logger = ctx.Logger(su)
if su.Refresh == 0 {
Expand Down Expand Up @@ -109,11 +104,11 @@ func (su *SRVUpstreams) Provision(ctx caddy.Context) error {
}

func (su SRVUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
suStr := su.String()
suAddr, service, proto, name := su.expandedAddr(r)

// first, use a cheap read-lock to return a cached result quickly
srvsMu.RLock()
cached := srvs[suStr]
cached := srvs[suAddr]
srvsMu.RUnlock()
if cached.isFresh() {
return cached.upstreams, nil
Expand All @@ -126,17 +121,11 @@ func (su SRVUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
// check to see if it's still stale, since we're now in a different
// lock from when we first checked freshness; another goroutine might
// have refreshed it in the meantime before we re-obtained our lock
cached = srvs[suStr]
cached = srvs[suAddr]
if cached.isFresh() {
return cached.upstreams, nil
}

// prepare parameters and perform the SRV lookup
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
service := repl.ReplaceAll(su.Service, "")
proto := repl.ReplaceAll(su.Proto, "")
name := repl.ReplaceAll(su.Name, "")

su.logger.Debug("refreshing SRV upstreams",
zap.String("service", service),
zap.String("proto", proto),
Expand Down Expand Up @@ -172,7 +161,7 @@ func (su SRVUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
}
}

srvs[suStr] = srvLookup{
srvs[suAddr] = srvLookup{
srvUpstreams: su,
freshness: time.Now(),
upstreams: upstreams,
Expand All @@ -181,6 +170,37 @@ func (su SRVUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
return upstreams, nil
}

func (su SRVUpstreams) String() string {
if su.Service == "" && su.Proto == "" {
return su.Name
}
return su.formattedAddr(su.Service, su.Proto, su.Name)
}

// expandedAddr expands placeholders in the configured SRV domain labels.
// The return values are: addr, the RFC 2782 representation of the SRV domain;
// service, the service; proto, the protocol; and name, the name.
// If su.Service and su.Proto are empty, name will be returned as addr instead.
func (su SRVUpstreams) expandedAddr(r *http.Request) (addr, service, proto, name string) {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
name = repl.ReplaceAll(su.Name, "")
if su.Service == "" && su.Proto == "" {
addr = name
name = ""
return
}
service = repl.ReplaceAll(su.Service, "")
proto = repl.ReplaceAll(su.Proto, "")
addr = su.formattedAddr(service, proto, name)
return
}

// formattedAddr the RFC 2782 representation of the SRV domain, in
// the form "_service._proto.name".
func (SRVUpstreams) formattedAddr(service, proto, name string) string {
return fmt.Sprintf("_%s._%s.%s", service, proto, name)
}

type srvLookup struct {
srvUpstreams SRVUpstreams
freshness time.Time
Expand Down Expand Up @@ -234,8 +254,6 @@ func (AUpstreams) CaddyModule() caddy.ModuleInfo {
}
}

func (au AUpstreams) String() string { return au.Name }

func (au *AUpstreams) Provision(_ caddy.Context) error {
if au.Refresh == 0 {
au.Refresh = caddy.Duration(time.Minute)
Expand Down Expand Up @@ -270,7 +288,8 @@ func (au *AUpstreams) Provision(_ caddy.Context) error {
}

func (au AUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
auStr := au.String()
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
auStr := repl.ReplaceAll(au.String(), "")

// first, use a cheap read-lock to return a cached result quickly
aAaaaMu.RLock()
Expand All @@ -292,7 +311,6 @@ func (au AUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
return cached.upstreams, nil
}

repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
name := repl.ReplaceAll(au.Name, "")
port := repl.ReplaceAll(au.Port, "")

Expand Down Expand Up @@ -325,6 +343,8 @@ func (au AUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
return upstreams, nil
}

func (au AUpstreams) String() string { return au.Name }

type aLookup struct {
aUpstreams AUpstreams
freshness time.Time
Expand Down

0 comments on commit dc4d147

Please sign in to comment.