@marcel91200 / caddy Mirror · read-only / commits / 335cd2e8
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 13, 03:00 UTC

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

Francis Lavoie committed May 5, 2023 at 17:19 UTC 335cd2e8a4f2a91cb2c55a6c2e624a4c4ccddb0c
2 files changed +77 -50
modules/caddyhttp/reverseproxy/healthchecks.go
+71 -2
@@ -24,7 +24,6 @@ import (
24 "regexp"
25 "runtime/debug"
26 "strconv"
27 - "strings"
27 "time"
28
29 "github.com/caddyserver/caddy/v2"
@@ -106,6 +105,76 @@ type ActiveHealthChecks struct {
105 logger *zap.Logger
106 }
107
108 +// Provision ensures that a is set up properly before use.
109 +func (a *ActiveHealthChecks) Provision(ctx caddy.Context, h *Handler) error {
110 + if !a.IsEnabled() {
111 + return nil
112 + }
113 +
114 + // Canonicalize the header keys ahead of time, since
115 + // JSON unmarshaled headers may be incorrect
116 + cleaned := http.Header{}
117 + for key, hdrs := range a.Headers {
118 + for _, val := range hdrs {
119 + cleaned.Add(key, val)
120 + }
121 + }
122 + a.Headers = cleaned
123 +
124 + h.HealthChecks.Active.logger = h.logger.Named("health_checker.active")
125 +
126 + timeout := time.Duration(a.Timeout)
127 + if timeout == 0 {
128 + timeout = 5 * time.Second
129 + }
130 +
131 + if a.Path != "" {
132 + a.logger.Warn("the 'path' option is deprecated, please use 'uri' instead!")
133 + }
134 +
135 + // parse the URI string (supports path and query)
136 + if a.URI != "" {
137 + parsedURI, err := url.Parse(a.URI)
138 + if err != nil {
139 + return err
140 + }
141 + a.uri = parsedURI
142 + }
143 +
144 + a.httpClient = &http.Client{
145 + Timeout: timeout,
146 + Transport: h.Transport,
147 + }
148 +
149 + for _, upstream := range h.Upstreams {
150 + // if there's an alternative port for health-check provided in the config,
151 + // then use it, otherwise use the port of upstream.
152 + if a.Port != 0 {
153 + upstream.activeHealthCheckPort = a.Port
154 + }
155 + }
156 +
157 + if a.Interval == 0 {
158 + a.Interval = caddy.Duration(30 * time.Second)
159 + }
160 +
161 + if a.ExpectBody != "" {
162 + var err error
163 + a.bodyRegexp, err = regexp.Compile(a.ExpectBody)
164 + if err != nil {
165 + return fmt.Errorf("expect_body: compiling regular expression: %v", err)
166 + }
167 + }
168 +
169 + return nil
170 +}
171 +
172 +// IsEnabled checks if the active health checks have
173 +// the minimum config necessary to be enabled.
174 +func (a *ActiveHealthChecks) IsEnabled() bool {
175 + return a.Path != "" || a.URI != "" || a.Port != 0
176 +}
177 +
178 // PassiveHealthChecks holds configuration related to passive
179 // health checks (that is, health checks which occur during
180 // the normal flow of request proxying).
@@ -280,7 +349,7 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre
349 ctx = context.WithValue(ctx, caddyhttp.OriginalRequestCtxKey, *req)
350 req = req.WithContext(ctx)
351 for key, hdrs := range h.HealthChecks.Active.Headers {
283 - if strings.ToLower(key) == "host" {
352 + if key == "Host" {
353 req.Host = h.HealthChecks.Active.Headers.Get(key)
354 } else {
355 req.Header[key] = hdrs
modules/caddyhttp/reverseproxy/reverseproxy.go
+6 -48
@@ -27,7 +27,6 @@ import (
27 "net/netip"
28 "net/textproto"
29 "net/url"
30 - "regexp"
30 "runtime"
31 "strconv"
32 "strings"
@@ -355,56 +354,15 @@ func (h *Handler) Provision(ctx caddy.Context) error {
354 }
355
356 // if active health checks are enabled, configure them and start a worker
358 - if h.HealthChecks.Active != nil && (h.HealthChecks.Active.Path != "" ||
359 - h.HealthChecks.Active.URI != "" ||
360 - h.HealthChecks.Active.Port != 0) {
361 -
362 - h.HealthChecks.Active.logger = h.logger.Named("health_checker.active")
363 -
364 - timeout := time.Duration(h.HealthChecks.Active.Timeout)
365 - if timeout == 0 {
366 - timeout = 5 * time.Second
367 - }
368 -
369 - if h.HealthChecks.Active.Path != "" {
370 - h.HealthChecks.Active.logger.Warn("the 'path' option is deprecated, please use 'uri' instead!")
371 - }
372 -
373 - // parse the URI string (supports path and query)
374 - if h.HealthChecks.Active.URI != "" {
375 - parsedURI, err := url.Parse(h.HealthChecks.Active.URI)
376 - if err != nil {
377 - return err
378 - }
379 - h.HealthChecks.Active.uri = parsedURI
380 - }
381 -
382 - h.HealthChecks.Active.httpClient = &http.Client{
383 - Timeout: timeout,
384 - Transport: h.Transport,
385 - }
386 -
387 - for _, upstream := range h.Upstreams {
388 - // if there's an alternative port for health-check provided in the config,
389 - // then use it, otherwise use the port of upstream.
390 - if h.HealthChecks.Active.Port != 0 {
391 - upstream.activeHealthCheckPort = h.HealthChecks.Active.Port
392 - }
393 - }
394 -
395 - if h.HealthChecks.Active.Interval == 0 {
396 - h.HealthChecks.Active.Interval = caddy.Duration(30 * time.Second)
357 + if h.HealthChecks.Active != nil {
358 + err := h.HealthChecks.Active.Provision(ctx, h)
359 + if err != nil {
360 + return err
361 }
362
399 - if h.HealthChecks.Active.ExpectBody != "" {
400 - var err error
401 - h.HealthChecks.Active.bodyRegexp, err = regexp.Compile(h.HealthChecks.Active.ExpectBody)
402 - if err != nil {
403 - return fmt.Errorf("expect_body: compiling regular expression: %v", err)
404 - }
363 + if h.HealthChecks.Active.IsEnabled() {
364 + go h.activeHealthChecker()
365 }
406 -
407 - go h.activeHealthChecker()
366 }
367 }
368