Compare commits
1
Commits
next
...
786290e177
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
786290e177 |
@@ -715,6 +715,12 @@ func (rq *request) requestTimedOut(limit string) {
|
||||
action: requestlog.ActionTimedOut,
|
||||
limit: limit,
|
||||
})
|
||||
// Go's server, before it sends the app's answer, and the
|
||||
// transport, as it closes the body, read what is left of a body
|
||||
// under 256 KiB without requestBody.Read, so waiting is false
|
||||
// while they wait for the client. Those reads end when the client
|
||||
// is out of time.
|
||||
rq.stopReadingBody(rq.clientRequestDeadline())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
@@ -248,6 +249,59 @@ func TestClientStopsSendingAfterTheAppHasAnswered(t *testing.T) {
|
||||
wantLimitHits(t, addr, clientRequestTimeout, 1)
|
||||
}
|
||||
|
||||
func TestClientStopsSendingWithLittleOfItsBodyLeft(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// appClosed is closed once smallwebwaf has closed its connection to
|
||||
// the app, which it does once it has the app's answer.
|
||||
appClosed := make(chan struct{})
|
||||
|
||||
// The app answers as soon as it has the headers, without reading the
|
||||
// body, and says it closes the connection.
|
||||
app := startApp(t, func(w http.ResponseWriter, _ *http.Request) {
|
||||
conn, buf, err := http.NewResponseController(w).Hijack()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = buf.WriteString("HTTP/1.1 404 Not Found\r\n" +
|
||||
"Content-Length: 0\r\nConnection: close\r\n\r\n")
|
||||
_ = buf.Flush()
|
||||
_, _ = io.Copy(io.Discard, buf.Reader)
|
||||
_ = conn.Close()
|
||||
|
||||
close(appClosed)
|
||||
})
|
||||
addr, out := startProxy(t, app.URL, map[string]string{
|
||||
clientRequestTimeout: shortTimeoutSetting,
|
||||
metricsToken: token,
|
||||
})
|
||||
|
||||
// Less than 256 KiB of the body is left after the first bytes, little
|
||||
// enough for Go's server in smallwebwaf to read the rest itself before
|
||||
// it sends the app's answer.
|
||||
start := time.Now()
|
||||
conn := dial(t, addr)
|
||||
send(t, conn, "POST /upload HTTP/1.1\r\nHost: app\r\nContent-Length: 200000\r\n\r\n"+
|
||||
"the first bytes")
|
||||
|
||||
select {
|
||||
case <-appClosed:
|
||||
case <-time.After(waitLimit):
|
||||
t.Fatal("smallwebwaf did not close its connection to the app")
|
||||
}
|
||||
|
||||
// More of the body, which can no longer go to the app, and then
|
||||
// nothing.
|
||||
send(t, conn, strings.Repeat("x", 8<<10))
|
||||
|
||||
got := readResponse(t, conn)
|
||||
wantTimedOut(t, start)
|
||||
wantStatus(t, got, http.StatusNotFound)
|
||||
wantLine(t, out.requestLine(t), http.StatusNotFound, requestlog.ActionTimedOut)
|
||||
wantLimitHits(t, addr, clientRequestTimeout, 1)
|
||||
}
|
||||
|
||||
func TestAppTooSlowToAnswer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user