1use crate::metadata::GRPC_CONTENT_TYPE;
26use crate::metadata::MetadataMap;
27use base64::Engine as _;
28use bytes::Bytes;
29use http::{
30 HeaderName,
31 header::{HeaderMap, HeaderValue},
32};
33use percent_encoding::{AsciiSet, CONTROLS, percent_decode, percent_encode};
34use std::{borrow::Cow, error::Error, fmt, sync::Arc};
35use tracing::{debug, trace, warn};
36
37const ENCODING_SET: &AsciiSet = &CONTROLS
38 .add(b' ')
39 .add(b'"')
40 .add(b'#')
41 .add(b'%')
42 .add(b'<')
43 .add(b'>')
44 .add(b'`')
45 .add(b'?')
46 .add(b'{')
47 .add(b'}');
48
49#[derive(Clone)]
62pub struct Status(Box<StatusInner>);
63
64#[derive(Clone)]
66struct StatusInner {
67 code: Code,
69 message: String,
71 details: Bytes,
73 metadata: MetadataMap,
77 source: Option<Arc<dyn Error + Send + Sync + 'static>>,
79}
80
81impl StatusInner {
82 fn into_status(self) -> Status {
83 Status(Box::new(self))
84 }
85}
86
87#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
93pub enum Code {
94 Ok = 0,
96
97 Cancelled = 1,
99
100 Unknown = 2,
102
103 InvalidArgument = 3,
105
106 DeadlineExceeded = 4,
108
109 NotFound = 5,
111
112 AlreadyExists = 6,
114
115 PermissionDenied = 7,
117
118 ResourceExhausted = 8,
120
121 FailedPrecondition = 9,
123
124 Aborted = 10,
126
127 OutOfRange = 11,
129
130 Unimplemented = 12,
132
133 Internal = 13,
135
136 Unavailable = 14,
138
139 DataLoss = 15,
141
142 Unauthenticated = 16,
144}
145
146impl Code {
147 pub fn description(&self) -> &'static str {
160 match self {
161 Code::Ok => "The operation completed successfully",
162 Code::Cancelled => "The operation was cancelled",
163 Code::Unknown => "Unknown error",
164 Code::InvalidArgument => "Client specified an invalid argument",
165 Code::DeadlineExceeded => "Deadline expired before operation could complete",
166 Code::NotFound => "Some requested entity was not found",
167 Code::AlreadyExists => "Some entity that we attempted to create already exists",
168 Code::PermissionDenied => {
169 "The caller does not have permission to execute the specified operation"
170 }
171 Code::ResourceExhausted => "Some resource has been exhausted",
172 Code::FailedPrecondition => {
173 "The system is not in a state required for the operation's execution"
174 }
175 Code::Aborted => "The operation was aborted",
176 Code::OutOfRange => "Operation was attempted past the valid range",
177 Code::Unimplemented => "Operation is not implemented or not supported",
178 Code::Internal => "Internal error",
179 Code::Unavailable => "The service is currently unavailable",
180 Code::DataLoss => "Unrecoverable data loss or corruption",
181 Code::Unauthenticated => "The request does not have valid authentication credentials",
182 }
183 }
184}
185
186impl std::fmt::Display for Code {
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 std::fmt::Display::fmt(self.description(), f)
189 }
190}
191
192impl Status {
195 pub fn new(code: Code, message: impl Into<String>) -> Status {
197 StatusInner {
198 code,
199 message: message.into(),
200 details: Bytes::new(),
201 metadata: MetadataMap::new(),
202 source: None,
203 }
204 .into_status()
205 }
206
207 pub fn ok(message: impl Into<String>) -> Status {
209 Status::new(Code::Ok, message)
210 }
211
212 pub fn cancelled(message: impl Into<String>) -> Status {
214 Status::new(Code::Cancelled, message)
215 }
216
217 pub fn unknown(message: impl Into<String>) -> Status {
222 Status::new(Code::Unknown, message)
223 }
224
225 pub fn invalid_argument(message: impl Into<String>) -> Status {
230 Status::new(Code::InvalidArgument, message)
231 }
232
233 pub fn deadline_exceeded(message: impl Into<String>) -> Status {
239 Status::new(Code::DeadlineExceeded, message)
240 }
241
242 pub fn not_found(message: impl Into<String>) -> Status {
244 Status::new(Code::NotFound, message)
245 }
246
247 pub fn already_exists(message: impl Into<String>) -> Status {
250 Status::new(Code::AlreadyExists, message)
251 }
252
253 pub fn permission_denied(message: impl Into<String>) -> Status {
259 Status::new(Code::PermissionDenied, message)
260 }
261
262 pub fn resource_exhausted(message: impl Into<String>) -> Status {
265 Status::new(Code::ResourceExhausted, message)
266 }
267
268 pub fn failed_precondition(message: impl Into<String>) -> Status {
283 Status::new(Code::FailedPrecondition, message)
284 }
285
286 pub fn aborted(message: impl Into<String>) -> Status {
292 Status::new(Code::Aborted, message)
293 }
294
295 pub fn out_of_range(message: impl Into<String>) -> Status {
309 Status::new(Code::OutOfRange, message)
310 }
311
312 pub fn unimplemented(message: impl Into<String>) -> Status {
314 Status::new(Code::Unimplemented, message)
315 }
316
317 pub fn internal(message: impl Into<String>) -> Status {
320 Status::new(Code::Internal, message)
321 }
322
323 pub fn unavailable(message: impl Into<String>) -> Status {
329 Status::new(Code::Unavailable, message)
330 }
331
332 pub fn data_loss(message: impl Into<String>) -> Status {
334 Status::new(Code::DataLoss, message)
335 }
336
337 pub fn unauthenticated(message: impl Into<String>) -> Status {
340 Status::new(Code::Unauthenticated, message)
341 }
342
343 pub(crate) fn from_error_generic(
344 err: impl Into<Box<dyn Error + Send + Sync + 'static>>,
345 ) -> Status {
346 Self::from_error(err.into())
347 }
348
349 pub fn from_error(err: Box<dyn Error + Send + Sync + 'static>) -> Status {
354 Status::try_from_error(err).unwrap_or_else(|err| {
355 let mut status = Status::new(Code::Unknown, err.to_string());
356 status.0.source = Some(err.into());
357 status
358 })
359 }
360
361 pub fn try_from_error(
369 err: Box<dyn Error + Send + Sync + 'static>,
370 ) -> Result<Status, Box<dyn Error + Send + Sync + 'static>> {
371 let err = match err.downcast::<Status>() {
372 Ok(status) => {
373 return Ok(*status);
374 }
375 Err(err) => err,
376 };
377
378 #[cfg(feature = "server")]
379 let err = match err.downcast::<h2::Error>() {
380 Ok(h2) => {
381 return Ok(Status::from_h2_error(h2));
382 }
383 Err(err) => err,
384 };
385
386 #[cfg(feature = "server")]
389 let err = match err.downcast::<tower::load_shed::error::Overloaded>() {
390 Ok(_) => {
391 return Ok(Status::resource_exhausted(
392 "Too many active requests for the connection",
393 ));
394 }
395 Err(err) => err,
396 };
397
398 if let Some(mut status) = find_status_in_source_chain(&*err) {
399 status.0.source = Some(err.into());
400 return Ok(status);
401 }
402
403 Err(err)
404 }
405
406 #[cfg(feature = "server")]
408 fn from_h2_error(err: Box<h2::Error>) -> Status {
409 let code = Self::code_from_h2(&err);
410
411 let mut status = Self::new(code, format!("h2 protocol error: {err}"));
412 status.0.source = Some(Arc::new(*err));
413 status
414 }
415
416 #[cfg(feature = "server")]
417 fn code_from_h2(err: &h2::Error) -> Code {
418 match err.reason() {
420 Some(h2::Reason::NO_ERROR)
423 | Some(h2::Reason::PROTOCOL_ERROR)
424 | Some(h2::Reason::INTERNAL_ERROR)
425 | Some(h2::Reason::FLOW_CONTROL_ERROR)
426 | Some(h2::Reason::SETTINGS_TIMEOUT)
427 | Some(h2::Reason::FRAME_SIZE_ERROR)
428 | Some(h2::Reason::COMPRESSION_ERROR)
429 | Some(h2::Reason::CONNECT_ERROR) => Code::Internal,
430 Some(h2::Reason::REFUSED_STREAM) => Code::Unavailable,
431 Some(h2::Reason::CANCEL) => Code::Cancelled,
432 Some(h2::Reason::ENHANCE_YOUR_CALM) => Code::ResourceExhausted,
433 Some(h2::Reason::INADEQUATE_SECURITY) => Code::PermissionDenied,
434
435 _ => Code::Unknown,
436 }
437 }
438
439 #[cfg(feature = "server")]
440 fn to_h2_error(&self) -> h2::Error {
441 let reason = match self.code() {
443 Code::Cancelled => h2::Reason::CANCEL,
444 _ => h2::Reason::INTERNAL_ERROR,
445 };
446
447 reason.into()
448 }
449
450 #[cfg(any(feature = "server", feature = "channel"))]
456 fn from_hyper_error(err: &hyper::Error) -> Option<Status> {
457 if err.is_timeout() {
464 return Some(Status::unavailable(err.to_string()));
465 }
466
467 if err.is_canceled() {
468 return Some(Status::cancelled(err.to_string()));
469 }
470
471 #[cfg(feature = "server")]
472 if let Some(h2_err) = err.source().and_then(|e| e.downcast_ref::<h2::Error>()) {
473 let code = Status::code_from_h2(h2_err);
474 let status = Self::new(code, format!("h2 protocol error: {err}"));
475
476 return Some(status);
477 }
478
479 None
480 }
481
482 pub(crate) fn map_error<E>(err: E) -> Status
483 where
484 E: Into<Box<dyn Error + Send + Sync>>,
485 {
486 let err: Box<dyn Error + Send + Sync> = err.into();
487 Status::from_error(err)
488 }
489
490 pub fn from_header_map(header_map: &HeaderMap) -> Option<Status> {
492 let code = Code::from_bytes(header_map.get(Self::GRPC_STATUS)?.as_ref());
493
494 let error_message = match header_map.get(Self::GRPC_MESSAGE) {
495 Some(header) => percent_decode(header.as_bytes())
496 .decode_utf8()
497 .map(|cow| cow.to_string()),
498 None => Ok(String::new()),
499 };
500
501 let details = match header_map.get(Self::GRPC_STATUS_DETAILS) {
502 Some(header) => crate::util::base64::STANDARD
503 .decode(header.as_bytes())
504 .expect("Invalid status header, expected base64 encoded value")
505 .into(),
506 None => Bytes::new(),
507 };
508
509 let other_headers = {
510 let mut header_map = header_map.clone();
511 header_map.remove(Self::GRPC_STATUS);
512 header_map.remove(Self::GRPC_MESSAGE);
513 header_map.remove(Self::GRPC_STATUS_DETAILS);
514 header_map
515 };
516
517 let (code, message) = match error_message {
518 Ok(message) => (code, message),
519 Err(e) => {
520 let error_message = format!("Error deserializing status message header: {e}");
521 warn!(error_message);
522 (Code::Unknown, error_message)
523 }
524 };
525
526 Some(
527 StatusInner {
528 code,
529 message,
530 details,
531 metadata: MetadataMap::from_headers(other_headers),
532 source: None,
533 }
534 .into_status(),
535 )
536 }
537
538 pub fn code(&self) -> Code {
540 self.0.code
541 }
542
543 pub fn message(&self) -> &str {
545 &self.0.message
546 }
547
548 pub fn details(&self) -> &[u8] {
550 &self.0.details
551 }
552
553 pub fn metadata(&self) -> &MetadataMap {
555 &self.0.metadata
556 }
557
558 pub fn metadata_mut(&mut self) -> &mut MetadataMap {
560 &mut self.0.metadata
561 }
562
563 pub(crate) fn to_header_map(&self) -> Result<HeaderMap, Self> {
564 let mut header_map = HeaderMap::with_capacity(3 + self.0.metadata.len());
565 self.add_header(&mut header_map)?;
566 Ok(header_map)
567 }
568
569 pub fn add_header(&self, header_map: &mut HeaderMap) -> Result<(), Self> {
571 header_map.extend(self.0.metadata.clone().into_sanitized_headers());
572
573 header_map.insert(Self::GRPC_STATUS, self.0.code.to_header_value());
574
575 if !self.0.message.is_empty() {
576 let to_write = Bytes::copy_from_slice(
577 Cow::from(percent_encode(self.message().as_bytes(), ENCODING_SET)).as_bytes(),
578 );
579
580 header_map.insert(
581 Self::GRPC_MESSAGE,
582 HeaderValue::from_maybe_shared(to_write).map_err(invalid_header_value_byte)?,
583 );
584 }
585
586 if !self.0.details.is_empty() {
587 let details = crate::util::base64::STANDARD_NO_PAD.encode(&self.0.details[..]);
588
589 header_map.insert(
590 Self::GRPC_STATUS_DETAILS,
591 HeaderValue::from_maybe_shared(details).map_err(invalid_header_value_byte)?,
592 );
593 }
594
595 Ok(())
596 }
597
598 pub fn with_details(code: Code, message: impl Into<String>, details: Bytes) -> Status {
600 Self::with_details_and_metadata(code, message, details, MetadataMap::new())
601 }
602
603 pub fn with_metadata(code: Code, message: impl Into<String>, metadata: MetadataMap) -> Status {
605 Self::with_details_and_metadata(code, message, Bytes::new(), metadata)
606 }
607
608 pub fn with_details_and_metadata(
610 code: Code,
611 message: impl Into<String>,
612 details: Bytes,
613 metadata: MetadataMap,
614 ) -> Status {
615 StatusInner {
616 code,
617 message: message.into(),
618 details,
619 metadata,
620 source: None,
621 }
622 .into_status()
623 }
624
625 pub fn set_source(&mut self, source: Arc<dyn Error + Send + Sync + 'static>) -> &mut Status {
627 self.0.source = Some(source);
628 self
629 }
630
631 pub fn into_http<B: Default>(self) -> http::Response<B> {
633 let mut response = http::Response::new(B::default());
634 response
635 .headers_mut()
636 .insert(http::header::CONTENT_TYPE, GRPC_CONTENT_TYPE);
637 self.add_header(response.headers_mut()).unwrap();
638 response.extensions_mut().insert(self);
639 response
640 }
641
642 #[doc(hidden)]
643 pub const GRPC_STATUS: HeaderName = HeaderName::from_static("grpc-status");
644 #[doc(hidden)]
645 pub const GRPC_MESSAGE: HeaderName = HeaderName::from_static("grpc-message");
646 #[doc(hidden)]
647 pub const GRPC_STATUS_DETAILS: HeaderName = HeaderName::from_static("grpc-status-details-bin");
648}
649
650fn find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option<Status> {
651 let mut source = Some(err);
652
653 while let Some(err) = source {
654 if let Some(status) = err.downcast_ref::<Status>() {
655 return Some(
656 StatusInner {
657 code: status.0.code,
658 message: status.0.message.clone(),
659 details: status.0.details.clone(),
660 metadata: status.0.metadata.clone(),
661 source: None,
664 }
665 .into_status(),
666 );
667 }
668
669 if let Some(timeout) = err.downcast_ref::<TimeoutExpired>() {
670 return Some(Status::cancelled(timeout.to_string()));
671 }
672
673 if let Some(connect) = err.downcast_ref::<ConnectError>() {
679 return Some(Status::unavailable(connect.to_string()));
680 }
681
682 #[cfg(any(feature = "server", feature = "channel"))]
683 if let Some(hyper) = err
684 .downcast_ref::<hyper::Error>()
685 .and_then(Status::from_hyper_error)
686 {
687 return Some(hyper);
688 }
689
690 source = err.source();
691 }
692
693 None
694}
695
696impl fmt::Debug for Status {
697 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
698 self.0.fmt(f)
699 }
700}
701
702impl fmt::Debug for StatusInner {
703 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
704 let mut builder = f.debug_struct("Status");
706
707 builder.field("code", &self.code);
708
709 if !self.message.is_empty() {
710 builder.field("message", &self.message);
711 }
712
713 if !self.details.is_empty() {
714 builder.field("details", &self.details);
715 }
716
717 if !self.metadata.is_empty() {
718 builder.field("metadata", &self.metadata);
719 }
720
721 builder.field("source", &self.source);
722
723 builder.finish()
724 }
725}
726
727fn invalid_header_value_byte<Error: fmt::Display>(err: Error) -> Status {
728 debug!("Invalid header: {}", err);
729 Status::new(
730 Code::Internal,
731 "Couldn't serialize non-text grpc status header".to_string(),
732 )
733}
734
735#[cfg(feature = "server")]
736impl From<h2::Error> for Status {
737 fn from(err: h2::Error) -> Self {
738 Status::from_h2_error(Box::new(err))
739 }
740}
741
742#[cfg(feature = "server")]
743impl From<Status> for h2::Error {
744 fn from(status: Status) -> Self {
745 status.to_h2_error()
746 }
747}
748
749impl From<std::io::Error> for Status {
750 fn from(err: std::io::Error) -> Self {
751 use std::io::ErrorKind;
752 let code = match err.kind() {
753 ErrorKind::BrokenPipe
754 | ErrorKind::WouldBlock
755 | ErrorKind::WriteZero
756 | ErrorKind::Interrupted => Code::Internal,
757 ErrorKind::ConnectionRefused
758 | ErrorKind::ConnectionReset
759 | ErrorKind::NotConnected
760 | ErrorKind::AddrInUse
761 | ErrorKind::AddrNotAvailable => Code::Unavailable,
762 ErrorKind::AlreadyExists => Code::AlreadyExists,
763 ErrorKind::ConnectionAborted => Code::Aborted,
764 ErrorKind::InvalidData => Code::DataLoss,
765 ErrorKind::InvalidInput => Code::InvalidArgument,
766 ErrorKind::NotFound => Code::NotFound,
767 ErrorKind::PermissionDenied => Code::PermissionDenied,
768 ErrorKind::TimedOut => Code::DeadlineExceeded,
769 ErrorKind::UnexpectedEof => Code::OutOfRange,
770 _ => Code::Unknown,
771 };
772 Status::new(code, err.to_string())
773 }
774}
775
776impl fmt::Display for Status {
777 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
778 write!(f, "code: '{}'", self.code())?;
779
780 if !self.message().is_empty() {
781 write!(f, ", message: {:?}", self.message())?;
782 }
783 if let Some(source) = self.source() {
787 write!(f, ", source: {source:?}")?;
788 }
789 Ok(())
790 }
791}
792
793impl Error for Status {
794 fn source(&self) -> Option<&(dyn Error + 'static)> {
795 self.0.source.as_ref().map(|err| (&**err) as _)
796 }
797}
798
799pub(crate) fn infer_grpc_status(
803 trailers: Option<&HeaderMap>,
804 status_code: http::StatusCode,
805) -> Result<(), Option<Status>> {
806 if let Some(trailers) = trailers
807 && let Some(status) = Status::from_header_map(trailers)
808 {
809 if status.code() == Code::Ok {
810 return Ok(());
811 } else {
812 return Err(status.into());
813 }
814 }
815 trace!("trailers missing grpc-status");
816 let code = match status_code {
817 http::StatusCode::BAD_REQUEST => Code::Internal,
819 http::StatusCode::UNAUTHORIZED => Code::Unauthenticated,
820 http::StatusCode::FORBIDDEN => Code::PermissionDenied,
821 http::StatusCode::NOT_FOUND => Code::Unimplemented,
822 http::StatusCode::TOO_MANY_REQUESTS
823 | http::StatusCode::BAD_GATEWAY
824 | http::StatusCode::SERVICE_UNAVAILABLE
825 | http::StatusCode::GATEWAY_TIMEOUT => Code::Unavailable,
826 http::StatusCode::OK => {
853 return Err(Some(Status::unknown(
854 "protocol error: missing grpc-status trailer, stream was terminated without a final status (possible truncation by a proxy or load balancer)",
855 )));
856 }
857 _ => Code::Unknown,
858 };
859
860 let msg = format!(
861 "grpc-status header missing, mapped from HTTP status code {}",
862 status_code.as_u16(),
863 );
864 let status = Status::new(code, msg);
865 Err(status.into())
866}
867
868impl Code {
871 pub const fn from_i32(i: i32) -> Code {
875 match i {
876 0 => Code::Ok,
877 1 => Code::Cancelled,
878 2 => Code::Unknown,
879 3 => Code::InvalidArgument,
880 4 => Code::DeadlineExceeded,
881 5 => Code::NotFound,
882 6 => Code::AlreadyExists,
883 7 => Code::PermissionDenied,
884 8 => Code::ResourceExhausted,
885 9 => Code::FailedPrecondition,
886 10 => Code::Aborted,
887 11 => Code::OutOfRange,
888 12 => Code::Unimplemented,
889 13 => Code::Internal,
890 14 => Code::Unavailable,
891 15 => Code::DataLoss,
892 16 => Code::Unauthenticated,
893
894 _ => Code::Unknown,
895 }
896 }
897
898 pub fn from_bytes(bytes: &[u8]) -> Code {
902 match bytes.len() {
903 1 => match bytes[0] {
904 b'0' => Code::Ok,
905 b'1' => Code::Cancelled,
906 b'2' => Code::Unknown,
907 b'3' => Code::InvalidArgument,
908 b'4' => Code::DeadlineExceeded,
909 b'5' => Code::NotFound,
910 b'6' => Code::AlreadyExists,
911 b'7' => Code::PermissionDenied,
912 b'8' => Code::ResourceExhausted,
913 b'9' => Code::FailedPrecondition,
914 _ => Code::parse_err(),
915 },
916 2 => match (bytes[0], bytes[1]) {
917 (b'1', b'0') => Code::Aborted,
918 (b'1', b'1') => Code::OutOfRange,
919 (b'1', b'2') => Code::Unimplemented,
920 (b'1', b'3') => Code::Internal,
921 (b'1', b'4') => Code::Unavailable,
922 (b'1', b'5') => Code::DataLoss,
923 (b'1', b'6') => Code::Unauthenticated,
924 _ => Code::parse_err(),
925 },
926 _ => Code::parse_err(),
927 }
928 }
929
930 fn to_header_value(self) -> HeaderValue {
931 match self {
932 Code::Ok => HeaderValue::from_static("0"),
933 Code::Cancelled => HeaderValue::from_static("1"),
934 Code::Unknown => HeaderValue::from_static("2"),
935 Code::InvalidArgument => HeaderValue::from_static("3"),
936 Code::DeadlineExceeded => HeaderValue::from_static("4"),
937 Code::NotFound => HeaderValue::from_static("5"),
938 Code::AlreadyExists => HeaderValue::from_static("6"),
939 Code::PermissionDenied => HeaderValue::from_static("7"),
940 Code::ResourceExhausted => HeaderValue::from_static("8"),
941 Code::FailedPrecondition => HeaderValue::from_static("9"),
942 Code::Aborted => HeaderValue::from_static("10"),
943 Code::OutOfRange => HeaderValue::from_static("11"),
944 Code::Unimplemented => HeaderValue::from_static("12"),
945 Code::Internal => HeaderValue::from_static("13"),
946 Code::Unavailable => HeaderValue::from_static("14"),
947 Code::DataLoss => HeaderValue::from_static("15"),
948 Code::Unauthenticated => HeaderValue::from_static("16"),
949 }
950 }
951
952 fn parse_err() -> Code {
953 trace!("error parsing grpc-status");
954 Code::Unknown
955 }
956}
957
958impl From<i32> for Code {
959 fn from(i: i32) -> Self {
960 Code::from_i32(i)
961 }
962}
963
964impl From<Code> for i32 {
965 #[inline]
966 fn from(code: Code) -> i32 {
967 code as i32
968 }
969}
970
971#[cfg(test)]
972mod tests {
973 use super::*;
974 use crate::BoxError;
975
976 #[derive(Debug)]
977 struct Nested(BoxError);
978
979 impl fmt::Display for Nested {
980 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
981 write!(f, "nested error: {}", self.0)
982 }
983 }
984
985 impl std::error::Error for Nested {
986 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
987 Some(&*self.0)
988 }
989 }
990
991 #[test]
992 fn from_error_status() {
993 let orig = Status::new(Code::OutOfRange, "weeaboo");
994 let found = Status::from_error(Box::new(orig));
995
996 assert_eq!(found.code(), Code::OutOfRange);
997 assert_eq!(found.message(), "weeaboo");
998 }
999
1000 #[test]
1001 fn from_error_unknown() {
1002 let orig: BoxError = "peek-a-boo".into();
1003 let found = Status::from_error(orig);
1004
1005 assert_eq!(found.code(), Code::Unknown);
1006 assert_eq!(found.message(), "peek-a-boo".to_string());
1007 }
1008
1009 #[test]
1010 fn from_error_nested() {
1011 let orig = Nested(Box::new(Status::new(Code::OutOfRange, "weeaboo")));
1012 let found = Status::from_error(Box::new(orig));
1013
1014 assert_eq!(found.code(), Code::OutOfRange);
1015 assert_eq!(found.message(), "weeaboo");
1016 }
1017
1018 #[test]
1019 #[cfg(feature = "server")]
1020 fn from_error_h2() {
1021 use std::error::Error as _;
1022
1023 let orig = h2::Error::from(h2::Reason::CANCEL);
1024 let found = Status::from_error(Box::new(orig));
1025
1026 assert_eq!(found.code(), Code::Cancelled);
1027
1028 let source = found
1029 .source()
1030 .and_then(|err| err.downcast_ref::<h2::Error>())
1031 .unwrap();
1032 assert_eq!(source.reason(), Some(h2::Reason::CANCEL));
1033 }
1034
1035 #[test]
1036 #[cfg(feature = "server")]
1037 fn code_from_h2_frame_size_error() {
1038 let err = h2::Error::from(h2::Reason::FRAME_SIZE_ERROR);
1039 assert_eq!(Status::code_from_h2(&err), Code::Internal);
1040 }
1041
1042 #[test]
1043 #[cfg(feature = "server")]
1044 fn to_h2_error() {
1045 let orig = Status::new(Code::Cancelled, "stop eet!");
1046 let err = orig.to_h2_error();
1047
1048 assert_eq!(err.reason(), Some(h2::Reason::CANCEL));
1049 }
1050
1051 #[test]
1052 fn code_from_i32() {
1053 for i in 0..(Code::Unauthenticated as i32) {
1056 let code = Code::from(i);
1057 assert_eq!(
1058 i, code as i32,
1059 "Code::from({}) returned {:?} which is {}",
1060 i, code, code as i32,
1061 );
1062 }
1063
1064 assert_eq!(Code::from(-1), Code::Unknown);
1065 }
1066
1067 #[test]
1068 fn constructors() {
1069 assert_eq!(Status::ok("").code(), Code::Ok);
1070 assert_eq!(Status::cancelled("").code(), Code::Cancelled);
1071 assert_eq!(Status::unknown("").code(), Code::Unknown);
1072 assert_eq!(Status::invalid_argument("").code(), Code::InvalidArgument);
1073 assert_eq!(Status::deadline_exceeded("").code(), Code::DeadlineExceeded);
1074 assert_eq!(Status::not_found("").code(), Code::NotFound);
1075 assert_eq!(Status::already_exists("").code(), Code::AlreadyExists);
1076 assert_eq!(Status::permission_denied("").code(), Code::PermissionDenied);
1077 assert_eq!(
1078 Status::resource_exhausted("").code(),
1079 Code::ResourceExhausted
1080 );
1081 assert_eq!(
1082 Status::failed_precondition("").code(),
1083 Code::FailedPrecondition
1084 );
1085 assert_eq!(Status::aborted("").code(), Code::Aborted);
1086 assert_eq!(Status::out_of_range("").code(), Code::OutOfRange);
1087 assert_eq!(Status::unimplemented("").code(), Code::Unimplemented);
1088 assert_eq!(Status::internal("").code(), Code::Internal);
1089 assert_eq!(Status::unavailable("").code(), Code::Unavailable);
1090 assert_eq!(Status::data_loss("").code(), Code::DataLoss);
1091 assert_eq!(Status::unauthenticated("").code(), Code::Unauthenticated);
1092 }
1093
1094 #[test]
1095 fn details() {
1096 const DETAILS: &[u8] = &[0, 2, 3];
1097
1098 let status = Status::with_details(Code::Unavailable, "some message", DETAILS.into());
1099
1100 assert_eq!(status.details(), DETAILS);
1101
1102 let header_map = status.to_header_map().unwrap();
1103
1104 let b64_details = crate::util::base64::STANDARD_NO_PAD.encode(DETAILS);
1105
1106 assert_eq!(header_map[Status::GRPC_STATUS_DETAILS], b64_details);
1107
1108 let status = Status::from_header_map(&header_map).unwrap();
1109
1110 assert_eq!(status.details(), DETAILS);
1111 }
1112}
1113
1114#[derive(Debug)]
1123pub struct TimeoutExpired(pub ());
1124
1125impl fmt::Display for TimeoutExpired {
1126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1127 write!(f, "Timeout expired")
1128 }
1129}
1130
1131impl std::error::Error for TimeoutExpired {}
1133
1134#[derive(Debug)]
1137pub struct ConnectError(pub Box<dyn std::error::Error + Send + Sync>);
1138
1139impl fmt::Display for ConnectError {
1140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1141 fmt::Display::fmt(&self.0, f)
1142 }
1143}
1144
1145impl std::error::Error for ConnectError {
1146 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1147 Some(self.0.as_ref())
1148 }
1149}