package fetchguard import ( "net/http" "net/http/httptest" "sync/atomic" "testing" "time" ) func TestClientNeverFollowsRedirects(t *testing.T) { handler := func(followed *atomic.Bool) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/target" { followed.Store(true) return } http.Redirect(w, r, "/target", http.StatusFound) }) } for _, mode := range []Mode{AllowHostsMode, PublicOnlyMode, TrustedMode} { var followed atomic.Bool var srv *httptest.Server policy := Policy{Mode: mode, AllowHosts: []string{"127.0.0.1"}, MaxBytes: 1024, Timeout: 5 * time.Second} if mode == TrustedMode { srv = httptest.NewServer(handler(&followed)) } else { srv = httptest.NewTLSServer(handler(&followed)) policy = withTestLoopback(srv, policy) } c, err := NewClient(policy, nil) if err != nil { t.Fatal(err) } res, err := c.PostJSON(t.Context(), srv.URL+"/start", nil, map[string]int{"n": 1}) srv.Close() if err != nil { t.Fatalf("mode %d: %v", mode, err) } if res.StatusCode != http.StatusFound || res.Header.Get("Location") != "/target" { t.Fatalf("mode %d: status %d location %q, want the 302 itself", mode, res.StatusCode, res.Header.Get("Location")) } if followed.Load() { t.Fatalf("mode %d: redirect target was requested", mode) } } } func TestNewClientNegativeLimitsUseDefaults(t *testing.T) { if _, err := NewClient(Policy{Mode: PublicOnlyMode, MaxBytes: -1, Timeout: -1}, nil); err != nil { // Negative limits fall back to defaults like zero does. t.Fatalf("NewClient: %v", err) } }