Skip to content

Commit 480919c

Browse files
committed
chore: box Status contents (hyperium#2253)
1 parent 696ea43 commit 480919c

File tree

1 file changed

+60
-36
lines changed

1 file changed

+60
-36
lines changed

tonic/src/status.rs

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,17 @@ const ENCODING_SET: &AsciiSet = &CONTROLS
3535
/// assert_eq!(status1.code(), status2.code());
3636
/// ```
3737
#[derive(Clone)]
38-
pub struct Status {
38+
pub struct Status(Box<StatusInner>);
39+
40+
impl From<StatusInner> for Status {
41+
fn from(value: StatusInner) -> Self {
42+
Self(Box::new(value))
43+
}
44+
}
45+
46+
/// Box the contents of Status to avoid large error variants
47+
#[derive(Clone)]
48+
struct StatusInner {
3949
/// The gRPC status code, found in the `grpc-status` header.
4050
code: Code,
4151
/// A relevant error message, found in the `grpc-message` header.
@@ -160,13 +170,14 @@ impl std::fmt::Display for Code {
160170
impl Status {
161171
/// Create a new `Status` with the associated code and message.
162172
pub fn new(code: Code, message: impl Into<String>) -> Status {
163-
Status {
173+
StatusInner {
164174
code,
165175
message: message.into(),
166176
details: Bytes::new(),
167177
metadata: MetadataMap::new(),
168178
source: None,
169179
}
180+
.into()
170181
}
171182

172183
/// The operation completed successfully.
@@ -318,7 +329,7 @@ impl Status {
318329
pub fn from_error(err: Box<dyn Error + Send + Sync + 'static>) -> Status {
319330
Status::try_from_error(err).unwrap_or_else(|err| {
320331
let mut status = Status::new(Code::Unknown, err.to_string());
321-
status.source = Some(err.into());
332+
status.0.source = Some(err.into());
322333
status
323334
})
324335
}
@@ -349,7 +360,7 @@ impl Status {
349360
};
350361

351362
if let Some(mut status) = find_status_in_source_chain(&*err) {
352-
status.source = Some(err.into());
363+
status.0.source = Some(err.into());
353364
return Ok(status);
354365
}
355366

@@ -362,7 +373,7 @@ impl Status {
362373
let code = Self::code_from_h2(&err);
363374

364375
let mut status = Self::new(code, format!("h2 protocol error: {err}"));
365-
status.source = Some(Arc::new(*err));
376+
status.0.source = Some(Arc::new(*err));
366377
status
367378
}
368379

@@ -389,7 +400,7 @@ impl Status {
389400
#[cfg(feature = "server")]
390401
fn to_h2_error(&self) -> h2::Error {
391402
// conservatively transform to h2 error codes...
392-
let reason = match self.code {
403+
let reason = match self.code() {
393404
Code::Cancelled => h2::Reason::CANCEL,
394405
_ => h2::Reason::INTERNAL_ERROR,
395406
};
@@ -473,53 +484,56 @@ impl Status {
473484
}
474485
};
475486

476-
Some(Status {
477-
code,
478-
message,
479-
details,
480-
metadata: MetadataMap::from_headers(other_headers),
481-
source: None,
482-
})
487+
Some(
488+
StatusInner {
489+
code,
490+
message,
491+
details,
492+
metadata: MetadataMap::from_headers(other_headers),
493+
source: None,
494+
}
495+
.into(),
496+
)
483497
}
484498

485499
/// Get the gRPC `Code` of this `Status`.
486500
pub fn code(&self) -> Code {
487-
self.code
501+
self.0.code
488502
}
489503

490504
/// Get the text error message of this `Status`.
491505
pub fn message(&self) -> &str {
492-
&self.message
506+
&self.0.message
493507
}
494508

495509
/// Get the opaque error details of this `Status`.
496510
pub fn details(&self) -> &[u8] {
497-
&self.details
511+
&self.0.details
498512
}
499513

500514
/// Get a reference to the custom metadata.
501515
pub fn metadata(&self) -> &MetadataMap {
502-
&self.metadata
516+
&self.0.metadata
503517
}
504518

505519
/// Get a mutable reference to the custom metadata.
506520
pub fn metadata_mut(&mut self) -> &mut MetadataMap {
507-
&mut self.metadata
521+
&mut self.0.metadata
508522
}
509523

510524
pub(crate) fn to_header_map(&self) -> Result<HeaderMap, Self> {
511-
let mut header_map = HeaderMap::with_capacity(3 + self.metadata.len());
525+
let mut header_map = HeaderMap::with_capacity(3 + self.0.metadata.len());
512526
self.add_header(&mut header_map)?;
513527
Ok(header_map)
514528
}
515529

516530
/// Add headers from this `Status` into `header_map`.
517531
pub fn add_header(&self, header_map: &mut HeaderMap) -> Result<(), Self> {
518-
header_map.extend(self.metadata.clone().into_sanitized_headers());
532+
header_map.extend(self.0.metadata.clone().into_sanitized_headers());
519533

520-
header_map.insert(Self::GRPC_STATUS, self.code.to_header_value());
534+
header_map.insert(Self::GRPC_STATUS, self.0.code.to_header_value());
521535

522-
if !self.message.is_empty() {
536+
if !self.0.message.is_empty() {
523537
let to_write = Bytes::copy_from_slice(
524538
Cow::from(percent_encode(self.message().as_bytes(), ENCODING_SET)).as_bytes(),
525539
);
@@ -530,8 +544,8 @@ impl Status {
530544
);
531545
}
532546

533-
if !self.details.is_empty() {
534-
let details = crate::util::base64::STANDARD_NO_PAD.encode(&self.details[..]);
547+
if !self.0.details.is_empty() {
548+
let details = crate::util::base64::STANDARD_NO_PAD.encode(&self.0.details[..]);
535549

536550
header_map.insert(
537551
Self::GRPC_STATUS_DETAILS,
@@ -559,18 +573,19 @@ impl Status {
559573
details: Bytes,
560574
metadata: MetadataMap,
561575
) -> Status {
562-
Status {
576+
StatusInner {
563577
code,
564578
message: message.into(),
565579
details,
566580
metadata,
567581
source: None,
568582
}
583+
.into()
569584
}
570585

571586
/// Add a source error to this status.
572587
pub fn set_source(&mut self, source: Arc<dyn Error + Send + Sync + 'static>) -> &mut Status {
573-
self.source = Some(source);
588+
self.0.source = Some(source);
574589
self
575590
}
576591

@@ -598,15 +613,18 @@ fn find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option<Status> {
598613

599614
while let Some(err) = source {
600615
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-
// Since `Status` is not `Clone`, any `source` on the original Status
607-
// cannot be cloned so must remain with the original `Status`.
608-
source: None,
609-
});
616+
return Some(
617+
StatusInner {
618+
code: status.0.code,
619+
message: status.0.message.clone(),
620+
details: status.0.details.clone(),
621+
metadata: status.0.metadata.clone(),
622+
// Since `Status` is not `Clone`, any `source` on the original Status
623+
// cannot be cloned so must remain with the original `Status`.
624+
source: None,
625+
}
626+
.into(),
627+
);
610628
}
611629

612630
if let Some(timeout) = err.downcast_ref::<TimeoutExpired>() {
@@ -637,6 +655,12 @@ fn find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option<Status> {
637655
}
638656

639657
impl fmt::Debug for Status {
658+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
659+
self.0.fmt(f)
660+
}
661+
}
662+
663+
impl fmt::Debug for StatusInner {
640664
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
641665
// A manual impl to reduce the noise of frequently empty fields.
642666
let mut builder = f.debug_struct("Status");
@@ -725,7 +749,7 @@ impl fmt::Display for Status {
725749

726750
impl Error for Status {
727751
fn source(&self) -> Option<&(dyn Error + 'static)> {
728-
self.source.as_ref().map(|err| (&**err) as _)
752+
self.0.source.as_ref().map(|err| (&**err) as _)
729753
}
730754
}
731755

0 commit comments

Comments
 (0)