diff --git a/caddy/caddy_test.go b/caddy/caddy_test.go index 4233fc3111..57bfde42b2 100644 --- a/caddy/caddy_test.go +++ b/caddy/caddy_test.go @@ -11,6 +11,7 @@ import ( "sync" "sync/atomic" "testing" + "time" "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddytest" @@ -19,6 +20,26 @@ import ( "github.com/stretchr/testify/require" ) +// waitForServerReady polls the server with retries until it responds to HTTP requests. +// This handles a race condition during Caddy config reload on macOS where SO_REUSEPORT +// can briefly route connections to the old listener being shut down, +// resulting in "connection reset by peer". +func waitForServerReady(t *testing.T, url string) { + t.Helper() + + client := &http.Client{Timeout: 1 * time.Second} + for range 10 { + resp, err := client.Get(url) + if err == nil { + require.NoError(t, resp.Body.Close()) + + return + } + + time.Sleep(100 * time.Millisecond) + } +} + var testPort = "9080" func TestPHP(t *testing.T) { @@ -406,6 +427,7 @@ func TestPHPServerDirective(t *testing.T) { } `, "caddyfile") + waitForServerReady(t, "http://localhost:"+testPort) tester.AssertGetResponse("http://localhost:"+testPort, http.StatusOK, "I am by birth a Genevese (i not set)") tester.AssertGetResponse("http://localhost:"+testPort+"/hello.txt", http.StatusOK, "Hello\n") tester.AssertGetResponse("http://localhost:"+testPort+"/not-found.txt", http.StatusOK, "I am by birth a Genevese (i not set)") @@ -431,6 +453,7 @@ func TestPHPServerDirectiveDisableFileServer(t *testing.T) { } `, "caddyfile") + waitForServerReady(t, "http://localhost:"+testPort) tester.AssertGetResponse("http://localhost:"+testPort, http.StatusOK, "I am by birth a Genevese (i not set)") tester.AssertGetResponse("http://localhost:"+testPort+"/not-found.txt", http.StatusOK, "I am by birth a Genevese (i not set)") }