fix(cliproxy): centralize client error status mapping and apply context cancellation/deadline HTTP codes

Closes: #4601
This commit is contained in:
Luis Pater
2026-08-08 04:53:34 +08:00
parent 37609fa179
commit 4b3cc55cdc
15 changed files with 297 additions and 59 deletions

View File

@@ -2,6 +2,7 @@
package clienterror
import (
"context"
"encoding/json"
"errors"
"net/http"
@@ -10,6 +11,10 @@ import (
"github.com/tidwall/gjson"
)
// StatusClientClosedRequest is the nginx-style status used when the client
// aborts the request before the proxy finishes (context.Canceled).
const StatusClientClosedRequest = 499
var requestFaultCodes = map[string]struct{}{
"cyber_policy": {},
"context_length_exceeded": {},
@@ -29,6 +34,40 @@ var requestFaultTypes = map[string]struct{}{
"invalid_prompt": {},
}
// HTTPStatusFromError extracts an HTTP status from err.
// Explicit StatusCode() values win. Otherwise context.Canceled maps to 499
// and context.DeadlineExceeded maps to 504. Returns 0 when unknown.
func HTTPStatusFromError(err error) int {
if err == nil {
return 0
}
type statusCoder interface {
StatusCode() int
}
var sc statusCoder
if errors.As(err, &sc) && sc != nil {
if code := sc.StatusCode(); code > 0 {
return code
}
}
if errors.Is(err, context.Canceled) {
return StatusClientClosedRequest
}
if errors.Is(err, context.DeadlineExceeded) {
return http.StatusGatewayTimeout
}
return 0
}
// HTTPStatusFromErrorOr is like HTTPStatusFromError but returns fallback when
// the error does not carry a known status.
func HTTPStatusFromErrorOr(err error, fallback int) int {
if code := HTTPStatusFromError(err); code > 0 {
return code
}
return fallback
}
// IsRequestFault reports whether an upstream failure is caused by the request
// and therefore must not rotate or penalize credentials.
func IsRequestFault(status int, err error) bool {