carlos / platform Public

host: routeReady does not follow redirects

Fix round 1: Add CheckRedirect policy to prevent following HTTP redirects,
matching internal/edge/prober.go behavior. The readiness probe must treat
redirect responses (3xx) as explicit status codes, not transparent hops.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev b4296708a462c7f3db3d2915627a37ca80a62001 parent 2731de7
2 files changed, +29 −1
  • internal/host/unitsettle.go +1 −1
  • internal/host/unitsettle_test.go +28 −0
diff --git a/internal/host/unitsettle.go b/internal/host/unitsettle.go
index 414f2d35d..adb3cbe5b 100644
--- a/internal/host/unitsettle.go
+++ b/internal/host/unitsettle.go
@@ -236,7 +236,7 @@ func routeReady(rt registry.Route, timeout time.Duration) func() error {
if hostHdr == "" {
hostHdr = rt.Host
}
- client := &http.Client{Timeout: timeout, Transport: &http.Transport{
+ client := &http.Client{Timeout: timeout, CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }, Transport: &http.Transport{
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, network, address)
diff --git a/internal/host/unitsettle_test.go b/internal/host/unitsettle_test.go
index 8bdfb7d58..2ec404380 100644
--- a/internal/host/unitsettle_test.go
+++ b/internal/host/unitsettle_test.go
@@ -258,3 +258,31 @@ func TestRouteReadyNoUpstreamIsNil(t *testing.T) {
t.Fatal("a route with no upstream got a ready check; settle must use the hold instead")
}
}
+
+func TestRouteReadyDoesNotFollowRedirects(t *testing.T) {
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path == "/healthz" {
+ w.Header().Set("Location", "/ok")
+ w.WriteHeader(302)
+ return
+ }
+ if r.URL.Path == "/ok" {
+ w.WriteHeader(200)
+ return
+ }
+ w.WriteHeader(500)
+ }))
+ t.Cleanup(srv.Close)
+ // Case 1: expect 200 at /healthz, but get 302; should error
+ rt1 := registry.Route{Host: "x.example.com", Addr: strings.TrimPrefix(srv.URL, "http://"),
+ Health: `{"path":"/healthz","status":200}`}
+ if err := routeReady(rt1, time.Second)(); err == nil || !strings.Contains(err.Error(), "302") {
+ t.Fatalf("ready with redirect = %v, want a 302 error", err)
+ }
+ // Case 2: expect 302 at /healthz; should succeed
+ rt2 := registry.Route{Host: "x.example.com", Addr: strings.TrimPrefix(srv.URL, "http://"),
+ Health: `{"path":"/healthz","status":302}`}
+ if err := routeReady(rt2, time.Second)(); err != nil {
+ t.Fatalf("ready expecting 302 = %v, want nil", err)
+ }
+}