SIGN IN SIGN UP

internal: record client disconnects as 499, not 200 or 502 (#1040)

A client that hangs up before a response is written was logged and
recorded as a successful 200 with a 0-byte body: the cancellation
branches return without touching the ResponseWriter, so the access log's
seeded 200 was what got reported, and the metrics path filed it through
the "empty body, recording minimal metrics" success arm. Aborted
requests were invisible to status-code monitoring. Once the model was
loaded, the same hangup surfaced as a 502, blaming a healthy upstream.

Add swaputil.StatusClientClosedRequest (nginx's non-standard 499) as the
sentinel. It is recorded only, never written to the connection: the
client is already gone, and on a streamed response a late WriteHeader
would just log "superfluous response.WriteHeader".

- add StatusMarker/MarkClientClosed to swaputil; the response recorders
  implement it and forward outward so log and metrics agree
- derive the sentinel in the access-log and metrics middleware, which
  covers every cancellation branch instead of each one separately
- classify cancellation in the model and peer proxy ErrorHandlers so a
  client hangup no longer reports 502
- record cancelled requests with a client-disconnected ErrorMsg at debug
  level and without a capture, since an impatient caller is normal
  traffic rather than a server fault
- tag health check polls so a booting upstream is not logged as a proxy
  error once per second

A response that already started keeps the status the client actually
received.

fix: #1029
B
Benson Wong committed
4ec317589b21f58b64802c2b3371a179b9fdaa53
Parent: 23432e6
Committed by GitHub <noreply@github.com> on 8/23/2026, 6:58:47 AM