tonic/
status.rs

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