xref: /tonic/tonic/src/status.rs (revision fe9613c7)
1 use crate::metadata::MetadataMap;
2 use crate::metadata::GRPC_CONTENT_TYPE;
3 use base64::Engine as _;
4 use bytes::Bytes;
5 use http::{
6     header::{HeaderMap, HeaderValue},
7     HeaderName,
8 };
9 use percent_encoding::{percent_decode, percent_encode, AsciiSet, CONTROLS};
10 use std::{borrow::Cow, error::Error, fmt, sync::Arc};
11 use tracing::{debug, trace, warn};
12 
13 const ENCODING_SET: &AsciiSet = &CONTROLS
14     .add(b' ')
15     .add(b'"')
16     .add(b'#')
17     .add(b'%')
18     .add(b'<')
19     .add(b'>')
20     .add(b'`')
21     .add(b'?')
22     .add(b'{')
23     .add(b'}');
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 
from_error_generic( err: impl Into<Box<dyn Error + Send + Sync + 'static>>, ) -> Status308     pub(crate) fn from_error_generic(
309         err: impl Into<Box<dyn Error + Send + Sync + 'static>>,
310     ) -> Status {
311         Self::from_error(err.into())
312     }
313 
314     /// Create a `Status` from various types of `Error`.
315     ///
316     /// Inspects the error source chain for recognizable errors, including statuses, HTTP2, and
317     /// hyper, and attempts to maps them to a `Status`, or else returns an Unknown `Status`.
from_error(err: Box<dyn Error + Send + Sync + 'static>) -> Status318     pub fn from_error(err: Box<dyn Error + Send + Sync + 'static>) -> Status {
319         Status::try_from_error(err).unwrap_or_else(|err| {
320             let mut status = Status::new(Code::Unknown, err.to_string());
321             status.source = Some(err.into());
322             status
323         })
324     }
325 
326     /// Create a `Status` from various types of `Error`.
327     ///
328     /// Returns the error if a status could not be created.
329     ///
330     /// # Downcast stability
331     /// This function does not provide any stability guarantees around how it will downcast errors into
332     /// status codes.
try_from_error( err: Box<dyn Error + Send + Sync + 'static>, ) -> Result<Status, Box<dyn Error + Send + Sync + 'static>>333     pub fn try_from_error(
334         err: Box<dyn Error + Send + Sync + 'static>,
335     ) -> Result<Status, Box<dyn Error + Send + Sync + 'static>> {
336         let err = match err.downcast::<Status>() {
337             Ok(status) => {
338                 return Ok(*status);
339             }
340             Err(err) => err,
341         };
342 
343         #[cfg(feature = "server")]
344         let err = match err.downcast::<h2::Error>() {
345             Ok(h2) => {
346                 return Ok(Status::from_h2_error(h2));
347             }
348             Err(err) => err,
349         };
350 
351         if let Some(mut status) = find_status_in_source_chain(&*err) {
352             status.source = Some(err.into());
353             return Ok(status);
354         }
355 
356         Err(err)
357     }
358 
359     // FIXME: bubble this into `transport` and expose generic http2 reasons.
360     #[cfg(feature = "server")]
from_h2_error(err: Box<h2::Error>) -> Status361     fn from_h2_error(err: Box<h2::Error>) -> Status {
362         let code = Self::code_from_h2(&err);
363 
364         let mut status = Self::new(code, format!("h2 protocol error: {}", err));
365         status.source = Some(Arc::new(*err));
366         status
367     }
368 
369     #[cfg(feature = "server")]
code_from_h2(err: &h2::Error) -> Code370     fn code_from_h2(err: &h2::Error) -> Code {
371         // See https://github.com/grpc/grpc/blob/3977c30/doc/PROTOCOL-HTTP2.md#errors
372         match err.reason() {
373             Some(h2::Reason::NO_ERROR)
374             | Some(h2::Reason::PROTOCOL_ERROR)
375             | Some(h2::Reason::INTERNAL_ERROR)
376             | Some(h2::Reason::FLOW_CONTROL_ERROR)
377             | Some(h2::Reason::SETTINGS_TIMEOUT)
378             | Some(h2::Reason::COMPRESSION_ERROR)
379             | Some(h2::Reason::CONNECT_ERROR) => Code::Internal,
380             Some(h2::Reason::REFUSED_STREAM) => Code::Unavailable,
381             Some(h2::Reason::CANCEL) => Code::Cancelled,
382             Some(h2::Reason::ENHANCE_YOUR_CALM) => Code::ResourceExhausted,
383             Some(h2::Reason::INADEQUATE_SECURITY) => Code::PermissionDenied,
384 
385             _ => Code::Unknown,
386         }
387     }
388 
389     #[cfg(feature = "server")]
to_h2_error(&self) -> h2::Error390     fn to_h2_error(&self) -> h2::Error {
391         // conservatively transform to h2 error codes...
392         let reason = match self.code {
393             Code::Cancelled => h2::Reason::CANCEL,
394             _ => h2::Reason::INTERNAL_ERROR,
395         };
396 
397         reason.into()
398     }
399 
400     /// Handles hyper errors specifically, which expose a number of different parameters about the
401     /// http stream's error: https://docs.rs/hyper/0.14.11/hyper/struct.Error.html.
402     ///
403     /// Returns Some if there's a way to handle the error, or None if the information from this
404     /// hyper error, but perhaps not its source, should be ignored.
405     #[cfg(any(feature = "server", feature = "channel"))]
from_hyper_error(err: &hyper::Error) -> Option<Status>406     fn from_hyper_error(err: &hyper::Error) -> Option<Status> {
407         // is_timeout results from hyper's keep-alive logic
408         // (https://docs.rs/hyper/0.14.11/src/hyper/error.rs.html#192-194).  Per the grpc spec
409         // > An expired client initiated PING will cause all calls to be closed with an UNAVAILABLE
410         // > status. Note that the frequency of PINGs is highly dependent on the network
411         // > environment, implementations are free to adjust PING frequency based on network and
412         // > application requirements, which is why it's mapped to unavailable here.
413         if err.is_timeout() {
414             return Some(Status::unavailable(err.to_string()));
415         }
416 
417         if err.is_canceled() {
418             return Some(Status::cancelled(err.to_string()));
419         }
420 
421         #[cfg(feature = "server")]
422         if let Some(h2_err) = err.source().and_then(|e| e.downcast_ref::<h2::Error>()) {
423             let code = Status::code_from_h2(h2_err);
424             let status = Self::new(code, format!("h2 protocol error: {}", err));
425 
426             return Some(status);
427         }
428 
429         None
430     }
431 
map_error<E>(err: E) -> Status where E: Into<Box<dyn Error + Send + Sync>>,432     pub(crate) fn map_error<E>(err: E) -> Status
433     where
434         E: Into<Box<dyn Error + Send + Sync>>,
435     {
436         let err: Box<dyn Error + Send + Sync> = err.into();
437         Status::from_error(err)
438     }
439 
440     /// Extract a `Status` from a hyper `HeaderMap`.
from_header_map(header_map: &HeaderMap) -> Option<Status>441     pub fn from_header_map(header_map: &HeaderMap) -> Option<Status> {
442         let code = Code::from_bytes(header_map.get(Self::GRPC_STATUS)?.as_ref());
443 
444         let error_message = match header_map.get(Self::GRPC_MESSAGE) {
445             Some(header) => percent_decode(header.as_bytes())
446                 .decode_utf8()
447                 .map(|cow| cow.to_string()),
448             None => Ok(String::new()),
449         };
450 
451         let details = match header_map.get(Self::GRPC_STATUS_DETAILS) {
452             Some(header) => crate::util::base64::STANDARD
453                 .decode(header.as_bytes())
454                 .expect("Invalid status header, expected base64 encoded value")
455                 .into(),
456             None => Bytes::new(),
457         };
458 
459         let other_headers = {
460             let mut header_map = header_map.clone();
461             header_map.remove(Self::GRPC_STATUS);
462             header_map.remove(Self::GRPC_MESSAGE);
463             header_map.remove(Self::GRPC_STATUS_DETAILS);
464             header_map
465         };
466 
467         let (code, message) = match error_message {
468             Ok(message) => (code, message),
469             Err(e) => {
470                 let error_message = format!("Error deserializing status message header: {e}");
471                 warn!(error_message);
472                 (Code::Unknown, error_message)
473             }
474         };
475 
476         Some(Status {
477             code,
478             message,
479             details,
480             metadata: MetadataMap::from_headers(other_headers),
481             source: None,
482         })
483     }
484 
485     /// Get the gRPC `Code` of this `Status`.
code(&self) -> Code486     pub fn code(&self) -> Code {
487         self.code
488     }
489 
490     /// Get the text error message of this `Status`.
message(&self) -> &str491     pub fn message(&self) -> &str {
492         &self.message
493     }
494 
495     /// Get the opaque error details of this `Status`.
details(&self) -> &[u8]496     pub fn details(&self) -> &[u8] {
497         &self.details
498     }
499 
500     /// Get a reference to the custom metadata.
metadata(&self) -> &MetadataMap501     pub fn metadata(&self) -> &MetadataMap {
502         &self.metadata
503     }
504 
505     /// Get a mutable reference to the custom metadata.
metadata_mut(&mut self) -> &mut MetadataMap506     pub fn metadata_mut(&mut self) -> &mut MetadataMap {
507         &mut self.metadata
508     }
509 
to_header_map(&self) -> Result<HeaderMap, Self>510     pub(crate) fn to_header_map(&self) -> Result<HeaderMap, Self> {
511         let mut header_map = HeaderMap::with_capacity(3 + self.metadata.len());
512         self.add_header(&mut header_map)?;
513         Ok(header_map)
514     }
515 
516     /// Add headers from this `Status` into `header_map`.
add_header(&self, header_map: &mut HeaderMap) -> Result<(), Self>517     pub fn add_header(&self, header_map: &mut HeaderMap) -> Result<(), Self> {
518         header_map.extend(self.metadata.clone().into_sanitized_headers());
519 
520         header_map.insert(Self::GRPC_STATUS, self.code.to_header_value());
521 
522         if !self.message.is_empty() {
523             let to_write = Bytes::copy_from_slice(
524                 Cow::from(percent_encode(self.message().as_bytes(), ENCODING_SET)).as_bytes(),
525             );
526 
527             header_map.insert(
528                 Self::GRPC_MESSAGE,
529                 HeaderValue::from_maybe_shared(to_write).map_err(invalid_header_value_byte)?,
530             );
531         }
532 
533         if !self.details.is_empty() {
534             let details = crate::util::base64::STANDARD_NO_PAD.encode(&self.details[..]);
535 
536             header_map.insert(
537                 Self::GRPC_STATUS_DETAILS,
538                 HeaderValue::from_maybe_shared(details).map_err(invalid_header_value_byte)?,
539             );
540         }
541 
542         Ok(())
543     }
544 
545     /// Create a new `Status` with the associated code, message, and binary details field.
with_details(code: Code, message: impl Into<String>, details: Bytes) -> Status546     pub fn with_details(code: Code, message: impl Into<String>, details: Bytes) -> Status {
547         Self::with_details_and_metadata(code, message, details, MetadataMap::new())
548     }
549 
550     /// Create a new `Status` with the associated code, message, and custom metadata
with_metadata(code: Code, message: impl Into<String>, metadata: MetadataMap) -> Status551     pub fn with_metadata(code: Code, message: impl Into<String>, metadata: MetadataMap) -> Status {
552         Self::with_details_and_metadata(code, message, Bytes::new(), metadata)
553     }
554 
555     /// 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, ) -> Status556     pub fn with_details_and_metadata(
557         code: Code,
558         message: impl Into<String>,
559         details: Bytes,
560         metadata: MetadataMap,
561     ) -> Status {
562         Status {
563             code,
564             message: message.into(),
565             details,
566             metadata,
567             source: None,
568         }
569     }
570 
571     /// Add a source error to this status.
set_source(&mut self, source: Arc<dyn Error + Send + Sync + 'static>) -> &mut Status572     pub fn set_source(&mut self, source: Arc<dyn Error + Send + Sync + 'static>) -> &mut Status {
573         self.source = Some(source);
574         self
575     }
576 
577     /// Build an `http::Response` from the given `Status`.
into_http<B: Default>(self) -> http::Response<B>578     pub fn into_http<B: Default>(self) -> http::Response<B> {
579         let mut response = http::Response::new(B::default());
580         response
581             .headers_mut()
582             .insert(http::header::CONTENT_TYPE, GRPC_CONTENT_TYPE);
583         self.add_header(response.headers_mut()).unwrap();
584         response
585     }
586 
587     #[doc(hidden)]
588     pub const GRPC_STATUS: HeaderName = HeaderName::from_static("grpc-status");
589     #[doc(hidden)]
590     pub const GRPC_MESSAGE: HeaderName = HeaderName::from_static("grpc-message");
591     #[doc(hidden)]
592     pub const GRPC_STATUS_DETAILS: HeaderName = HeaderName::from_static("grpc-status-details-bin");
593 }
594 
find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option<Status>595 fn find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option<Status> {
596     let mut source = Some(err);
597 
598     while let Some(err) = source {
599         if let Some(status) = err.downcast_ref::<Status>() {
600             return Some(Status {
601                 code: status.code,
602                 message: status.message.clone(),
603                 details: status.details.clone(),
604                 metadata: status.metadata.clone(),
605                 // Since `Status` is not `Clone`, any `source` on the original Status
606                 // cannot be cloned so must remain with the original `Status`.
607                 source: None,
608             });
609         }
610 
611         if let Some(timeout) = err.downcast_ref::<TimeoutExpired>() {
612             return Some(Status::cancelled(timeout.to_string()));
613         }
614 
615         // If we are unable to connect to the server, map this to UNAVAILABLE.  This is
616         // consistent with the behavior of a C++ gRPC client when the server is not running, and
617         // matches the spec of:
618         // > The service is currently unavailable. This is most likely a transient condition that
619         // > can be corrected if retried with a backoff.
620         if let Some(connect) = err.downcast_ref::<ConnectError>() {
621             return Some(Status::unavailable(connect.to_string()));
622         }
623 
624         #[cfg(any(feature = "server", feature = "channel"))]
625         if let Some(hyper) = err
626             .downcast_ref::<hyper::Error>()
627             .and_then(Status::from_hyper_error)
628         {
629             return Some(hyper);
630         }
631 
632         source = err.source();
633     }
634 
635     None
636 }
637 
638 impl fmt::Debug for Status {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result639     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
640         // A manual impl to reduce the noise of frequently empty fields.
641         let mut builder = f.debug_struct("Status");
642 
643         builder.field("code", &self.code);
644 
645         if !self.message.is_empty() {
646             builder.field("message", &self.message);
647         }
648 
649         if !self.details.is_empty() {
650             builder.field("details", &self.details);
651         }
652 
653         if !self.metadata.is_empty() {
654             builder.field("metadata", &self.metadata);
655         }
656 
657         builder.field("source", &self.source);
658 
659         builder.finish()
660     }
661 }
662 
invalid_header_value_byte<Error: fmt::Display>(err: Error) -> Status663 fn invalid_header_value_byte<Error: fmt::Display>(err: Error) -> Status {
664     debug!("Invalid header: {}", err);
665     Status::new(
666         Code::Internal,
667         "Couldn't serialize non-text grpc status header".to_string(),
668     )
669 }
670 
671 #[cfg(feature = "server")]
672 impl From<h2::Error> for Status {
from(err: h2::Error) -> Self673     fn from(err: h2::Error) -> Self {
674         Status::from_h2_error(Box::new(err))
675     }
676 }
677 
678 #[cfg(feature = "server")]
679 impl From<Status> for h2::Error {
from(status: Status) -> Self680     fn from(status: Status) -> Self {
681         status.to_h2_error()
682     }
683 }
684 
685 impl From<std::io::Error> for Status {
from(err: std::io::Error) -> Self686     fn from(err: std::io::Error) -> Self {
687         use std::io::ErrorKind;
688         let code = match err.kind() {
689             ErrorKind::BrokenPipe
690             | ErrorKind::WouldBlock
691             | ErrorKind::WriteZero
692             | ErrorKind::Interrupted => Code::Internal,
693             ErrorKind::ConnectionRefused
694             | ErrorKind::ConnectionReset
695             | ErrorKind::NotConnected
696             | ErrorKind::AddrInUse
697             | ErrorKind::AddrNotAvailable => Code::Unavailable,
698             ErrorKind::AlreadyExists => Code::AlreadyExists,
699             ErrorKind::ConnectionAborted => Code::Aborted,
700             ErrorKind::InvalidData => Code::DataLoss,
701             ErrorKind::InvalidInput => Code::InvalidArgument,
702             ErrorKind::NotFound => Code::NotFound,
703             ErrorKind::PermissionDenied => Code::PermissionDenied,
704             ErrorKind::TimedOut => Code::DeadlineExceeded,
705             ErrorKind::UnexpectedEof => Code::OutOfRange,
706             _ => Code::Unknown,
707         };
708         Status::new(code, err.to_string())
709     }
710 }
711 
712 impl fmt::Display for Status {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result713     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
714         write!(
715             f,
716             "status: {:?}, message: {:?}, details: {:?}, metadata: {:?}",
717             self.code(),
718             self.message(),
719             self.details(),
720             self.metadata(),
721         )
722     }
723 }
724 
725 impl Error for Status {
source(&self) -> Option<&(dyn Error + 'static)>726     fn source(&self) -> Option<&(dyn Error + 'static)> {
727         self.source.as_ref().map(|err| (&**err) as _)
728     }
729 }
730 
731 ///
732 /// Take the `Status` value from `trailers` if it is available, else from `status_code`.
733 ///
infer_grpc_status( trailers: Option<&HeaderMap>, status_code: http::StatusCode, ) -> Result<(), Option<Status>>734 pub(crate) fn infer_grpc_status(
735     trailers: Option<&HeaderMap>,
736     status_code: http::StatusCode,
737 ) -> Result<(), Option<Status>> {
738     if let Some(trailers) = trailers {
739         if let Some(status) = Status::from_header_map(trailers) {
740             if status.code() == Code::Ok {
741                 return Ok(());
742             } else {
743                 return Err(status.into());
744             }
745         }
746     }
747     trace!("trailers missing grpc-status");
748     let code = match status_code {
749         // Borrowed from https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md
750         http::StatusCode::BAD_REQUEST => Code::Internal,
751         http::StatusCode::UNAUTHORIZED => Code::Unauthenticated,
752         http::StatusCode::FORBIDDEN => Code::PermissionDenied,
753         http::StatusCode::NOT_FOUND => Code::Unimplemented,
754         http::StatusCode::TOO_MANY_REQUESTS
755         | http::StatusCode::BAD_GATEWAY
756         | http::StatusCode::SERVICE_UNAVAILABLE
757         | http::StatusCode::GATEWAY_TIMEOUT => Code::Unavailable,
758         // We got a 200 but no trailers, we can infer that this request is finished.
759         //
760         // This can happen when a streaming response sends two Status but
761         // gRPC requires that we end the stream after the first status.
762         //
763         // https://github.com/hyperium/tonic/issues/681
764         http::StatusCode::OK => return Err(None),
765         _ => Code::Unknown,
766     };
767 
768     let msg = format!(
769         "grpc-status header missing, mapped from HTTP status code {}",
770         status_code.as_u16(),
771     );
772     let status = Status::new(code, msg);
773     Err(status.into())
774 }
775 
776 // ===== impl Code =====
777 
778 impl Code {
779     /// Get the `Code` that represents the integer, if known.
780     ///
781     /// If not known, returns `Code::Unknown` (surprise!).
from_i32(i: i32) -> Code782     pub fn from_i32(i: i32) -> Code {
783         Code::from(i)
784     }
785 
786     /// Convert the string representation of a `Code` (as stored, for example, in the `grpc-status`
787     /// header in a response) into a `Code`. Returns `Code::Unknown` if the code string is not a
788     /// valid gRPC status code.
from_bytes(bytes: &[u8]) -> Code789     pub fn from_bytes(bytes: &[u8]) -> Code {
790         match bytes.len() {
791             1 => match bytes[0] {
792                 b'0' => Code::Ok,
793                 b'1' => Code::Cancelled,
794                 b'2' => Code::Unknown,
795                 b'3' => Code::InvalidArgument,
796                 b'4' => Code::DeadlineExceeded,
797                 b'5' => Code::NotFound,
798                 b'6' => Code::AlreadyExists,
799                 b'7' => Code::PermissionDenied,
800                 b'8' => Code::ResourceExhausted,
801                 b'9' => Code::FailedPrecondition,
802                 _ => Code::parse_err(),
803             },
804             2 => match (bytes[0], bytes[1]) {
805                 (b'1', b'0') => Code::Aborted,
806                 (b'1', b'1') => Code::OutOfRange,
807                 (b'1', b'2') => Code::Unimplemented,
808                 (b'1', b'3') => Code::Internal,
809                 (b'1', b'4') => Code::Unavailable,
810                 (b'1', b'5') => Code::DataLoss,
811                 (b'1', b'6') => Code::Unauthenticated,
812                 _ => Code::parse_err(),
813             },
814             _ => Code::parse_err(),
815         }
816     }
817 
to_header_value(self) -> HeaderValue818     fn to_header_value(self) -> HeaderValue {
819         match self {
820             Code::Ok => HeaderValue::from_static("0"),
821             Code::Cancelled => HeaderValue::from_static("1"),
822             Code::Unknown => HeaderValue::from_static("2"),
823             Code::InvalidArgument => HeaderValue::from_static("3"),
824             Code::DeadlineExceeded => HeaderValue::from_static("4"),
825             Code::NotFound => HeaderValue::from_static("5"),
826             Code::AlreadyExists => HeaderValue::from_static("6"),
827             Code::PermissionDenied => HeaderValue::from_static("7"),
828             Code::ResourceExhausted => HeaderValue::from_static("8"),
829             Code::FailedPrecondition => HeaderValue::from_static("9"),
830             Code::Aborted => HeaderValue::from_static("10"),
831             Code::OutOfRange => HeaderValue::from_static("11"),
832             Code::Unimplemented => HeaderValue::from_static("12"),
833             Code::Internal => HeaderValue::from_static("13"),
834             Code::Unavailable => HeaderValue::from_static("14"),
835             Code::DataLoss => HeaderValue::from_static("15"),
836             Code::Unauthenticated => HeaderValue::from_static("16"),
837         }
838     }
839 
parse_err() -> Code840     fn parse_err() -> Code {
841         trace!("error parsing grpc-status");
842         Code::Unknown
843     }
844 }
845 
846 impl From<i32> for Code {
from(i: i32) -> Self847     fn from(i: i32) -> Self {
848         match i {
849             0 => Code::Ok,
850             1 => Code::Cancelled,
851             2 => Code::Unknown,
852             3 => Code::InvalidArgument,
853             4 => Code::DeadlineExceeded,
854             5 => Code::NotFound,
855             6 => Code::AlreadyExists,
856             7 => Code::PermissionDenied,
857             8 => Code::ResourceExhausted,
858             9 => Code::FailedPrecondition,
859             10 => Code::Aborted,
860             11 => Code::OutOfRange,
861             12 => Code::Unimplemented,
862             13 => Code::Internal,
863             14 => Code::Unavailable,
864             15 => Code::DataLoss,
865             16 => Code::Unauthenticated,
866 
867             _ => Code::Unknown,
868         }
869     }
870 }
871 
872 impl From<Code> for i32 {
873     #[inline]
from(code: Code) -> i32874     fn from(code: Code) -> i32 {
875         code as i32
876     }
877 }
878 
879 #[cfg(test)]
880 mod tests {
881     use super::*;
882     use crate::BoxError;
883 
884     #[derive(Debug)]
885     struct Nested(BoxError);
886 
887     impl fmt::Display for Nested {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result888         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
889             write!(f, "nested error: {}", self.0)
890         }
891     }
892 
893     impl std::error::Error for Nested {
source(&self) -> Option<&(dyn std::error::Error + 'static)>894         fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
895             Some(&*self.0)
896         }
897     }
898 
899     #[test]
from_error_status()900     fn from_error_status() {
901         let orig = Status::new(Code::OutOfRange, "weeaboo");
902         let found = Status::from_error(Box::new(orig));
903 
904         assert_eq!(found.code(), Code::OutOfRange);
905         assert_eq!(found.message(), "weeaboo");
906     }
907 
908     #[test]
from_error_unknown()909     fn from_error_unknown() {
910         let orig: BoxError = "peek-a-boo".into();
911         let found = Status::from_error(orig);
912 
913         assert_eq!(found.code(), Code::Unknown);
914         assert_eq!(found.message(), "peek-a-boo".to_string());
915     }
916 
917     #[test]
from_error_nested()918     fn from_error_nested() {
919         let orig = Nested(Box::new(Status::new(Code::OutOfRange, "weeaboo")));
920         let found = Status::from_error(Box::new(orig));
921 
922         assert_eq!(found.code(), Code::OutOfRange);
923         assert_eq!(found.message(), "weeaboo");
924     }
925 
926     #[test]
927     #[cfg(feature = "server")]
from_error_h2()928     fn from_error_h2() {
929         use std::error::Error as _;
930 
931         let orig = h2::Error::from(h2::Reason::CANCEL);
932         let found = Status::from_error(Box::new(orig));
933 
934         assert_eq!(found.code(), Code::Cancelled);
935 
936         let source = found
937             .source()
938             .and_then(|err| err.downcast_ref::<h2::Error>())
939             .unwrap();
940         assert_eq!(source.reason(), Some(h2::Reason::CANCEL));
941     }
942 
943     #[test]
944     #[cfg(feature = "server")]
to_h2_error()945     fn to_h2_error() {
946         let orig = Status::new(Code::Cancelled, "stop eet!");
947         let err = orig.to_h2_error();
948 
949         assert_eq!(err.reason(), Some(h2::Reason::CANCEL));
950     }
951 
952     #[test]
code_from_i32()953     fn code_from_i32() {
954         // This for loop should catch if we ever add a new variant and don't
955         // update From<i32>.
956         for i in 0..(Code::Unauthenticated as i32) {
957             let code = Code::from(i);
958             assert_eq!(
959                 i, code as i32,
960                 "Code::from({}) returned {:?} which is {}",
961                 i, code, code as i32,
962             );
963         }
964 
965         assert_eq!(Code::from(-1), Code::Unknown);
966     }
967 
968     #[test]
constructors()969     fn constructors() {
970         assert_eq!(Status::ok("").code(), Code::Ok);
971         assert_eq!(Status::cancelled("").code(), Code::Cancelled);
972         assert_eq!(Status::unknown("").code(), Code::Unknown);
973         assert_eq!(Status::invalid_argument("").code(), Code::InvalidArgument);
974         assert_eq!(Status::deadline_exceeded("").code(), Code::DeadlineExceeded);
975         assert_eq!(Status::not_found("").code(), Code::NotFound);
976         assert_eq!(Status::already_exists("").code(), Code::AlreadyExists);
977         assert_eq!(Status::permission_denied("").code(), Code::PermissionDenied);
978         assert_eq!(
979             Status::resource_exhausted("").code(),
980             Code::ResourceExhausted
981         );
982         assert_eq!(
983             Status::failed_precondition("").code(),
984             Code::FailedPrecondition
985         );
986         assert_eq!(Status::aborted("").code(), Code::Aborted);
987         assert_eq!(Status::out_of_range("").code(), Code::OutOfRange);
988         assert_eq!(Status::unimplemented("").code(), Code::Unimplemented);
989         assert_eq!(Status::internal("").code(), Code::Internal);
990         assert_eq!(Status::unavailable("").code(), Code::Unavailable);
991         assert_eq!(Status::data_loss("").code(), Code::DataLoss);
992         assert_eq!(Status::unauthenticated("").code(), Code::Unauthenticated);
993     }
994 
995     #[test]
details()996     fn details() {
997         const DETAILS: &[u8] = &[0, 2, 3];
998 
999         let status = Status::with_details(Code::Unavailable, "some message", DETAILS.into());
1000 
1001         assert_eq!(status.details(), DETAILS);
1002 
1003         let header_map = status.to_header_map().unwrap();
1004 
1005         let b64_details = crate::util::base64::STANDARD_NO_PAD.encode(DETAILS);
1006 
1007         assert_eq!(header_map[Status::GRPC_STATUS_DETAILS], b64_details);
1008 
1009         let status = Status::from_header_map(&header_map).unwrap();
1010 
1011         assert_eq!(status.details(), DETAILS);
1012     }
1013 }
1014 
1015 /// Error returned if a request didn't complete within the configured timeout.
1016 ///
1017 /// Timeouts can be configured either with [`Endpoint::timeout`], [`Server::timeout`], or by
1018 /// setting the [`grpc-timeout` metadata value][spec].
1019 ///
1020 /// [`Endpoint::timeout`]: crate::transport::server::Server::timeout
1021 /// [`Server::timeout`]: crate::transport::channel::Endpoint::timeout
1022 /// [spec]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
1023 #[derive(Debug)]
1024 pub struct TimeoutExpired(pub ());
1025 
1026 impl fmt::Display for TimeoutExpired {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1027     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1028         write!(f, "Timeout expired")
1029     }
1030 }
1031 
1032 // std::error::Error only requires a type to impl Debug and Display
1033 impl std::error::Error for TimeoutExpired {}
1034 
1035 /// Wrapper type to indicate that an error occurs during the connection
1036 /// process, so that the appropriate gRPC Status can be inferred.
1037 #[derive(Debug)]
1038 pub struct ConnectError(pub Box<dyn std::error::Error + Send + Sync>);
1039 
1040 impl fmt::Display for ConnectError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result1041     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1042         fmt::Display::fmt(&self.0, f)
1043     }
1044 }
1045 
1046 impl std::error::Error for ConnectError {
source(&self) -> Option<&(dyn std::error::Error + 'static)>1047     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1048         Some(self.0.as_ref())
1049     }
1050 }
1051