reverseproxy: log status 499 instead of 0 when client disconnects (#7827)
parent
ab56721a73
commit
d2e0ad1e92
|
|
@ -0,0 +1,54 @@
|
|||
package reverseproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
)
|
||||
|
||||
// TestClientDisconnectRecordsStatus verifies that when the downstream client
|
||||
// disconnects (its request context is canceled) before the upstream sends any
|
||||
// response headers, the recorded status is 499 ("client closed request")
|
||||
// rather than 0.
|
||||
func TestClientDisconnectRecordsStatus(t *testing.T) {
|
||||
// backend that blocks until the client goes away, so it never gets
|
||||
// the chance to send response headers
|
||||
gotRequest := make(chan struct{})
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
close(gotRequest)
|
||||
<-r.Context().Done()
|
||||
}))
|
||||
defer backend.Close()
|
||||
|
||||
h := minimalHandler(0, &Upstream{
|
||||
Host: new(Host),
|
||||
Dial: backend.Listener.Addr().String(),
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil).WithContext(ctx)
|
||||
req = prepareTestRequest(req)
|
||||
|
||||
rec := caddyhttp.NewResponseRecorder(httptest.NewRecorder(), nil, nil)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_ = h.ServeHTTP(rec, req, caddyhttp.HandlerFunc(func(http.ResponseWriter, *http.Request) error {
|
||||
return nil
|
||||
}))
|
||||
}()
|
||||
|
||||
<-gotRequest
|
||||
cancel()
|
||||
wg.Wait()
|
||||
|
||||
if got := rec.Status(); got != 499 {
|
||||
t.Errorf("expected status 499 after client disconnect, got %d", got)
|
||||
}
|
||||
}
|
||||
|
|
@ -699,9 +699,15 @@ func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w h
|
|||
|
||||
// proxy the request to that upstream
|
||||
proxyErr = h.reverseProxy(w, r, origReq, repl, dialInfo, next)
|
||||
if proxyErr == nil || errors.Is(proxyErr, context.Canceled) {
|
||||
// context.Canceled happens when the downstream client
|
||||
// cancels the request, which is not our failure
|
||||
if proxyErr == nil {
|
||||
return true, nil
|
||||
}
|
||||
if errors.Is(proxyErr, context.Canceled) {
|
||||
// context.Canceled happens when the downstream client cancels the
|
||||
// request, which is not our failure; don't retry or ding the upstream.
|
||||
// Record a 499 (client closed request) so the access log reflects the
|
||||
// disconnect instead of a misleading 0 status (see #7396).
|
||||
w.WriteHeader(499)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue