diff --git a/web/api/v1/api.go b/web/api/v1/api.go index f560693d4..7c4ef1908 100644 --- a/web/api/v1/api.go +++ b/web/api/v1/api.go @@ -63,6 +63,10 @@ type status string const ( statusSuccess status = "success" statusError status = "error" + + // Non-standard status code (originally introduced by nginx) for the case when a client closes + // the connection while the server is still processing the request. + statusClientClosedConnection = 499 ) type errorType string @@ -593,6 +597,10 @@ func returnAPIError(err error) *apiError { return &apiError{errorInternal, err} } + if errors.Is(err, context.Canceled) { + return &apiError{errorCanceled, err} + } + return &apiError{errorExec, err} } @@ -1599,7 +1607,9 @@ func (api *API) respondError(w http.ResponseWriter, apiErr *apiError, data inter code = http.StatusBadRequest case errorExec: code = http.StatusUnprocessableEntity - case errorCanceled, errorTimeout: + case errorCanceled: + code = statusClientClosedConnection + case errorTimeout: code = http.StatusServiceUnavailable case errorInternal: code = http.StatusInternalServerError diff --git a/web/api/v1/errors_test.go b/web/api/v1/errors_test.go index c86ceb1c9..da0d4b3f2 100644 --- a/web/api/v1/errors_test.go +++ b/web/api/v1/errors_test.go @@ -58,7 +58,7 @@ func TestApiStatusCodes(t *testing.T) { "promql.ErrQueryCanceled": { err: promql.ErrQueryCanceled("some error"), expectedString: "query was canceled", - expectedCode: http.StatusServiceUnavailable, + expectedCode: statusClientClosedConnection, }, "promql.ErrQueryTimeout": { @@ -76,7 +76,7 @@ func TestApiStatusCodes(t *testing.T) { "context.Canceled": { err: context.Canceled, expectedString: "context canceled", - expectedCode: http.StatusUnprocessableEntity, + expectedCode: statusClientClosedConnection, }, } { for k, q := range map[string]storage.SampleAndChunkQueryable{