Skip to content

Commit 67250d1

Browse files
committed
fix(status): prevent panic on invalid base64 in grpc-status-details-bin
Instead of panicking with `.expect()` when base64 decoding of the `grpc-status-details-bin` header fails, fall back to setting the status code to `Unknown` and include the decoding error in the message. This is consistent with how Tonic handles failures to decode the `grpc-message` header. Added a unit test `details_invalid_base64` to verify this behavior.
1 parent f9a885b commit 67250d1

1 file changed

Lines changed: 25 additions & 8 deletions

File tree

tonic/src/status.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -473,12 +473,11 @@ impl Status {
473473
None => Ok(String::new()),
474474
};
475475

476-
let details = match header_map.get(Self::GRPC_STATUS_DETAILS) {
476+
let details_result = match header_map.get(Self::GRPC_STATUS_DETAILS) {
477477
Some(header) => crate::util::base64::STANDARD
478478
.decode(header.as_bytes())
479-
.expect("Invalid status header, expected base64 encoded value")
480-
.into(),
481-
None => Bytes::new(),
479+
.map(Bytes::from),
480+
None => Ok(Bytes::new()),
482481
};
483482

484483
let other_headers = {
@@ -489,12 +488,17 @@ impl Status {
489488
header_map
490489
};
491490

492-
let (code, message) = match error_message {
493-
Ok(message) => (code, message),
494-
Err(e) => {
491+
let (code, message, details) = match (error_message, details_result) {
492+
(Ok(msg), Ok(det)) => (code, msg, det),
493+
(Err(e), _) => {
495494
let error_message = format!("Error deserializing status message header: {e}");
496495
warn!(error_message);
497-
(Code::Unknown, error_message)
496+
(Code::Unknown, error_message, Bytes::new())
497+
}
498+
(Ok(_msg), Err(e)) => {
499+
let error_message = format!("Error deserializing status details header: {e}");
500+
warn!(error_message);
501+
(Code::Unknown, error_message, Bytes::new())
498502
}
499503
};
500504

@@ -1077,6 +1081,19 @@ mod tests {
10771081

10781082
assert_eq!(status.details(), DETAILS);
10791083
}
1084+
1085+
#[test]
1086+
fn details_invalid_base64() {
1087+
let mut header_map = HeaderMap::new();
1088+
header_map.insert(Status::GRPC_STATUS, HeaderValue::from_static("14")); // Unavailable
1089+
header_map.insert(Status::GRPC_STATUS_DETAILS, HeaderValue::from_static("invalid base64!"));
1090+
1091+
let status = Status::from_header_map(&header_map).unwrap();
1092+
1093+
assert_eq!(status.code(), Code::Unknown);
1094+
assert!(status.message().contains("Error deserializing status details header"));
1095+
assert!(status.details().is_empty());
1096+
}
10801097
}
10811098

10821099
/// Error returned if a request didn't complete within the configured timeout.

0 commit comments

Comments
 (0)