1use crate::metadata::GRPC_CONTENT_TYPE;
2use crate::metadata::MetadataMap;
3use base64::Engine as _;
4use bytes::Bytes;
5use http::{
6 HeaderName,
7 header::{HeaderMap, HeaderValue},
8};
9use percent_encoding::{AsciiSet, CONTROLS, percent_decode, percent_encode};
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#[derive(Clone)]
38pub struct Status(Box<StatusInner>);
39
40#[derive(Clone)]
42struct StatusInner {
43 code: Code,
45 message: String,
47 details: Bytes,
49 metadata: MetadataMap,
53 source: Option<Arc<dyn Error + Send + Sync + 'static>>,
55}
56
57impl StatusInner {
58 fn into_status(self) -> Status {
59 Status(Box::new(self))
60 }
61}
62
63#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
69pub enum Code {
70 Ok = 0,
72
73 Cancelled = 1,
75
76 Unknown = 2,
78
79 InvalidArgument = 3,
81
82 DeadlineExceeded = 4,
84
85 NotFound = 5,
87
88 AlreadyExists = 6,
90
91 PermissionDenied = 7,
93
94 ResourceExhausted = 8,
96
97 FailedPrecondition = 9,
99
100 Aborted = 10,
102
103 OutOfRange = 11,
105
106 Unimplemented = 12,
108
109 Internal = 13,
111
112 Unavailable = 14,
114
115 DataLoss = 15,
117
118 Unauthenticated = 16,
120}
121
122impl Code {
123 pub fn description(&self) -> &'static str {
136 match self {
137 Code::Ok => "The operation completed successfully",
138 Code::Cancelled => "The operation was cancelled",
139 Code::Unknown => "Unknown error",
140 Code::InvalidArgument => "Client specified an invalid argument",
141 Code::DeadlineExceeded => "Deadline expired before operation could complete",
142 Code::NotFound => "Some requested entity was not found",
143 Code::AlreadyExists => "Some entity that we attempted to create already exists",
144 Code::PermissionDenied => {
145 "The caller does not have permission to execute the specified operation"
146 }
147 Code::ResourceExhausted => "Some resource has been exhausted",
148 Code::FailedPrecondition => {
149 "The system is not in a state required for the operation's execution"
150 }
151 Code::Aborted => "The operation was aborted",
152 Code::OutOfRange => "Operation was attempted past the valid range",
153 Code::Unimplemented => "Operation is not implemented or not supported",
154 Code::Internal => "Internal error",
155 Code::Unavailable => "The service is currently unavailable",
156 Code::DataLoss => "Unrecoverable data loss or corruption",
157 Code::Unauthenticated => "The request does not have valid authentication credentials",
158 }
159 }
160}
161
162impl std::fmt::Display for Code {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 std::fmt::Display::fmt(self.description(), f)
165 }
166}
167
168impl Status {
171 pub fn new(code: Code, message: impl Into<String>) -> Status {
173 StatusInner {
174 code,
175 message: message.into(),
176 details: Bytes::new(),
177 metadata: MetadataMap::new(),
178 source: None,
179 }
180 .into_status()
181 }
182
183 pub fn ok(message: impl Into<String>) -> Status {
185 Status::new(Code::Ok, message)
186 }
187
188 pub fn cancelled(message: impl Into<String>) -> Status {
190 Status::new(Code::Cancelled, message)
191 }
192
193 pub fn unknown(message: impl Into<String>) -> Status {
198 Status::new(Code::Unknown, message)
199 }
200
201 pub fn invalid_argument(message: impl Into<String>) -> Status {
206 Status::new(Code::InvalidArgument, message)
207 }
208
209 pub fn deadline_exceeded(message: impl Into<String>) -> Status {
215 Status::new(Code::DeadlineExceeded, message)
216 }
217
218 pub fn not_found(message: impl Into<String>) -> Status {
220 Status::new(Code::NotFound, message)
221 }
222
223 pub fn already_exists(message: impl Into<String>) -> Status {
226 Status::new(Code::AlreadyExists, message)
227 }
228
229 pub fn permission_denied(message: impl Into<String>) -> Status {
235 Status::new(Code::PermissionDenied, message)
236 }
237
238 pub fn resource_exhausted(message: impl Into<String>) -> Status {
241 Status::new(Code::ResourceExhausted, message)
242 }
243
244 pub fn failed_precondition(message: impl Into<String>) -> Status {
259 Status::new(Code::FailedPrecondition, message)
260 }
261
262 pub fn aborted(message: impl Into<String>) -> Status {
268 Status::new(Code::Aborted, message)
269 }
270
271 pub fn out_of_range(message: impl Into<String>) -> Status {
285 Status::new(Code::OutOfRange, message)
286 }
287
288 pub fn unimplemented(message: impl Into<String>) -> Status {
290 Status::new(Code::Unimplemented, message)
291 }
292
293 pub fn internal(message: impl Into<String>) -> Status {
296 Status::new(Code::Internal, message)
297 }
298
299 pub fn unavailable(message: impl Into<String>) -> Status {
305 Status::new(Code::Unavailable, message)
306 }
307
308 pub fn data_loss(message: impl Into<String>) -> Status {
310 Status::new(Code::DataLoss, message)
311 }
312
313 pub fn unauthenticated(message: impl Into<String>) -> Status {
316 Status::new(Code::Unauthenticated, message)
317 }
318
319 pub(crate) fn from_error_generic(
320 err: impl Into<Box<dyn Error + Send + Sync + 'static>>,
321 ) -> Status {
322 Self::from_error(err.into())
323 }
324
325 pub fn from_error(err: Box<dyn Error + Send + Sync + 'static>) -> Status {
330 Status::try_from_error(err).unwrap_or_else(|err| {
331 let mut status = Status::new(Code::Unknown, err.to_string());
332 status.0.source = Some(err.into());
333 status
334 })
335 }
336
337 pub fn try_from_error(
345 err: Box<dyn Error + Send + Sync + 'static>,
346 ) -> Result<Status, Box<dyn Error + Send + Sync + 'static>> {
347 let err = match err.downcast::<Status>() {
348 Ok(status) => {
349 return Ok(*status);
350 }
351 Err(err) => err,
352 };
353
354 #[cfg(feature = "server")]
355 let err = match err.downcast::<h2::Error>() {
356 Ok(h2) => {
357 return Ok(Status::from_h2_error(h2));
358 }
359 Err(err) => err,
360 };
361
362 #[cfg(feature = "server")]
365 let err = match err.downcast::<tower::load_shed::error::Overloaded>() {
366 Ok(_) => {
367 return Ok(Status::resource_exhausted(
368 "Too many active requests for the connection",
369 ));
370 }
371 Err(err) => err,
372 };
373
374 if let Some(mut status) = find_status_in_source_chain(&*err) {
375 status.0.source = Some(err.into());
376 return Ok(status);
377 }
378
379 Err(err)
380 }
381
382 #[cfg(feature = "server")]
384 fn from_h2_error(err: Box<h2::Error>) -> Status {
385 let code = Self::code_from_h2(&err);
386
387 let mut status = Self::new(code, format!("h2 protocol error: {err}"));
388 status.0.source = Some(Arc::new(*err));
389 status
390 }
391
392 #[cfg(feature = "server")]
393 fn code_from_h2(err: &h2::Error) -> Code {
394 match err.reason() {
396 Some(h2::Reason::NO_ERROR)
399 | Some(h2::Reason::PROTOCOL_ERROR)
400 | Some(h2::Reason::INTERNAL_ERROR)
401 | Some(h2::Reason::FLOW_CONTROL_ERROR)
402 | Some(h2::Reason::SETTINGS_TIMEOUT)
403 | Some(h2::Reason::COMPRESSION_ERROR)
404 | Some(h2::Reason::CONNECT_ERROR) => Code::Internal,
405 Some(h2::Reason::REFUSED_STREAM) => Code::Unavailable,
406 Some(h2::Reason::CANCEL) => Code::Cancelled,
407 Some(h2::Reason::ENHANCE_YOUR_CALM) => Code::ResourceExhausted,
408 Some(h2::Reason::INADEQUATE_SECURITY) => Code::PermissionDenied,
409
410 _ => Code::Unknown,
411 }
412 }
413
414 #[cfg(feature = "server")]
415 fn to_h2_error(&self) -> h2::Error {
416 let reason = match self.code() {
418 Code::Cancelled => h2::Reason::CANCEL,
419 _ => h2::Reason::INTERNAL_ERROR,
420 };
421
422 reason.into()
423 }
424
425 #[cfg(any(feature = "server", feature = "channel"))]
431 fn from_hyper_error(err: &hyper::Error) -> Option<Status> {
432 if err.is_timeout() {
439 return Some(Status::unavailable(err.to_string()));
440 }
441
442 if err.is_canceled() {
443 return Some(Status::cancelled(err.to_string()));
444 }
445
446 #[cfg(feature = "server")]
447 if let Some(h2_err) = err.source().and_then(|e| e.downcast_ref::<h2::Error>()) {
448 let code = Status::code_from_h2(h2_err);
449 let status = Self::new(code, format!("h2 protocol error: {err}"));
450
451 return Some(status);
452 }
453
454 None
455 }
456
457 pub(crate) fn map_error<E>(err: E) -> Status
458 where
459 E: Into<Box<dyn Error + Send + Sync>>,
460 {
461 let err: Box<dyn Error + Send + Sync> = err.into();
462 Status::from_error(err)
463 }
464
465 pub fn from_header_map(header_map: &HeaderMap) -> Option<Status> {
467 let code = Code::from_bytes(header_map.get(Self::GRPC_STATUS)?.as_ref());
468
469 let error_message = match header_map.get(Self::GRPC_MESSAGE) {
470 Some(header) => percent_decode(header.as_bytes())
471 .decode_utf8()
472 .map(|cow| cow.to_string()),
473 None => Ok(String::new()),
474 };
475
476 let details = match header_map.get(Self::GRPC_STATUS_DETAILS) {
477 Some(header) => crate::util::base64::STANDARD
478 .decode(header.as_bytes())
479 .expect("Invalid status header, expected base64 encoded value")
480 .into(),
481 None => Bytes::new(),
482 };
483
484 let other_headers = {
485 let mut header_map = header_map.clone();
486 header_map.remove(Self::GRPC_STATUS);
487 header_map.remove(Self::GRPC_MESSAGE);
488 header_map.remove(Self::GRPC_STATUS_DETAILS);
489 header_map
490 };
491
492 let (code, message) = match error_message {
493 Ok(message) => (code, message),
494 Err(e) => {
495 let error_message = format!("Error deserializing status message header: {e}");
496 warn!(error_message);
497 (Code::Unknown, error_message)
498 }
499 };
500
501 Some(
502 StatusInner {
503 code,
504 message,
505 details,
506 metadata: MetadataMap::from_headers(other_headers),
507 source: None,
508 }
509 .into_status(),
510 )
511 }
512
513 pub fn code(&self) -> Code {
515 self.0.code
516 }
517
518 pub fn message(&self) -> &str {
520 &self.0.message
521 }
522
523 pub fn details(&self) -> &[u8] {
525 &self.0.details
526 }
527
528 pub fn metadata(&self) -> &MetadataMap {
530 &self.0.metadata
531 }
532
533 pub fn metadata_mut(&mut self) -> &mut MetadataMap {
535 &mut self.0.metadata
536 }
537
538 pub(crate) fn to_header_map(&self) -> Result<HeaderMap, Self> {
539 let mut header_map = HeaderMap::with_capacity(3 + self.0.metadata.len());
540 self.add_header(&mut header_map)?;
541 Ok(header_map)
542 }
543
544 pub fn add_header(&self, header_map: &mut HeaderMap) -> Result<(), Self> {
546 header_map.extend(self.0.metadata.clone().into_sanitized_headers());
547
548 header_map.insert(Self::GRPC_STATUS, self.0.code.to_header_value());
549
550 if !self.0.message.is_empty() {
551 let to_write = Bytes::copy_from_slice(
552 Cow::from(percent_encode(self.message().as_bytes(), ENCODING_SET)).as_bytes(),
553 );
554
555 header_map.insert(
556 Self::GRPC_MESSAGE,
557 HeaderValue::from_maybe_shared(to_write).map_err(invalid_header_value_byte)?,
558 );
559 }
560
561 if !self.0.details.is_empty() {
562 let details = crate::util::base64::STANDARD_NO_PAD.encode(&self.0.details[..]);
563
564 header_map.insert(
565 Self::GRPC_STATUS_DETAILS,
566 HeaderValue::from_maybe_shared(details).map_err(invalid_header_value_byte)?,
567 );
568 }
569
570 Ok(())
571 }
572
573 pub fn with_details(code: Code, message: impl Into<String>, details: Bytes) -> Status {
575 Self::with_details_and_metadata(code, message, details, MetadataMap::new())
576 }
577
578 pub fn with_metadata(code: Code, message: impl Into<String>, metadata: MetadataMap) -> Status {
580 Self::with_details_and_metadata(code, message, Bytes::new(), metadata)
581 }
582
583 pub fn with_details_and_metadata(
585 code: Code,
586 message: impl Into<String>,
587 details: Bytes,
588 metadata: MetadataMap,
589 ) -> Status {
590 StatusInner {
591 code,
592 message: message.into(),
593 details,
594 metadata,
595 source: None,
596 }
597 .into_status()
598 }
599
600 pub fn set_source(&mut self, source: Arc<dyn Error + Send + Sync + 'static>) -> &mut Status {
602 self.0.source = Some(source);
603 self
604 }
605
606 pub fn into_http<B: Default>(self) -> http::Response<B> {
608 let mut response = http::Response::new(B::default());
609 response
610 .headers_mut()
611 .insert(http::header::CONTENT_TYPE, GRPC_CONTENT_TYPE);
612 self.add_header(response.headers_mut()).unwrap();
613 response.extensions_mut().insert(self);
614 response
615 }
616
617 #[doc(hidden)]
618 pub const GRPC_STATUS: HeaderName = HeaderName::from_static("grpc-status");
619 #[doc(hidden)]
620 pub const GRPC_MESSAGE: HeaderName = HeaderName::from_static("grpc-message");
621 #[doc(hidden)]
622 pub const GRPC_STATUS_DETAILS: HeaderName = HeaderName::from_static("grpc-status-details-bin");
623}
624
625fn find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option<Status> {
626 let mut source = Some(err);
627
628 while let Some(err) = source {
629 if let Some(status) = err.downcast_ref::<Status>() {
630 return Some(
631 StatusInner {
632 code: status.0.code,
633 message: status.0.message.clone(),
634 details: status.0.details.clone(),
635 metadata: status.0.metadata.clone(),
636 source: None,
639 }
640 .into_status(),
641 );
642 }
643
644 if let Some(timeout) = err.downcast_ref::<TimeoutExpired>() {
645 return Some(Status::cancelled(timeout.to_string()));
646 }
647
648 if let Some(connect) = err.downcast_ref::<ConnectError>() {
654 return Some(Status::unavailable(connect.to_string()));
655 }
656
657 #[cfg(any(feature = "server", feature = "channel"))]
658 if let Some(hyper) = err
659 .downcast_ref::<hyper::Error>()
660 .and_then(Status::from_hyper_error)
661 {
662 return Some(hyper);
663 }
664
665 source = err.source();
666 }
667
668 None
669}
670
671impl fmt::Debug for Status {
672 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
673 self.0.fmt(f)
674 }
675}
676
677impl fmt::Debug for StatusInner {
678 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
679 let mut builder = f.debug_struct("Status");
681
682 builder.field("code", &self.code);
683
684 if !self.message.is_empty() {
685 builder.field("message", &self.message);
686 }
687
688 if !self.details.is_empty() {
689 builder.field("details", &self.details);
690 }
691
692 if !self.metadata.is_empty() {
693 builder.field("metadata", &self.metadata);
694 }
695
696 builder.field("source", &self.source);
697
698 builder.finish()
699 }
700}
701
702fn invalid_header_value_byte<Error: fmt::Display>(err: Error) -> Status {
703 debug!("Invalid header: {}", err);
704 Status::new(
705 Code::Internal,
706 "Couldn't serialize non-text grpc status header".to_string(),
707 )
708}
709
710#[cfg(feature = "server")]
711impl From<h2::Error> for Status {
712 fn from(err: h2::Error) -> Self {
713 Status::from_h2_error(Box::new(err))
714 }
715}
716
717#[cfg(feature = "server")]
718impl From<Status> for h2::Error {
719 fn from(status: Status) -> Self {
720 status.to_h2_error()
721 }
722}
723
724impl From<std::io::Error> for Status {
725 fn from(err: std::io::Error) -> Self {
726 use std::io::ErrorKind;
727 let code = match err.kind() {
728 ErrorKind::BrokenPipe
729 | ErrorKind::WouldBlock
730 | ErrorKind::WriteZero
731 | ErrorKind::Interrupted => Code::Internal,
732 ErrorKind::ConnectionRefused
733 | ErrorKind::ConnectionReset
734 | ErrorKind::NotConnected
735 | ErrorKind::AddrInUse
736 | ErrorKind::AddrNotAvailable => Code::Unavailable,
737 ErrorKind::AlreadyExists => Code::AlreadyExists,
738 ErrorKind::ConnectionAborted => Code::Aborted,
739 ErrorKind::InvalidData => Code::DataLoss,
740 ErrorKind::InvalidInput => Code::InvalidArgument,
741 ErrorKind::NotFound => Code::NotFound,
742 ErrorKind::PermissionDenied => Code::PermissionDenied,
743 ErrorKind::TimedOut => Code::DeadlineExceeded,
744 ErrorKind::UnexpectedEof => Code::OutOfRange,
745 _ => Code::Unknown,
746 };
747 Status::new(code, err.to_string())
748 }
749}
750
751impl fmt::Display for Status {
752 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
753 write!(f, "code: '{}'", self.code())?;
754
755 if !self.message().is_empty() {
756 write!(f, ", message: {:?}", self.message())?;
757 }
758 if let Some(source) = self.source() {
762 write!(f, ", source: {source:?}")?;
763 }
764 Ok(())
765 }
766}
767
768impl Error for Status {
769 fn source(&self) -> Option<&(dyn Error + 'static)> {
770 self.0.source.as_ref().map(|err| (&**err) as _)
771 }
772}
773
774pub(crate) fn infer_grpc_status(
778 trailers: Option<&HeaderMap>,
779 status_code: http::StatusCode,
780) -> Result<(), Option<Status>> {
781 if let Some(trailers) = trailers {
782 if let Some(status) = Status::from_header_map(trailers) {
783 if status.code() == Code::Ok {
784 return Ok(());
785 } else {
786 return Err(status.into());
787 }
788 }
789 }
790 trace!("trailers missing grpc-status");
791 let code = match status_code {
792 http::StatusCode::BAD_REQUEST => Code::Internal,
794 http::StatusCode::UNAUTHORIZED => Code::Unauthenticated,
795 http::StatusCode::FORBIDDEN => Code::PermissionDenied,
796 http::StatusCode::NOT_FOUND => Code::Unimplemented,
797 http::StatusCode::TOO_MANY_REQUESTS
798 | http::StatusCode::BAD_GATEWAY
799 | http::StatusCode::SERVICE_UNAVAILABLE
800 | http::StatusCode::GATEWAY_TIMEOUT => Code::Unavailable,
801 http::StatusCode::OK => {
828 return Err(Some(Status::unknown(
829 "protocol error: missing grpc-status trailer, stream was terminated without a final status (possible truncation by a proxy or load balancer)",
830 )));
831 }
832 _ => Code::Unknown,
833 };
834
835 let msg = format!(
836 "grpc-status header missing, mapped from HTTP status code {}",
837 status_code.as_u16(),
838 );
839 let status = Status::new(code, msg);
840 Err(status.into())
841}
842
843impl Code {
846 pub const fn from_i32(i: i32) -> Code {
850 match i {
851 0 => Code::Ok,
852 1 => Code::Cancelled,
853 2 => Code::Unknown,
854 3 => Code::InvalidArgument,
855 4 => Code::DeadlineExceeded,
856 5 => Code::NotFound,
857 6 => Code::AlreadyExists,
858 7 => Code::PermissionDenied,
859 8 => Code::ResourceExhausted,
860 9 => Code::FailedPrecondition,
861 10 => Code::Aborted,
862 11 => Code::OutOfRange,
863 12 => Code::Unimplemented,
864 13 => Code::Internal,
865 14 => Code::Unavailable,
866 15 => Code::DataLoss,
867 16 => Code::Unauthenticated,
868
869 _ => Code::Unknown,
870 }
871 }
872
873 pub fn from_bytes(bytes: &[u8]) -> Code {
877 match bytes.len() {
878 1 => match bytes[0] {
879 b'0' => Code::Ok,
880 b'1' => Code::Cancelled,
881 b'2' => Code::Unknown,
882 b'3' => Code::InvalidArgument,
883 b'4' => Code::DeadlineExceeded,
884 b'5' => Code::NotFound,
885 b'6' => Code::AlreadyExists,
886 b'7' => Code::PermissionDenied,
887 b'8' => Code::ResourceExhausted,
888 b'9' => Code::FailedPrecondition,
889 _ => Code::parse_err(),
890 },
891 2 => match (bytes[0], bytes[1]) {
892 (b'1', b'0') => Code::Aborted,
893 (b'1', b'1') => Code::OutOfRange,
894 (b'1', b'2') => Code::Unimplemented,
895 (b'1', b'3') => Code::Internal,
896 (b'1', b'4') => Code::Unavailable,
897 (b'1', b'5') => Code::DataLoss,
898 (b'1', b'6') => Code::Unauthenticated,
899 _ => Code::parse_err(),
900 },
901 _ => Code::parse_err(),
902 }
903 }
904
905 fn to_header_value(self) -> HeaderValue {
906 match self {
907 Code::Ok => HeaderValue::from_static("0"),
908 Code::Cancelled => HeaderValue::from_static("1"),
909 Code::Unknown => HeaderValue::from_static("2"),
910 Code::InvalidArgument => HeaderValue::from_static("3"),
911 Code::DeadlineExceeded => HeaderValue::from_static("4"),
912 Code::NotFound => HeaderValue::from_static("5"),
913 Code::AlreadyExists => HeaderValue::from_static("6"),
914 Code::PermissionDenied => HeaderValue::from_static("7"),
915 Code::ResourceExhausted => HeaderValue::from_static("8"),
916 Code::FailedPrecondition => HeaderValue::from_static("9"),
917 Code::Aborted => HeaderValue::from_static("10"),
918 Code::OutOfRange => HeaderValue::from_static("11"),
919 Code::Unimplemented => HeaderValue::from_static("12"),
920 Code::Internal => HeaderValue::from_static("13"),
921 Code::Unavailable => HeaderValue::from_static("14"),
922 Code::DataLoss => HeaderValue::from_static("15"),
923 Code::Unauthenticated => HeaderValue::from_static("16"),
924 }
925 }
926
927 fn parse_err() -> Code {
928 trace!("error parsing grpc-status");
929 Code::Unknown
930 }
931}
932
933impl From<i32> for Code {
934 fn from(i: i32) -> Self {
935 Code::from_i32(i)
936 }
937}
938
939impl From<Code> for i32 {
940 #[inline]
941 fn from(code: Code) -> i32 {
942 code as i32
943 }
944}
945
946#[cfg(test)]
947mod tests {
948 use super::*;
949 use crate::BoxError;
950
951 #[derive(Debug)]
952 struct Nested(BoxError);
953
954 impl fmt::Display for Nested {
955 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
956 write!(f, "nested error: {}", self.0)
957 }
958 }
959
960 impl std::error::Error for Nested {
961 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
962 Some(&*self.0)
963 }
964 }
965
966 #[test]
967 fn from_error_status() {
968 let orig = Status::new(Code::OutOfRange, "weeaboo");
969 let found = Status::from_error(Box::new(orig));
970
971 assert_eq!(found.code(), Code::OutOfRange);
972 assert_eq!(found.message(), "weeaboo");
973 }
974
975 #[test]
976 fn from_error_unknown() {
977 let orig: BoxError = "peek-a-boo".into();
978 let found = Status::from_error(orig);
979
980 assert_eq!(found.code(), Code::Unknown);
981 assert_eq!(found.message(), "peek-a-boo".to_string());
982 }
983
984 #[test]
985 fn from_error_nested() {
986 let orig = Nested(Box::new(Status::new(Code::OutOfRange, "weeaboo")));
987 let found = Status::from_error(Box::new(orig));
988
989 assert_eq!(found.code(), Code::OutOfRange);
990 assert_eq!(found.message(), "weeaboo");
991 }
992
993 #[test]
994 #[cfg(feature = "server")]
995 fn from_error_h2() {
996 use std::error::Error as _;
997
998 let orig = h2::Error::from(h2::Reason::CANCEL);
999 let found = Status::from_error(Box::new(orig));
1000
1001 assert_eq!(found.code(), Code::Cancelled);
1002
1003 let source = found
1004 .source()
1005 .and_then(|err| err.downcast_ref::<h2::Error>())
1006 .unwrap();
1007 assert_eq!(source.reason(), Some(h2::Reason::CANCEL));
1008 }
1009
1010 #[test]
1011 #[cfg(feature = "server")]
1012 fn to_h2_error() {
1013 let orig = Status::new(Code::Cancelled, "stop eet!");
1014 let err = orig.to_h2_error();
1015
1016 assert_eq!(err.reason(), Some(h2::Reason::CANCEL));
1017 }
1018
1019 #[test]
1020 fn code_from_i32() {
1021 for i in 0..(Code::Unauthenticated as i32) {
1024 let code = Code::from(i);
1025 assert_eq!(
1026 i, code as i32,
1027 "Code::from({}) returned {:?} which is {}",
1028 i, code, code as i32,
1029 );
1030 }
1031
1032 assert_eq!(Code::from(-1), Code::Unknown);
1033 }
1034
1035 #[test]
1036 fn constructors() {
1037 assert_eq!(Status::ok("").code(), Code::Ok);
1038 assert_eq!(Status::cancelled("").code(), Code::Cancelled);
1039 assert_eq!(Status::unknown("").code(), Code::Unknown);
1040 assert_eq!(Status::invalid_argument("").code(), Code::InvalidArgument);
1041 assert_eq!(Status::deadline_exceeded("").code(), Code::DeadlineExceeded);
1042 assert_eq!(Status::not_found("").code(), Code::NotFound);
1043 assert_eq!(Status::already_exists("").code(), Code::AlreadyExists);
1044 assert_eq!(Status::permission_denied("").code(), Code::PermissionDenied);
1045 assert_eq!(
1046 Status::resource_exhausted("").code(),
1047 Code::ResourceExhausted
1048 );
1049 assert_eq!(
1050 Status::failed_precondition("").code(),
1051 Code::FailedPrecondition
1052 );
1053 assert_eq!(Status::aborted("").code(), Code::Aborted);
1054 assert_eq!(Status::out_of_range("").code(), Code::OutOfRange);
1055 assert_eq!(Status::unimplemented("").code(), Code::Unimplemented);
1056 assert_eq!(Status::internal("").code(), Code::Internal);
1057 assert_eq!(Status::unavailable("").code(), Code::Unavailable);
1058 assert_eq!(Status::data_loss("").code(), Code::DataLoss);
1059 assert_eq!(Status::unauthenticated("").code(), Code::Unauthenticated);
1060 }
1061
1062 #[test]
1063 fn details() {
1064 const DETAILS: &[u8] = &[0, 2, 3];
1065
1066 let status = Status::with_details(Code::Unavailable, "some message", DETAILS.into());
1067
1068 assert_eq!(status.details(), DETAILS);
1069
1070 let header_map = status.to_header_map().unwrap();
1071
1072 let b64_details = crate::util::base64::STANDARD_NO_PAD.encode(DETAILS);
1073
1074 assert_eq!(header_map[Status::GRPC_STATUS_DETAILS], b64_details);
1075
1076 let status = Status::from_header_map(&header_map).unwrap();
1077
1078 assert_eq!(status.details(), DETAILS);
1079 }
1080}
1081
1082#[derive(Debug)]
1091pub struct TimeoutExpired(pub ());
1092
1093impl fmt::Display for TimeoutExpired {
1094 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1095 write!(f, "Timeout expired")
1096 }
1097}
1098
1099impl std::error::Error for TimeoutExpired {}
1101
1102#[derive(Debug)]
1105pub struct ConnectError(pub Box<dyn std::error::Error + Send + Sync>);
1106
1107impl fmt::Display for ConnectError {
1108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1109 fmt::Display::fmt(&self.0, f)
1110 }
1111}
1112
1113impl std::error::Error for ConnectError {
1114 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1115 Some(self.0.as_ref())
1116 }
1117}