diff --git a/examples/src/authentication/client.rs b/examples/src/authentication/client.rs index 7297c4716..2fa83ebd2 100644 --- a/examples/src/authentication/client.rs +++ b/examples/src/authentication/client.rs @@ -9,7 +9,7 @@ use tonic::{metadata::MetadataValue, transport::Channel, Request}; async fn main() -> Result<(), Box> { let channel = Channel::from_static("http://[::1]:50051").connect().await?; - let token = MetadataValue::from_str("Bearer some-auth-token")?; + let token: MetadataValue<_> = "Bearer some-auth-token".parse()?; let mut client = EchoClient::with_interceptor(channel, move |mut req: Request<()>| { req.metadata_mut().insert("authorization", token.clone()); diff --git a/examples/src/authentication/server.rs b/examples/src/authentication/server.rs index 951154079..289eaf80d 100644 --- a/examples/src/authentication/server.rs +++ b/examples/src/authentication/server.rs @@ -59,7 +59,7 @@ async fn main() -> Result<(), Box> { } fn check_auth(req: Request<()>) -> Result, Status> { - let token = MetadataValue::from_str("Bearer some-secret-token").unwrap(); + let token: MetadataValue<_> = "Bearer some-secret-token".parse().unwrap(); match req.metadata().get("authorization") { Some(t) if token == t => Ok(req), diff --git a/examples/src/gcp/client.rs b/examples/src/gcp/client.rs index b434ed112..57b78aea0 100644 --- a/examples/src/gcp/client.rs +++ b/examples/src/gcp/client.rs @@ -22,7 +22,7 @@ async fn main() -> Result<(), Box> { .ok_or_else(|| "Expected a project name as the first argument.".to_string())?; let bearer_token = format!("Bearer {}", token); - let header_value = MetadataValue::from_str(&bearer_token)?; + let header_value: MetadataValue<_> = bearer_token.parse()?; let certs = tokio::fs::read("examples/data/gcp/roots.pem").await?; diff --git a/interop/src/client.rs b/interop/src/client.rs index a47441d2f..578999505 100644 --- a/interop/src/client.rs +++ b/interop/src/client.rs @@ -342,7 +342,7 @@ pub async fn unimplemented_service( pub async fn custom_metadata(client: &mut TestClient, assertions: &mut Vec) { let key1 = "x-grpc-test-echo-initial"; - let value1 = MetadataValue::from_str("test_initial_metadata_value").unwrap(); + let value1: MetadataValue<_> = "test_initial_metadata_value".parse().unwrap(); let key2 = "x-grpc-test-echo-trailing-bin"; let value2 = MetadataValue::from_bytes(&[0xab, 0xab, 0xab]); diff --git a/tonic/src/metadata/value.rs b/tonic/src/metadata/value.rs index b976440c9..57e9719ac 100644 --- a/tonic/src/metadata/value.rs +++ b/tonic/src/metadata/value.rs @@ -7,6 +7,7 @@ use super::key::MetadataKey; use bytes::Bytes; use http::header::HeaderValue; +use std::convert::TryFrom; use std::error::Error; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; @@ -86,9 +87,6 @@ impl MetadataValue { /// For Binary metadata values this method cannot fail. See also the Binary /// only version of this method `from_bytes`. /// - /// This function is intended to be replaced in the future by a `TryFrom` - /// implementation once the trait is stabilized in std. - /// /// # Examples /// /// ``` @@ -105,11 +103,9 @@ impl MetadataValue { /// assert!(val.is_err()); /// ``` #[inline] + #[deprecated = "Use TryFrom instead"] pub fn try_from_bytes(src: &[u8]) -> Result { - VE::from_bytes(src).map(|value| MetadataValue { - inner: value, - phantom: PhantomData, - }) + Self::try_from(src) } /// Attempt to convert a `Bytes` buffer to a `MetadataValue`. @@ -122,15 +118,10 @@ impl MetadataValue { /// error is returned. In use cases where the input is not base64 encoded, /// use `from_bytes`; if the value has to be encoded it's not possible to /// share the memory anyways. - /// - /// This function is intended to be replaced in the future by a `TryFrom` - /// implementation once the trait is stabilized in std. #[inline] + #[deprecated = "Use TryFrom instead"] pub fn from_shared(src: Bytes) -> Result { - VE::from_shared(src).map(|value| MetadataValue { - inner: value, - phantom: PhantomData, - }) + Self::try_from(src) } /// Convert a `Bytes` directly into a `MetadataValue` without validating. @@ -282,6 +273,165 @@ impl MetadataValue { } } +/// Attempt to convert a byte slice to a `MetadataValue`. +/// +/// For Ascii metadata values, If the argument contains invalid metadata +/// value bytes, an error is returned. Only byte values between 32 and 255 +/// (inclusive) are permitted, excluding byte 127 (DEL). +/// +/// For Binary metadata values this method cannot fail. See also the Binary +/// only version of this method `from_bytes`. +/// +/// # Examples +/// +/// ``` +/// # use tonic::metadata::*; +/// # use std::convert::TryFrom; +/// let val = AsciiMetadataValue::try_from(b"hello\xfa").unwrap(); +/// assert_eq!(val, &b"hello\xfa"[..]); +/// ``` +/// +/// An invalid value +/// +/// ``` +/// # use tonic::metadata::*; +/// # use std::convert::TryFrom; +/// let val = AsciiMetadataValue::try_from(b"\n"); +/// assert!(val.is_err()); +/// ``` +impl<'a, VE: ValueEncoding> TryFrom<&'a [u8]> for MetadataValue { + type Error = InvalidMetadataValueBytes; + + #[inline] + fn try_from(src: &[u8]) -> Result { + VE::from_bytes(src).map(|value| MetadataValue { + inner: value, + phantom: PhantomData, + }) + } +} + +/// Attempt to convert a byte slice to a `MetadataValue`. +/// +/// For Ascii metadata values, If the argument contains invalid metadata +/// value bytes, an error is returned. Only byte values between 32 and 255 +/// (inclusive) are permitted, excluding byte 127 (DEL). +/// +/// For Binary metadata values this method cannot fail. See also the Binary +/// only version of this method `from_bytes`. +/// +/// # Examples +/// +/// ``` +/// # use tonic::metadata::*; +/// # use std::convert::TryFrom; +/// let val = AsciiMetadataValue::try_from(b"hello\xfa").unwrap(); +/// assert_eq!(val, &b"hello\xfa"[..]); +/// ``` +/// +/// An invalid value +/// +/// ``` +/// # use tonic::metadata::*; +/// # use std::convert::TryFrom; +/// let val = AsciiMetadataValue::try_from(b"\n"); +/// assert!(val.is_err()); +/// ``` +impl<'a, VE: ValueEncoding, const N: usize> TryFrom<&'a [u8; N]> for MetadataValue { + type Error = InvalidMetadataValueBytes; + + #[inline] + fn try_from(src: &[u8; N]) -> Result { + Self::try_from(src.as_ref()) + } +} + +/// Attempt to convert a `Bytes` buffer to a `MetadataValue`. +/// +/// For `MetadataValue`, if the argument contains invalid metadata +/// value bytes, an error is returned. Only byte values between 32 and 255 +/// (inclusive) are permitted, excluding byte 127 (DEL). +/// +/// For `MetadataValue`, if the argument is not valid base64, an +/// error is returned. In use cases where the input is not base64 encoded, +/// use `from_bytes`; if the value has to be encoded it's not possible to +/// share the memory anyways. +impl TryFrom for MetadataValue { + type Error = InvalidMetadataValueBytes; + + #[inline] + fn try_from(src: Bytes) -> Result { + VE::from_shared(src).map(|value| MetadataValue { + inner: value, + phantom: PhantomData, + }) + } +} + +/// Attempt to convert a Vec of bytes to a `MetadataValue`. +/// +/// For `MetadataValue`, if the argument contains invalid metadata +/// value bytes, an error is returned. Only byte values between 32 and 255 +/// (inclusive) are permitted, excluding byte 127 (DEL). +/// +/// For `MetadataValue`, if the argument is not valid base64, an +/// error is returned. In use cases where the input is not base64 encoded, +/// use `from_bytes`; if the value has to be encoded it's not possible to +/// share the memory anyways. +impl TryFrom> for MetadataValue { + type Error = InvalidMetadataValueBytes; + + #[inline] + fn try_from(src: Vec) -> Result { + Self::try_from(src.as_slice()) + } +} + +/// Attempt to convert a string to a `MetadataValue`. +/// +/// If the argument contains invalid metadata value characters, an error is +/// returned. Only visible ASCII characters (32-127) are permitted. Use +/// `from_bytes` to create a `MetadataValue` that includes opaque octets +/// (128-255). +impl<'a> TryFrom<&'a str> for MetadataValue { + type Error = InvalidMetadataValue; + + #[inline] + fn try_from(s: &'a str) -> Result { + s.parse() + } +} + +/// Attempt to convert a string to a `MetadataValue`. +/// +/// If the argument contains invalid metadata value characters, an error is +/// returned. Only visible ASCII characters (32-127) are permitted. Use +/// `from_bytes` to create a `MetadataValue` that includes opaque octets +/// (128-255). +impl<'a> TryFrom<&'a String> for MetadataValue { + type Error = InvalidMetadataValue; + + #[inline] + fn try_from(s: &'a String) -> Result { + s.parse() + } +} + +/// Attempt to convert a string to a `MetadataValue`. +/// +/// If the argument contains invalid metadata value characters, an error is +/// returned. Only visible ASCII characters (32-127) are permitted. Use +/// `from_bytes` to create a `MetadataValue` that includes opaque octets +/// (128-255). +impl TryFrom for MetadataValue { + type Error = InvalidMetadataValue; + + #[inline] + fn try_from(s: String) -> Result { + s.parse() + } +} + // is_empty is defined in the generic impl block above #[allow(clippy::len_without_is_empty)] impl MetadataValue { @@ -292,9 +442,6 @@ impl MetadataValue { /// `from_bytes` to create a `MetadataValue` that includes opaque octets /// (128-255). /// - /// This function is intended to be replaced in the future by a `TryFrom` - /// implementation once the trait is stabilized in std. - /// /// # Examples /// /// ``` @@ -311,14 +458,10 @@ impl MetadataValue { /// assert!(val.is_err()); /// ``` #[allow(clippy::should_implement_trait)] + #[deprecated = "Use TryFrom or FromStr instead"] #[inline] pub fn from_str(src: &str) -> Result { - HeaderValue::from_str(src) - .map(|value| MetadataValue { - inner: value, - phantom: PhantomData, - }) - .map_err(|_| InvalidMetadataValue::new()) + src.parse() } /// Converts a MetadataKey into a MetadataValue. @@ -330,8 +473,9 @@ impl MetadataValue { /// /// ``` /// # use tonic::metadata::*; + /// # use std::convert::TryFrom; /// let val = AsciiMetadataValue::from_key::("accept".parse().unwrap()); - /// assert_eq!(val, AsciiMetadataValue::try_from_bytes(b"accept").unwrap()); + /// assert_eq!(val, AsciiMetadataValue::try_from(b"accept").unwrap()); /// ``` #[inline] pub fn from_key(key: MetadataKey) -> Self { @@ -402,8 +546,8 @@ impl MetadataValue { /// ``` #[inline] pub fn from_bytes(src: &[u8]) -> Self { - // Only the Ascii version of try_from_bytes can fail. - Self::try_from_bytes(src).unwrap() + // Only the Ascii version of try_from can fail. + Self::try_from(src).unwrap() } } @@ -501,7 +645,7 @@ mod from_metadata_value_tests { assert_eq!( map.get("accept").unwrap(), - AsciiMetadataValue::try_from_bytes(b"hello-world").unwrap() + AsciiMetadataValue::try_from(b"hello-world").unwrap() ); } } @@ -511,7 +655,12 @@ impl FromStr for MetadataValue { #[inline] fn from_str(s: &str) -> Result, Self::Err> { - MetadataValue::::from_str(s) + HeaderValue::from_str(s) + .map(|value| MetadataValue { + inner: value, + phantom: PhantomData, + }) + .map_err(|_| InvalidMetadataValue::new()) } } @@ -730,7 +879,7 @@ fn test_debug() { ]; for &(value, expected) in cases { - let val = AsciiMetadataValue::try_from_bytes(value.as_bytes()).unwrap(); + let val = AsciiMetadataValue::try_from(value.as_bytes()).unwrap(); let actual = format!("{:?}", val); assert_eq!(expected, actual); } @@ -760,7 +909,7 @@ fn test_is_empty() { #[test] fn test_from_shared_base64_encodes() { - let value = BinaryMetadataValue::from_shared(Bytes::from_static(b"Hello")).unwrap(); + let value = BinaryMetadataValue::try_from(Bytes::from_static(b"Hello")).unwrap(); assert_eq!(value.as_encoded_bytes(), b"SGVsbG8"); } diff --git a/tonic/src/request.rs b/tonic/src/request.rs index 98e8bedb4..5827bb77b 100644 --- a/tonic/src/request.rs +++ b/tonic/src/request.rs @@ -285,7 +285,7 @@ impl Request { /// /// [the spec]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md pub fn set_timeout(&mut self, deadline: Duration) { - let value = MetadataValue::from_str(&duration_to_grpc_timeout(deadline)).unwrap(); + let value: MetadataValue<_> = duration_to_grpc_timeout(deadline).parse().unwrap(); self.metadata_mut() .insert(crate::metadata::GRPC_TIMEOUT_HEADER, value); }