From 7da6e147ef72c6697489e988508d36d33c720b82 Mon Sep 17 00:00:00 2001 From: sneak Date: Sun, 4 Oct 2026 02:55:18 +0000 Subject: [PATCH] TestRequestTimeouts expects 504 when none of the body reached the app (closes #49) In the cases where the app reads and the client stops sending halfway, smallwebwaf waits on the client only once it has passed the first bytes of the body to the app. A test process held up for the whole 300 ms timeout before then rightly gets 504, as SPEC.md asks, so the test was wrong to expect 408 every time. The app in those cases now records whether it received any of the body. Once the app has finished with the request, the case expects 408 and its log line if it did, and 504 and its log line if not. Model: opus-5-5 --- internal/proxy/timeouts_test.go | 37 ++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/internal/proxy/timeouts_test.go b/internal/proxy/timeouts_test.go index e964d57..8a78a2b 100644 --- a/internal/proxy/timeouts_test.go +++ b/internal/proxy/timeouts_test.go @@ -5,8 +5,10 @@ import ( "io" "net" "net/http" + "net/http/httptest" "strconv" "sync" + "sync/atomic" "testing" "time" @@ -66,32 +68,47 @@ func TestRequestTimeouts(t *testing.T) { t.Parallel() var ( + app *httptest.Server appURL string sendRequest func(*testing.T, string) net.Conn + appGotBody atomic.Bool ) if tc.appTakesNothing { appURL, sendRequest = startAppThatTakesNothing(t), sendLargeBody } else { - appURL, sendRequest = startApp(t, readBody).URL, sendPartOfBody + app = startApp(t, func(_ http.ResponseWriter, r *http.Request) { + n, _ := io.Copy(io.Discard, r.Body) + appGotBody.Store(n > 0) + }) + appURL, sendRequest = app.URL, sendPartOfBody } addr, out := startProxy(t, appURL, tc.env) start := time.Now() - conn := sendRequest(t, addr) - - wantStatus(t, readResponse(t, conn), tc.want) + got := readResponse(t, sendRequest(t, addr)) wantTimedOut(t, start) - wantLine(t, out.requestLine(t), tc.want, requestlog.ActionTimedOut) + + want := tc.want + + if app != nil { + // Close returns once the app has finished with the request. + app.Close() + + // Until some of the body has reached the app, smallwebwaf + // waits on the app, and SPEC.md asks for 504; the timeout + // runs out then only if the test process is held up. + if !appGotBody.Load() { + want = http.StatusGatewayTimeout + } + } + + wantStatus(t, got, want) + wantLine(t, out.requestLine(t), want, requestlog.ActionTimedOut) }) } } -// readBody is an app that reads the request body, then answers. -func readBody(_ http.ResponseWriter, r *http.Request) { - _, _ = io.Copy(io.Discard, r.Body) -} - // startAppThatTakesNothing starts an app that accepts connections and // never reads from them, and returns its URL. func startAppThatTakesNothing(t *testing.T) string { -- 2.54.0