github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/util/server/error.go (about) 1 package server 2 3 import ( 4 "context" 5 "errors" 6 "net/http" 7 8 "github.com/prometheus/prometheus/promql" 9 "github.com/weaveworks/common/httpgrpc" 10 "github.com/weaveworks/common/user" 11 "google.golang.org/grpc/codes" 12 "google.golang.org/grpc/status" 13 14 "github.com/grafana/loki/pkg/logqlmodel" 15 storage_errors "github.com/grafana/loki/pkg/storage/errors" 16 "github.com/grafana/loki/pkg/util" 17 ) 18 19 // StatusClientClosedRequest is the status code for when a client request cancellation of an http request 20 const StatusClientClosedRequest = 499 21 22 const ( 23 ErrClientCanceled = "The request was cancelled by the client." 24 ErrDeadlineExceeded = "Request timed out, decrease the duration of the request or add more label matchers (prefer exact match over regex match) to reduce the amount of data processed." 25 ) 26 27 // WriteError write a go error with the correct status code. 28 func WriteError(err error, w http.ResponseWriter) { 29 status, cerr := ClientHTTPStatusAndError(err) 30 http.Error(w, cerr.Error(), status) 31 } 32 33 // ClientHTTPStatusAndError returns error and http status that is "safe" to return to client without 34 // exposing any implementation details. 35 func ClientHTTPStatusAndError(err error) (int, error) { 36 var ( 37 queryErr storage_errors.QueryError 38 promErr promql.ErrStorage 39 ) 40 41 me, ok := err.(util.MultiError) 42 if ok && me.Is(context.Canceled) { 43 return StatusClientClosedRequest, errors.New(ErrClientCanceled) 44 } 45 if ok && me.IsDeadlineExceeded() { 46 return http.StatusGatewayTimeout, errors.New(ErrDeadlineExceeded) 47 } 48 49 s, isRPC := status.FromError(err) 50 switch { 51 case errors.Is(err, context.Canceled) || 52 (errors.As(err, &promErr) && errors.Is(promErr.Err, context.Canceled)): 53 return StatusClientClosedRequest, errors.New(ErrClientCanceled) 54 case errors.Is(err, context.DeadlineExceeded) || 55 (isRPC && s.Code() == codes.DeadlineExceeded): 56 return http.StatusGatewayTimeout, errors.New(ErrDeadlineExceeded) 57 case errors.As(err, &queryErr): 58 return http.StatusBadRequest, err 59 case errors.Is(err, logqlmodel.ErrLimit) || errors.Is(err, logqlmodel.ErrParse) || errors.Is(err, logqlmodel.ErrPipeline): 60 return http.StatusBadRequest, err 61 case errors.Is(err, user.ErrNoOrgID): 62 return http.StatusBadRequest, err 63 default: 64 if grpcErr, ok := httpgrpc.HTTPResponseFromError(err); ok { 65 return int(grpcErr.Code), errors.New(string(grpcErr.Body)) 66 } 67 return http.StatusInternalServerError, err 68 } 69 }