apache_dubbo/status.rs
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. 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
18use std::error::Error;
19use std::fmt;
20
21use http::HeaderValue;
22
23pub const GRPC_STATUS: &str = "grpc-status";
24pub const GRPC_MESSAGE: &str = "grpc-message";
25
26/// error codes for grpc APIs
27/// https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum Code {
30 // Not an error; returned on success
31 //
32 // HTTP Mapping: 200 OK
33 Ok = 0,
34
35 // The operation was cancelled, typically by the caller.
36 //
37 // HTTP Mapping: 499 Client Closed Request
38 Cancelled = 1,
39
40 // Unknown error. For example, this error may be returned when
41 // a `Status` value received from another address space belongs to
42 // an error space that is not known in this address space. Also
43 // errors raised by APIs that do not return enough error information
44 // may be converted to this error.
45 //
46 // HTTP Mapping: 500 INTERNAL Server Error
47 Unknown = 2,
48
49 // The client specified an invalid argument. Note that this differs
50 // from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments
51 // that are problematic regardless of the state of the system
52 // (e.g., a malformed file name).
53 //
54 // HTTP Mapping: 400 Bad Request
55 InvalidArgument = 3,
56
57 // The deadline expired before the operation could complete. For operations
58 // that change the state of the system, this error may be returned
59 // even if the operation has completed successfully. For example, a
60 // successful response from a server could have been delayed long
61 // enough for the deadline to expire.
62 //
63 // HTTP Mapping: 504 Gateway Timeout
64 DeadlineExceeded = 4,
65
66 // Some requested entity (e.g., file or directory) was not found.
67 //
68 // Note to server developers: if a request is denied for an entire class
69 // of users, such as gradual feature rollout or undocumented whitelist,
70 // `NOT_FOUND` may be used. If a request is denied for some users within
71 // a class of users, such as user-based access control, `PERMISSION_DENIED`
72 // must be used.
73 //
74 // HTTP Mapping: 404 Not Found
75 NotFound = 5,
76
77 // The entity that a client attempted to create (e.g., file or directory)
78 // already exists.
79 //
80 // HTTP Mapping: 409 Conflict
81 AlreadyExists = 6,
82
83 // The caller does not have permission to execute the specified
84 // operation. `PERMISSION_DENIED` must not be used for rejections
85 // caused by exhausting some resource (use `RESOURCE_EXHAUSTED`
86 // instead for those errors). `PERMISSION_DENIED` must not be
87 // used if the caller can not be identified (use `UNAUTHENTICATED`
88 // instead for those errors). This error code does not imply the
89 // request is valid or the requested entity exists or satisfies
90 // other pre-conditions.
91 //
92 // HTTP Mapping: 403 Forbidden
93 PermissionDenied = 7,
94
95 // Some resource has been exhausted, perhaps a per-user quota, or
96 // perhaps the entire file system is out of space.
97 //
98 // HTTP Mapping: 429 Too Many Requests
99 ResourceExhausted = 8,
100
101 // The operation was rejected because the system is not in a state
102 // required for the operation's execution. For example, the directory
103 // to be deleted is non-empty, an rmdir operation is applied to
104 // a non-directory, etc.
105 //
106 // Service implementors can use the following guidelines to decide
107 // between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`:
108 // (a) Use `UNAVAILABLE` if the client can retry just the failing call.
109 // (b) Use `ABORTED` if the client should retry at a higher level
110 // (e.g., when a client-specified test-and-set fails, indicating the
111 // client should restart a read-modify-write sequence).
112 // (c) Use `FAILED_PRECONDITION` if the client should not retry until
113 // the system state has been explicitly fixed. E.g., if an "rmdir"
114 // fails because the directory is non-empty, `FAILED_PRECONDITION`
115 // should be returned since the client should not retry unless
116 // the files are deleted from the directory.
117 //
118 // HTTP Mapping: 400 Bad Request
119 FailedPrecondition = 9,
120
121 // The operation was aborted, typically due to a concurrency issue such as
122 // a sequencer check failure or transaction abort.
123 //
124 // See the guidelines above for deciding between `FAILED_PRECONDITION`,
125 // `ABORTED`, and `UNAVAILABLE`.
126 //
127 // HTTP Mapping: 409 Conflict
128 Aborted = 10,
129
130 // The operation was attempted past the valid range. E.g., seeking or
131 // reading past end-of-file.
132 //
133 // Unlike `INVALID_ARGUMENT`, this error indicates a problem that may
134 // be fixed if the system state changes. For example, a 32-bit file
135 // system will generate `INVALID_ARGUMENT` if asked to read at an
136 // offset that is not in the range [0,2^32-1], but it will generate
137 // `OUT_OF_RANGE` if asked to read from an offset past the current
138 // file size.
139 //
140 // There is a fair bit of overlap between `FAILED_PRECONDITION` and
141 // `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific
142 // error) when it applies so that callers who are iterating through
143 // a space can easily look for an `OUT_OF_RANGE` error to detect when
144 // they are done.
145 //
146 // HTTP Mapping: 400 Bad Request
147 OutOfRange = 11,
148
149 // The operation is not implemented or is not supported/enabled in this
150 // service.
151 //
152 // HTTP Mapping: 501 Not Implemented
153 Unimplemented = 12,
154
155 // INTERNAL errors. This means that some invariants expected by the
156 // underlying system have been broken. This error code is reserved
157 // for serious errors.
158 //
159 // HTTP Mapping: 500 INTERNAL Server Error
160 Internal = 13,
161
162 // The service is currently unavailable. This is most likely a
163 // transient condition, which can be corrected by retrying with
164 // a backoff. Note that it is not always safe to retry
165 // non-idempotent operations.
166 //
167 // See the guidelines above for deciding between `FAILED_PRECONDITION`,
168 // `ABORTED`, and `UNAVAILABLE`.
169 //
170 // HTTP Mapping: 503 Service Unavailable
171 Unavailable = 14,
172
173 // Unrecoverable data loss or corruption.
174 //
175 // HTTP Mapping: 500 INTERNAL Server Error
176 DataLoss = 15,
177
178 // The request does not have valid authentication credentials for the
179 // operation.
180 //
181 // HTTP Mapping: 401 Unauthorized
182 Unauthenticated = 16,
183}
184
185impl Code {
186 pub fn from_i32(i: i32) -> Code {
187 Code::from(i)
188 }
189
190 pub fn description(&self) -> &'static str {
191 match self {
192 Code::Ok => "The operation completed successfully",
193 Code::Unknown => "Unknown error",
194 Code::Unimplemented => "Operation is not implemented or not supported",
195 Code::Internal => "Internal error",
196 _ => "aa",
197 }
198 }
199
200 pub fn to_http_header_value(&self) -> HeaderValue {
201 match *self {
202 Code::Ok => HeaderValue::from_static("0"),
203 Code::Cancelled => HeaderValue::from_static("1"),
204 Code::Unknown => HeaderValue::from_static("2"),
205 Code::InvalidArgument => HeaderValue::from_static("3"),
206 Code::DeadlineExceeded => HeaderValue::from_static("4"),
207 Code::NotFound => HeaderValue::from_static("5"),
208 Code::AlreadyExists => HeaderValue::from_static("6"),
209 Code::PermissionDenied => HeaderValue::from_static("7"),
210 Code::ResourceExhausted => HeaderValue::from_static("8"),
211 Code::FailedPrecondition => HeaderValue::from_static("9"),
212 Code::Aborted => HeaderValue::from_static("10"),
213 Code::OutOfRange => HeaderValue::from_static("11"),
214 Code::Unimplemented => HeaderValue::from_static("12"),
215 Code::Internal => HeaderValue::from_static("13"),
216 Code::Unavailable => HeaderValue::from_static("14"),
217 Code::DataLoss => HeaderValue::from_static("15"),
218 Code::Unauthenticated => HeaderValue::from_static("16"),
219 }
220 }
221}
222
223impl std::fmt::Display for Code {
224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225 std::fmt::Display::fmt(self.description(), f)
226 }
227}
228
229impl From<i32> for Code {
230 fn from(i: i32) -> Self {
231 match i {
232 0 => Code::Ok,
233 1 => Code::Cancelled,
234 2 => Code::Unknown,
235 3 => Code::InvalidArgument,
236 4 => Code::DeadlineExceeded,
237 5 => Code::NotFound,
238 6 => Code::AlreadyExists,
239 7 => Code::PermissionDenied,
240 8 => Code::ResourceExhausted,
241 9 => Code::FailedPrecondition,
242 10 => Code::Aborted,
243 11 => Code::OutOfRange,
244 12 => Code::Unimplemented,
245 13 => Code::Internal,
246 14 => Code::Unavailable,
247 15 => Code::DataLoss,
248 16 => Code::Unauthenticated,
249
250 _ => Code::Unknown,
251 }
252 }
253}
254
255#[derive(Debug, Clone)]
256pub struct Status {
257 // grpc-status
258 code: Code,
259
260 // grpc-message
261 message: String,
262}
263
264impl Status {
265 pub fn new(code: Code, message: String) -> Self {
266 Status { code, message }
267 }
268
269 pub fn with_message(self, message: String) -> Self {
270 Status { message, ..self }
271 }
272
273 pub fn from_std_erro<T: std::error::Error>(err: T) -> Self {
274 Status::new(Code::Internal, err.to_string())
275 }
276
277 pub fn from_error(err: crate::Error) -> Self {
278 Status::new(Code::Internal, err.to_string())
279 }
280
281 pub fn code(&self) -> Code {
282 self.code
283 }
284
285 pub fn to_http(&self) -> http::Response<crate::BoxBody> {
286 let (mut parts, _) = http::Response::new(()).into_parts();
287
288 parts.headers.insert(
289 http::header::CONTENT_TYPE,
290 http::HeaderValue::from_static("application/grpc"),
291 );
292
293 parts
294 .headers
295 .insert(GRPC_STATUS, self.code.to_http_header_value());
296 parts.headers.insert(
297 GRPC_MESSAGE,
298 http::HeaderValue::from_str(&self.message).unwrap(),
299 );
300
301 parts.headers.insert(
302 "grpc-accept-encoding",
303 http::HeaderValue::from_static("gzip,identity"),
304 );
305
306 http::Response::from_parts(parts, crate::empty_body())
307 }
308}
309
310impl From<std::io::Error> for Status {
311 fn from(err: std::io::Error) -> Self {
312 Status::new(crate::status::Code::Internal, err.to_string())
313 }
314}
315
316impl std::fmt::Display for Status {
317 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318 f.write_fmt(format_args!(
319 "grpc status, code: {}, message: {}",
320 self.code, self.message
321 ))
322 }
323}
324
325impl Error for Status {}
326
327#[derive(Debug)]
328pub struct DubboError {
329 message: String,
330}
331
332impl DubboError {
333 pub fn new(message: String) -> Self {
334 Self { message }
335 }
336}
337
338impl fmt::Display for DubboError {
339 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 write!(f, "Dubbo internal Error: {}", self.message)
341 }
342}
343
344impl Error for DubboError {}
345
346unsafe impl Send for DubboError {}
347
348unsafe impl Sync for DubboError {}