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#[derive(Clone)]
38pub struct Status {
39 code: Code,
41 message: String,
43 details: Bytes,
45 metadata: MetadataMap,
49 source: Option<Arc<dyn Error + Send + Sync + 'static>>,
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
59pub enum Code {
60 Ok = 0,
62
63 Cancelled = 1,
65
66 Unknown = 2,
68
69 InvalidArgument = 3,
71
72 DeadlineExceeded = 4,
74
75 NotFound = 5,
77
78 AlreadyExists = 6,
80
81 PermissionDenied = 7,
83
84 ResourceExhausted = 8,
86
87 FailedPrecondition = 9,
89
90 Aborted = 10,
92
93 OutOfRange = 11,
95
96 Unimplemented = 12,
98
99 Internal = 13,
101
102 Unavailable = 14,
104
105 DataLoss = 15,
107
108 Unauthenticated = 16,
110}
111
112impl Code {
113 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
158impl Status {
161 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 pub fn ok(message: impl Into<String>) -> Status {
174 Status::new(Code::Ok, message)
175 }
176
177 pub fn cancelled(message: impl Into<String>) -> Status {
179 Status::new(Code::Cancelled, message)
180 }
181
182 pub fn unknown(message: impl Into<String>) -> Status {
187 Status::new(Code::Unknown, message)
188 }
189
190 pub fn invalid_argument(message: impl Into<String>) -> Status {
195 Status::new(Code::InvalidArgument, message)
196 }
197
198 pub fn deadline_exceeded(message: impl Into<String>) -> Status {
204 Status::new(Code::DeadlineExceeded, message)
205 }
206
207 pub fn not_found(message: impl Into<String>) -> Status {
209 Status::new(Code::NotFound, message)
210 }
211
212 pub fn already_exists(message: impl Into<String>) -> Status {
215 Status::new(Code::AlreadyExists, message)
216 }
217
218 pub fn permission_denied(message: impl Into<String>) -> Status {
224 Status::new(Code::PermissionDenied, message)
225 }
226
227 pub fn resource_exhausted(message: impl Into<String>) -> Status {
230 Status::new(Code::ResourceExhausted, message)
231 }
232
233 pub fn failed_precondition(message: impl Into<String>) -> Status {
248 Status::new(Code::FailedPrecondition, message)
249 }
250
251 pub fn aborted(message: impl Into<String>) -> Status {
257 Status::new(Code::Aborted, message)
258 }
259
260 pub fn out_of_range(message: impl Into<String>) -> Status {
274 Status::new(Code::OutOfRange, message)
275 }
276
277 pub fn unimplemented(message: impl Into<String>) -> Status {
279 Status::new(Code::Unimplemented, message)
280 }
281
282 pub fn internal(message: impl Into<String>) -> Status {
285 Status::new(Code::Internal, message)
286 }
287
288 pub fn unavailable(message: impl Into<String>) -> Status {
294 Status::new(Code::Unavailable, message)
295 }
296
297 pub fn data_loss(message: impl Into<String>) -> Status {
299 Status::new(Code::DataLoss, message)
300 }
301
302 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 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 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 #[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 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 let reason = match self.code {
393 Code::Cancelled => h2::Reason::CANCEL,
394 _ => h2::Reason::INTERNAL_ERROR,
395 };
396
397 reason.into()
398 }
399
400 #[cfg(any(feature = "server", feature = "channel"))]
406 fn from_hyper_error(err: &hyper::Error) -> Option<Status> {
407 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 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 pub fn code(&self) -> Code {
487 self.code
488 }
489
490 pub fn message(&self) -> &str {
492 &self.message
493 }
494
495 pub fn details(&self) -> &[u8] {
497 &self.details
498 }
499
500 pub fn metadata(&self) -> &MetadataMap {
502 &self.metadata
503 }
504
505 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 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 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 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 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 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 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 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 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 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
732pub(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 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 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
777impl Code {
780 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 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 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#[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
1033impl std::error::Error for TimeoutExpired {}
1035
1036#[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}