Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

## [0.24.3] - 2025-10-19

### Fixed
- Reused stack-allocated format buffers when emitting gRPC metadata for HTTP
status codes and retry hints, and added regression coverage to ensure metadata
strings remain ASCII encoded.

## [0.24.2] - 2025-10-18

### Added
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "masterror"
version = "0.24.2"
version = "0.24.3"
rust-version = "1.90"
edition = "2024"
license = "MIT OR Apache-2.0"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ The build script keeps the full feature snippet below in sync with

~~~toml
[dependencies]
masterror = { version = "0.24.2", default-features = false }
masterror = { version = "0.24.3", default-features = false }
# or with features:
# masterror = { version = "0.24.2", features = [
# masterror = { version = "0.24.3", features = [
# "std", "axum", "actix", "openapi",
# "serde_json", "tracing", "metrics", "backtrace",
# "sqlx", "sqlx-migrate", "reqwest", "redis",
Expand Down
31 changes: 25 additions & 6 deletions src/convert/tonic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use core::convert::Infallible;
use std::borrow::Cow;

use itoa::Buffer as IntegerBuffer;
use tonic::{
Code, Status,
metadata::{MetadataMap, MetadataValue}
Expand Down Expand Up @@ -68,11 +69,9 @@ fn status_from_error(error: &Error) -> Status {
let mut meta = MetadataMap::new();

insert_ascii(&mut meta, "app-code", error.code.as_str());
insert_ascii(
&mut meta,
"app-http-status",
mapping.http_status().to_string()
);
let mut http_status_buffer = IntegerBuffer::new();
let http_status = http_status_buffer.format(mapping.http_status());
insert_ascii(&mut meta, "app-http-status", http_status);
insert_ascii(&mut meta, "app-problem-type", mapping.problem_type());

if let Some(advice) = error.retry {
Expand Down Expand Up @@ -104,7 +103,9 @@ fn sanitize_detail(
}

fn insert_retry(meta: &mut MetadataMap, retry: RetryAdvice) {
insert_ascii(meta, "retry-after", retry.after_seconds.to_string());
let mut retry_after_buffer = IntegerBuffer::new();
let retry_after = retry_after_buffer.format(retry.after_seconds);
insert_ascii(meta, "retry-after", retry_after);
}

fn attach_metadata(meta: &mut MetadataMap, metadata: &Metadata) {
Expand Down Expand Up @@ -215,4 +216,22 @@ mod tests {
Some("2")
);
}

#[test]
fn timeout_status_carries_ascii_metadata() {
let status = Status::from(AppError::timeout("deadline exceeded").with_retry_after_secs(7));
let metadata = status.metadata();
assert_eq!(
metadata
.get("app-http-status")
.and_then(|value| value.to_str().ok()),
Some("504")
);
assert_eq!(
metadata
.get("retry-after")
.and_then(|value| value.to_str().ok()),
Some("7")
);
}
}
Loading