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