Skip to content

Commit 11b56c6

Browse files
authored
reverseproxy: Fix health_port being ignored in health checks (#7533)
1 parent f283062 commit 11b56c6

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

caddytest/integration/reverseproxy_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,68 @@ func TestReverseProxyHealthCheck(t *testing.T) {
386386
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
387387
}
388388

389+
// TestReverseProxyHealthCheckPortUsed verifies that health_port is actually
390+
// used for active health checks and not the upstream's main port. This is a
391+
// regression test for https://github.com/caddyserver/caddy/issues/7524.
392+
func TestReverseProxyHealthCheckPortUsed(t *testing.T) {
393+
// upstream server: serves proxied requests normally, but returns 503 for
394+
// /health so that if health checks mistakenly hit this port the upstream
395+
// gets marked unhealthy and the proxy returns 503.
396+
upstreamSrv := &http.Server{
397+
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
398+
if req.URL.Path == "/health" {
399+
w.WriteHeader(http.StatusServiceUnavailable)
400+
return
401+
}
402+
_, _ = w.Write([]byte("Hello, World!"))
403+
}),
404+
}
405+
ln0, err := net.Listen("tcp", "127.0.0.1:2022")
406+
if err != nil {
407+
t.Fatalf("failed to listen on 127.0.0.1:2022: %v", err)
408+
}
409+
go upstreamSrv.Serve(ln0)
410+
t.Cleanup(func() { upstreamSrv.Close(); ln0.Close() })
411+
412+
// separate health check server on the configured health_port: returns 200
413+
// so the upstream is marked healthy only if health checks go to this port.
414+
healthSrv := &http.Server{
415+
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
416+
_, _ = w.Write([]byte("ok"))
417+
}),
418+
}
419+
ln1, err := net.Listen("tcp", "127.0.0.1:2023")
420+
if err != nil {
421+
t.Fatalf("failed to listen on 127.0.0.1:2023: %v", err)
422+
}
423+
go healthSrv.Serve(ln1)
424+
t.Cleanup(func() { healthSrv.Close(); ln1.Close() })
425+
426+
tester := caddytest.NewTester(t)
427+
tester.InitServer(`
428+
{
429+
skip_install_trust
430+
admin localhost:2999
431+
http_port 9080
432+
https_port 9443
433+
grace_period 1ns
434+
}
435+
http://localhost:9080 {
436+
reverse_proxy {
437+
to localhost:2022
438+
439+
health_uri /health
440+
health_port 2023
441+
health_interval 10ms
442+
health_timeout 100ms
443+
health_passes 1
444+
health_fails 1
445+
}
446+
}
447+
`, "caddyfile")
448+
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
449+
}
450+
389451
func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
390452
if runtime.GOOS == "windows" {
391453
t.SkipNow()

modules/caddyhttp/reverseproxy/healthchecks.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,12 @@ func (h *Handler) doActiveHealthCheckForAllHosts() {
359359
dialInfoUpstream = &Upstream{
360360
Dial: h.HealthChecks.Active.Upstream,
361361
}
362+
} else if upstream.activeHealthCheckPort != 0 {
363+
// health_port overrides the port; addr has already been updated
364+
// with the health port, so use its address for dialing
365+
dialInfoUpstream = &Upstream{
366+
Dial: addr.JoinHostPort(0),
367+
}
362368
}
363369
dialInfo, _ := dialInfoUpstream.fillDialInfo(repl)
364370

0 commit comments

Comments
 (0)