1 use crate::body::BoxBody;
2 use crate::metadata::MetadataMap;
3 use base64::Engine as _;
4 use bytes::Bytes;
5 use http::header::{HeaderMap, HeaderValue};
6 use percent_encoding::{percent_decode, percent_encode, AsciiSet, CONTROLS};
7 use std::{borrow::Cow, error::Error, fmt, sync::Arc};
8 use tracing::{debug, trace, warn};
9 
10 const ENCODING_SET: &AsciiSet = &CONTROLS
11     .add(b' ')
12     .add(b'"')
13     .add(b'#')
14     .add(b'<')
15     .add(b'>')
16     .add(b'`')
17     .add(b'?')
18     .add(b'{')
19     .add(b'}');
20 
21 const GRPC_STATUS_HEADER_CODE: &str = "grpc-status";
22 const GRPC_STATUS_MESSAGE_HEADER: &str = "grpc-message";
23 const GRPC_STATUS_DETAILS_HEADER: &str = "grpc-status-details-bin";
24 
25 /// A gRPC status describing the result of an RPC call.
26 ///
27 /// Values can be created using the `new` function or one of the specialized
28 /// associated functions.
29 /// ```rust
30 /// # use tonic::{Status, Code};
31 /// let status1 = Status::new(Code::InvalidArgument, "name is invalid");
32 /// let status2 = Status::invalid_argument("name is invalid");
33 ///
34 /// assert_eq!(status1.code(), Code::InvalidArgument);
35 /// assert_eq!(status1.code(), status2.code());
36 /// ```
37 #[derive(Clone)]
38 pub struct Status {
39     /// The gRPC status code, found in the `grpc-status` header.
40     code: Code,
41     /// A relevant error message, found in the `grpc-message` header.
42     message: String,
43     /// Binary opaque details, found in the `grpc-status-details-bin` header.
44     details: Bytes,
45     /// Custom metadata, found in the user-defined headers.
46     /// If the metadata contains any headers with names reserved either by the gRPC spec
47     /// or by `Status` fields above, they will be ignored.
48     metadata: MetadataMap,
49     /// Optional underlying error.
50     source: Option<Arc<dyn Error + Send + Sync + 'static>>,
51 }
52 
53 /// gRPC status codes used by [`Status`].
54 ///
55 /// These variants match the [gRPC status codes].
56 ///
57 /// [gRPC status codes]: https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc
58 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
59 pub enum Code {
60     /// The operation completed successfully.
61     Ok = 0,
62 
63     /// The operation was cancelled.
64     Cancelled = 1,
65 
66     /// Unknown error.
67     Unknown = 2,
68 
69     /// Client specified an invalid argument.
70     InvalidArgument = 3,
71 
72     /// Deadline expired before operation could complete.
73     DeadlineExceeded = 4,
74 
75     /// Some requested entity was not found.
76     NotFound = 5,
77 
78     /// Some entity that we attempted to create already exists.
79     AlreadyExists = 6,
80 
81     /// The caller does not have permission to execute the specified operation.
82     PermissionDenied = 7,
83 
84     /// Some resource has been exhausted.
85     ResourceExhausted = 8,
86 
87     /// The system is not in a state required for the operation's execution.
88     FailedPrecondition = 9,
89 
90     /// The operation was aborted.
91     Aborted = 10,
92 
93     /// Operation was attempted past the valid range.
94     OutOfRange = 11,
95 
96     /// Operation is not implemented or not supported.
97     Unimplemented = 12,
98 
99     /// Internal error.
100     Internal = 13,
101 
102     /// The service is currently unavailable.
103     Unavailable = 14,
104 
105     /// Unrecoverable data loss or corruption.
106     DataLoss = 15,
107 
108     /// The request does not have valid authentication credentials
109     Unauthenticated = 16,
110 }
111 
112 impl Code {
113     /// Get description of this `Code`.
114     /// ```
115     /// fn make_grpc_request() -> tonic::Code {
116     ///     // ...
117     ///     tonic::Code::Ok
118     /// }
119     /// let code = make_grpc_request();
120     /// println!("Operation completed. Human readable description: {}", code.description());
121     /// ```
122     /// If you only need description in `println`, `format`, `log` and other
123     /// formatting contexts, you may want to use `Display` impl for `Code`
124     /// instead.
description(&self) -> &'static str125     pub fn description(&self) -> &'static str {
126         match self {
127             Code::Ok => "The operation completed successfully",
128             Code::Cancelled => "The operation was cancelled",
129             Code::Unknown => "Unknown error",
130             Code::InvalidArgument => "Client specified an invalid argument",
131             Code::DeadlineExceeded => "Deadline expired before operation could complete",
132             Code::NotFound => "Some requested entity was not found",
133             Code::AlreadyExists => "Some entity that we attempted to create already exists",
134             Code::PermissionDenied => {
135                 "The caller does not have permission to execute the specified operation"
136             }
137             Code::ResourceExhausted => "Some resource has been exhausted",
138             Code::FailedPrecondition => {
139                 "The system is not in a state required for the operation's execution"
140             }
141             Code::Aborted => "The operation was aborted",
142             Code::OutOfRange => "Operation was attempted past the valid range",
143             Code::Unimplemented => "Operation is not implemented or not supported",
144             Code::Internal => "Internal error",
145             Code::Unavailable => "The service is currently unavailable",
146             Code::DataLoss => "Unrecoverable data loss or corruption",
147             Code::Unauthenticated => "The request does not have valid authentication credentials",
148         }
149     }
150 }
151 
152 impl std::fmt::Display for Code {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result153     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154         std::fmt::Display::fmt(self.description(), f)
155     }
156 }
157 
158 // ===== impl Status =====
159 
160 impl Status {
161     /// Create a new `Status` with the associated code and message.
new(code: Code, message: impl Into<String>) -> Status162     pub fn new(code: Code, message: impl Into<String>) -> Status {
163         Status {
164             code,
165             message: message.into(),
166             details: Bytes::new(),
167             metadata: MetadataMap::new(),
168             source: None,
169         }
170     }
171 
172     /// The operation completed successfully.
ok(message: impl Into<String>) -> Status173     pub fn ok(message: impl Into<String>) -> Status {
174         Status::new(Code::Ok, message)
175     }
176 
177     /// The operation was cancelled (typically by the caller).
cancelled(message: impl Into<String>) -> Status178     pub fn cancelled(message: impl Into<String>) -> Status {
179         Status::new(Code::Cancelled, message)
180     }
181 
182     /// Unknown error. An example of where this error may be returned is if a
183     /// `Status` value received from another address space belongs to an error-space
184     /// that is not known in this address space. Also errors raised by APIs that
185     /// do not return enough error information may be converted to this error.
unknown(message: impl Into<String>) -> Status186     pub fn unknown(message: impl Into<String>) -> Status {
187         Status::new(Code::Unknown, message)
188     }
189 
190     /// Client specified an invalid argument. Note that this differs from
191     /// `FailedPrecondition`. `InvalidArgument` indicates arguments that are
192     /// problematic regardless of the state of the system (e.g., a malformed file
193     /// name).
invalid_argument(message: impl Into<String>) -> Status194     pub fn invalid_argument(message: impl Into<String>) -> Status {
195         Status::new(Code::InvalidArgument, message)
196     }
197 
198     /// Deadline expired before operation could complete. For operations that
199     /// change the state of the system, this error may be returned even if the
200     /// operation has completed successfully. For example, a successful response
201     /// from a server could have been delayed long enough for the deadline to
202     /// expire.
deadline_exceeded(message: impl Into<String>) -> Status203     pub fn deadline_exceeded(message: impl Into<String>) -> Status {
204         Status::new(Code::DeadlineExceeded, message)
205     }
206 
207     /// Some requested entity (e.g., file or directory) was not found.
not_found(message: impl Into<String>) -> Status208     pub fn not_found(message: impl Into<String>) -> Status {
209         Status::new(Code::NotFound, message)
210     }
211 
212     /// Some entity that we attempted to create (e.g., file or directory) already
213     /// exists.
already_exists(message: impl Into<String>) -> Status214     pub fn already_exists(message: impl Into<String>) -> Status {
215         Status::new(Code::AlreadyExists, message)
216     }
217 
218     /// The caller does not have permission to execute the specified operation.
219     /// `PermissionDenied` must not be used for rejections caused by exhausting
220     /// some resource (use `ResourceExhausted` instead for those errors).
221     /// `PermissionDenied` must not be used if the caller cannot be identified
222     /// (use `Unauthenticated` instead for those errors).
permission_denied(message: impl Into<String>) -> Status223     pub fn permission_denied(message: impl Into<String>) -> Status {
224         Status::new(Code::PermissionDenied, message)
225     }
226 
227     /// Some resource has been exhausted, perhaps a per-user quota, or perhaps
228     /// the entire file system is out of space.
resource_exhausted(message: impl Into<String>) -> Status229     pub fn resource_exhausted(message: impl Into<String>) -> Status {
230         Status::new(Code::ResourceExhausted, message)
231     }
232 
233     /// Operation was rejected because the system is not in a state required for
234     /// the operation's execution. For example, directory to be deleted may be
235     /// non-empty, an rmdir operation is applied to a non-directory, etc.
236     ///
237     /// A litmus test that may help a service implementor in deciding between
238     /// `FailedPrecondition`, `Aborted`, and `Unavailable`:
239     /// (a) Use `Unavailable` if the client can retry just the failing call.
240     /// (b) Use `Aborted` if the client should retry at a higher-level (e.g.,
241     ///     restarting a read-modify-write sequence).
242     /// (c) Use `FailedPrecondition` if the client should not retry until the
243     ///     system state has been explicitly fixed.  E.g., if an "rmdir" fails
244     ///     because the directory is non-empty, `FailedPrecondition` should be
245     ///     returned since the client should not retry unless they have first
246     ///     fixed up the directory by deleting files from it.
failed_precondition(message: impl Into<String>) -> Status247     pub fn failed_precondition(message: impl Into<String>) -> Status {
248         Status::new(Code::FailedPrecondition, message)
249     }
250 
251     /// The operation was aborted, typically due to a concurrency issue like
252     /// sequencer check failures, transaction aborts, etc.
253     ///
254     /// See litmus test above for deciding between `FailedPrecondition`,
255     /// `Aborted`, and `Unavailable`.
aborted(message: impl Into<String>) -> Status256     pub fn aborted(message: impl Into<String>) -> Status {
257         Status::new(Code::Aborted, message)
258     }
259 
260     /// Operation was attempted past the valid range. E.g., seeking or reading
261     /// past end of file.
262     ///
263     /// Unlike `InvalidArgument`, this error indicates a problem that may be
264     /// fixed if the system state changes. For example, a 32-bit file system will
265     /// generate `InvalidArgument if asked to read at an offset that is not in the
266     /// range [0,2^32-1], but it will generate `OutOfRange` if asked to read from
267     /// an offset past the current file size.
268     ///
269     /// There is a fair bit of overlap between `FailedPrecondition` and
270     /// `OutOfRange`. We recommend using `OutOfRange` (the more specific error)
271     /// when it applies so that callers who are iterating through a space can
272     /// easily look for an `OutOfRange` error to detect when they are done.
out_of_range(message: impl Into<String>) -> Status273     pub fn out_of_range(message: impl Into<String>) -> Status {
274         Status::new(Code::OutOfRange, message)
275     }
276 
277     /// Operation is not implemented or not supported/enabled in this service.
unimplemented(message: impl Into<String>) -> Status278     pub fn unimplemented(message: impl Into<String>) -> Status {
279         Status::new(Code::Unimplemented, message)
280     }
281 
282     /// Internal errors. Means some invariants expected by underlying system has
283     /// been broken. If you see one of these errors, something is very broken.
internal(message: impl Into<String>) -> Status284     pub fn internal(message: impl Into<String>) -> Status {
285         Status::new(Code::Internal, message)
286     }
287 
288     /// The service is currently unavailable.  This is a most likely a transient
289     /// condition and may be corrected by retrying with a back-off.
290     ///
291     /// See litmus test above for deciding between `FailedPrecondition`,
292     /// `Aborted`, and `Unavailable`.
unavailable(message: impl Into<String>) -> Status293     pub fn unavailable(message: impl Into<String>) -> Status {
294         Status::new(Code::Unavailable, message)
295     }
296 
297     /// Unrecoverable data loss or corruption.
data_loss(message: impl Into<String>) -> Status298     pub fn data_loss(message: impl Into<String>) -> Status {
299         Status::new(Code::DataLoss, message)
300     }
301 
302     /// The request does not have valid authentication credentials for the
303     /// operation.
unauthenticated(message: impl Into<String>) -> Status304     pub fn unauthenticated(message: impl Into<String>) -> Status {
305         Status::new(Code::Unauthenticated, message)
306     }
307 
308     #[cfg_attr(not(feature = "transport"), allow(dead_code))]
from_error_generic( err: impl Into<Box<dyn Error + Send + Sync + 'static>>, ) -> Status309     pub(crate) fn from_error_generic(
310         err: impl Into<Box<dyn Error + Send + Sync + 'static>>,
311     ) -> Status {
312         Self::from_error(err.into())
313     }
314 
315     /// Create a `Status` from various types of `Error`.
316     ///
317     /// Inspects the error source chain for recognizable errors, including statuses, HTTP2, and
318     /// hyper, and attempts to maps them to a `Status`, or else returns an Unknown `Status`.
319     #[cfg_attr(not(feature = "transport"), allow(dead_code))]
from_error(err: Box<dyn Error + Send + Sync + 'static>) -> Status320     pub fn from_error(err: Box<dyn Error + Send + Sync + 'static>) -> Status {
321         Status::try_from_error(err).unwrap_or_else(|err| {
322             let mut status = Status::new(Code::Unknown, err.to_string());
323             status.source = Some(err.into());
324             status
325         })
326     }
327 
328     /// Create a `Status` from various types of `Error`.
329     ///
330     /// Returns the error if a status could not be created.
331     ///
332     /// # Downcast stability
333     /// This function does not provide any stability guarantees around how it will downcast errors into
334     /// status codes.
try_from_error( err: Box<dyn Error + Send + Sync + 'static>, ) -> Result<Status, Box<dyn Error + Send + Sync + 'static>>335     pub fn try_from_error(
336         err: Box<dyn Error + Send + Sync + 'static>,
337     ) -> Result<Status, Box<dyn Error + Send + Sync + 'static>> {
338         let err = match err.downcast::<Status>() {
339             Ok(status) => {
340                 return Ok(*status);
341             }
342             Err(err) => err,
343         };
344 
345         #[cfg(feature = "transport")]
346         let err = match err.downcast::<h2::Error>() {
347             Ok(h2) => {
348                 return Ok(Status::from_h2_error(h2));
349             }
350             Err(err) => err,
351         };
352 
353         if let Some(mut status) = find_status_in_source_chain(&*err) {
354             status.source = Some(err.into());
355             return Ok(status);
356         }
357 
358         Err(err)
359     }
360 
361     // FIXME: bubble this into `transport` and expose generic http2 reasons.
362     #[cfg(feature = "transport")]
from_h2_error(err: Box<h2::Error>) -> Status363     fn from_h2_error(err: Box<h2::Error>) -> Status {
364         let code = Self::code_from_h2(&err);
365 
366         let mut status = Self::new(code, format!("h2 protocol error: {}", err));
367         status.source = Some(Arc::new(*err));
368         status
369     }
370 
371     #[cfg(feature = "transport")]
code_from_h2(err: &h2::Error) -> Code372     fn code_from_h2(err: &h2::Error) -> Code {
373         // See https://github.com/grpc/grpc/blob/3977c30/doc/PROTOCOL-HTTP2.md#errors
374         match err.reason() {
375             Some(h2::Reason::NO_ERROR)
376             | Some(h2::Reason::PROTOCOL_ERROR)
377             | Some(h2::Reason::INTERNAL_ERROR)
378             | Some(h2::Reason::FLOW_CONTROL_ERROR)
379             | Some(h2::Reason::SETTINGS_TIMEOUT)
380             | Some(h2::Reason::COMPRESSION_ERROR)
381             | Some(h2::Reason::CONNECT_ERROR) => Code::Internal,
382             Some(h2::Reason::REFUSED_STREAM) => Code::Unavailable,
383             Some(h2::Reason::CANCEL) => Code::Cancelled,
384             Some(h2::Reason::ENHANCE_YOUR_CALM) => Code::ResourceExhausted,
385             Some(h2::Reason::INADEQUATE_SECURITY) => Code::PermissionDenied,
386 
387             _ => Code::Unknown,
388         }
389     }
390 
391     #[cfg(feature = "transport")]
to_h2_error(&self) -> h2::Error392     fn to_h2_error(&self) -> h2::Error {
393         // conservatively transform to h2 error codes...
394         let reason = match self.code {
395             Code::Cancelled => h2::Reason::CANCEL,
396             _ => h2::Reason::INTERNAL_ERROR,
397         };
398 
399         reason.into()
400     }
401 
402     /// Handles hyper errors specifically, which expose a number of different parameters about the
403     /// http stream's error: https://docs.rs/hyper/0.14.11/hyper/struct.Error.html.
404     ///
405     /// Returns Some if there's a way to handle the error, or None if the information from this
406     /// hyper error, but perhaps not its source, should be ignored.
407     #[cfg(feature = "transport")]
from_hyper_error(err: &hyper::Error) -> Option<Status>408     fn from_hyper_error(err: &hyper::Error) -> Option<Status> {
409         // is_timeout results from hyper's keep-alive logic
410         // (https://docs.rs/hyper/0.14.11/src/hyper/error.rs.html#192-194).  Per the grpc spec
411         // > An expired client initiated PING will cause all calls to be closed with an UNAVAILABLE
412         // > status. Note that the frequency of PINGs is highly dependent on the network
413         // > environment, implementations are free to adjust PING frequency based on network and
414         // > application requirements, which is why it's mapped to unavailable here.
415         //
416         // Likewise, if we are unable to connect to the server, map this to UNAVAILABLE.  This is
417         // consistent with the behavior of a C++ gRPC client when the server is not running, and
418         // matches the spec of:
419         // > The service is currently unavailable. This is most likely a transient condition that
420         // > can be corrected if retried with a backoff.
421         if err.is_timeout() || err.is_connect() {
422             return Some(Status::unavailable(err.to_string()));
423         }
424 
425         if let Some(h2_err) = err.source().and_then(|e| e.downcast_ref::<h2::Error>()) {
426             let code = Status::code_from_h2(h2_err);
427             let status = Self::new(code, format!("h2 protocol error: {}", err));
428 
429             return Some(status);
430         }
431 
432         None
433     }
434 
map_error<E>(err: E) -> Status where E: Into<Box<dyn Error + Send + Sync>>,435     pub(crate) fn map_error<E>(err: E) -> Status
436     where
437         E: Into<Box<dyn Error + Send + Sync>>,
438     {
439         let err: Box<dyn Error + Send + Sync> = err.into();
440         Status::from_error(err)
441     }
442 
443     /// Extract a `Status` from a hyper `HeaderMap`.
from_header_map(header_map: &HeaderMap) -> Option<Status>444     pub fn from_header_map(header_map: &HeaderMap) -> Option<Status> {
445         header_map.get(GRPC_STATUS_HEADER_CODE).map(|code| {
446             let code = Code::from_bytes(code.as_ref());
447             let error_message = header_map
448                 .get(GRPC_STATUS_MESSAGE_HEADER)
449                 .map(|header| {
450                     percent_decode(header.as_bytes())
451                         .decode_utf8()
452                         .map(|cow| cow.to_string())
453                 })
454                 .unwrap_or_else(|| Ok(String::new()));
455 
456             let details = header_map
457                 .get(GRPC_STATUS_DETAILS_HEADER)
458                 .map(|h| {
459                     crate::util::base64::STANDARD
460                         .decode(h.as_bytes())
461                         .expect("Invalid status header, expected base64 encoded value")
462                 })
463                 .map(Bytes::from)
464                 .unwrap_or_default();
465 
466             let mut other_headers = header_map.clone();
467             other_headers.remove(GRPC_STATUS_HEADER_CODE);
468             other_headers.remove(GRPC_STATUS_MESSAGE_HEADER);
469             other_headers.remove(GRPC_STATUS_DETAILS_HEADER);
470 
471             match error_message {
472                 Ok(message) => Status {
473                     code,
474                     message,
475                     details,
476                     metadata: MetadataMap::from_headers(other_headers),
477                     source: None,
478                 },
479                 Err(err) => {
480                     warn!("Error deserializing status message header: {}", err);
481                     Status {
482                         code: Code::Unknown,
483                         message: format!("Error deserializing status message header: {}", err),
484                         details,
485                         metadata: MetadataMap::from_headers(other_headers),
486                         source: None,
487                     }
488                 }
489             }
490         })
491     }
492 
493     /// Get the gRPC `Code` of this `Status`.
code(&self) -> Code494     pub fn code(&self) -> Code {
495         self.code
496     }
497 
498     /// Get the text error message of this `Status`.
message(&self) -> &str499     pub fn message(&self) -> &str {
500         &self.message
501     }
502 
503     /// Get the opaque error details of this `Status`.
details(&self) -> &[u8]504     pub fn details(&self) -> &[u8] {
505         &self.details
506     }
507 
508     /// Get a reference to the custom metadata.
metadata(&self) -> &MetadataMap509     pub fn metadata(&self) -> &MetadataMap {
510         &self.metadata
511     }
512 
513     /// Get a mutable reference to the custom metadata.
metadata_mut(&mut self) -> &mut MetadataMap514     pub fn metadata_mut(&mut self) -> &mut MetadataMap {
515         &mut self.metadata
516     }
517 
to_header_map(&self) -> Result<HeaderMap, Self>518     pub(crate) fn to_header_map(&self) -> Result<HeaderMap, Self> {
519         let mut header_map = HeaderMap::with_capacity(3 + self.metadata.len());
520         self.add_header(&mut header_map)?;
521         Ok(header_map)
522     }
523 
524     /// Add headers from this `Status` into `header_map`.
add_header(&self, header_map: &mut HeaderMap) -> Result<(), Self>525     pub fn add_header(&self, header_map: &mut HeaderMap) -> Result<(), Self> {
526         header_map.extend(self.metadata.clone().into_sanitized_headers());
527 
528         header_map.insert(GRPC_STATUS_HEADER_CODE, self.code.to_header_value());
529 
530         if !self.message.is_empty() {
531             let to_write = Bytes::copy_from_slice(
532                 Cow::from(percent_encode(self.message().as_bytes(), ENCODING_SET)).as_bytes(),
533             );
534 
535             header_map.insert(
536                 GRPC_STATUS_MESSAGE_HEADER,
537                 HeaderValue::from_maybe_shared(to_write).map_err(invalid_header_value_byte)?,
538             );
539         }
540 
541         if !self.details.is_empty() {
542             let details = crate::util::base64::STANDARD_NO_PAD.encode(&self.details[..]);
543 
544             header_map.insert(
545                 GRPC_STATUS_DETAILS_HEADER,
546                 HeaderValue::from_maybe_shared(details).map_err(invalid_header_value_byte)?,
547             );
548         }
549 
550         Ok(())
551     }
552 
553     /// Create a new `Status` with the associated code, message, and binary details field.
with_details(code: Code, message: impl Into<String>, details: Bytes) -> Status554     pub fn with_details(code: Code, message: impl Into<String>, details: Bytes) -> Status {
555         Self::with_details_and_metadata(code, message, details, MetadataMap::new())
556     }
557 
558     /// Create a new `Status` with the associated code, message, and custom metadata
with_metadata(code: Code, message: impl Into<String>, metadata: MetadataMap) -> Status559     pub fn with_metadata(code: Code, message: impl Into<String>, metadata: MetadataMap) -> Status {
560         Self::with_details_and_metadata(code, message, Bytes::new(), metadata)
561     }
562 
563     /// Create a new `Status` with the associated code, message, binary details field and custom metadata
with_details_and_metadata( code: Code, message: impl Into<String>, details: Bytes, metadata: MetadataMap, ) -> Status564     pub fn with_details_and_metadata(
565         code: Code,
566         message: impl Into<String>,
567         details: Bytes,
568         metadata: MetadataMap,
569     ) -> Status {
570         Status {
571             code,
572             message: message.into(),
573             details,
574             metadata,
575             source: None,
576         }
577     }
578 
579     /// Add a source error to this status.
set_source(&mut self, source: Arc<dyn Error + Send + Sync + 'static>) -> &mut Status580     pub fn set_source(&mut self, source: Arc<dyn Error + Send + Sync + 'static>) -> &mut Status {
581         self.source = Some(source);
582         self
583     }
584 
585     #[allow(clippy::wrong_self_convention)]
586     /// Build an `http::Response` from the given `Status`.
to_http(self) -> http::Response<BoxBody>587     pub fn to_http(self) -> http::Response<BoxBody> {
588         let (mut parts, _body) = http::Response::new(()).into_parts();
589 
590         parts.headers.insert(
591             http::header::CONTENT_TYPE,
592             http::header::HeaderValue::from_static("application/grpc"),
593         );
594 
595         self.add_header(&mut parts.headers).unwrap();
596 
597         http::Response::from_parts(parts, crate::body::empty_body())
598     }
599 }
600 
find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option<Status>601 fn find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option<Status> {
602     let mut source = Some(err);
603 
604     while let Some(err) = source {
605         if let Some(status) = err.downcast_ref::<Status>() {
606             return Some(Status {
607                 code: status.code,
608                 message: status.message.clone(),
609                 details: status.details.clone(),
610                 metadata: status.metadata.clone(),
611                 // Since `Status` is not `Clone`, any `source` on the original Status
612                 // cannot be cloned so must remain with the original `Status`.
613                 source: None,
614             });
615         }
616 
617         #[cfg(feature = "transport")]
618         if let Some(timeout) = err.downcast_ref::<crate::transport::TimeoutExpired>() {
619             return Some(Status::cancelled(timeout.to_string()));
620         }
621 
622         #[cfg(feature = "transport")]
623         if let Some(hyper) = err
624             .downcast_ref::<hyper::Error>()
625             .and_then(Status::from_hyper_error)
626         {
627             return Some(hyper);
628         }
629 
630         source = err.source();
631     }
632 
633     None
634 }
635 
636 impl fmt::Debug for Status {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result637     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638         // A manual impl to reduce the noise of frequently empty fields.
639         let mut builder = f.debug_struct("Status");
640 
641         builder.field("code", &self.code);
642 
643         if !self.message.is_empty() {
644             builder.field("message", &self.message);
645         }
646 
647         if !self.details.is_empty() {
648             builder.field("details", &self.details);
649         }
650 
651         if !self.metadata.is_empty() {
652             builder.field("metadata", &self.metadata);
653         }
654 
655         builder.field("source", &self.source);
656 
657         builder.finish()
658     }
659 }
660 
invalid_header_value_byte<Error: fmt::Display>(err: Error) -> Status661 fn invalid_header_value_byte<Error: fmt::Display>(err: Error) -> Status {
662     debug!("Invalid header: {}", err);
663     Status::new(
664         Code::Internal,
665         "Couldn't serialize non-text grpc status header".to_string(),
666     )
667 }
668 
669 #[cfg(feature = "transport")]
670 impl From<h2::Error> for Status {
from(err: h2::Error) -> Self671     fn from(err: h2::Error) -> Self {
672         Status::from_h2_error(Box::new(err))
673     }
674 }
675 
676 #[cfg(feature = "transport")]
677 impl From<Status> for h2::Error {
from(status: Status) -> Self678     fn from(status: Status) -> Self {
679         status.to_h2_error()
680     }
681 }
682 
683 impl From<std::io::Error> for Status {
from(err: std::io::Error) -> Self684     fn from(err: std::io::Error) -> Self {
685         use std::io::ErrorKind;
686         let code = match err.kind() {
687             ErrorKind::BrokenPipe
688             | ErrorKind::WouldBlock
689             | ErrorKind::WriteZero
690             | ErrorKind::Interrupted => Code::Internal,
691             ErrorKind::ConnectionRefused
692             | ErrorKind::ConnectionReset
693             | ErrorKind::NotConnected
694             | ErrorKind::AddrInUse
695             | ErrorKind::AddrNotAvailable => Code::Unavailable,
696             ErrorKind::AlreadyExists => Code::AlreadyExists,
697             ErrorKind::ConnectionAborted => Code::Aborted,
698             ErrorKind::InvalidData => Code::DataLoss,
699             ErrorKind::InvalidInput => Code::InvalidArgument,
700             ErrorKind::NotFound => Code::NotFound,
701             ErrorKind::PermissionDenied => Code::PermissionDenied,
702             ErrorKind::TimedOut => Code::DeadlineExceeded,
703             ErrorKind::UnexpectedEof => Code::OutOfRange,
704             _ => Code::Unknown,
705         };
706         Status::new(code, err.to_string())
707     }
708 }
709 
710 impl fmt::Display for Status {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result711     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
712         write!(
713             f,
714             "status: {:?}, message: {:?}, details: {:?}, metadata: {:?}",
715             self.code(),
716             self.message(),
717             self.details(),
718             self.metadata(),
719         )
720     }
721 }
722 
723 impl Error for Status {
source(&self) -> Option<&(dyn Error + 'static)>724     fn source(&self) -> Option<&(dyn Error + 'static)> {
725         self.source.as_ref().map(|err| (&**err) as _)
726     }
727 }
728 
729 ///
730 /// Take the `Status` value from `trailers` if it is available, else from `status_code`.
731 ///
infer_grpc_status( trailers: Option<&HeaderMap>, status_code: http::StatusCode, ) -> Result<(), Option<Status>>732 pub(crate) fn infer_grpc_status(
733     trailers: Option<&HeaderMap>,
734     status_code: http::StatusCode,
735 ) -> Result<(), Option<Status>> {
736     if let Some(trailers) = trailers {
737         if let Some(status) = Status::from_header_map(trailers) {
738             if status.code() == Code::Ok {
739                 return Ok(());
740             } else {
741                 return Err(status.into());
742             }
743         }
744     }
745     trace!("trailers missing grpc-status");
746     let code = match status_code {
747         // Borrowed from https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md
748         http::StatusCode::BAD_REQUEST => Code::Internal,
749         http::StatusCode::UNAUTHORIZED => Code::Unauthenticated,
750         http::StatusCode::FORBIDDEN => Code::PermissionDenied,
751         http::StatusCode::NOT_FOUND => Code::Unimplemented,
752         http::StatusCode::TOO_MANY_REQUESTS
753         | http::StatusCode::BAD_GATEWAY
754         | http::StatusCode::SERVICE_UNAVAILABLE
755         | http::StatusCode::GATEWAY_TIMEOUT => Code::Unavailable,
756         // We got a 200 but no trailers, we can infer that this request is finished.
757         //
758         // This can happen when a streaming response sends two Status but
759         // gRPC requires that we end the stream after the first status.
760         //
761         // https://github.com/hyperium/tonic/issues/681
762         http::StatusCode::OK => return Err(None),
763         _ => Code::Unknown,
764     };
765 
766     let msg = format!(
767         "grpc-status header missing, mapped from HTTP status code {}",
768         status_code.as_u16(),
769     );
770     let status = Status::new(code, msg);
771     Err(status.into())
772 }
773 
774 // ===== impl Code =====
775 
776 impl Code {
777     /// Get the `Code` that represents the integer, if known.
778     ///
779     /// If not known, returns `Code::Unknown` (surprise!).
from_i32(i: i32) -> Code780     pub fn from_i32(i: i32) -> Code {
781         Code::from(i)
782     }
783 
784     /// Convert the string representation of a `Code` (as stored, for example, in the `grpc-status`
785     /// header in a response) into a `Code`. Returns `Code::Unknown` if the code string is not a
786     /// valid gRPC status code.
from_bytes(bytes: &[u8]) -> Code787     pub fn from_bytes(bytes: &[u8]) -> Code {
788         match bytes.len() {
789             1 => match bytes[0] {
790                 b'0' => Code::Ok,
791                 b'1' => Code::Cancelled,
792                 b'2' => Code::Unknown,
793                 b'3' => Code::InvalidArgument,
794                 b'4' => Code::DeadlineExceeded,
795                 b'5' => Code::NotFound,
796                 b'6' => Code::AlreadyExists,
797                 b'7' => Code::PermissionDenied,
798                 b'8' => Code::ResourceExhausted,
799                 b'9' => Code::FailedPrecondition,
800                 _ => Code::parse_err(),
801             },
802             2 => match (bytes[0], bytes[1]) {
803                 (b'1', b'0') => Code::Aborted,
804                 (b'1', b'1') => Code::OutOfRange,
805                 (b'1', b'2') => Code::Unimplemented,
806                 (b'1', b'3') => Code::Internal,
807                 (b'1', b'4') => Code::Unavailable,
808                 (b'1', b'5') => Code::DataLoss,
809                 (b'1', b'6') => Code::Unauthenticated,
810                 _ => Code::parse_err(),
811             },
812             _ => Code::parse_err(),
813         }
814     }
815 
to_header_value(self) -> HeaderValue816     fn to_header_value(self) -> HeaderValue {
817         match self {
818             Code::Ok => HeaderValue::from_static("0"),
819             Code::Cancelled => HeaderValue::from_static("1"),
820             Code::Unknown => HeaderValue::from_static("2"),
821             Code::InvalidArgument => HeaderValue::from_static("3"),
822             Code::DeadlineExceeded => HeaderValue::from_static("4"),
823             Code::NotFound => HeaderValue::from_static("5"),
824             Code::AlreadyExists => HeaderValue::from_static("6"),
825             Code::PermissionDenied => HeaderValue::from_static("7"),
826             Code::ResourceExhausted => HeaderValue::from_static("8"),
827             Code::FailedPrecondition => HeaderValue::from_static("9"),
828             Code::Aborted => HeaderValue::from_static("10"),
829             Code::OutOfRange => HeaderValue::from_static("11"),
830             Code::Unimplemented => HeaderValue::from_static("12"),
831             Code::Internal => HeaderValue::from_static("13"),
832             Code::Unavailable => HeaderValue::from_static("14"),
833             Code::DataLoss => HeaderValue::from_static("15"),
834             Code::Unauthenticated => HeaderValue::from_static("16"),
835         }
836     }
837 
parse_err() -> Code838     fn parse_err() -> Code {
839         trace!("error parsing grpc-status");
840         Code::Unknown
841     }
842 }
843 
844 impl From<i32> for Code {
from(i: i32) -> Self845     fn from(i: i32) -> Self {
846         match i {
847             0 => Code::Ok,
848             1 => Code::Cancelled,
849             2 => Code::Unknown,
850             3 => Code::InvalidArgument,
851             4 => Code::DeadlineExceeded,
852             5 => Code::NotFound,
853             6 => Code::AlreadyExists,
854             7 => Code::PermissionDenied,
855             8 => Code::ResourceExhausted,
856             9 => Code::FailedPrecondition,
857             10 => Code::Aborted,
858             11 => Code::OutOfRange,
859             12 => Code::Unimplemented,
860             13 => Code::Internal,
861             14 => Code::Unavailable,
862             15 => Code::DataLoss,
863             16 => Code::Unauthenticated,
864 
865             _ => Code::Unknown,
866         }
867     }
868 }
869 
870 impl From<Code> for i32 {
871     #[inline]
from(code: Code) -> i32872     fn from(code: Code) -> i32 {
873         code as i32
874     }
875 }
876 
877 #[cfg(test)]
878 mod tests {
879     use super::*;
880     use crate::Error;
881 
882     #[derive(Debug)]
883     struct Nested(Error);
884 
885     impl fmt::Display for Nested {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result886         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
887             write!(f, "nested error: {}", self.0)
888         }
889     }
890 
891     impl std::error::Error for Nested {
source(&self) -> Option<&(dyn std::error::Error + 'static)>892         fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
893             Some(&*self.0)
894         }
895     }
896 
897     #[test]
from_error_status()898     fn from_error_status() {
899         let orig = Status::new(Code::OutOfRange, "weeaboo");
900         let found = Status::from_error(Box::new(orig));
901 
902         assert_eq!(found.code(), Code::OutOfRange);
903         assert_eq!(found.message(), "weeaboo");
904     }
905 
906     #[test]
from_error_unknown()907     fn from_error_unknown() {
908         let orig: Error = "peek-a-boo".into();
909         let found = Status::from_error(orig);
910 
911         assert_eq!(found.code(), Code::Unknown);
912         assert_eq!(found.message(), "peek-a-boo".to_string());
913     }
914 
915     #[test]
from_error_nested()916     fn from_error_nested() {
917         let orig = Nested(Box::new(Status::new(Code::OutOfRange, "weeaboo")));
918         let found = Status::from_error(Box::new(orig));
919 
920         assert_eq!(found.code(), Code::OutOfRange);
921         assert_eq!(found.message(), "weeaboo");
922     }
923 
924     #[test]
925     #[cfg(feature = "transport")]
from_error_h2()926     fn from_error_h2() {
927         use std::error::Error as _;
928 
929         let orig = h2::Error::from(h2::Reason::CANCEL);
930         let found = Status::from_error(Box::new(orig));
931 
932         assert_eq!(found.code(), Code::Cancelled);
933 
934         let source = found
935             .source()
936             .and_then(|err| err.downcast_ref::<h2::Error>())
937             .unwrap();
938         assert_eq!(source.reason(), Some(h2::Reason::CANCEL));
939     }
940 
941     #[test]
942     #[cfg(feature = "transport")]
to_h2_error()943     fn to_h2_error() {
944         let orig = Status::new(Code::Cancelled, "stop eet!");
945         let err = orig.to_h2_error();
946 
947         assert_eq!(err.reason(), Some(h2::Reason::CANCEL));
948     }
949 
950     #[test]
code_from_i32()951     fn code_from_i32() {
952         // This for loop should catch if we ever add a new variant and don't
953         // update From<i32>.
954         for i in 0..(Code::Unauthenticated as i32) {
955             let code = Code::from(i);
956             assert_eq!(
957                 i, code as i32,
958                 "Code::from({}) returned {:?} which is {}",
959                 i, code, code as i32,
960             );
961         }
962 
963         assert_eq!(Code::from(-1), Code::Unknown);
964     }
965 
966     #[test]
constructors()967     fn constructors() {
968         assert_eq!(Status::ok("").code(), Code::Ok);
969         assert_eq!(Status::cancelled("").code(), Code::Cancelled);
970         assert_eq!(Status::unknown("").code(), Code::Unknown);
971         assert_eq!(Status::invalid_argument("").code(), Code::InvalidArgument);
972         assert_eq!(Status::deadline_exceeded("").code(), Code::DeadlineExceeded);
973         assert_eq!(Status::not_found("").code(), Code::NotFound);
974         assert_eq!(Status::already_exists("").code(), Code::AlreadyExists);
975         assert_eq!(Status::permission_denied("").code(), Code::PermissionDenied);
976         assert_eq!(
977             Status::resource_exhausted("").code(),
978             Code::ResourceExhausted
979         );
980         assert_eq!(
981             Status::failed_precondition("").code(),
982             Code::FailedPrecondition
983         );
984         assert_eq!(Status::aborted("").code(), Code::Aborted);
985         assert_eq!(Status::out_of_range("").code(), Code::OutOfRange);
986         assert_eq!(Status::unimplemented("").code(), Code::Unimplemented);
987         assert_eq!(Status::internal("").code(), Code::Internal);
988         assert_eq!(Status::unavailable("").code(), Code::Unavailable);
989         assert_eq!(Status::data_loss("").code(), Code::DataLoss);
990         assert_eq!(Status::unauthenticated("").code(), Code::Unauthenticated);
991     }
992 
993     #[test]
details()994     fn details() {
995         const DETAILS: &[u8] = &[0, 2, 3];
996 
997         let status = Status::with_details(Code::Unavailable, "some message", DETAILS.into());
998 
999         assert_eq!(status.details(), DETAILS);
1000 
1001         let header_map = status.to_header_map().unwrap();
1002 
1003         let b64_details = crate::util::base64::STANDARD_NO_PAD.encode(DETAILS);
1004 
1005         assert_eq!(header_map[super::GRPC_STATUS_DETAILS_HEADER], b64_details);
1006 
1007         let status = Status::from_header_map(&header_map).unwrap();
1008 
1009         assert_eq!(status.details(), DETAILS);
1010     }
1011 }
1012