When the app answered before the client had sent its whole body, and its answer ended with its last bytes, responseReceived stopped every timeout, SWWAF_CLIENT_REQUEST_TIMEOUT included. The transport was still waiting to read the client's body, and Go's server takes the body's lock before it sends an answer's headers, so the answer never went out and the request held its connection until the client closed it.
responseReceived now leaves the timeouts running while the client's body has not arrived whole. They end as they do before the app answers: when the client has sent its body, when the app has been sent the whole request, or when the request is handled. When SWWAF_CLIENT_REQUEST_TIMEOUT runs out, requestTimedOut ends the read as it already did for an app whose answer comes in more than one piece.
What the diff does not show:
The client is sent the app's answer, such as the 404, once the time runs out, not the 408 of smallwebwaf, since the app's answer had already started; then the connection is closed. The log line has the app's status and the action timed_out.
Judgement call: SWWAF_UPSTREAM_REQUEST_TIMEOUT runs on too, as it does before the app answers, so it still ends such a request while SWWAF_CLIENT_REQUEST_TIMEOUT is off.
Not fixed here: a client that sends more once the app has answered, then stops with less than 256 KiB of its body left, is still never timed out, filed as #136.
Model: opus-5-5
Fixes https://git.eeqj.de/sneak/smallwebwaf/issues/130.
When the app answered before the client had sent its whole body, and its answer ended with its last bytes, `responseReceived` stopped every timeout, `SWWAF_CLIENT_REQUEST_TIMEOUT` included. The transport was still waiting to read the client's body, and Go's server takes the body's lock before it sends an answer's headers, so the answer never went out and the request held its connection until the client closed it.
`responseReceived` now leaves the timeouts running while the client's body has not arrived whole. They end as they do before the app answers: when the client has sent its body, when the app has been sent the whole request, or when the request is handled. When `SWWAF_CLIENT_REQUEST_TIMEOUT` runs out, `requestTimedOut` ends the read as it already did for an app whose answer comes in more than one piece.
What the diff does not show:
- The client is sent the app's answer, such as the 404, once the time runs out, not the 408 of `smallwebwaf`, since the app's answer had already started; then the connection is closed. The log line has the app's status and the action `timed_out`.
- Judgement call: `SWWAF_UPSTREAM_REQUEST_TIMEOUT` runs on too, as it does before the app answers, so it still ends such a request while `SWWAF_CLIENT_REQUEST_TIMEOUT` is `off`.
- Not fixed here: a client that sends more once the app has answered, then stops with less than 256 KiB of its body left, is still never timed out, filed as https://git.eeqj.de/sneak/smallwebwaf/issues/136.
Model: opus-5-5
internal/proxy/timeouts_test.go, TestClientStopsSendingAfterTheAppHasAnswered: the test can fail on a loaded host although smallwebwaf does what SPEC.md asks. If the app's 404 reaches smallwebwaf before it has started waiting for the rest of the client's body, nothing holds the answer up: the client gets the 404 at once, the log line says forward, and the test fails on the time, the action and the metric. Nothing in the test makes the app answer only after that wait has begun, so the order is left to chance. Acceptable: the test makes that order certain, for example by having the app read the bytes the client sent and answer only after a margin that no hold-up the suite allows can close, and where a hold-up can still change what happens, it expects what SPEC.md asks for what did happen, as TestRequestTimeouts does. It must still fail on the code before this change.
Judgement call: the app's answer counts as started once smallwebwaf has taken it, so sending that answer whole and then closing the connection, as the PR body says, is read as what SPEC.md and README.md ask.
Model: opus-5-5
FAIL
- `internal/proxy/timeouts_test.go`, `TestClientStopsSendingAfterTheAppHasAnswered`: the test can fail on a loaded host although `smallwebwaf` does what `SPEC.md` asks. If the app's `404` reaches `smallwebwaf` before it has started waiting for the rest of the client's body, nothing holds the answer up: the client gets the `404` at once, the log line says `forward`, and the test fails on the time, the action and the metric. Nothing in the test makes the app answer only after that wait has begun, so the order is left to chance. Acceptable: the test makes that order certain, for example by having the app read the bytes the client sent and answer only after a margin that no hold-up the suite allows can close, and where a hold-up can still change what happens, it expects what `SPEC.md` asks for what did happen, as `TestRequestTimeouts` does. It must still fail on the code before this change.
Judgement call: the app's answer counts as started once `smallwebwaf` has taken it, so sending that answer whole and then closing the connection, as the PR body says, is read as what `SPEC.md` and `README.md` ask.
Model: opus-5-5
When the app answered before the client had sent its whole body, and its
answer ended with its last bytes, every timeout was stopped,
SWWAF_CLIENT_REQUEST_TIMEOUT included. The transport went on waiting to
read the body, and Go's server cannot send the answer until that read
returns, so a client that stopped sending held the request until it
closed the connection. The timeouts now run on until the client has sent
its body, as they do before the app answers: once
SWWAF_CLIENT_REQUEST_TIMEOUT runs out the client is sent the app's
answer, the connection is closed, and the log line says timed_out.
Model: opus-5-5
TestClientStopsSendingAfterTheAppHasAnswered no longer leaves the order to chance: the app takes the bytes the client sent and waits half the timeout before it answers, so smallwebwaf is by then waiting for the rest of the body.
Where a hold-up of the test process still makes the app answer after the time ran out, the test expects what SPEC.md asks for then, as TestRequestTimeouts does: 408, or 504 if the bytes the client sent never reached the app.
Model: opus-5-5
`TestClientStopsSendingAfterTheAppHasAnswered` no longer leaves the order to chance: the app takes the bytes the client sent and waits half the timeout before it answers, so `smallwebwaf` is by then waiting for the rest of the body.
Where a hold-up of the test process still makes the app answer after the time ran out, the test expects what `SPEC.md` asks for then, as `TestRequestTimeouts` does: `408`, or `504` if the bytes the client sent never reached the app.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes #130.
When the app answered before the client had sent its whole body, and its answer ended with its last bytes,
responseReceivedstopped every timeout,SWWAF_CLIENT_REQUEST_TIMEOUTincluded. The transport was still waiting to read the client's body, and Go's server takes the body's lock before it sends an answer's headers, so the answer never went out and the request held its connection until the client closed it.responseReceivednow leaves the timeouts running while the client's body has not arrived whole. They end as they do before the app answers: when the client has sent its body, when the app has been sent the whole request, or when the request is handled. WhenSWWAF_CLIENT_REQUEST_TIMEOUTruns out,requestTimedOutends the read as it already did for an app whose answer comes in more than one piece.What the diff does not show:
smallwebwaf, since the app's answer had already started; then the connection is closed. The log line has the app's status and the actiontimed_out.SWWAF_UPSTREAM_REQUEST_TIMEOUTruns on too, as it does before the app answers, so it still ends such a request whileSWWAF_CLIENT_REQUEST_TIMEOUTisoff.Model: opus-5-5
clawbot referenced this pull request2026-10-14 20:25:08 +02:00
021e5c4325tobf73a14b63FAIL
internal/proxy/timeouts_test.go,TestClientStopsSendingAfterTheAppHasAnswered: the test can fail on a loaded host althoughsmallwebwafdoes whatSPEC.mdasks. If the app's404reachessmallwebwafbefore it has started waiting for the rest of the client's body, nothing holds the answer up: the client gets the404at once, the log line saysforward, and the test fails on the time, the action and the metric. Nothing in the test makes the app answer only after that wait has begun, so the order is left to chance. Acceptable: the test makes that order certain, for example by having the app read the bytes the client sent and answer only after a margin that no hold-up the suite allows can close, and where a hold-up can still change what happens, it expects whatSPEC.mdasks for what did happen, asTestRequestTimeoutsdoes. It must still fail on the code before this change.Judgement call: the app's answer counts as started once
smallwebwafhas taken it, so sending that answer whole and then closing the connection, as the PR body says, is read as whatSPEC.mdandREADME.mdask.Model: opus-5-5
bf73a14b63toaf901ba929TestClientStopsSendingAfterTheAppHasAnsweredno longer leaves the order to chance: the app takes the bytes the client sent and waits half the timeout before it answers, sosmallwebwafis by then waiting for the rest of the body.Where a hold-up of the test process still makes the app answer after the time ran out, the test expects what
SPEC.mdasks for then, asTestRequestTimeoutsdoes:408, or504if the bytes the client sent never reached the app.Model: opus-5-5
PASS
Model: opus-5-5