blob: 641c45c6fed5114e5efa303d4fb61e2bc6160c48 [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001/*
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.
28package status
29
30import (
31 "context"
32 "errors"
33 "fmt"
34
35 "github.com/golang/protobuf/proto"
36 "github.com/golang/protobuf/ptypes"
37 spb "google.golang.org/genproto/googleapis/rpc/status"
38 "google.golang.org/grpc/codes"
39 "google.golang.org/grpc/internal"
40)
41
42func init() {
43 internal.StatusRawProto = statusRawProto
44}
45
46func statusRawProto(s *Status) *spb.Status { return s.s }
47
48// statusError is an alias of a status proto. It implements error and Status,
49// and a nil statusError should never be returned by this package.
50type statusError spb.Status
51
52func (se *statusError) Error() string {
53 p := (*spb.Status)(se)
54 return fmt.Sprintf("rpc error: code = %s desc = %s", codes.Code(p.GetCode()), p.GetMessage())
55}
56
57func (se *statusError) GRPCStatus() *Status {
58 return &Status{s: (*spb.Status)(se)}
59}
60
61// Status represents an RPC status code, message, and details. It is immutable
62// and should be created with New, Newf, or FromProto.
63type Status struct {
64 s *spb.Status
65}
66
67// Code returns the status code contained in s.
68func (s *Status) Code() codes.Code {
69 if s == nil || s.s == nil {
70 return codes.OK
71 }
72 return codes.Code(s.s.Code)
73}
74
75// Message returns the message contained in s.
76func (s *Status) Message() string {
77 if s == nil || s.s == nil {
78 return ""
79 }
80 return s.s.Message
81}
82
83// Proto returns s's status as an spb.Status proto message.
84func (s *Status) Proto() *spb.Status {
85 if s == nil {
86 return nil
87 }
88 return proto.Clone(s.s).(*spb.Status)
89}
90
91// Err returns an immutable error representing s; returns nil if s.Code() is
92// OK.
93func (s *Status) Err() error {
94 if s.Code() == codes.OK {
95 return nil
96 }
97 return (*statusError)(s.s)
98}
99
100// New returns a Status representing c and msg.
101func New(c codes.Code, msg string) *Status {
102 return &Status{s: &spb.Status{Code: int32(c), Message: msg}}
103}
104
105// Newf returns New(c, fmt.Sprintf(format, a...)).
106func Newf(c codes.Code, format string, a ...interface{}) *Status {
107 return New(c, fmt.Sprintf(format, a...))
108}
109
110// Error returns an error representing c and msg. If c is OK, returns nil.
111func Error(c codes.Code, msg string) error {
112 return New(c, msg).Err()
113}
114
115// Errorf returns Error(c, fmt.Sprintf(format, a...)).
116func Errorf(c codes.Code, format string, a ...interface{}) error {
117 return Error(c, fmt.Sprintf(format, a...))
118}
119
120// ErrorProto returns an error representing s. If s.Code is OK, returns nil.
121func ErrorProto(s *spb.Status) error {
122 return FromProto(s).Err()
123}
124
125// FromProto returns a Status representing s.
126func FromProto(s *spb.Status) *Status {
127 return &Status{s: proto.Clone(s).(*spb.Status)}
128}
129
130// FromError returns a Status representing err if it was produced from this
131// package or has a method `GRPCStatus() *Status`. Otherwise, ok is false and a
132// Status is returned with codes.Unknown and the original error message.
133func FromError(err error) (s *Status, ok bool) {
134 if err == nil {
135 return &Status{s: &spb.Status{Code: int32(codes.OK)}}, true
136 }
137 if se, ok := err.(interface {
138 GRPCStatus() *Status
139 }); ok {
140 return se.GRPCStatus(), true
141 }
142 return New(codes.Unknown, err.Error()), false
143}
144
145// Convert is a convenience function which removes the need to handle the
146// boolean return value from FromError.
147func Convert(err error) *Status {
148 s, _ := FromError(err)
149 return s
150}
151
152// WithDetails returns a new status with the provided details messages appended to the status.
153// If any errors are encountered, it returns nil and the first error encountered.
154func (s *Status) WithDetails(details ...proto.Message) (*Status, error) {
155 if s.Code() == codes.OK {
156 return nil, errors.New("no error details for status with code OK")
157 }
158 // s.Code() != OK implies that s.Proto() != nil.
159 p := s.Proto()
160 for _, detail := range details {
161 any, err := ptypes.MarshalAny(detail)
162 if err != nil {
163 return nil, err
164 }
165 p.Details = append(p.Details, any)
166 }
167 return &Status{s: p}, nil
168}
169
170// Details returns a slice of details messages attached to the status.
171// If a detail cannot be decoded, the error is returned in place of the detail.
172func (s *Status) Details() []interface{} {
173 if s == nil || s.s == nil {
174 return nil
175 }
176 details := make([]interface{}, 0, len(s.s.Details))
177 for _, any := range s.s.Details {
178 detail := &ptypes.DynamicAny{}
179 if err := ptypes.UnmarshalAny(any, detail); err != nil {
180 details = append(details, err)
181 continue
182 }
183 details = append(details, detail.Message)
184 }
185 return details
186}
187
188// Code returns the Code of the error if it is a Status error, codes.OK if err
189// is nil, or codes.Unknown otherwise.
190func Code(err error) codes.Code {
191 // Don't use FromError to avoid allocation of OK status.
192 if err == nil {
193 return codes.OK
194 }
195 if se, ok := err.(interface {
196 GRPCStatus() *Status
197 }); ok {
198 return se.GRPCStatus().Code()
199 }
200 return codes.Unknown
201}
202
203// FromContextError converts a context error into a Status. It returns a
204// Status with codes.OK if err is nil, or a Status with codes.Unknown if err is
205// non-nil and not a context error.
206func FromContextError(err error) *Status {
207 switch err {
208 case nil:
209 return New(codes.OK, "")
210 case context.DeadlineExceeded:
211 return New(codes.DeadlineExceeded, err.Error())
212 case context.Canceled:
213 return New(codes.Canceled, err.Error())
214 default:
215 return New(codes.Unknown, err.Error())
216 }
217}