1
Fork 0
mirror of https://github.com/caddyserver/caddy.git synced 2024-12-16 21:56:40 -05:00

reverseproxy: Fix active health check header canonicalization, refactor (#5446)

This commit is contained in:
Francis Lavoie 2023-05-05 17:19:22 -04:00 committed by GitHub
parent 48598e1f2a
commit 335cd2e8a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 50 deletions

View file

@ -24,7 +24,6 @@ import (
"regexp"
"runtime/debug"
"strconv"
"strings"
"time"
"github.com/caddyserver/caddy/v2"
@ -106,6 +105,76 @@ type ActiveHealthChecks struct {
logger *zap.Logger
}
// Provision ensures that a is set up properly before use.
func (a *ActiveHealthChecks) Provision(ctx caddy.Context, h *Handler) error {
if !a.IsEnabled() {
return nil
}
// Canonicalize the header keys ahead of time, since
// JSON unmarshaled headers may be incorrect
cleaned := http.Header{}
for key, hdrs := range a.Headers {
for _, val := range hdrs {
cleaned.Add(key, val)
}
}
a.Headers = cleaned
h.HealthChecks.Active.logger = h.logger.Named("health_checker.active")
timeout := time.Duration(a.Timeout)
if timeout == 0 {
timeout = 5 * time.Second
}
if a.Path != "" {
a.logger.Warn("the 'path' option is deprecated, please use 'uri' instead!")
}
// parse the URI string (supports path and query)
if a.URI != "" {
parsedURI, err := url.Parse(a.URI)
if err != nil {
return err
}
a.uri = parsedURI
}
a.httpClient = &http.Client{
Timeout: timeout,
Transport: h.Transport,
}
for _, upstream := range h.Upstreams {
// if there's an alternative port for health-check provided in the config,
// then use it, otherwise use the port of upstream.
if a.Port != 0 {
upstream.activeHealthCheckPort = a.Port
}
}
if a.Interval == 0 {
a.Interval = caddy.Duration(30 * time.Second)
}
if a.ExpectBody != "" {
var err error
a.bodyRegexp, err = regexp.Compile(a.ExpectBody)
if err != nil {
return fmt.Errorf("expect_body: compiling regular expression: %v", err)
}
}
return nil
}
// IsEnabled checks if the active health checks have
// the minimum config necessary to be enabled.
func (a *ActiveHealthChecks) IsEnabled() bool {
return a.Path != "" || a.URI != "" || a.Port != 0
}
// PassiveHealthChecks holds configuration related to passive
// health checks (that is, health checks which occur during
// the normal flow of request proxying).
@ -280,7 +349,7 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
ctx = context.WithValue(ctx, caddyhttp.OriginalRequestCtxKey, *req)
req = req.WithContext(ctx)
for key, hdrs := range h.HealthChecks.Active.Headers {
if strings.ToLower(key) == "host" {
if key == "Host" {
req.Host = h.HealthChecks.Active.Headers.Get(key)
} else {
req.Header[key] = hdrs

View file

@ -27,7 +27,6 @@ import (
"net/netip"
"net/textproto"
"net/url"
"regexp"
"runtime"
"strconv"
"strings"
@ -355,56 +354,15 @@ func (h *Handler) Provision(ctx caddy.Context) error {
}
// if active health checks are enabled, configure them and start a worker
if h.HealthChecks.Active != nil && (h.HealthChecks.Active.Path != "" ||
h.HealthChecks.Active.URI != "" ||
h.HealthChecks.Active.Port != 0) {
h.HealthChecks.Active.logger = h.logger.Named("health_checker.active")
timeout := time.Duration(h.HealthChecks.Active.Timeout)
if timeout == 0 {
timeout = 5 * time.Second
if h.HealthChecks.Active != nil {
err := h.HealthChecks.Active.Provision(ctx, h)
if err != nil {
return err
}
if h.HealthChecks.Active.Path != "" {
h.HealthChecks.Active.logger.Warn("the 'path' option is deprecated, please use 'uri' instead!")
if h.HealthChecks.Active.IsEnabled() {
go h.activeHealthChecker()
}
// parse the URI string (supports path and query)
if h.HealthChecks.Active.URI != "" {
parsedURI, err := url.Parse(h.HealthChecks.Active.URI)
if err != nil {
return err
}
h.HealthChecks.Active.uri = parsedURI
}
h.HealthChecks.Active.httpClient = &http.Client{
Timeout: timeout,
Transport: h.Transport,
}
for _, upstream := range h.Upstreams {
// if there's an alternative port for health-check provided in the config,
// then use it, otherwise use the port of upstream.
if h.HealthChecks.Active.Port != 0 {
upstream.activeHealthCheckPort = h.HealthChecks.Active.Port
}
}
if h.HealthChecks.Active.Interval == 0 {
h.HealthChecks.Active.Interval = caddy.Duration(30 * time.Second)
}
if h.HealthChecks.Active.ExpectBody != "" {
var err error
h.HealthChecks.Active.bodyRegexp, err = regexp.Compile(h.HealthChecks.Active.ExpectBody)
if err != nil {
return fmt.Errorf("expect_body: compiling regular expression: %v", err)
}
}
go h.activeHealthChecker()
}
}