rproxy: read the rest of a Connection: close visitor's body after an early answer (closes #373)
check / check (push) Waiting to run
check / check (push) Waiting to run
Go's server reads up to 256 KiB of a body left unread once the handler returns, but not a body with a declared length from a visitor that sent Connection: close. It closes that connection with the rest unread, and the visitor, still sending, gets a reset. rproxy now reads that rest itself, until the deadline already set on the connection, through a MaxBytesReader made with Go's own ResponseWriter. With more than 256 KiB left, that has Go's server tell the visitor it sends no more and wait 500 ms before it closes, as for any other visitor. Model: opus-5-5
This commit is contained in:
1 parent
c4bf76c966
commit
420f37576a
3 files changed
+108
-4
No files matched your search
@@ -38,6 +38,10 @@ const plainText = "text/plain; charset=utf-8"
|
||||
// and the visitor then gets a closed connection and no answer.
|
||||
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
|
||||
// SWW_RPROXY_MAX_RESPONSE_TIME_MS has run out.
|
||||
var errResponseTooLate = errors.New(
|
||||
@@ -338,8 +342,8 @@ func (rq *request) answer(status int) {
|
||||
}
|
||||
|
||||
// A visitor found too slow is read no more; any other may go on sending until
|
||||
// its time is up, so that Go's server can read the rest of the body and end
|
||||
// the request cleanly.
|
||||
// its time is up, so that the rest of the body is read and the request ends
|
||||
// cleanly: by Go's server, or by readRestOfBody.
|
||||
deadline := rq.requestDeadline()
|
||||
if status == http.StatusRequestTimeout {
|
||||
deadline = time.Now()
|
||||
@@ -523,6 +527,31 @@ func (rq *request) stopReadingBody(deadline time.Time) {
|
||||
_ = rq.rc.SetReadDeadline(deadline)
|
||||
}
|
||||
|
||||
// readRestOfBody reads the rest of the body once the visitor has been
|
||||
// answered, when the visitor sent Connection: close and the body has a declared
|
||||
// length. Go's server closes such a connection with the rest unread. The
|
||||
// visitor, still sending, gets a reset and may never read the answer. For any
|
||||
// other visitor, and for a chunked body, Go's server reads up to 256 KiB of the
|
||||
// rest itself. When more is left, it tells the visitor that it sends no more,
|
||||
// then waits 500 ms before it closes the connection. readRestOfBody does the
|
||||
// same, and its read ends at the deadline already set on the connection.
|
||||
// MaxBytesReader, made with Go's own ResponseWriter, has Go's server end the
|
||||
// connection that way when more is left.
|
||||
func (rq *request) readRestOfBody() {
|
||||
if !rq.in.Close || rq.in.ContentLength <= 0 || rq.body.received.Load() ||
|
||||
rq.out.status == 0 ||
|
||||
rq.out.status == http.StatusSwitchingProtocols {
|
||||
return
|
||||
}
|
||||
|
||||
rest := http.MaxBytesReader(
|
||||
rq.out.ResponseWriter,
|
||||
rq.in.Body,
|
||||
maxBodyReadAfterAnswer,
|
||||
)
|
||||
_, _ = io.Copy(io.Discard, rest)
|
||||
}
|
||||
|
||||
// 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
|
||||
// server reads it once the handler returns, but with no deadline at all if
|
||||
|
||||
Reference in new issue
Block a user