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  }