Skip to content

Commit 54a0c8f

Browse files
authored
reverseproxy: Active health checks request body option (#6520)
* Add an option to specify the body used for active health checks * Replacer on request body
1 parent 043fe41 commit 54a0c8f

3 files changed

Lines changed: 78 additions & 12 deletions

File tree

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,20 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
6969
// lb_retry_match <request-matcher>
7070
//
7171
// # 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>
8082
// health_follow_redirects
8183
// health_headers {
8284
// <field> [<values...>]
8385
// }
84-
// health_method <value>
8586
//
8687
// # passive health checking
8788
// fail_duration <duration>
@@ -425,6 +426,18 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
425426
}
426427
h.HealthChecks.Active.Method = d.Val()
427428

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+
428441
case "health_interval":
429442
if !d.NextArg() {
430443
return d.ArgErr()

modules/caddyhttp/reverseproxy/healthchecks.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"regexp"
2525
"runtime/debug"
2626
"strconv"
27+
"strings"
2728
"time"
2829

2930
"go.uber.org/zap"
@@ -93,6 +94,9 @@ type ActiveHealthChecks struct {
9394
// The HTTP method to use for health checks (default "GET").
9495
Method string `json:"method,omitempty"`
9596

97+
// The body to send with the health check request.
98+
Body string `json:"body,omitempty"`
99+
96100
// Whether to follow HTTP redirects in response to active health checks (default off).
97101
FollowRedirects bool `json:"follow_redirects,omitempty"`
98102

@@ -396,22 +400,31 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
396400
u.Path = h.HealthChecks.Active.Path
397401
}
398402

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+
399413
// attach dialing information to this request, as well as context values that
400414
// may be expected by handlers of this request
401415
ctx := h.ctx.Context
402416
ctx = context.WithValue(ctx, caddy.ReplacerCtxKey, caddy.NewReplacer())
403417
ctx = context.WithValue(ctx, caddyhttp.VarsCtxKey, map[string]any{
404418
dialInfoVarKey: dialInfo,
405419
})
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)
407421
if err != nil {
408422
return fmt.Errorf("making request: %v", err)
409423
}
410424
ctx = context.WithValue(ctx, caddyhttp.OriginalRequestCtxKey, *req)
411425
req = req.WithContext(ctx)
412426

413-
// set headers, using a replacer with only globals (env vars, system info, etc.)
414-
repl := caddy.NewReplacer()
427+
// set headers, using replacer
415428
repl.Set("http.reverse_proxy.active.target_upstream", networkAddr)
416429
for key, vals := range h.HealthChecks.Active.Headers {
417430
key = repl.ReplaceAll(key, "")

0 commit comments

Comments
 (0)