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