status.go raw

   1  /*
   2   *
   3   * Copyright 2017 gRPC authors.
   4   *
   5   * Licensed under the Apache License, Version 2.0 (the "License");
   6   * you may not use this file except in compliance with the License.
   7   * You may obtain a copy of the License at
   8   *
   9   *     http://www.apache.org/licenses/LICENSE-2.0
  10   *
  11   * Unless required by applicable law or agreed to in writing, software
  12   * distributed under the License is distributed on an "AS IS" BASIS,
  13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14   * See the License for the specific language governing permissions and
  15   * limitations under the License.
  16   *
  17   */
  18  
  19  // Package status implements errors returned by gRPC.  These errors are
  20  // serialized and transmitted on the wire between server and client, and allow
  21  // for additional data to be transmitted via the Details field in the status
  22  // proto.  gRPC service handlers should return an error created by this
  23  // package, and gRPC clients should expect a corresponding error to be
  24  // returned from the RPC call.
  25  //
  26  // This package upholds the invariants that a non-nil error may not
  27  // contain an OK code, and an OK code must result in a nil error.
  28  package status
  29  
  30  import (
  31  	"context"
  32  	"errors"
  33  	"fmt"
  34  
  35  	spb "google.golang.org/genproto/googleapis/rpc/status"
  36  
  37  	"google.golang.org/grpc/codes"
  38  	"google.golang.org/grpc/internal/status"
  39  )
  40  
  41  // Status references google.golang.org/grpc/internal/status. It represents an
  42  // RPC status code, message, and details.  It is immutable and should be
  43  // created with New, Newf, or FromProto.
  44  // https://godoc.org/google.golang.org/grpc/internal/status
  45  type Status = status.Status
  46  
  47  // New returns a Status representing c and msg.
  48  func New(c codes.Code, msg string) *Status {
  49  	return status.New(c, msg)
  50  }
  51  
  52  // Newf returns New(c, fmt.Sprintf(format, a...)).
  53  func Newf(c codes.Code, format string, a ...any) *Status {
  54  	return New(c, fmt.Sprintf(format, a...))
  55  }
  56  
  57  // Error returns an error representing c and msg.  If c is OK, returns nil.
  58  func Error(c codes.Code, msg string) error {
  59  	return New(c, msg).Err()
  60  }
  61  
  62  // Errorf returns Error(c, fmt.Sprintf(format, a...)).
  63  func Errorf(c codes.Code, format string, a ...any) error {
  64  	return Error(c, fmt.Sprintf(format, a...))
  65  }
  66  
  67  // ErrorProto returns an error representing s.  If s.Code is OK, returns nil.
  68  func ErrorProto(s *spb.Status) error {
  69  	return FromProto(s).Err()
  70  }
  71  
  72  // FromProto returns a Status representing s.
  73  func FromProto(s *spb.Status) *Status {
  74  	return status.FromProto(s)
  75  }
  76  
  77  // FromError returns a Status representation of err.
  78  //
  79  //   - If err was produced by this package or implements the method `GRPCStatus()
  80  //     *Status` and `GRPCStatus()` does not return nil, or if err wraps a type
  81  //     satisfying this, the Status from `GRPCStatus()` is returned.  For wrapped
  82  //     errors, the message returned contains the entire err.Error() text and not
  83  //     just the wrapped status. In that case, ok is true.
  84  //
  85  //   - If err is nil, a Status is returned with codes.OK and no message, and ok
  86  //     is true.
  87  //
  88  //   - If err implements the method `GRPCStatus() *Status` and `GRPCStatus()`
  89  //     returns nil (which maps to Codes.OK), or if err wraps a type
  90  //     satisfying this, a Status is returned with codes.Unknown and err's
  91  //     Error() message, and ok is false.
  92  //
  93  //   - Otherwise, err is an error not compatible with this package.  In this
  94  //     case, a Status is returned with codes.Unknown and err's Error() message,
  95  //     and ok is false.
  96  func FromError(err error) (s *Status, ok bool) {
  97  	if err == nil {
  98  		return nil, true
  99  	}
 100  	type grpcstatus interface{ GRPCStatus() *Status }
 101  	if gs, ok := err.(grpcstatus); ok {
 102  		grpcStatus := gs.GRPCStatus()
 103  		if grpcStatus == nil {
 104  			// Error has status nil, which maps to codes.OK. There
 105  			// is no sensible behavior for this, so we turn it into
 106  			// an error with codes.Unknown and discard the existing
 107  			// status.
 108  			return New(codes.Unknown, err.Error()), false
 109  		}
 110  		return grpcStatus, true
 111  	}
 112  	var gs grpcstatus
 113  	if errors.As(err, &gs) {
 114  		grpcStatus := gs.GRPCStatus()
 115  		if grpcStatus == nil {
 116  			// Error wraps an error that has status nil, which maps
 117  			// to codes.OK.  There is no sensible behavior for this,
 118  			// so we turn it into an error with codes.Unknown and
 119  			// discard the existing status.
 120  			return New(codes.Unknown, err.Error()), false
 121  		}
 122  		p := grpcStatus.Proto()
 123  		p.Message = err.Error()
 124  		return status.FromProto(p), true
 125  	}
 126  	return New(codes.Unknown, err.Error()), false
 127  }
 128  
 129  // Convert is a convenience function which removes the need to handle the
 130  // boolean return value from FromError.
 131  func Convert(err error) *Status {
 132  	s, _ := FromError(err)
 133  	return s
 134  }
 135  
 136  // Code returns the Code of the error if it is a Status error or if it wraps a
 137  // Status error. If that is not the case, it returns codes.OK if err is nil, or
 138  // codes.Unknown otherwise.
 139  func Code(err error) codes.Code {
 140  	// Don't use FromError to avoid allocation of OK status.
 141  	if err == nil {
 142  		return codes.OK
 143  	}
 144  
 145  	return Convert(err).Code()
 146  }
 147  
 148  // FromContextError converts a context error or wrapped context error into a
 149  // Status.  It returns a Status with codes.OK if err is nil, or a Status with
 150  // codes.Unknown if err is non-nil and not a context error.
 151  func FromContextError(err error) *Status {
 152  	if err == nil {
 153  		return nil
 154  	}
 155  	if errors.Is(err, context.DeadlineExceeded) {
 156  		return New(codes.DeadlineExceeded, err.Error())
 157  	}
 158  	if errors.Is(err, context.Canceled) {
 159  		return New(codes.Canceled, err.Error())
 160  	}
 161  	return New(codes.Unknown, err.Error())
 162  }
 163