[go: up one dir, main page]

tonic/
status.rs

1use crate::metadata::MetadataMap;
2use crate::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    .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)]
38pub 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)]
59pub 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
112impl 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.
125    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
152impl std::fmt::Display for Code {
153    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
160impl Status {
161    /// Create a new `Status` with the associated code and message.
162    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.
173    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).
178    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.
186    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).
194    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.
203    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.
208    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.
214    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).
223    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.
229    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.
247    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`.
256    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.
273    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.
278    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.
284    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`.
293    pub fn unavailable(message: impl Into<String>) -> Status {
294        Status::new(Code::Unavailable, message)
295    }
296
297    /// Unrecoverable data loss or corruption.
298    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.
304    pub fn unauthenticated(message: impl Into<String>) -> Status {
305        Status::new(Code::Unauthenticated, message)
306    }
307
308    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`.
318    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.
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")]
361    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")]
370    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")]
390    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"))]
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
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`.
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`.
486    pub fn code(&self) -> Code {
487        self.code
488    }
489
490    /// Get the text error message of this `Status`.
491    pub fn message(&self) -> &str {
492        &self.message
493    }
494
495    /// Get the opaque error details of this `Status`.
496    pub fn details(&self) -> &[u8] {
497        &self.details
498    }
499
500    /// Get a reference to the custom metadata.
501    pub fn metadata(&self) -> &MetadataMap {
502        &self.metadata
503    }
504
505    /// Get a mutable reference to the custom metadata.
506    pub fn metadata_mut(&mut self) -> &mut MetadataMap {
507        &mut self.metadata
508    }
509
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`.
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.
546    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
551    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
556    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.
572    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`.
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.extensions_mut().insert(self);
585        response
586    }
587
588    #[doc(hidden)]
589    pub const GRPC_STATUS: HeaderName = HeaderName::from_static("grpc-status");
590    #[doc(hidden)]
591    pub const GRPC_MESSAGE: HeaderName = HeaderName::from_static("grpc-message");
592    #[doc(hidden)]
593    pub const GRPC_STATUS_DETAILS: HeaderName = HeaderName::from_static("grpc-status-details-bin");
594}
595
596fn find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option<Status> {
597    let mut source = Some(err);
598
599    while let Some(err) = source {
600        if let Some(status) = err.downcast_ref::<Status>() {
601            return Some(Status {
602                code: status.code,
603                message: status.message.clone(),
604                details: status.details.clone(),
605                metadata: status.metadata.clone(),
606                // Since `Status` is not `Clone`, any `source` on the original Status
607                // cannot be cloned so must remain with the original `Status`.
608                source: None,
609            });
610        }
611
612        if let Some(timeout) = err.downcast_ref::<TimeoutExpired>() {
613            return Some(Status::cancelled(timeout.to_string()));
614        }
615
616        // If we are unable to connect to the server, map this to UNAVAILABLE.  This is
617        // consistent with the behavior of a C++ gRPC client when the server is not running, and
618        // matches the spec of:
619        // > The service is currently unavailable. This is most likely a transient condition that
620        // > can be corrected if retried with a backoff.
621        if let Some(connect) = err.downcast_ref::<ConnectError>() {
622            return Some(Status::unavailable(connect.to_string()));
623        }
624
625        #[cfg(any(feature = "server", feature = "channel"))]
626        if let Some(hyper) = err
627            .downcast_ref::<hyper::Error>()
628            .and_then(Status::from_hyper_error)
629        {
630            return Some(hyper);
631        }
632
633        source = err.source();
634    }
635
636    None
637}
638
639impl fmt::Debug for Status {
640    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
641        // A manual impl to reduce the noise of frequently empty fields.
642        let mut builder = f.debug_struct("Status");
643
644        builder.field("code", &self.code);
645
646        if !self.message.is_empty() {
647            builder.field("message", &self.message);
648        }
649
650        if !self.details.is_empty() {
651            builder.field("details", &self.details);
652        }
653
654        if !self.metadata.is_empty() {
655            builder.field("metadata", &self.metadata);
656        }
657
658        builder.field("source", &self.source);
659
660        builder.finish()
661    }
662}
663
664fn invalid_header_value_byte<Error: fmt::Display>(err: Error) -> Status {
665    debug!("Invalid header: {}", err);
666    Status::new(
667        Code::Internal,
668        "Couldn't serialize non-text grpc status header".to_string(),
669    )
670}
671
672#[cfg(feature = "server")]
673impl From<h2::Error> for Status {
674    fn from(err: h2::Error) -> Self {
675        Status::from_h2_error(Box::new(err))
676    }
677}
678
679#[cfg(feature = "server")]
680impl From<Status> for h2::Error {
681    fn from(status: Status) -> Self {
682        status.to_h2_error()
683    }
684}
685
686impl From<std::io::Error> for Status {
687    fn from(err: std::io::Error) -> Self {
688        use std::io::ErrorKind;
689        let code = match err.kind() {
690            ErrorKind::BrokenPipe
691            | ErrorKind::WouldBlock
692            | ErrorKind::WriteZero
693            | ErrorKind::Interrupted => Code::Internal,
694            ErrorKind::ConnectionRefused
695            | ErrorKind::ConnectionReset
696            | ErrorKind::NotConnected
697            | ErrorKind::AddrInUse
698            | ErrorKind::AddrNotAvailable => Code::Unavailable,
699            ErrorKind::AlreadyExists => Code::AlreadyExists,
700            ErrorKind::ConnectionAborted => Code::Aborted,
701            ErrorKind::InvalidData => Code::DataLoss,
702            ErrorKind::InvalidInput => Code::InvalidArgument,
703            ErrorKind::NotFound => Code::NotFound,
704            ErrorKind::PermissionDenied => Code::PermissionDenied,
705            ErrorKind::TimedOut => Code::DeadlineExceeded,
706            ErrorKind::UnexpectedEof => Code::OutOfRange,
707            _ => Code::Unknown,
708        };
709        Status::new(code, err.to_string())
710    }
711}
712
713impl fmt::Display for Status {
714    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
715        write!(
716            f,
717            "status: {:?}, message: {:?}, details: {:?}, metadata: {:?}",
718            self.code(),
719            self.message(),
720            self.details(),
721            self.metadata(),
722        )
723    }
724}
725
726impl Error for Status {
727    fn source(&self) -> Option<&(dyn Error + 'static)> {
728        self.source.as_ref().map(|err| (&**err) as _)
729    }
730}
731
732///
733/// Take the `Status` value from `trailers` if it is available, else from `status_code`.
734///
735pub(crate) fn infer_grpc_status(
736    trailers: Option<&HeaderMap>,
737    status_code: http::StatusCode,
738) -> Result<(), Option<Status>> {
739    if let Some(trailers) = trailers {
740        if let Some(status) = Status::from_header_map(trailers) {
741            if status.code() == Code::Ok {
742                return Ok(());
743            } else {
744                return Err(status.into());
745            }
746        }
747    }
748    trace!("trailers missing grpc-status");
749    let code = match status_code {
750        // Borrowed from https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md
751        http::StatusCode::BAD_REQUEST => Code::Internal,
752        http::StatusCode::UNAUTHORIZED => Code::Unauthenticated,
753        http::StatusCode::FORBIDDEN => Code::PermissionDenied,
754        http::StatusCode::NOT_FOUND => Code::Unimplemented,
755        http::StatusCode::TOO_MANY_REQUESTS
756        | http::StatusCode::BAD_GATEWAY
757        | http::StatusCode::SERVICE_UNAVAILABLE
758        | http::StatusCode::GATEWAY_TIMEOUT => Code::Unavailable,
759        // We got a 200 but no trailers, we can infer that this request is finished.
760        //
761        // This can happen when a streaming response sends two Status but
762        // gRPC requires that we end the stream after the first status.
763        //
764        // https://github.com/hyperium/tonic/issues/681
765        http::StatusCode::OK => return Err(None),
766        _ => Code::Unknown,
767    };
768
769    let msg = format!(
770        "grpc-status header missing, mapped from HTTP status code {}",
771        status_code.as_u16(),
772    );
773    let status = Status::new(code, msg);
774    Err(status.into())
775}
776
777// ===== impl Code =====
778
779impl Code {
780    /// Get the `Code` that represents the integer, if known.
781    ///
782    /// If not known, returns `Code::Unknown` (surprise!).
783    pub const fn from_i32(i: i32) -> Code {
784        match i {
785            0 => Code::Ok,
786            1 => Code::Cancelled,
787            2 => Code::Unknown,
788            3 => Code::InvalidArgument,
789            4 => Code::DeadlineExceeded,
790            5 => Code::NotFound,
791            6 => Code::AlreadyExists,
792            7 => Code::PermissionDenied,
793            8 => Code::ResourceExhausted,
794            9 => Code::FailedPrecondition,
795            10 => Code::Aborted,
796            11 => Code::OutOfRange,
797            12 => Code::Unimplemented,
798            13 => Code::Internal,
799            14 => Code::Unavailable,
800            15 => Code::DataLoss,
801            16 => Code::Unauthenticated,
802
803            _ => Code::Unknown,
804        }
805    }
806
807    /// Convert the string representation of a `Code` (as stored, for example, in the `grpc-status`
808    /// header in a response) into a `Code`. Returns `Code::Unknown` if the code string is not a
809    /// valid gRPC status code.
810    pub fn from_bytes(bytes: &[u8]) -> Code {
811        match bytes.len() {
812            1 => match bytes[0] {
813                b'0' => Code::Ok,
814                b'1' => Code::Cancelled,
815                b'2' => Code::Unknown,
816                b'3' => Code::InvalidArgument,
817                b'4' => Code::DeadlineExceeded,
818                b'5' => Code::NotFound,
819                b'6' => Code::AlreadyExists,
820                b'7' => Code::PermissionDenied,
821                b'8' => Code::ResourceExhausted,
822                b'9' => Code::FailedPrecondition,
823                _ => Code::parse_err(),
824            },
825            2 => match (bytes[0], bytes[1]) {
826                (b'1', b'0') => Code::Aborted,
827                (b'1', b'1') => Code::OutOfRange,
828                (b'1', b'2') => Code::Unimplemented,
829                (b'1', b'3') => Code::Internal,
830                (b'1', b'4') => Code::Unavailable,
831                (b'1', b'5') => Code::DataLoss,
832                (b'1', b'6') => Code::Unauthenticated,
833                _ => Code::parse_err(),
834            },
835            _ => Code::parse_err(),
836        }
837    }
838
839    fn to_header_value(self) -> HeaderValue {
840        match self {
841            Code::Ok => HeaderValue::from_static("0"),
842            Code::Cancelled => HeaderValue::from_static("1"),
843            Code::Unknown => HeaderValue::from_static("2"),
844            Code::InvalidArgument => HeaderValue::from_static("3"),
845            Code::DeadlineExceeded => HeaderValue::from_static("4"),
846            Code::NotFound => HeaderValue::from_static("5"),
847            Code::AlreadyExists => HeaderValue::from_static("6"),
848            Code::PermissionDenied => HeaderValue::from_static("7"),
849            Code::ResourceExhausted => HeaderValue::from_static("8"),
850            Code::FailedPrecondition => HeaderValue::from_static("9"),
851            Code::Aborted => HeaderValue::from_static("10"),
852            Code::OutOfRange => HeaderValue::from_static("11"),
853            Code::Unimplemented => HeaderValue::from_static("12"),
854            Code::Internal => HeaderValue::from_static("13"),
855            Code::Unavailable => HeaderValue::from_static("14"),
856            Code::DataLoss => HeaderValue::from_static("15"),
857            Code::Unauthenticated => HeaderValue::from_static("16"),
858        }
859    }
860
861    fn parse_err() -> Code {
862        trace!("error parsing grpc-status");
863        Code::Unknown
864    }
865}
866
867impl From<i32> for Code {
868    fn from(i: i32) -> Self {
869        Code::from_i32(i)
870    }
871}
872
873impl From<Code> for i32 {
874    #[inline]
875    fn from(code: Code) -> i32 {
876        code as i32
877    }
878}
879
880#[cfg(test)]
881mod tests {
882    use super::*;
883    use crate::BoxError;
884
885    #[derive(Debug)]
886    struct Nested(BoxError);
887
888    impl fmt::Display for Nested {
889        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
890            write!(f, "nested error: {}", self.0)
891        }
892    }
893
894    impl std::error::Error for Nested {
895        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
896            Some(&*self.0)
897        }
898    }
899
900    #[test]
901    fn from_error_status() {
902        let orig = Status::new(Code::OutOfRange, "weeaboo");
903        let found = Status::from_error(Box::new(orig));
904
905        assert_eq!(found.code(), Code::OutOfRange);
906        assert_eq!(found.message(), "weeaboo");
907    }
908
909    #[test]
910    fn from_error_unknown() {
911        let orig: BoxError = "peek-a-boo".into();
912        let found = Status::from_error(orig);
913
914        assert_eq!(found.code(), Code::Unknown);
915        assert_eq!(found.message(), "peek-a-boo".to_string());
916    }
917
918    #[test]
919    fn from_error_nested() {
920        let orig = Nested(Box::new(Status::new(Code::OutOfRange, "weeaboo")));
921        let found = Status::from_error(Box::new(orig));
922
923        assert_eq!(found.code(), Code::OutOfRange);
924        assert_eq!(found.message(), "weeaboo");
925    }
926
927    #[test]
928    #[cfg(feature = "server")]
929    fn from_error_h2() {
930        use std::error::Error as _;
931
932        let orig = h2::Error::from(h2::Reason::CANCEL);
933        let found = Status::from_error(Box::new(orig));
934
935        assert_eq!(found.code(), Code::Cancelled);
936
937        let source = found
938            .source()
939            .and_then(|err| err.downcast_ref::<h2::Error>())
940            .unwrap();
941        assert_eq!(source.reason(), Some(h2::Reason::CANCEL));
942    }
943
944    #[test]
945    #[cfg(feature = "server")]
946    fn to_h2_error() {
947        let orig = Status::new(Code::Cancelled, "stop eet!");
948        let err = orig.to_h2_error();
949
950        assert_eq!(err.reason(), Some(h2::Reason::CANCEL));
951    }
952
953    #[test]
954    fn code_from_i32() {
955        // This for loop should catch if we ever add a new variant and don't
956        // update From<i32>.
957        for i in 0..(Code::Unauthenticated as i32) {
958            let code = Code::from(i);
959            assert_eq!(
960                i, code as i32,
961                "Code::from({}) returned {:?} which is {}",
962                i, code, code as i32,
963            );
964        }
965
966        assert_eq!(Code::from(-1), Code::Unknown);
967    }
968
969    #[test]
970    fn constructors() {
971        assert_eq!(Status::ok("").code(), Code::Ok);
972        assert_eq!(Status::cancelled("").code(), Code::Cancelled);
973        assert_eq!(Status::unknown("").code(), Code::Unknown);
974        assert_eq!(Status::invalid_argument("").code(), Code::InvalidArgument);
975        assert_eq!(Status::deadline_exceeded("").code(), Code::DeadlineExceeded);
976        assert_eq!(Status::not_found("").code(), Code::NotFound);
977        assert_eq!(Status::already_exists("").code(), Code::AlreadyExists);
978        assert_eq!(Status::permission_denied("").code(), Code::PermissionDenied);
979        assert_eq!(
980            Status::resource_exhausted("").code(),
981            Code::ResourceExhausted
982        );
983        assert_eq!(
984            Status::failed_precondition("").code(),
985            Code::FailedPrecondition
986        );
987        assert_eq!(Status::aborted("").code(), Code::Aborted);
988        assert_eq!(Status::out_of_range("").code(), Code::OutOfRange);
989        assert_eq!(Status::unimplemented("").code(), Code::Unimplemented);
990        assert_eq!(Status::internal("").code(), Code::Internal);
991        assert_eq!(Status::unavailable("").code(), Code::Unavailable);
992        assert_eq!(Status::data_loss("").code(), Code::DataLoss);
993        assert_eq!(Status::unauthenticated("").code(), Code::Unauthenticated);
994    }
995
996    #[test]
997    fn details() {
998        const DETAILS: &[u8] = &[0, 2, 3];
999
1000        let status = Status::with_details(Code::Unavailable, "some message", DETAILS.into());
1001
1002        assert_eq!(status.details(), DETAILS);
1003
1004        let header_map = status.to_header_map().unwrap();
1005
1006        let b64_details = crate::util::base64::STANDARD_NO_PAD.encode(DETAILS);
1007
1008        assert_eq!(header_map[Status::GRPC_STATUS_DETAILS], b64_details);
1009
1010        let status = Status::from_header_map(&header_map).unwrap();
1011
1012        assert_eq!(status.details(), DETAILS);
1013    }
1014}
1015
1016/// Error returned if a request didn't complete within the configured timeout.
1017///
1018/// Timeouts can be configured either with [`Endpoint::timeout`], [`Server::timeout`], or by
1019/// setting the [`grpc-timeout` metadata value][spec].
1020///
1021/// [`Endpoint::timeout`]: crate::transport::server::Server::timeout
1022/// [`Server::timeout`]: crate::transport::channel::Endpoint::timeout
1023/// [spec]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
1024#[derive(Debug)]
1025pub struct TimeoutExpired(pub ());
1026
1027impl fmt::Display for TimeoutExpired {
1028    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1029        write!(f, "Timeout expired")
1030    }
1031}
1032
1033// std::error::Error only requires a type to impl Debug and Display
1034impl std::error::Error for TimeoutExpired {}
1035
1036/// Wrapper type to indicate that an error occurs during the connection
1037/// process, so that the appropriate gRPC Status can be inferred.
1038#[derive(Debug)]
1039pub struct ConnectError(pub Box<dyn std::error::Error + Send + Sync>);
1040
1041impl fmt::Display for ConnectError {
1042    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1043        fmt::Display::fmt(&self.0, f)
1044    }
1045}
1046
1047impl std::error::Error for ConnectError {
1048    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1049        Some(self.0.as_ref())
1050    }
1051}