@marcel91200 / caddy Mirror · read-only / commits / 54a0c8f9
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 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, "")