Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 12, 09:30 UTC
reverseproxy: Active health checks request body option (#6520)
* Add an option to specify the body used for active health checks * Replacer on request body
Jesper Brix Rosenkilde committed
Aug 19, 2024 at 18:55 UTC
54a0c8f94821f393edec1a1bc9459f2363c95117
3 files changed
+78
-12
caddytest/integration/caddyfile_adapt/reverse_proxy_health_reqbody.caddyfiletest
new
+40
@@ -0,0 +1,40 @@
1
+:8884
2
+
3
+reverse_proxy 127.0.0.1:65535 {
4
+ health_uri /health
5
+ health_request_body "test body"
6
+}
7
+----------
8
+{
9
+ "apps": {
10
+ "http": {
11
+ "servers": {
12
+ "srv0": {
13
+ "listen": [
14
+ ":8884"
15
+ ],
16
+ "routes": [
17
+ {
18
+ "handle": [
19
+ {
20
+ "handler": "reverse_proxy",
21
+ "health_checks": {
22
+ "active": {
23
+ "body": "test body",
24
+ "uri": "/health"
25
+ }
26
+ },
27
+ "upstreams": [
28
+ {
29
+ "dial": "127.0.0.1:65535"
30
+ }
31
+ ]
32
+ }
33
+ ]
34
+ }
35
+ ]
36
+ }
37
+ }
38
+ }
39
+ }
40
+}
modules/caddyhttp/reverseproxy/caddyfile.go
+22
-9
@@ -69,19 +69,20 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
69
// lb_retry_match <request-matcher>
70
//
71
// # active health checking
72
-// health_uri <uri>
73
-// health_port <port>
74
-// health_interval <interval>
75
-// health_passes <num>
76
-// health_fails <num>
77
-// health_timeout <duration>
78
-// health_status <status>
79
-// health_body <regexp>
72
+// health_uri <uri>
73
+// health_port <port>
74
+// health_interval <interval>
75
+// health_passes <num>
76
+// health_fails <num>
77
+// health_timeout <duration>
78
+// health_status <status>
79
+// health_body <regexp>
80
+// health_method <value>
81
+// health_request_body <value>
82
// health_follow_redirects
83
// health_headers {
84
// <field> [<values...>]
85
// }
84
-// health_method <value>
86
//
87
// # passive health checking
88
// fail_duration <duration>
@@ -425,6 +426,18 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
426
}
427
h.HealthChecks.Active.Method = d.Val()
428
429
+ case "health_request_body":
430
+ if !d.NextArg() {
431
+ return d.ArgErr()
432
+ }
433
+ if h.HealthChecks == nil {
434
+ h.HealthChecks = new(HealthChecks)
435
+ }
436
+ if h.HealthChecks.Active == nil {
437
+ h.HealthChecks.Active = new(ActiveHealthChecks)
438
+ }
439
+ h.HealthChecks.Active.Body = d.Val()
440
+
441
case "health_interval":
442
if !d.NextArg() {
443
return d.ArgErr()
modules/caddyhttp/reverseproxy/healthchecks.go
+16
-3
@@ -24,6 +24,7 @@ import (
24
"regexp"
25
"runtime/debug"
26
"strconv"
27
+ "strings"
28
"time"
29
30
"go.uber.org/zap"
@@ -93,6 +94,9 @@ type ActiveHealthChecks struct {
94
// The HTTP method to use for health checks (default "GET").
95
Method string `json:"method,omitempty"`
96
97
+ // The body to send with the health check request.
98
+ Body string `json:"body,omitempty"`
99
+
100
// Whether to follow HTTP redirects in response to active health checks (default off).
101
FollowRedirects bool `json:"follow_redirects,omitempty"`
102
@@ -396,6 +400,16 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
400
u.Path = h.HealthChecks.Active.Path
401
}
402
403
+ // replacer used for both body and headers. Only globals (env vars, system info, etc.) are available
404
+ repl := caddy.NewReplacer()
405
+
406
+ // if body is provided, create a reader for it, otherwise nil
407
+ var requestBody io.Reader
408
+ if h.HealthChecks.Active.Body != "" {
409
+ // set body, using replacer
410
+ requestBody = strings.NewReader(repl.ReplaceAll(h.HealthChecks.Active.Body, ""))
411
+ }
412
+
413
// attach dialing information to this request, as well as context values that
414
// may be expected by handlers of this request
415
ctx := h.ctx.Context
@@ -403,15 +417,14 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
417
ctx = context.WithValue(ctx, caddyhttp.VarsCtxKey, map[string]any{
418
dialInfoVarKey: dialInfo,
419
})
406
- req, err := http.NewRequestWithContext(ctx, h.HealthChecks.Active.Method, u.String(), nil)
420
+ req, err := http.NewRequestWithContext(ctx, h.HealthChecks.Active.Method, u.String(), requestBody)
421
if err != nil {
422
return fmt.Errorf("making request: %v", err)
423
}
424
ctx = context.WithValue(ctx, caddyhttp.OriginalRequestCtxKey, *req)
425
req = req.WithContext(ctx)
426
413
- // set headers, using a replacer with only globals (env vars, system info, etc.)
414
- repl := caddy.NewReplacer()
427
+ // set headers, using replacer
428
repl.Set("http.reverse_proxy.active.target_upstream", networkAddr)
429
for key, vals := range h.HealthChecks.Active.Headers {
430
key = repl.ReplaceAll(key, "")