diff --git a/caddytest/integration/caddyfile_adapt/reverse_proxy_ids.txt b/caddytest/integration/caddyfile_adapt/reverse_proxy_ids.txt new file mode 100644 index 00000000000..4d72f9b810c --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/reverse_proxy_ids.txt @@ -0,0 +1,46 @@ +:8884 + +reverse_proxy one|http://localhost two|http://localhost { + to three|srv+http://localhost four|srv+http://localhost +} +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":8884" + ], + "routes": [ + { + "handle": [ + { + "handler": "reverse_proxy", + "upstreams": [ + { + "dial": "localhost:80", + "id": "one" + }, + { + "dial": "localhost:80", + "id": "two" + }, + { + "id": "three", + "lookup_srv": "localhost" + }, + { + "id": "four", + "lookup_srv": "localhost" + } + ] + } + ] + } + ] + } + } + } + } +} diff --git a/modules/caddyhttp/reverseproxy/admin.go b/modules/caddyhttp/reverseproxy/admin.go index 25685a3a302..1126b9bfdca 100644 --- a/modules/caddyhttp/reverseproxy/admin.go +++ b/modules/caddyhttp/reverseproxy/admin.go @@ -34,7 +34,8 @@ type adminUpstreams struct{} // upstreamResults holds the status of a particular upstream type upstreamStatus struct { - Address string `json:"address"` + ID string `json:"id"` + Address string `json:"address"` // Address is deprecated, should be removed in a future release. Healthy bool `json:"healthy"` NumRequests int `json:"num_requests"` Fails int `json:"fails"` @@ -78,7 +79,7 @@ func (adminUpstreams) handleUpstreams(w http.ResponseWriter, r *http.Request) er // Iterate over the upstream pool (needs to be fast) var rangeErr error hosts.Range(func(key, val interface{}) bool { - address, ok := key.(string) + id, ok := key.(string) if !ok { rangeErr = caddy.APIError{ HTTPStatus: http.StatusInternalServerError, @@ -97,7 +98,8 @@ func (adminUpstreams) handleUpstreams(w http.ResponseWriter, r *http.Request) er } results = append(results, upstreamStatus{ - Address: address, + ID: id, + Address: id, Healthy: !upstream.Unhealthy(), NumRequests: upstream.NumRequests(), Fails: upstream.Fails(), diff --git a/modules/caddyhttp/reverseproxy/caddyfile.go b/modules/caddyhttp/reverseproxy/caddyfile.go index c7f555f8a44..f6d4ce578c3 100644 --- a/modules/caddyhttp/reverseproxy/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/caddyfile.go @@ -219,6 +219,12 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { // treated as a SRV-based upstream, and any port will be // dropped. appendUpstream := func(address string) error { + var id string + if strings.Contains(address, "|") { + parts := strings.SplitN(address, "|", 2) + id = parts[0] + address = parts[1] + } isSRV := strings.HasPrefix(address, "srv+") if isSRV { address = strings.TrimPrefix(address, "srv+") @@ -231,9 +237,9 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { if host, _, err := net.SplitHostPort(dialAddr); err == nil { dialAddr = host } - h.Upstreams = append(h.Upstreams, &Upstream{LookupSRV: dialAddr}) + h.Upstreams = append(h.Upstreams, &Upstream{ID: id, LookupSRV: dialAddr}) } else { - h.Upstreams = append(h.Upstreams, &Upstream{Dial: dialAddr}) + h.Upstreams = append(h.Upstreams, &Upstream{ID: id, Dial: dialAddr}) } return nil } diff --git a/modules/caddyhttp/reverseproxy/hosts.go b/modules/caddyhttp/reverseproxy/hosts.go index b9817d2370d..147d9a7b231 100644 --- a/modules/caddyhttp/reverseproxy/hosts.go +++ b/modules/caddyhttp/reverseproxy/hosts.go @@ -65,6 +65,12 @@ type UpstreamPool []*Upstream type Upstream struct { Host `json:"-"` + // The unique ID for this upstream, to disambiguate multiple + // upstreams with the same Dial address. This is optional, + // and only necessary if the upstream states need to be + // separate, such as having different health checking policies. + ID string `json:"id,omitempty"` + // The [network address](/docs/conventions#network-addresses) // to dial to connect to the upstream. Must represent precisely // one socket (i.e. no port ranges). A valid network address @@ -98,6 +104,9 @@ type Upstream struct { } func (u Upstream) String() string { + if u.ID != "" { + return u.ID + } if u.LookupSRV != "" { return u.LookupSRV }