Skip to content

Commit edc5a0d

Browse files
adamchalmersAdam Chalmers
and
Adam Chalmers
authoredMay 2, 2022
feat: Add TryFrom implementations for MetadataValue (#990)
Co-authored-by: Adam Chalmers <adamschalmers@gmail.com>
1 parent 2112ecc commit edc5a0d

File tree

6 files changed

+184
-35
lines changed

6 files changed

+184
-35
lines changed
 

‎examples/src/authentication/client.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use tonic::{metadata::MetadataValue, transport::Channel, Request};
99
async fn main() -> Result<(), Box<dyn std::error::Error>> {
1010
let channel = Channel::from_static("http://[::1]:50051").connect().await?;
1111

12-
let token = MetadataValue::from_str("Bearer some-auth-token")?;
12+
let token: MetadataValue<_> = "Bearer some-auth-token".parse()?;
1313

1414
let mut client = EchoClient::with_interceptor(channel, move |mut req: Request<()>| {
1515
req.metadata_mut().insert("authorization", token.clone());

‎examples/src/authentication/server.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
5959
}
6060

6161
fn check_auth(req: Request<()>) -> Result<Request<()>, Status> {
62-
let token = MetadataValue::from_str("Bearer some-secret-token").unwrap();
62+
let token: MetadataValue<_> = "Bearer some-secret-token".parse().unwrap();
6363

6464
match req.metadata().get("authorization") {
6565
Some(t) if token == t => Ok(req),

‎examples/src/gcp/client.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
2222
.ok_or_else(|| "Expected a project name as the first argument.".to_string())?;
2323

2424
let bearer_token = format!("Bearer {}", token);
25-
let header_value = MetadataValue::from_str(&bearer_token)?;
25+
let header_value: MetadataValue<_> = bearer_token.parse()?;
2626

2727
let certs = tokio::fs::read("examples/data/gcp/roots.pem").await?;
2828

‎interop/src/client.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ pub async fn unimplemented_service(
342342

343343
pub async fn custom_metadata(client: &mut TestClient, assertions: &mut Vec<TestAssertion>) {
344344
let key1 = "x-grpc-test-echo-initial";
345-
let value1 = MetadataValue::from_str("test_initial_metadata_value").unwrap();
345+
let value1: MetadataValue<_> = "test_initial_metadata_value".parse().unwrap();
346346
let key2 = "x-grpc-test-echo-trailing-bin";
347347
let value2 = MetadataValue::from_bytes(&[0xab, 0xab, 0xab]);
348348

‎tonic/src/metadata/value.rs

+179-30
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::key::MetadataKey;
77

88
use bytes::Bytes;
99
use http::header::HeaderValue;
10+
use std::convert::TryFrom;
1011
use std::error::Error;
1112
use std::hash::{Hash, Hasher};
1213
use std::marker::PhantomData;
@@ -86,9 +87,6 @@ impl<VE: ValueEncoding> MetadataValue<VE> {
8687
/// For Binary metadata values this method cannot fail. See also the Binary
8788
/// only version of this method `from_bytes`.
8889
///
89-
/// This function is intended to be replaced in the future by a `TryFrom`
90-
/// implementation once the trait is stabilized in std.
91-
///
9290
/// # Examples
9391
///
9492
/// ```
@@ -105,11 +103,9 @@ impl<VE: ValueEncoding> MetadataValue<VE> {
105103
/// assert!(val.is_err());
106104
/// ```
107105
#[inline]
106+
#[deprecated = "Use TryFrom instead"]
108107
pub fn try_from_bytes(src: &[u8]) -> Result<Self, InvalidMetadataValueBytes> {
109-
VE::from_bytes(src).map(|value| MetadataValue {
110-
inner: value,
111-
phantom: PhantomData,
112-
})
108+
Self::try_from(src)
113109
}
114110

115111
/// Attempt to convert a `Bytes` buffer to a `MetadataValue`.
@@ -122,15 +118,10 @@ impl<VE: ValueEncoding> MetadataValue<VE> {
122118
/// error is returned. In use cases where the input is not base64 encoded,
123119
/// use `from_bytes`; if the value has to be encoded it's not possible to
124120
/// share the memory anyways.
125-
///
126-
/// This function is intended to be replaced in the future by a `TryFrom`
127-
/// implementation once the trait is stabilized in std.
128121
#[inline]
122+
#[deprecated = "Use TryFrom instead"]
129123
pub fn from_shared(src: Bytes) -> Result<Self, InvalidMetadataValueBytes> {
130-
VE::from_shared(src).map(|value| MetadataValue {
131-
inner: value,
132-
phantom: PhantomData,
133-
})
124+
Self::try_from(src)
134125
}
135126

136127
/// Convert a `Bytes` directly into a `MetadataValue` without validating.
@@ -282,6 +273,165 @@ impl<VE: ValueEncoding> MetadataValue<VE> {
282273
}
283274
}
284275

276+
/// Attempt to convert a byte slice to a `MetadataValue`.
277+
///
278+
/// For Ascii metadata values, If the argument contains invalid metadata
279+
/// value bytes, an error is returned. Only byte values between 32 and 255
280+
/// (inclusive) are permitted, excluding byte 127 (DEL).
281+
///
282+
/// For Binary metadata values this method cannot fail. See also the Binary
283+
/// only version of this method `from_bytes`.
284+
///
285+
/// # Examples
286+
///
287+
/// ```
288+
/// # use tonic::metadata::*;
289+
/// # use std::convert::TryFrom;
290+
/// let val = AsciiMetadataValue::try_from(b"hello\xfa").unwrap();
291+
/// assert_eq!(val, &b"hello\xfa"[..]);
292+
/// ```
293+
///
294+
/// An invalid value
295+
///
296+
/// ```
297+
/// # use tonic::metadata::*;
298+
/// # use std::convert::TryFrom;
299+
/// let val = AsciiMetadataValue::try_from(b"\n");
300+
/// assert!(val.is_err());
301+
/// ```
302+
impl<'a, VE: ValueEncoding> TryFrom<&'a [u8]> for MetadataValue<VE> {
303+
type Error = InvalidMetadataValueBytes;
304+
305+
#[inline]
306+
fn try_from(src: &[u8]) -> Result<Self, Self::Error> {
307+
VE::from_bytes(src).map(|value| MetadataValue {
308+
inner: value,
309+
phantom: PhantomData,
310+
})
311+
}
312+
}
313+
314+
/// Attempt to convert a byte slice to a `MetadataValue`.
315+
///
316+
/// For Ascii metadata values, If the argument contains invalid metadata
317+
/// value bytes, an error is returned. Only byte values between 32 and 255
318+
/// (inclusive) are permitted, excluding byte 127 (DEL).
319+
///
320+
/// For Binary metadata values this method cannot fail. See also the Binary
321+
/// only version of this method `from_bytes`.
322+
///
323+
/// # Examples
324+
///
325+
/// ```
326+
/// # use tonic::metadata::*;
327+
/// # use std::convert::TryFrom;
328+
/// let val = AsciiMetadataValue::try_from(b"hello\xfa").unwrap();
329+
/// assert_eq!(val, &b"hello\xfa"[..]);
330+
/// ```
331+
///
332+
/// An invalid value
333+
///
334+
/// ```
335+
/// # use tonic::metadata::*;
336+
/// # use std::convert::TryFrom;
337+
/// let val = AsciiMetadataValue::try_from(b"\n");
338+
/// assert!(val.is_err());
339+
/// ```
340+
impl<'a, VE: ValueEncoding, const N: usize> TryFrom<&'a [u8; N]> for MetadataValue<VE> {
341+
type Error = InvalidMetadataValueBytes;
342+
343+
#[inline]
344+
fn try_from(src: &[u8; N]) -> Result<Self, Self::Error> {
345+
Self::try_from(src.as_ref())
346+
}
347+
}
348+
349+
/// Attempt to convert a `Bytes` buffer to a `MetadataValue`.
350+
///
351+
/// For `MetadataValue<Ascii>`, if the argument contains invalid metadata
352+
/// value bytes, an error is returned. Only byte values between 32 and 255
353+
/// (inclusive) are permitted, excluding byte 127 (DEL).
354+
///
355+
/// For `MetadataValue<Binary>`, if the argument is not valid base64, an
356+
/// error is returned. In use cases where the input is not base64 encoded,
357+
/// use `from_bytes`; if the value has to be encoded it's not possible to
358+
/// share the memory anyways.
359+
impl<VE: ValueEncoding> TryFrom<Bytes> for MetadataValue<VE> {
360+
type Error = InvalidMetadataValueBytes;
361+
362+
#[inline]
363+
fn try_from(src: Bytes) -> Result<Self, Self::Error> {
364+
VE::from_shared(src).map(|value| MetadataValue {
365+
inner: value,
366+
phantom: PhantomData,
367+
})
368+
}
369+
}
370+
371+
/// Attempt to convert a Vec of bytes to a `MetadataValue`.
372+
///
373+
/// For `MetadataValue<Ascii>`, if the argument contains invalid metadata
374+
/// value bytes, an error is returned. Only byte values between 32 and 255
375+
/// (inclusive) are permitted, excluding byte 127 (DEL).
376+
///
377+
/// For `MetadataValue<Binary>`, if the argument is not valid base64, an
378+
/// error is returned. In use cases where the input is not base64 encoded,
379+
/// use `from_bytes`; if the value has to be encoded it's not possible to
380+
/// share the memory anyways.
381+
impl<VE: ValueEncoding> TryFrom<Vec<u8>> for MetadataValue<VE> {
382+
type Error = InvalidMetadataValueBytes;
383+
384+
#[inline]
385+
fn try_from(src: Vec<u8>) -> Result<Self, Self::Error> {
386+
Self::try_from(src.as_slice())
387+
}
388+
}
389+
390+
/// Attempt to convert a string to a `MetadataValue<Ascii>`.
391+
///
392+
/// If the argument contains invalid metadata value characters, an error is
393+
/// returned. Only visible ASCII characters (32-127) are permitted. Use
394+
/// `from_bytes` to create a `MetadataValue` that includes opaque octets
395+
/// (128-255).
396+
impl<'a> TryFrom<&'a str> for MetadataValue<Ascii> {
397+
type Error = InvalidMetadataValue;
398+
399+
#[inline]
400+
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
401+
s.parse()
402+
}
403+
}
404+
405+
/// Attempt to convert a string to a `MetadataValue<Ascii>`.
406+
///
407+
/// If the argument contains invalid metadata value characters, an error is
408+
/// returned. Only visible ASCII characters (32-127) are permitted. Use
409+
/// `from_bytes` to create a `MetadataValue` that includes opaque octets
410+
/// (128-255).
411+
impl<'a> TryFrom<&'a String> for MetadataValue<Ascii> {
412+
type Error = InvalidMetadataValue;
413+
414+
#[inline]
415+
fn try_from(s: &'a String) -> Result<Self, Self::Error> {
416+
s.parse()
417+
}
418+
}
419+
420+
/// Attempt to convert a string to a `MetadataValue<Ascii>`.
421+
///
422+
/// If the argument contains invalid metadata value characters, an error is
423+
/// returned. Only visible ASCII characters (32-127) are permitted. Use
424+
/// `from_bytes` to create a `MetadataValue` that includes opaque octets
425+
/// (128-255).
426+
impl TryFrom<String> for MetadataValue<Ascii> {
427+
type Error = InvalidMetadataValue;
428+
429+
#[inline]
430+
fn try_from(s: String) -> Result<Self, Self::Error> {
431+
s.parse()
432+
}
433+
}
434+
285435
// is_empty is defined in the generic impl block above
286436
#[allow(clippy::len_without_is_empty)]
287437
impl MetadataValue<Ascii> {
@@ -292,9 +442,6 @@ impl MetadataValue<Ascii> {
292442
/// `from_bytes` to create a `MetadataValue` that includes opaque octets
293443
/// (128-255).
294444
///
295-
/// This function is intended to be replaced in the future by a `TryFrom`
296-
/// implementation once the trait is stabilized in std.
297-
///
298445
/// # Examples
299446
///
300447
/// ```
@@ -311,14 +458,10 @@ impl MetadataValue<Ascii> {
311458
/// assert!(val.is_err());
312459
/// ```
313460
#[allow(clippy::should_implement_trait)]
461+
#[deprecated = "Use TryFrom or FromStr instead"]
314462
#[inline]
315463
pub fn from_str(src: &str) -> Result<Self, InvalidMetadataValue> {
316-
HeaderValue::from_str(src)
317-
.map(|value| MetadataValue {
318-
inner: value,
319-
phantom: PhantomData,
320-
})
321-
.map_err(|_| InvalidMetadataValue::new())
464+
src.parse()
322465
}
323466

324467
/// Converts a MetadataKey into a MetadataValue<Ascii>.
@@ -330,8 +473,9 @@ impl MetadataValue<Ascii> {
330473
///
331474
/// ```
332475
/// # use tonic::metadata::*;
476+
/// # use std::convert::TryFrom;
333477
/// let val = AsciiMetadataValue::from_key::<Ascii>("accept".parse().unwrap());
334-
/// assert_eq!(val, AsciiMetadataValue::try_from_bytes(b"accept").unwrap());
478+
/// assert_eq!(val, AsciiMetadataValue::try_from(b"accept").unwrap());
335479
/// ```
336480
#[inline]
337481
pub fn from_key<KeyVE: ValueEncoding>(key: MetadataKey<KeyVE>) -> Self {
@@ -402,8 +546,8 @@ impl MetadataValue<Binary> {
402546
/// ```
403547
#[inline]
404548
pub fn from_bytes(src: &[u8]) -> Self {
405-
// Only the Ascii version of try_from_bytes can fail.
406-
Self::try_from_bytes(src).unwrap()
549+
// Only the Ascii version of try_from can fail.
550+
Self::try_from(src).unwrap()
407551
}
408552
}
409553

@@ -501,7 +645,7 @@ mod from_metadata_value_tests {
501645

502646
assert_eq!(
503647
map.get("accept").unwrap(),
504-
AsciiMetadataValue::try_from_bytes(b"hello-world").unwrap()
648+
AsciiMetadataValue::try_from(b"hello-world").unwrap()
505649
);
506650
}
507651
}
@@ -511,7 +655,12 @@ impl FromStr for MetadataValue<Ascii> {
511655

512656
#[inline]
513657
fn from_str(s: &str) -> Result<MetadataValue<Ascii>, Self::Err> {
514-
MetadataValue::<Ascii>::from_str(s)
658+
HeaderValue::from_str(s)
659+
.map(|value| MetadataValue {
660+
inner: value,
661+
phantom: PhantomData,
662+
})
663+
.map_err(|_| InvalidMetadataValue::new())
515664
}
516665
}
517666

@@ -730,7 +879,7 @@ fn test_debug() {
730879
];
731880

732881
for &(value, expected) in cases {
733-
let val = AsciiMetadataValue::try_from_bytes(value.as_bytes()).unwrap();
882+
let val = AsciiMetadataValue::try_from(value.as_bytes()).unwrap();
734883
let actual = format!("{:?}", val);
735884
assert_eq!(expected, actual);
736885
}
@@ -760,7 +909,7 @@ fn test_is_empty() {
760909

761910
#[test]
762911
fn test_from_shared_base64_encodes() {
763-
let value = BinaryMetadataValue::from_shared(Bytes::from_static(b"Hello")).unwrap();
912+
let value = BinaryMetadataValue::try_from(Bytes::from_static(b"Hello")).unwrap();
764913
assert_eq!(value.as_encoded_bytes(), b"SGVsbG8");
765914
}
766915

‎tonic/src/request.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ impl<T> Request<T> {
285285
///
286286
/// [the spec]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
287287
pub fn set_timeout(&mut self, deadline: Duration) {
288-
let value = MetadataValue::from_str(&duration_to_grpc_timeout(deadline)).unwrap();
288+
let value: MetadataValue<_> = duration_to_grpc_timeout(deadline).parse().unwrap();
289289
self.metadata_mut()
290290
.insert(crate::metadata::GRPC_TIMEOUT_HEADER, value);
291291
}

0 commit comments

Comments
 (0)
Please sign in to comment.