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#[derive(Clone)]
37pub struct Status {
38 code: Code,
40 message: String,
42 details: Bytes,
44 metadata: MetadataMap,
48 source: Option<Arc<dyn Error + Send + Sync + 'static>>,
50}
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
58pub enum Code {
59 Ok = 0,
61
62 Cancelled = 1,
64
65 Unknown = 2,
67
68 InvalidArgument = 3,
70
71 DeadlineExceeded = 4,
73
74 NotFound = 5,
76
77 AlreadyExists = 6,
79
80 PermissionDenied = 7,
82
83 ResourceExhausted = 8,
85
86 FailedPrecondition = 9,
88
89 Aborted = 10,
91
92 OutOfRange = 11,
94
95 Unimplemented = 12,
97
98 Internal = 13,
100
101 Unavailable = 14,
103
104 DataLoss = 15,
106
107 Unauthenticated = 16,
109}
110
111impl Code {
112 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
157impl Status {
160 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 pub fn ok(message: impl Into<String>) -> Status {
173 Status::new(Code::Ok, message)
174 }
175
176 pub fn cancelled(message: impl Into<String>) -> Status {
178 Status::new(Code::Cancelled, message)
179 }
180
181 pub fn unknown(message: impl Into<String>) -> Status {
186 Status::new(Code::Unknown, message)
187 }
188
189 pub fn invalid_argument(message: impl Into<String>) -> Status {
194 Status::new(Code::InvalidArgument, message)
195 }
196
197 pub fn deadline_exceeded(message: impl Into<String>) -> Status {
203 Status::new(Code::DeadlineExceeded, message)
204 }
205
206 pub fn not_found(message: impl Into<String>) -> Status {
208 Status::new(Code::NotFound, message)
209 }
210
211 pub fn already_exists(message: impl Into<String>) -> Status {
214 Status::new(Code::AlreadyExists, message)
215 }
216
217 pub fn permission_denied(message: impl Into<String>) -> Status {
223 Status::new(Code::PermissionDenied, message)
224 }
225
226 pub fn resource_exhausted(message: impl Into<String>) -> Status {
229 Status::new(Code::ResourceExhausted, message)
230 }
231
232 pub fn failed_precondition(message: impl Into<String>) -> Status {
247 Status::new(Code::FailedPrecondition, message)
248 }
249
250 pub fn aborted(message: impl Into<String>) -> Status {
256 Status::new(Code::Aborted, message)
257 }
258
259 pub fn out_of_range(message: impl Into<String>) -> Status {
273 Status::new(Code::OutOfRange, message)
274 }
275
276 pub fn unimplemented(message: impl Into<String>) -> Status {
278 Status::new(Code::Unimplemented, message)
279 }
280
281 pub fn internal(message: impl Into<String>) -> Status {
284 Status::new(Code::Internal, message)
285 }
286
287 pub fn unavailable(message: impl Into<String>) -> Status {
293 Status::new(Code::Unavailable, message)
294 }
295
296 pub fn data_loss(message: impl Into<String>) -> Status {
298 Status::new(Code::DataLoss, message)
299 }
300
301 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 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 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 #[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 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 let reason = match self.code {
392 Code::Cancelled => h2::Reason::CANCEL,
393 _ => h2::Reason::INTERNAL_ERROR,
394 };
395
396 reason.into()
397 }
398
399 #[cfg(any(feature = "server", feature = "channel"))]
405 fn from_hyper_error(err: &hyper::Error) -> Option<Status> {
406 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 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 pub fn code(&self) -> Code {
491 self.code
492 }
493
494 pub fn message(&self) -> &str {
496 &self.message
497 }
498
499 pub fn details(&self) -> &[u8] {
501 &self.details
502 }
503
504 pub fn metadata(&self) -> &MetadataMap {
506 &self.metadata
507 }
508
509 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 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 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 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 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 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 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 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 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 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
735pub(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 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 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
780impl Code {
783 pub fn from_i32(i: i32) -> Code {
787 Code::from(i)
788 }
789
790 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 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#[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
1036impl std::error::Error for TimeoutExpired {}
1038
1039#[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}