Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4bf76c966 |
No files matched your search
@@ -93,10 +93,15 @@ func TestABodyAnnouncedOverItsLimitIsRefusedOnceWAFAllowsIt(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
got := visit(t, newRequest(t, http.MethodPost, addr, "/",
|
// The visitor sends no Connection: close, so Go's server reads the rest
|
||||||
bytes.NewReader(make([]byte, tc.size))))
|
// of the body before it closes the connection. Closed with the rest
|
||||||
if got.status != http.StatusRequestEntityTooLarge {
|
// unread, the connection is reset while the visitor still sends.
|
||||||
t.Errorf("answered %+v, want 413", got)
|
conn := dial(t, addr)
|
||||||
|
send(t, conn, post+announcedLength(tc.size)+
|
||||||
|
strings.Repeat("x", tc.size))
|
||||||
|
|
||||||
|
if status := readStatus(t, conn); status != http.StatusRequestEntityTooLarge {
|
||||||
|
t.Errorf("status %d, want 413", status)
|
||||||
}
|
}
|
||||||
|
|
||||||
if asked := standIn.question(t); len(asked.body) != tc.asked {
|
if asked := standIn.question(t); len(asked.body) != tc.asked {
|
||||||
@@ -131,67 +136,6 @@ func TestADenyOutranksTheBodySizeLimit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAVisitorSendingConnectionCloseReadsAnAnswerBeforeTheRestOfItsBody(
|
|
||||||
t *testing.T,
|
|
||||||
) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, tc := range []struct {
|
|
||||||
name string
|
|
||||||
waf http.HandlerFunc
|
|
||||||
status int
|
|
||||||
}{
|
|
||||||
{"over the size limit", allow, http.StatusRequestEntityTooLarge},
|
|
||||||
{"denied", deny, http.StatusForbidden},
|
|
||||||
} {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
standIn := startWAF(t, tc.waf)
|
|
||||||
app := startApp(t, func(http.ResponseWriter, *http.Request) {
|
|
||||||
t.Error("the app was asked")
|
|
||||||
})
|
|
||||||
addr, _ := startRProxy(t, standIn.URL, app.URL,
|
|
||||||
map[string]string{maxRequestSize: "1K"})
|
|
||||||
|
|
||||||
const size = 129 << 10
|
|
||||||
|
|
||||||
conn := dial(t, addr)
|
|
||||||
send(t, conn, post+"Connection: close\r\n"+
|
|
||||||
announcedLength(size)+
|
|
||||||
strings.Repeat("x", questionBodyMaxBytes))
|
|
||||||
|
|
||||||
reader := bufio.NewReader(conn)
|
|
||||||
|
|
||||||
res, err := http.ReadResponse(reader, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("read the answer: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _ = io.Copy(io.Discard, res.Body)
|
|
||||||
_ = res.Body.Close()
|
|
||||||
|
|
||||||
if res.StatusCode != tc.status {
|
|
||||||
t.Errorf("status %d, want %d", res.StatusCode, tc.status)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The rest goes in small parts, so that a reset fails a later one.
|
|
||||||
for sent := questionBodyMaxBytes; sent < size; sent += 1 << 10 {
|
|
||||||
send(t, conn, strings.Repeat("x", 1<<10))
|
|
||||||
}
|
|
||||||
|
|
||||||
rest, err := io.ReadAll(reader)
|
|
||||||
if err != nil || len(rest) != 0 {
|
|
||||||
t.Errorf(
|
|
||||||
"after the answer, read %q and %v, want the end",
|
|
||||||
rest,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestABodyNotAnnouncedOverItsLimitIsRefused(t *testing.T) {
|
func TestABodyNotAnnouncedOverItsLimitIsRefused(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -38,10 +38,6 @@ const plainText = "text/plain; charset=utf-8"
|
|||||||
// and the visitor then gets a closed connection and no answer.
|
// and the visitor then gets a closed connection and no answer.
|
||||||
const minSendTime = 10 * time.Second
|
const minSendTime = 10 * time.Second
|
||||||
|
|
||||||
// maxBodyReadAfterAnswer is the most of a body left unread that Go's server
|
|
||||||
// reads once the handler returns, for a visitor without Connection: close.
|
|
||||||
const maxBodyReadAfterAnswer = 256 << 10
|
|
||||||
|
|
||||||
// errResponseTooLate refuses an app's answer that arrives once
|
// errResponseTooLate refuses an app's answer that arrives once
|
||||||
// SWW_RPROXY_MAX_RESPONSE_TIME_MS has run out.
|
// SWW_RPROXY_MAX_RESPONSE_TIME_MS has run out.
|
||||||
var errResponseTooLate = errors.New(
|
var errResponseTooLate = errors.New(
|
||||||
@@ -342,8 +338,8 @@ func (rq *request) answer(status int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A visitor found too slow is read no more; any other may go on sending until
|
// A visitor found too slow is read no more; any other may go on sending until
|
||||||
// its time is up, so that the rest of the body is read and the request ends
|
// its time is up, so that Go's server can read the rest of the body and end
|
||||||
// cleanly: by Go's server, or by readRestOfBody.
|
// the request cleanly.
|
||||||
deadline := rq.requestDeadline()
|
deadline := rq.requestDeadline()
|
||||||
if status == http.StatusRequestTimeout {
|
if status == http.StatusRequestTimeout {
|
||||||
deadline = time.Now()
|
deadline = time.Now()
|
||||||
@@ -527,22 +523,6 @@ func (rq *request) stopReadingBody(deadline time.Time) {
|
|||||||
_ = rq.rc.SetReadDeadline(deadline)
|
_ = rq.rc.SetReadDeadline(deadline)
|
||||||
}
|
}
|
||||||
|
|
||||||
// readRestOfBody reads, once the visitor has been answered, the rest of a body
|
|
||||||
// that has not arrived whole, when the visitor sent Connection: close. Go's
|
|
||||||
// server reads it for any other visitor, but closes this visitor's connection
|
|
||||||
// with the rest unread: the visitor, still sending, then gets a reset and may
|
|
||||||
// never read the answer. The read ends at the deadline already set on the
|
|
||||||
// connection, and after as much as Go's server reads.
|
|
||||||
func (rq *request) readRestOfBody() {
|
|
||||||
if !rq.in.Close || rq.body == nil || rq.body.received.Load() ||
|
|
||||||
rq.out.status == 0 ||
|
|
||||||
rq.out.status == http.StatusSwitchingProtocols {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _ = io.CopyN(io.Discard, rq.in.Body, maxBodyReadAfterAnswer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// stopPassingOnBody ends the transport's reading of a visitor's body that has
|
// stopPassingOnBody ends the transport's reading of a visitor's body that has
|
||||||
// not arrived whole, and lets Go's server read the rest until deadline. Go's
|
// not arrived whole, and lets Go's server read the rest until deadline. Go's
|
||||||
// server reads it once the handler returns, but with no deadline at all if
|
// server reads it once the handler returns, but with no deadline at all if
|
||||||
|
|||||||
@@ -169,7 +169,6 @@ func newWAFTransport() *http.Transport {
|
|||||||
// it writes the request's log line and sends its report.
|
// it writes the request's log line and sends its report.
|
||||||
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
rq := h.newRequest(w, r)
|
rq := h.newRequest(w, r)
|
||||||
defer rq.readRestOfBody()
|
|
||||||
defer rq.finish(r.Context())
|
defer rq.finish(r.Context())
|
||||||
|
|
||||||
if r.Method == http.MethodGet && r.URL.Path == waf.HealthPath {
|
if r.Method == http.MethodGet && r.URL.Path == waf.HealthPath {
|
||||||
|
|||||||
Reference in new issue
Block a user