diff --git a/tonic-macros/Cargo.toml b/tonic-macros/Cargo.toml index 45285d8..5ae8cf4 100644 --- a/tonic-macros/Cargo.toml +++ b/tonic-macros/Cargo.toml @@ -17,3 +17,4 @@ tower-service = { git = "https://github.com/tower-rs/tower", branch = "std-futur [dev-dependencies] tokio = "=0.2.0-alpha.1" tonic = { path = "../tonic" } +futures-preview = "=0.3.0-alpha.17" diff --git a/tonic-macros/src/lib.rs b/tonic-macros/src/lib.rs index fef66d9..8ca9580 100644 --- a/tonic-macros/src/lib.rs +++ b/tonic-macros/src/lib.rs @@ -1,4 +1,5 @@ #![feature(async_await)] +#![recursion_limit = "256"] extern crate proc_macro; use proc_macro::TokenStream; @@ -36,7 +37,12 @@ pub fn server(attr: TokenStream, item: TokenStream) -> TokenStream { } } + // let service_name = service.proto_name.clone(); + let ts = quote! { + use tonic::_codegen; + + #[derive(Clone)] pub struct GrpcServer { inner: std::sync::Arc<#s>, } @@ -47,20 +53,86 @@ pub fn server(attr: TokenStream, item: TokenStream) -> TokenStream { } } - impl tower_service::Service> for GrpcServer { - type Response = tonic::Response<()>; - type Error = Status; - type Future = tonic::ResponseFuture<'static, Self::Response>; + impl _codegen::Service<()> for GrpcServer { + type Response = Self; + type Error = tonic::error::Never; + type Future = _codegen::Ready>; - fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll> { + fn poll_ready(&mut self, _cx: &mut _codegen::Context<'_>) -> _codegen::Poll> { std::task::Poll::Ready(Ok(())) } - fn call(&mut self, request: tonic::Request<()>) -> tonic::ResponseFuture<'static, Self::Response> { + fn call(&mut self, _: ()) -> Self::Future { + _codegen::ok(self.clone()) + } + } + + impl _codegen::Service<_codegen::http::Request<()>> for GrpcServer { + type Response = tonic::Response<()>; + type Error = tonic::error::Never; + type Future = greeter::ResponseFuture; + + fn poll_ready(&mut self, _cx: &mut _codegen::Context<'_>) -> _codegen::Poll> { + Ok(()).into() + } + + fn call(&mut self, request: _codegen::http::Request<()>) -> Self::Future { let inner = self.inner.clone(); - Box::pin(async move { - inner.#m_ident(request).await - }) + + match request.uri().path() { + "/helloworld.Greeter/SayHello" => { + // let kind = greeter::methods::SayHello(self.inner.clone()); + // greeter::ResponseFuture { kind: greeter::Kind::SayHello(kind) } + self.inner.stream(request).await?; + unimplemented!() + }, + _ => unimplemented!("use grpc unimplemented") + } + } + } + + // TODO: get actual service name + pub mod greeter { + use tonic::_codegen::*; + + pub struct ResponseFuture { + pub kind: Kind, + } + + pub enum Kind { + SayHello(methods::SayHello), + } + + impl Future for ResponseFuture { + type Output = Result, tonic::error::Never>; + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + unimplemented!() + } + } + + pub mod methods { + use tonic::_codegen::*; + + pub struct SayHello(pub std::sync::Arc); + + impl Service> for SayHello { + type Response = tonic::Response<()>; + type Error = tonic::Status; + type Future = ResponseFuture; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Ok(()).into() + } + + fn call(&mut self, request: tonic::Request<()>) -> Self::Future { + let inner = self.0.clone(); + + Box::pin(async move { + inner.#m_ident(request).await + }) + } + } } } }; diff --git a/tonic-macros/tests/server.rs b/tonic-macros/tests/server.rs index c26f72d..dc743bd 100644 --- a/tonic-macros/tests/server.rs +++ b/tonic-macros/tests/server.rs @@ -1,5 +1,6 @@ #![feature(async_await)] +use futures::Stream; use std::time::Duration; use tokio::timer::Delay; use tonic::{Request, Response, Status}; @@ -10,7 +11,7 @@ use tonic::{Request, Response, Status}; // struct HelloResponse; #[derive(Default, Clone)] -struct MyGreeter { +pub struct MyGreeter { data: String, } @@ -30,13 +31,18 @@ impl MyGreeter { Ok(Response::new(())) } + + pub async fn server_stream(&self, request: Request<()>) -> Result { + unimplemented!() + } + + pub async fn client_stream(&self, request: Request) -> Result<(), Status> { + unimplemented!() + } } #[tokio::test] async fn grpc() { let svc = MyGreeter::default(); - let mut server = GrpcServer::from(svc); - - use tower_service::Service; - server.call(tonic::Request::new(())).await.unwrap(); + let mut _server = GrpcServer::from(svc); } diff --git a/tonic/Cargo.toml b/tonic/Cargo.toml index 0220f86..1f69363 100644 --- a/tonic/Cargo.toml +++ b/tonic/Cargo.toml @@ -7,5 +7,16 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tower-grpc = { git = "https://github.com/tower-rs/tower-grpc", branch = "std-future" } +futures-core-preview = "=0.3.0-alpha.17" +futures-util-preview = "=0.3.0-alpha.17" tonic-macros = { path = "../tonic-macros" } +tracing = "0.1" +http = "0.1.14" +base64 = "0.10" +bytes = "0.4.7" +prost = "0.5" +percent-encoding = "1.0.1" +tower-service = { git = "https://github.com/tower-rs/tower", branch = "std-future" } +tokio-codec = "=0.2.0-alpha.1" +async-stream = { path = "../../async-stream/async-stream" } +http-body = { git = "https://github.com/hyperium/http-body", branch = "std-future" } diff --git a/tonic/src/body.rs b/tonic/src/body.rs new file mode 100644 index 0000000..19f01c3 --- /dev/null +++ b/tonic/src/body.rs @@ -0,0 +1,43 @@ +use crate::{Code, Status}; +use bytes::{Bytes, IntoBuf}; +use futures_core::TryStream; +use futures_util::{ready, TryStreamExt}; +use http::HeaderMap; +use http_body::Body; +use std::task::{Context, Poll}; + +pub type BytesBuf = ::Buf; + +pub struct AsyncBody { + inner: S, + error: Option, +} + +impl Body for AsyncBody +where + S: TryStream + Unpin, +{ + type Data = BytesBuf; + type Error = Status; + + fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll>> { + match ready!(self.inner.try_poll_next_unpin(cx)) { + Some(Ok(d)) => Some(Ok(d)).into(), + Some(Err(status)) => { + self.error = Some(status); + None.into() + } + None => None.into(), + } + } + + fn poll_trailers(&mut self, _cx: &mut Context<'_>) -> Poll, Status>> { + let status = if let Some(status) = self.error.take() { + status + } else { + Status::new(Code::Ok, "") + }; + + Poll::Ready(Ok(Some(status.to_header_map()?))) + } +} diff --git a/tonic/src/codec.rs b/tonic/src/codec.rs new file mode 100644 index 0000000..b90d086 --- /dev/null +++ b/tonic/src/codec.rs @@ -0,0 +1,6 @@ +pub trait Codec { + type Encode; + type Decode; + + type Encoder; +} diff --git a/tonic/src/error.rs b/tonic/src/error.rs new file mode 100644 index 0000000..9ad0196 --- /dev/null +++ b/tonic/src/error.rs @@ -0,0 +1,16 @@ +use std::fmt; + +#[allow(dead_code)] +pub(crate) type Error = Box; + +#[derive(Debug)] +#[allow(dead_code)] +pub enum Never {} + +impl fmt::Display for Never { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self {} + } +} + +impl std::error::Error for Never {} diff --git a/tonic/src/lib.rs b/tonic/src/lib.rs index fe00055..90c8a0d 100644 --- a/tonic/src/lib.rs +++ b/tonic/src/lib.rs @@ -1,16 +1,45 @@ -pub use tower_grpc::*; +#![feature(async_await, type_alias_impl_trait)] +//! gRPC implementation + +#[doc(hidden)] +pub mod error; +pub mod metadata; + +mod body; +mod codec; +mod request; +mod response; +mod server; +mod status; + +pub use request::Request; +pub use response::Response; +pub use status::{Code, Status}; pub use tonic_macros::server; use std::future::Future; -use std::pin::Pin; use std::sync::Arc; -pub type ResponseFuture<'a, T> = Pin> + Send + 'a>>; - pub trait GrpcInnerService { type Response; type Future: Future>; fn call(self: Arc, request: Request) -> Self::Future; } + +#[doc(hidden)] + +pub mod _codegen { + pub use futures_util::future::{ok, Ready}; + pub use std::future::Future; + pub use std::pin::Pin; + pub use std::task::{Context, Poll}; + pub use tower_service::Service; + pub type ResponseFuture = + self::Pin> + Send + 'static>>; + + pub mod http { + pub use http::*; + } +} diff --git a/tonic/src/metadata/encoding.rs b/tonic/src/metadata/encoding.rs new file mode 100644 index 0000000..f954cc4 --- /dev/null +++ b/tonic/src/metadata/encoding.rs @@ -0,0 +1,213 @@ +use bytes::Bytes; +use http::header::HeaderValue; +use std::error::Error; +use std::fmt; +use std::hash::Hash; + +/// A possible error when converting a `MetadataValue` from a string or byte +/// slice. +#[derive(Debug)] +pub struct InvalidMetadataValue { + _priv: (), +} + +mod value_encoding { + use super::InvalidMetadataValueBytes; + use bytes::Bytes; + use http::header::HeaderValue; + use std::fmt; + + pub trait Sealed { + #[doc(hidden)] + fn is_empty(value: &[u8]) -> bool; + + #[doc(hidden)] + fn from_bytes(value: &[u8]) -> Result; + + #[doc(hidden)] + fn from_shared(value: Bytes) -> Result; + + #[doc(hidden)] + fn from_static(value: &'static str) -> HeaderValue; + + #[doc(hidden)] + fn decode(value: &[u8]) -> Result; + + #[doc(hidden)] + fn equals(a: &HeaderValue, b: &[u8]) -> bool; + + #[doc(hidden)] + fn values_equal(a: &HeaderValue, b: &HeaderValue) -> bool; + + #[doc(hidden)] + fn fmt(value: &HeaderValue, f: &mut fmt::Formatter<'_>) -> fmt::Result; + } +} + +pub trait ValueEncoding: Clone + Eq + PartialEq + Hash + self::value_encoding::Sealed { + /// Returns true if the provided key is valid for this ValueEncoding type. + /// For example, `Ascii::is_valid_key("a") == true`, + /// `Ascii::is_valid_key("a-bin") == false`. + fn is_valid_key(key: &str) -> bool; +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum Ascii {} +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum Binary {} + +// ===== impl ValueEncoding ===== + +impl self::value_encoding::Sealed for Ascii { + fn is_empty(value: &[u8]) -> bool { + value.is_empty() + } + + fn from_bytes(value: &[u8]) -> Result { + HeaderValue::from_bytes(value).map_err(|_| InvalidMetadataValueBytes::new()) + } + + fn from_shared(value: Bytes) -> Result { + HeaderValue::from_shared(value).map_err(|_| InvalidMetadataValueBytes::new()) + } + + fn from_static(value: &'static str) -> HeaderValue { + HeaderValue::from_static(value) + } + + fn decode(value: &[u8]) -> Result { + Ok(Bytes::from(value)) + } + + fn equals(a: &HeaderValue, b: &[u8]) -> bool { + a.as_bytes() == b + } + + fn values_equal(a: &HeaderValue, b: &HeaderValue) -> bool { + a == b + } + + fn fmt(value: &HeaderValue, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(value, f) + } +} + +impl ValueEncoding for Ascii { + fn is_valid_key(key: &str) -> bool { + !Binary::is_valid_key(key) + } +} + +impl self::value_encoding::Sealed for Binary { + fn is_empty(value: &[u8]) -> bool { + for c in value { + if *c != b'=' { + return false; + } + } + true + } + + fn from_bytes(value: &[u8]) -> Result { + let encoded_value: String = base64::encode_config(value, base64::STANDARD_NO_PAD); + HeaderValue::from_shared(encoded_value.into()).map_err(|_| InvalidMetadataValueBytes::new()) + } + + fn from_shared(value: Bytes) -> Result { + Self::from_bytes(value.as_ref()) + } + + fn from_static(value: &'static str) -> HeaderValue { + if !base64::decode(value).is_ok() { + panic!("Invalid base64 passed to from_static: {}", value); + } + unsafe { + // Because this is valid base64 this must be a valid HTTP header value, + // no need to check again by calling from_shared. + HeaderValue::from_shared_unchecked(Bytes::from_static(value.as_ref())) + } + } + + fn decode(value: &[u8]) -> Result { + base64::decode(value) + .map(|bytes_vec| bytes_vec.into()) + .map_err(|_| InvalidMetadataValueBytes::new()) + } + + fn equals(a: &HeaderValue, b: &[u8]) -> bool { + if let Ok(decoded) = base64::decode(a.as_bytes()) { + decoded == b + } else { + a.as_bytes() == b + } + } + + fn values_equal(a: &HeaderValue, b: &HeaderValue) -> bool { + let decoded_a = Self::decode(a.as_bytes()); + let decoded_b = Self::decode(b.as_bytes()); + if decoded_a.is_ok() && decoded_b.is_ok() { + decoded_a.unwrap() == decoded_b.unwrap() + } else { + !decoded_a.is_ok() && !decoded_b.is_ok() + } + } + + fn fmt(value: &HeaderValue, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Ok(decoded) = Self::decode(value.as_bytes()) { + write!(f, "{:?}", decoded) + } else { + write!(f, "b[invalid]{:?}", value) + } + } +} + +impl ValueEncoding for Binary { + fn is_valid_key(key: &str) -> bool { + key.ends_with("-bin") + } +} + +// ===== impl InvalidMetadataValue ===== + +impl InvalidMetadataValue { + pub(crate) fn new() -> Self { + InvalidMetadataValue { _priv: () } + } +} + +impl fmt::Display for InvalidMetadataValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.description().fmt(f) + } +} + +impl Error for InvalidMetadataValue { + fn description(&self) -> &str { + "failed to parse metadata value" + } +} + +/// A possible error when converting a `MetadataValue` from a string or byte +/// slice. +#[derive(Debug)] +pub struct InvalidMetadataValueBytes(InvalidMetadataValue); + +// ===== impl InvalidMetadataValueBytes ===== + +impl InvalidMetadataValueBytes { + pub(crate) fn new() -> Self { + InvalidMetadataValueBytes(InvalidMetadataValue::new()) + } +} + +impl fmt::Display for InvalidMetadataValueBytes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Error for InvalidMetadataValueBytes { + fn description(&self) -> &str { + self.0.description() + } +} diff --git a/tonic/src/metadata/key.rs b/tonic/src/metadata/key.rs new file mode 100644 index 0000000..14a076a --- /dev/null +++ b/tonic/src/metadata/key.rs @@ -0,0 +1,278 @@ +use bytes::Bytes; +use http::header::HeaderName; +use std::borrow::Borrow; +use std::error::Error; +use std::fmt; +use std::marker::PhantomData; +use std::str::FromStr; + +use super::encoding::{Ascii, Binary, ValueEncoding}; + +/// Represents a custom metadata field name. +/// +/// `MetadataKey` is used as the [`MetadataMap`] key. +/// +/// [`HeaderMap`]: struct.HeaderMap.html +#[derive(Clone, Eq, PartialEq, Hash)] +#[repr(transparent)] +pub struct MetadataKey { + // Note: There are unsafe transmutes that assume that the memory layout + // of MetadataValue is identical to HeaderName + pub(crate) inner: http::header::HeaderName, + phantom: PhantomData, +} + +/// A possible error when converting a `MetadataKey` from another type. +#[derive(Debug)] +pub struct InvalidMetadataKey { + _priv: (), +} + +pub type AsciiMetadataKey = MetadataKey; +pub type BinaryMetadataKey = MetadataKey; + +impl MetadataKey { + /// Converts a slice of bytes to a `MetadataKey`. + /// + /// This function normalizes the input. + pub fn from_bytes(src: &[u8]) -> Result { + match HeaderName::from_bytes(src) { + Ok(name) => { + if !VE::is_valid_key(name.as_str()) { + panic!("invalid metadata key") + } + + Ok(MetadataKey { + inner: name, + phantom: PhantomData, + }) + } + Err(_) => Err(InvalidMetadataKey::new()), + } + } + + /// Converts a static string to a `MetadataKey`. + /// + /// This function panics when the static string is a invalid metadata key. + /// + /// This function requires the static string to only contain lowercase + /// characters, numerals and symbols, as per the HTTP/2.0 specification + /// and header names internal representation within this library. + /// + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// // Parsing a metadata key + /// let CUSTOM_KEY: &'static str = "custom-key"; + /// + /// let a = AsciiMetadataKey::from_bytes(b"custom-key").unwrap(); + /// let b = AsciiMetadataKey::from_static(CUSTOM_KEY); + /// assert_eq!(a, b); + /// ``` + /// + /// ```should_panic + /// # use tonic::metadata::*; + /// // Parsing a metadata key that contains invalid symbols(s): + /// AsciiMetadataKey::from_static("content{}{}length"); // This line panics! + /// ``` + /// + /// ```should_panic + /// # use tonic::metadata::*; + /// // Parsing a metadata key that contains invalid uppercase characters. + /// let a = AsciiMetadataKey::from_static("foobar"); + /// let b = AsciiMetadataKey::from_static("FOOBAR"); // This line panics! + /// ``` + /// + /// ```should_panic + /// # use tonic::metadata::*; + /// // Parsing a -bin metadata key as an Ascii key. + /// let b = AsciiMetadataKey::from_static("hello-bin"); // This line panics! + /// ``` + /// + /// ```should_panic + /// # use tonic::metadata::*; + /// // Parsing a non-bin metadata key as an Binary key. + /// let b = BinaryMetadataKey::from_static("hello"); // This line panics! + /// ``` + pub fn from_static(src: &'static str) -> Self { + let name = HeaderName::from_static(src); + if !VE::is_valid_key(name.as_str()) { + panic!("invalid metadata key") + } + + MetadataKey { + inner: name, + phantom: PhantomData, + } + } + + /// Returns a `str` representation of the metadata key. + /// + /// The returned string will always be lower case. + #[inline] + pub fn as_str(&self) -> &str { + self.inner.as_str() + } + + /// Converts a HeaderName reference to a MetadataKey. This method assumes + /// that the caller has made sure that the header name has the correct + /// "-bin" or non-"-bin" suffix, it does not validate its input. + #[inline] + pub(crate) fn unchecked_from_header_name_ref(header_name: &HeaderName) -> &Self { + unsafe { &*(header_name as *const HeaderName as *const Self) } + } + + /// Converts a HeaderName reference to a MetadataKey. This method assumes + /// that the caller has made sure that the header name has the correct + /// "-bin" or non-"-bin" suffix, it does not validate its input. + #[inline] + pub(crate) fn unchecked_from_header_name(name: HeaderName) -> Self { + MetadataKey { + inner: name, + phantom: PhantomData, + } + } +} + +impl FromStr for MetadataKey { + type Err = InvalidMetadataKey; + + fn from_str(s: &str) -> Result { + MetadataKey::from_bytes(s.as_bytes()).map_err(|_| InvalidMetadataKey::new()) + } +} + +impl AsRef for MetadataKey { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl AsRef<[u8]> for MetadataKey { + fn as_ref(&self) -> &[u8] { + self.as_str().as_bytes() + } +} + +impl Borrow for MetadataKey { + fn borrow(&self) -> &str { + self.as_str() + } +} + +impl fmt::Debug for MetadataKey { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.as_str(), fmt) + } +} + +impl fmt::Display for MetadataKey { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_str(), fmt) + } +} + +impl InvalidMetadataKey { + pub fn new() -> InvalidMetadataKey { + InvalidMetadataKey { _priv: () } + } +} + +impl<'a, VE: ValueEncoding> From<&'a MetadataKey> for MetadataKey { + fn from(src: &'a MetadataKey) -> MetadataKey { + src.clone() + } +} + +impl From> for Bytes { + #[inline] + fn from(name: MetadataKey) -> Bytes { + name.inner.into() + } +} + +impl<'a, VE: ValueEncoding> PartialEq<&'a MetadataKey> for MetadataKey { + #[inline] + fn eq(&self, other: &&'a MetadataKey) -> bool { + *self == **other + } +} + +impl<'a, VE: ValueEncoding> PartialEq> for &'a MetadataKey { + #[inline] + fn eq(&self, other: &MetadataKey) -> bool { + *other == *self + } +} + +impl PartialEq for MetadataKey { + /// Performs a case-insensitive comparison of the string against the header + /// name + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let content_length = AsciiMetadataKey::from_static("content-length"); + /// + /// assert_eq!(content_length, "content-length"); + /// assert_eq!(content_length, "Content-Length"); + /// assert_ne!(content_length, "content length"); + /// ``` + #[inline] + fn eq(&self, other: &str) -> bool { + self.inner.eq(other) + } +} + +impl PartialEq> for str { + /// Performs a case-insensitive comparison of the string against the header + /// name + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let content_length = AsciiMetadataKey::from_static("content-length"); + /// + /// assert_eq!(content_length, "content-length"); + /// assert_eq!(content_length, "Content-Length"); + /// assert_ne!(content_length, "content length"); + /// ``` + #[inline] + fn eq(&self, other: &MetadataKey) -> bool { + (*other).inner == *self + } +} + +impl<'a, VE: ValueEncoding> PartialEq<&'a str> for MetadataKey { + /// Performs a case-insensitive comparison of the string against the header + /// name + #[inline] + fn eq(&self, other: &&'a str) -> bool { + *self == **other + } +} + +impl<'a, VE: ValueEncoding> PartialEq> for &'a str { + /// Performs a case-insensitive comparison of the string against the header + /// name + #[inline] + fn eq(&self, other: &MetadataKey) -> bool { + *other == *self + } +} + +impl fmt::Display for InvalidMetadataKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.description().fmt(f) + } +} + +impl Error for InvalidMetadataKey { + fn description(&self) -> &str { + "invalid gRPC metadata key name" + } +} diff --git a/tonic/src/metadata/map.rs b/tonic/src/metadata/map.rs new file mode 100644 index 0000000..a1484dc --- /dev/null +++ b/tonic/src/metadata/map.rs @@ -0,0 +1,2694 @@ +pub use self::as_encoding_agnostic_metadata_key::AsEncodingAgnosticMetadataKey; +pub use self::as_metadata_key::AsMetadataKey; +pub use self::into_metadata_key::IntoMetadataKey; + +use super::encoding::{Ascii, Binary, ValueEncoding}; +use super::key::{InvalidMetadataKey, MetadataKey}; +use super::value::MetadataValue; + +use std::marker::PhantomData; + +/// A set of gRPC custom metadata entries. +/// +/// # Examples +/// +/// Basic usage +/// +/// ``` +/// # use tonic::metadata::*; +/// let mut map = MetadataMap::new(); +/// +/// map.insert("x-host", "example.com".parse().unwrap()); +/// map.insert("x-number", "123".parse().unwrap()); +/// map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"[binary data]")); +/// +/// assert!(map.contains_key("x-host")); +/// assert!(!map.contains_key("x-location")); +/// +/// assert_eq!(map.get("x-host").unwrap(), "example.com"); +/// +/// map.remove("x-host"); +/// +/// assert!(!map.contains_key("x-host")); +/// ``` +#[derive(Clone, Debug, Default)] +pub struct MetadataMap { + headers: http::HeaderMap, +} + +/// `MetadataMap` entry iterator. +/// +/// Yields `KeyAndValueRef` values. The same header name may be yielded +/// more than once if it has more than one associated value. +#[derive(Debug)] +pub struct Iter<'a> { + inner: http::header::Iter<'a, http::header::HeaderValue>, +} + +/// Reference to a key and an associated value in a `MetadataMap`. It can point +/// to either an ascii or a binary ("*-bin") key. +#[derive(Debug)] +pub enum KeyAndValueRef<'a> { + Ascii(&'a MetadataKey, &'a MetadataValue), + Binary(&'a MetadataKey, &'a MetadataValue), +} + +/// Reference to a key and an associated value in a `MetadataMap`. It can point +/// to either an ascii or a binary ("*-bin") key. +#[derive(Debug)] +pub enum KeyAndMutValueRef<'a> { + Ascii(&'a MetadataKey, &'a mut MetadataValue), + Binary(&'a MetadataKey, &'a mut MetadataValue), +} + +/// `MetadataMap` entry iterator. +/// +/// Yields `(&MetadataKey, &mut value)` tuples. The same header name may be yielded +/// more than once if it has more than one associated value. +#[derive(Debug)] +pub struct IterMut<'a> { + inner: http::header::IterMut<'a, http::header::HeaderValue>, +} + +/// A drain iterator of all values associated with a single metadata key. +#[derive(Debug)] +pub struct ValueDrain<'a, VE: ValueEncoding> { + inner: http::header::ValueDrain<'a, http::header::HeaderValue>, + phantom: PhantomData, +} + +/// An iterator over `MetadataMap` keys. +/// +/// Yields `KeyRef` values. Each header name is yielded only once, even if it +/// has more than one associated value. +#[derive(Debug)] +pub struct Keys<'a> { + inner: http::header::Keys<'a, http::header::HeaderValue>, +} + +/// Reference to a key in a `MetadataMap`. It can point +/// to either an ascii or a binary ("*-bin") key. +#[derive(Debug)] +pub enum KeyRef<'a> { + Ascii(&'a MetadataKey), + Binary(&'a MetadataKey), +} + +/// `MetadataMap` value iterator. +/// +/// Yields `ValueRef` values. Each value contained in the `MetadataMap` will be +/// yielded. +#[derive(Debug)] +pub struct Values<'a> { + // Need to use http::header::Iter and not http::header::Values to be able + // to know if a value is binary or not. + inner: http::header::Iter<'a, http::header::HeaderValue>, +} + +/// Reference to a value in a `MetadataMap`. It can point +/// to either an ascii or a binary ("*-bin" key) value. +#[derive(Debug)] +pub enum ValueRef<'a> { + Ascii(&'a MetadataValue), + Binary(&'a MetadataValue), +} + +/// `MetadataMap` value iterator. +/// +/// Each value contained in the `MetadataMap` will be yielded. +#[derive(Debug)] +pub struct ValuesMut<'a> { + // Need to use http::header::IterMut and not http::header::ValuesMut to be + // able to know if a value is binary or not. + inner: http::header::IterMut<'a, http::header::HeaderValue>, +} + +/// Reference to a value in a `MetadataMap`. It can point +/// to either an ascii or a binary ("*-bin" key) value. +#[derive(Debug)] +pub enum ValueRefMut<'a> { + Ascii(&'a mut MetadataValue), + Binary(&'a mut MetadataValue), +} + +/// An iterator of all values associated with a single metadata key. +#[derive(Debug)] +pub struct ValueIter<'a, VE: ValueEncoding> { + inner: Option>, + phantom: PhantomData, +} + +/// An iterator of all values associated with a single metadata key. +#[derive(Debug)] +pub struct ValueIterMut<'a, VE: ValueEncoding> { + inner: http::header::ValueIterMut<'a, http::header::HeaderValue>, + phantom: PhantomData, +} + +/// A view to all values stored in a single entry. +/// +/// This struct is returned by `MetadataMap::get_all` and +/// `MetadataMap::get_all_bin`. +#[derive(Debug)] +pub struct GetAll<'a, VE: ValueEncoding> { + inner: Option>, + phantom: PhantomData, +} + +/// A view into a single location in a `MetadataMap`, which may be vacant or +/// occupied. +#[derive(Debug)] +pub enum Entry<'a, VE: ValueEncoding> { + /// An occupied entry + Occupied(OccupiedEntry<'a, VE>), + + /// A vacant entry + Vacant(VacantEntry<'a, VE>), +} + +/// A view into a single empty location in a `MetadataMap`. +/// +/// This struct is returned as part of the `Entry` enum. +#[derive(Debug)] +pub struct VacantEntry<'a, VE: ValueEncoding> { + inner: http::header::VacantEntry<'a, http::header::HeaderValue>, + phantom: PhantomData, +} + +/// A view into a single occupied location in a `MetadataMap`. +/// +/// This struct is returned as part of the `Entry` enum. +#[derive(Debug)] +pub struct OccupiedEntry<'a, VE: ValueEncoding> { + inner: http::header::OccupiedEntry<'a, http::header::HeaderValue>, + phantom: PhantomData, +} + +// ===== impl MetadataMap ===== + +impl MetadataMap { + /// Create an empty `MetadataMap`. + /// + /// The map will be created without any capacity. This function will not + /// allocate. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let map = MetadataMap::new(); + /// + /// assert!(map.is_empty()); + /// assert_eq!(0, map.capacity()); + /// ``` + pub fn new() -> Self { + MetadataMap::with_capacity(0) + } + + /// Convert an HTTP HeaderMap to a MetadataMap + pub fn from_headers(headers: http::HeaderMap) -> Self { + MetadataMap { headers: headers } + } + + /// Convert a MetadataMap into a HTTP HeaderMap + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("x-host", "example.com".parse().unwrap()); + /// + /// let http_map = map.into_headers(); + /// + /// assert_eq!(http_map.get("x-host").unwrap(), "example.com"); + /// ``` + pub fn into_headers(self) -> http::HeaderMap { + self.headers + } + + /// Create an empty `MetadataMap` with the specified capacity. + /// + /// The returned map will allocate internal storage in order to hold about + /// `capacity` elements without reallocating. However, this is a "best + /// effort" as there are usage patterns that could cause additional + /// allocations before `capacity` metadata entries are stored in the map. + /// + /// More capacity than requested may be allocated. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let map: MetadataMap = MetadataMap::with_capacity(10); + /// + /// assert!(map.is_empty()); + /// assert!(map.capacity() >= 10); + /// ``` + pub fn with_capacity(capacity: usize) -> MetadataMap { + MetadataMap { + headers: http::HeaderMap::with_capacity(capacity), + } + } + + /// Returns the number of metadata entries (ascii and binary) stored in the + /// map. + /// + /// This number represents the total number of **values** stored in the map. + /// This number can be greater than or equal to the number of **keys** + /// stored given that a single key may have more than one associated value. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// assert_eq!(0, map.len()); + /// + /// map.insert("x-host-ip", "127.0.0.1".parse().unwrap()); + /// map.insert_bin("x-host-name-bin", MetadataValue::from_bytes(b"localhost")); + /// + /// assert_eq!(2, map.len()); + /// + /// map.append("x-host-ip", "text/html".parse().unwrap()); + /// + /// assert_eq!(3, map.len()); + /// ``` + pub fn len(&self) -> usize { + self.headers.len() + } + + /// Returns the number of keys (ascii and binary) stored in the map. + /// + /// This number will be less than or equal to `len()` as each key may have + /// more than one associated value. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// assert_eq!(0, map.keys_len()); + /// + /// map.insert("x-host-ip", "127.0.0.1".parse().unwrap()); + /// map.insert_bin("x-host-name-bin", MetadataValue::from_bytes(b"localhost")); + /// + /// assert_eq!(2, map.keys_len()); + /// + /// map.append("x-host-ip", "text/html".parse().unwrap()); + /// + /// assert_eq!(2, map.keys_len()); + /// ``` + pub fn keys_len(&self) -> usize { + self.headers.keys_len() + } + + /// Returns true if the map contains no elements. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// assert!(map.is_empty()); + /// + /// map.insert("x-host", "hello.world".parse().unwrap()); + /// + /// assert!(!map.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.headers.is_empty() + } + + /// Clears the map, removing all key-value pairs. Keeps the allocated memory + /// for reuse. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("x-host", "hello.world".parse().unwrap()); + /// + /// map.clear(); + /// assert!(map.is_empty()); + /// assert!(map.capacity() > 0); + /// ``` + pub fn clear(&mut self) { + self.headers.clear(); + } + + /// Returns the number of custom metadata entries the map can hold without + /// reallocating. + /// + /// This number is an approximation as certain usage patterns could cause + /// additional allocations before the returned capacity is filled. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// assert_eq!(0, map.capacity()); + /// + /// map.insert("x-host", "hello.world".parse().unwrap()); + /// assert_eq!(6, map.capacity()); + /// ``` + pub fn capacity(&self) -> usize { + self.headers.capacity() + } + + /// Reserves capacity for at least `additional` more custom metadata to be + /// inserted into the `MetadataMap`. + /// + /// The metadata map may reserve more space to avoid frequent reallocations. + /// Like with `with_capacity`, this will be a "best effort" to avoid + /// allocations until `additional` more custom metadata is inserted. Certain + /// usage patterns could cause additional allocations before the number is + /// reached. + /// + /// # Panics + /// + /// Panics if the new allocation size overflows `usize`. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.reserve(10); + /// # map.insert("x-host", "bar".parse().unwrap()); + /// ``` + pub fn reserve(&mut self, additional: usize) { + self.headers.reserve(additional); + } + + /// Returns a reference to the value associated with the key. This method + /// is for ascii metadata entries (those whose names don't end with + /// "-bin"). For binary entries, use get_bin. + /// + /// If there are multiple values associated with the key, then the first one + /// is returned. Use `get_all` to get all values associated with a given + /// key. Returns `None` if there are no values associated with the key. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// assert!(map.get("x-host").is_none()); + /// + /// map.insert("x-host", "hello".parse().unwrap()); + /// assert_eq!(map.get("x-host").unwrap(), &"hello"); + /// assert_eq!(map.get("x-host").unwrap(), &"hello"); + /// + /// map.append("x-host", "world".parse().unwrap()); + /// assert_eq!(map.get("x-host").unwrap(), &"hello"); + /// + /// // Attempting to read a key of the wrong type fails by not + /// // finding anything. + /// map.append_bin("host-bin", MetadataValue::from_bytes(b"world")); + /// assert!(map.get("host-bin").is_none()); + /// assert!(map.get("host-bin".to_string()).is_none()); + /// assert!(map.get(&("host-bin".to_string())).is_none()); + /// + /// // Attempting to read an invalid key string fails by not + /// // finding anything. + /// assert!(map.get("host{}bin").is_none()); + /// assert!(map.get("host{}bin".to_string()).is_none()); + /// assert!(map.get(&("host{}bin".to_string())).is_none()); + /// ``` + pub fn get(&self, key: K) -> Option<&MetadataValue> + where + K: AsMetadataKey, + { + key.get(self) + } + + /// Like get, but for Binary keys (for example "trace-proto-bin"). + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// assert!(map.get_bin("trace-proto-bin").is_none()); + /// + /// map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"hello")); + /// assert_eq!(map.get_bin("trace-proto-bin").unwrap(), &"hello"); + /// assert_eq!(map.get_bin("trace-proto-bin").unwrap(), &"hello"); + /// + /// map.append_bin("trace-proto-bin", MetadataValue::from_bytes(b"world")); + /// assert_eq!(map.get_bin("trace-proto-bin").unwrap(), &"hello"); + /// + /// // Attempting to read a key of the wrong type fails by not + /// // finding anything. + /// map.append("host", "world".parse().unwrap()); + /// assert!(map.get_bin("host").is_none()); + /// assert!(map.get_bin("host".to_string()).is_none()); + /// assert!(map.get_bin(&("host".to_string())).is_none()); + /// + /// // Attempting to read an invalid key string fails by not + /// // finding anything. + /// assert!(map.get_bin("host{}-bin").is_none()); + /// assert!(map.get_bin("host{}-bin".to_string()).is_none()); + /// assert!(map.get_bin(&("host{}-bin".to_string())).is_none()); + /// ``` + pub fn get_bin(&self, key: K) -> Option<&MetadataValue> + where + K: AsMetadataKey, + { + key.get(self) + } + + /// Returns a mutable reference to the value associated with the key. This + /// method is for ascii metadata entries (those whose names don't end with + /// "-bin"). For binary entries, use get_mut_bin. + /// + /// If there are multiple values associated with the key, then the first one + /// is returned. Use `entry` to get all values associated with a given + /// key. Returns `None` if there are no values associated with the key. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::default(); + /// map.insert("x-host", "hello".parse().unwrap()); + /// map.get_mut("x-host").unwrap().set_sensitive(true); + /// + /// assert!(map.get("x-host").unwrap().is_sensitive()); + /// + /// // Attempting to read a key of the wrong type fails by not + /// // finding anything. + /// map.append_bin("host-bin", MetadataValue::from_bytes(b"world")); + /// assert!(map.get_mut("host-bin").is_none()); + /// assert!(map.get_mut("host-bin".to_string()).is_none()); + /// assert!(map.get_mut(&("host-bin".to_string())).is_none()); + /// + /// // Attempting to read an invalid key string fails by not + /// // finding anything. + /// assert!(map.get_mut("host{}").is_none()); + /// assert!(map.get_mut("host{}".to_string()).is_none()); + /// assert!(map.get_mut(&("host{}".to_string())).is_none()); + /// ``` + pub fn get_mut(&mut self, key: K) -> Option<&mut MetadataValue> + where + K: AsMetadataKey, + { + key.get_mut(self) + } + + /// Like get_mut, but for Binary keys (for example "trace-proto-bin"). + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::default(); + /// map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"hello")); + /// map.get_bin_mut("trace-proto-bin").unwrap().set_sensitive(true); + /// + /// assert!(map.get_bin("trace-proto-bin").unwrap().is_sensitive()); + /// + /// // Attempting to read a key of the wrong type fails by not + /// // finding anything. + /// map.append("host", "world".parse().unwrap()); + /// assert!(map.get_bin_mut("host").is_none()); + /// assert!(map.get_bin_mut("host".to_string()).is_none()); + /// assert!(map.get_bin_mut(&("host".to_string())).is_none()); + /// + /// // Attempting to read an invalid key string fails by not + /// // finding anything. + /// assert!(map.get_bin_mut("host{}-bin").is_none()); + /// assert!(map.get_bin_mut("host{}-bin".to_string()).is_none()); + /// assert!(map.get_bin_mut(&("host{}-bin".to_string())).is_none()); + /// ``` + pub fn get_bin_mut(&mut self, key: K) -> Option<&mut MetadataValue> + where + K: AsMetadataKey, + { + key.get_mut(self) + } + + /// Returns a view of all values associated with a key. This method is for + /// ascii metadata entries (those whose names don't end with "-bin"). For + /// binary entries, use get_all_bin. + /// + /// The returned view does not incur any allocations and allows iterating + /// the values associated with the key. See [`GetAll`] for more details. + /// Returns `None` if there are no values associated with the key. + /// + /// [`GetAll`]: struct.GetAll.html + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// map.insert("x-host", "hello".parse().unwrap()); + /// map.append("x-host", "goodbye".parse().unwrap()); + /// + /// { + /// let view = map.get_all("x-host"); + /// + /// let mut iter = view.iter(); + /// assert_eq!(&"hello", iter.next().unwrap()); + /// assert_eq!(&"goodbye", iter.next().unwrap()); + /// assert!(iter.next().is_none()); + /// } + /// + /// // Attempting to read a key of the wrong type fails by not + /// // finding anything. + /// map.append_bin("host-bin", MetadataValue::from_bytes(b"world")); + /// assert!(map.get_all("host-bin").iter().next().is_none()); + /// assert!(map.get_all("host-bin".to_string()).iter().next().is_none()); + /// assert!(map.get_all(&("host-bin".to_string())).iter().next().is_none()); + /// + /// // Attempting to read an invalid key string fails by not + /// // finding anything. + /// assert!(map.get_all("host{}").iter().next().is_none()); + /// assert!(map.get_all("host{}".to_string()).iter().next().is_none()); + /// assert!(map.get_all(&("host{}".to_string())).iter().next().is_none()); + /// ``` + pub fn get_all(&self, key: K) -> GetAll<'_, Ascii> + where + K: AsMetadataKey, + { + GetAll { + inner: key.get_all(self), + phantom: PhantomData, + } + } + + /// Like get_all, but for Binary keys (for example "trace-proto-bin"). + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"hello")); + /// map.append_bin("trace-proto-bin", MetadataValue::from_bytes(b"goodbye")); + /// + /// { + /// let view = map.get_all_bin("trace-proto-bin"); + /// + /// let mut iter = view.iter(); + /// assert_eq!(&"hello", iter.next().unwrap()); + /// assert_eq!(&"goodbye", iter.next().unwrap()); + /// assert!(iter.next().is_none()); + /// } + /// + /// // Attempting to read a key of the wrong type fails by not + /// // finding anything. + /// map.append("host", "world".parse().unwrap()); + /// assert!(map.get_all_bin("host").iter().next().is_none()); + /// assert!(map.get_all_bin("host".to_string()).iter().next().is_none()); + /// assert!(map.get_all_bin(&("host".to_string())).iter().next().is_none()); + /// + /// // Attempting to read an invalid key string fails by not + /// // finding anything. + /// assert!(map.get_all_bin("host{}-bin").iter().next().is_none()); + /// assert!(map.get_all_bin("host{}-bin".to_string()).iter().next().is_none()); + /// assert!(map.get_all_bin(&("host{}-bin".to_string())).iter().next().is_none()); + /// ``` + pub fn get_all_bin(&self, key: K) -> GetAll<'_, Binary> + where + K: AsMetadataKey, + { + GetAll { + inner: key.get_all(self), + phantom: PhantomData, + } + } + + /// Returns true if the map contains a value for the specified key. This + /// method works for both ascii and binary entries. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// assert!(!map.contains_key("x-host")); + /// + /// map.append_bin("host-bin", MetadataValue::from_bytes(b"world")); + /// map.insert("x-host", "world".parse().unwrap()); + /// + /// // contains_key works for both Binary and Ascii keys: + /// assert!(map.contains_key("x-host")); + /// assert!(map.contains_key("host-bin")); + /// + /// // contains_key returns false for invalid keys: + /// assert!(!map.contains_key("x{}host")); + /// ``` + pub fn contains_key(&self, key: K) -> bool + where + K: AsEncodingAgnosticMetadataKey, + { + key.contains_key(self) + } + + /// An iterator visiting all key-value pairs (both ascii and binary). + /// + /// The iteration order is arbitrary, but consistent across platforms for + /// the same crate version. Each key will be yielded once per associated + /// value. So, if a key has 3 associated values, it will be yielded 3 times. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// map.insert("x-word", "hello".parse().unwrap()); + /// map.append("x-word", "goodbye".parse().unwrap()); + /// map.insert("x-number", "123".parse().unwrap()); + /// + /// for key_and_value in map.iter() { + /// match key_and_value { + /// KeyAndValueRef::Ascii(ref key, ref value) => + /// println!("Ascii: {:?}: {:?}", key, value), + /// KeyAndValueRef::Binary(ref key, ref value) => + /// println!("Binary: {:?}: {:?}", key, value), + /// } + /// } + /// ``` + pub fn iter(&self) -> Iter<'_> { + Iter { + inner: self.headers.iter(), + } + } + + /// An iterator visiting all key-value pairs, with mutable value references. + /// + /// The iterator order is arbitrary, but consistent across platforms for the + /// same crate version. Each key will be yielded once per associated value, + /// so if a key has 3 associated values, it will be yielded 3 times. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// map.insert("x-word", "hello".parse().unwrap()); + /// map.append("x-word", "goodbye".parse().unwrap()); + /// map.insert("x-number", "123".parse().unwrap()); + /// + /// for key_and_value in map.iter_mut() { + /// match key_and_value { + /// KeyAndMutValueRef::Ascii(key, mut value) => + /// value.set_sensitive(true), + /// KeyAndMutValueRef::Binary(key, mut value) => + /// value.set_sensitive(false), + /// } + /// } + /// ``` + pub fn iter_mut(&mut self) -> IterMut<'_> { + IterMut { + inner: self.headers.iter_mut(), + } + } + + /// An iterator visiting all keys. + /// + /// The iteration order is arbitrary, but consistent across platforms for + /// the same crate version. Each key will be yielded only once even if it + /// has multiple associated values. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// map.insert("x-word", "hello".parse().unwrap()); + /// map.append("x-word", "goodbye".parse().unwrap()); + /// map.insert_bin("x-number-bin", MetadataValue::from_bytes(b"123")); + /// + /// for key in map.keys() { + /// match key { + /// KeyRef::Ascii(ref key) => + /// println!("Ascii key: {:?}", key), + /// KeyRef::Binary(ref key) => + /// println!("Binary key: {:?}", key), + /// } + /// println!("{:?}", key); + /// } + /// ``` + pub fn keys(&self) -> Keys<'_> { + Keys { + inner: self.headers.keys(), + } + } + + /// An iterator visiting all values (both ascii and binary). + /// + /// The iteration order is arbitrary, but consistent across platforms for + /// the same crate version. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// map.insert("x-word", "hello".parse().unwrap()); + /// map.append("x-word", "goodbye".parse().unwrap()); + /// map.insert_bin("x-number-bin", MetadataValue::from_bytes(b"123")); + /// + /// for value in map.values() { + /// match value { + /// ValueRef::Ascii(ref value) => + /// println!("Ascii value: {:?}", value), + /// ValueRef::Binary(ref value) => + /// println!("Binary value: {:?}", value), + /// } + /// println!("{:?}", value); + /// } + /// ``` + pub fn values(&self) -> Values<'_> { + Values { + inner: self.headers.iter(), + } + } + + /// An iterator visiting all values mutably. + /// + /// The iteration order is arbitrary, but consistent across platforms for + /// the same crate version. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::default(); + /// + /// map.insert("x-word", "hello".parse().unwrap()); + /// map.append("x-word", "goodbye".parse().unwrap()); + /// map.insert("x-number", "123".parse().unwrap()); + /// + /// for value in map.values_mut() { + /// match value { + /// ValueRefMut::Ascii(mut value) => + /// value.set_sensitive(true), + /// ValueRefMut::Binary(mut value) => + /// value.set_sensitive(false), + /// } + /// } + /// ``` + pub fn values_mut(&mut self) -> ValuesMut<'_> { + ValuesMut { + inner: self.headers.iter_mut(), + } + } + + /// Gets the given ascii key's corresponding entry in the map for in-place + /// manipulation. For binary keys, use `entry_bin`. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::default(); + /// + /// let headers = &[ + /// "content-length", + /// "x-hello", + /// "Content-Length", + /// "x-world", + /// ]; + /// + /// for &header in headers { + /// let counter = map.entry(header).unwrap().or_insert("".parse().unwrap()); + /// *counter = format!("{}{}", counter.to_str().unwrap(), "1").parse().unwrap(); + /// } + /// + /// assert_eq!(map.get("content-length").unwrap(), "11"); + /// assert_eq!(map.get("x-hello").unwrap(), "1"); + /// + /// // Gracefully handles parting invalid key strings + /// assert!(!map.entry("a{}b").is_ok()); + /// + /// // Attempting to read a key of the wrong type fails by not + /// // finding anything. + /// map.append_bin("host-bin", MetadataValue::from_bytes(b"world")); + /// assert!(!map.entry("host-bin").is_ok()); + /// assert!(!map.entry("host-bin".to_string()).is_ok()); + /// assert!(!map.entry(&("host-bin".to_string())).is_ok()); + /// + /// // Attempting to read an invalid key string fails by not + /// // finding anything. + /// assert!(!map.entry("host{}").is_ok()); + /// assert!(!map.entry("host{}".to_string()).is_ok()); + /// assert!(!map.entry(&("host{}".to_string())).is_ok()); + /// ``` + pub fn entry(&mut self, key: K) -> Result, InvalidMetadataKey> + where + K: AsMetadataKey, + { + self.generic_entry::(key) + } + + /// Gets the given Binary key's corresponding entry in the map for in-place + /// manipulation. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// # use std::str; + /// let mut map = MetadataMap::default(); + /// + /// let headers = &[ + /// "content-length-bin", + /// "x-hello-bin", + /// "Content-Length-bin", + /// "x-world-bin", + /// ]; + /// + /// for &header in headers { + /// let counter = map.entry_bin(header).unwrap().or_insert(MetadataValue::from_bytes(b"")); + /// *counter = MetadataValue::from_bytes(format!("{}{}", str::from_utf8(counter.to_bytes().unwrap().as_ref()).unwrap(), "1").as_bytes()); + /// } + /// + /// assert_eq!(map.get_bin("content-length-bin").unwrap(), "11"); + /// assert_eq!(map.get_bin("x-hello-bin").unwrap(), "1"); + /// + /// // Attempting to read a key of the wrong type fails by not + /// // finding anything. + /// map.append("host", "world".parse().unwrap()); + /// assert!(!map.entry_bin("host").is_ok()); + /// assert!(!map.entry_bin("host".to_string()).is_ok()); + /// assert!(!map.entry_bin(&("host".to_string())).is_ok()); + /// + /// // Attempting to read an invalid key string fails by not + /// // finding anything. + /// assert!(!map.entry_bin("host{}-bin").is_ok()); + /// assert!(!map.entry_bin("host{}-bin".to_string()).is_ok()); + /// assert!(!map.entry_bin(&("host{}-bin".to_string())).is_ok()); + /// ``` + pub fn entry_bin(&mut self, key: K) -> Result, InvalidMetadataKey> + where + K: AsMetadataKey, + { + self.generic_entry::(key) + } + + fn generic_entry( + &mut self, + key: K, + ) -> Result, InvalidMetadataKey> + where + K: AsMetadataKey, + { + match key.entry(self) { + Ok(entry) => Ok(match entry { + http::header::Entry::Occupied(e) => Entry::Occupied(OccupiedEntry { + inner: e, + phantom: PhantomData, + }), + http::header::Entry::Vacant(e) => Entry::Vacant(VacantEntry { + inner: e, + phantom: PhantomData, + }), + }), + Err(err) => Err(err), + } + } + + /// Inserts an ascii key-value pair into the map. To insert a binary entry, + /// use `insert_bin`. + /// + /// This method panics when the given key is a string and it cannot be + /// converted to a MetadataKey. + /// + /// If the map did not previously have this key present, then `None` is + /// returned. + /// + /// If the map did have this key present, the new value is associated with + /// the key and all previous values are removed. **Note** that only a single + /// one of the previous values is returned. If there are multiple values + /// that have been previously associated with the key, then the first one is + /// returned. See `insert_mult` on `OccupiedEntry` for an API that returns + /// all values. + /// + /// The key is not updated, though; this matters for types that can be `==` + /// without being identical. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// assert!(map.insert("x-host", "world".parse().unwrap()).is_none()); + /// assert!(!map.is_empty()); + /// + /// let mut prev = map.insert("x-host", "earth".parse().unwrap()).unwrap(); + /// assert_eq!("world", prev); + /// ``` + /// + /// ```should_panic + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// // Trying to insert a key that is not valid panics. + /// map.insert("x{}host", "world".parse().unwrap()); + /// ``` + /// + /// ```should_panic + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// // Trying to insert a key that is binary panics (use insert_bin). + /// map.insert("x-host-bin", "world".parse().unwrap()); + /// ``` + pub fn insert(&mut self, key: K, val: MetadataValue) -> Option> + where + K: IntoMetadataKey, + { + key.insert(self, val) + } + + /// Like insert, but for Binary keys (for example "trace-proto-bin"). + /// + /// This method panics when the given key is a string and it cannot be + /// converted to a MetadataKey. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// assert!(map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"world")).is_none()); + /// assert!(!map.is_empty()); + /// + /// let mut prev = map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"earth")).unwrap(); + /// assert_eq!("world", prev); + /// ``` + /// + /// ```should_panic + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::default(); + /// // Attempting to add a binary metadata entry with an invalid name + /// map.insert_bin("trace-proto", MetadataValue::from_bytes(b"hello")); // This line panics! + /// ``` + /// + /// ```should_panic + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// // Trying to insert a key that is not valid panics. + /// map.insert_bin("x{}host-bin", MetadataValue::from_bytes(b"world")); // This line panics! + /// ``` + pub fn insert_bin( + &mut self, + key: K, + val: MetadataValue, + ) -> Option> + where + K: IntoMetadataKey, + { + key.insert(self, val) + } + + /// Inserts an ascii key-value pair into the map. To insert a binary entry, + /// use `append_bin`. + /// + /// This method panics when the given key is a string and it cannot be + /// converted to a MetadataKey. + /// + /// If the map did not previously have this key present, then `false` is + /// returned. + /// + /// If the map did have this key present, the new value is pushed to the end + /// of the list of values currently associated with the key. The key is not + /// updated, though; this matters for types that can be `==` without being + /// identical. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// assert!(map.insert("x-host", "world".parse().unwrap()).is_none()); + /// assert!(!map.is_empty()); + /// + /// map.append("x-host", "earth".parse().unwrap()); + /// + /// let values = map.get_all("x-host"); + /// let mut i = values.iter(); + /// assert_eq!("world", *i.next().unwrap()); + /// assert_eq!("earth", *i.next().unwrap()); + /// ``` + /// + /// ```should_panic + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// // Trying to append a key that is not valid panics. + /// map.append("x{}host", "world".parse().unwrap()); // This line panics! + /// ``` + /// + /// ```should_panic + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// // Trying to append a key that is binary panics (use append_bin). + /// map.append("x-host-bin", "world".parse().unwrap()); // This line panics! + /// ``` + pub fn append(&mut self, key: K, value: MetadataValue) -> bool + where + K: IntoMetadataKey, + { + key.append(self, value) + } + + /// Like append, but for binary keys (for example "trace-proto-bin"). + /// + /// This method panics when the given key is a string and it cannot be + /// converted to a MetadataKey. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// assert!(map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"world")).is_none()); + /// assert!(!map.is_empty()); + /// + /// map.append_bin("trace-proto-bin", MetadataValue::from_bytes(b"earth")); + /// + /// let values = map.get_all_bin("trace-proto-bin"); + /// let mut i = values.iter(); + /// assert_eq!("world", *i.next().unwrap()); + /// assert_eq!("earth", *i.next().unwrap()); + /// ``` + /// + /// ```should_panic + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// // Trying to append a key that is not valid panics. + /// map.append_bin("x{}host-bin", MetadataValue::from_bytes(b"world")); // This line panics! + /// ``` + /// + /// ```should_panic + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// // Trying to append a key that is ascii panics (use append). + /// map.append_bin("x-host", MetadataValue::from_bytes(b"world")); // This line panics! + /// ``` + pub fn append_bin(&mut self, key: K, value: MetadataValue) -> bool + where + K: IntoMetadataKey, + { + key.append(self, value) + } + + /// Removes an ascii key from the map, returning the value associated with + /// the key. To remove a binary key, use `remove_bin`. + /// + /// Returns `None` if the map does not contain the key. If there are + /// multiple values associated with the key, then the first one is returned. + /// See `remove_entry_mult` on `OccupiedEntry` for an API that yields all + /// values. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("x-host", "hello.world".parse().unwrap()); + /// + /// let prev = map.remove("x-host").unwrap(); + /// assert_eq!("hello.world", prev); + /// + /// assert!(map.remove("x-host").is_none()); + /// + /// // Attempting to remove a key of the wrong type fails by not + /// // finding anything. + /// map.append_bin("host-bin", MetadataValue::from_bytes(b"world")); + /// assert!(map.remove("host-bin").is_none()); + /// assert!(map.remove("host-bin".to_string()).is_none()); + /// assert!(map.remove(&("host-bin".to_string())).is_none()); + /// + /// // Attempting to remove an invalid key string fails by not + /// // finding anything. + /// assert!(map.remove("host{}").is_none()); + /// assert!(map.remove("host{}".to_string()).is_none()); + /// assert!(map.remove(&("host{}".to_string())).is_none()); + /// ``` + pub fn remove(&mut self, key: K) -> Option> + where + K: AsMetadataKey, + { + key.remove(self) + } + + /// Like remove, but for Binary keys (for example "trace-proto-bin"). + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert_bin("trace-proto-bin", MetadataValue::from_bytes(b"hello.world")); + /// + /// let prev = map.remove_bin("trace-proto-bin").unwrap(); + /// assert_eq!("hello.world", prev); + /// + /// assert!(map.remove_bin("trace-proto-bin").is_none()); + /// + /// // Attempting to remove a key of the wrong type fails by not + /// // finding anything. + /// map.append("host", "world".parse().unwrap()); + /// assert!(map.remove_bin("host").is_none()); + /// assert!(map.remove_bin("host".to_string()).is_none()); + /// assert!(map.remove_bin(&("host".to_string())).is_none()); + /// + /// // Attempting to remove an invalid key string fails by not + /// // finding anything. + /// assert!(map.remove_bin("host{}-bin").is_none()); + /// assert!(map.remove_bin("host{}-bin".to_string()).is_none()); + /// assert!(map.remove_bin(&("host{}-bin".to_string())).is_none()); + /// ``` + pub fn remove_bin(&mut self, key: K) -> Option> + where + K: AsMetadataKey, + { + key.remove(self) + } +} + +// ===== impl Iter ===== + +impl<'a> Iterator for Iter<'a> { + type Item = KeyAndValueRef<'a>; + + fn next(&mut self) -> Option { + self.inner.next().map(|item| { + let (ref name, value) = item; + if Ascii::is_valid_key(name.as_str()) { + KeyAndValueRef::Ascii( + MetadataKey::unchecked_from_header_name_ref(name), + MetadataValue::unchecked_from_header_value_ref(value), + ) + } else { + KeyAndValueRef::Binary( + MetadataKey::unchecked_from_header_name_ref(name), + MetadataValue::unchecked_from_header_value_ref(value), + ) + } + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +unsafe impl<'a> Sync for Iter<'a> {} +unsafe impl<'a> Send for Iter<'a> {} + +// ===== impl IterMut ===== + +impl<'a> Iterator for IterMut<'a> { + type Item = KeyAndMutValueRef<'a>; + + fn next(&mut self) -> Option { + self.inner.next().map(|item| { + let (name, value) = item; + if Ascii::is_valid_key(name.as_str()) { + KeyAndMutValueRef::Ascii( + MetadataKey::unchecked_from_header_name_ref(name), + MetadataValue::unchecked_from_mut_header_value_ref(value), + ) + } else { + KeyAndMutValueRef::Binary( + MetadataKey::unchecked_from_header_name_ref(name), + MetadataValue::unchecked_from_mut_header_value_ref(value), + ) + } + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +unsafe impl<'a> Sync for IterMut<'a> {} +unsafe impl<'a> Send for IterMut<'a> {} + +// ===== impl ValueDrain ===== + +impl<'a, VE: ValueEncoding> Iterator for ValueDrain<'a, VE> { + type Item = MetadataValue; + + fn next(&mut self) -> Option { + self.inner + .next() + .map(|value| MetadataValue::unchecked_from_header_value(value)) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +unsafe impl<'a, VE: ValueEncoding> Sync for ValueDrain<'a, VE> {} +unsafe impl<'a, VE: ValueEncoding> Send for ValueDrain<'a, VE> {} + +// ===== impl Keys ===== + +impl<'a> Iterator for Keys<'a> { + type Item = KeyRef<'a>; + + fn next(&mut self) -> Option { + self.inner.next().map(|key| { + if Ascii::is_valid_key(key.as_str()) { + KeyRef::Ascii(MetadataKey::unchecked_from_header_name_ref(key)) + } else { + KeyRef::Binary(MetadataKey::unchecked_from_header_name_ref(key)) + } + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl<'a> ExactSizeIterator for Keys<'a> {} + +// ===== impl Values ==== + +impl<'a> Iterator for Values<'a> { + type Item = ValueRef<'a>; + + fn next(&mut self) -> Option { + self.inner.next().map(|item| { + let (ref name, value) = item; + if Ascii::is_valid_key(name.as_str()) { + ValueRef::Ascii(MetadataValue::unchecked_from_header_value_ref(value)) + } else { + ValueRef::Binary(MetadataValue::unchecked_from_header_value_ref(value)) + } + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +// ===== impl Values ==== + +impl<'a> Iterator for ValuesMut<'a> { + type Item = ValueRefMut<'a>; + + fn next(&mut self) -> Option { + self.inner.next().map(|item| { + let (name, value) = item; + if Ascii::is_valid_key(name.as_str()) { + ValueRefMut::Ascii(MetadataValue::unchecked_from_mut_header_value_ref(value)) + } else { + ValueRefMut::Binary(MetadataValue::unchecked_from_mut_header_value_ref(value)) + } + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +// ===== impl ValueIter ===== + +impl<'a, VE: ValueEncoding> Iterator for ValueIter<'a, VE> +where + VE: 'a, +{ + type Item = &'a MetadataValue; + + fn next(&mut self) -> Option { + match self.inner { + Some(ref mut inner) => inner + .next() + .map(&MetadataValue::unchecked_from_header_value_ref), + None => None, + } + } + + fn size_hint(&self) -> (usize, Option) { + match self.inner { + Some(ref inner) => inner.size_hint(), + None => (0, Some(0)), + } + } +} + +impl<'a, VE: ValueEncoding> DoubleEndedIterator for ValueIter<'a, VE> +where + VE: 'a, +{ + fn next_back(&mut self) -> Option { + match self.inner { + Some(ref mut inner) => inner + .next_back() + .map(&MetadataValue::unchecked_from_header_value_ref), + None => None, + } + } +} + +// ===== impl ValueIterMut ===== + +impl<'a, VE: ValueEncoding> Iterator for ValueIterMut<'a, VE> +where + VE: 'a, +{ + type Item = &'a mut MetadataValue; + + fn next(&mut self) -> Option { + self.inner + .next() + .map(&MetadataValue::unchecked_from_mut_header_value_ref) + } +} + +impl<'a, VE: ValueEncoding> DoubleEndedIterator for ValueIterMut<'a, VE> +where + VE: 'a, +{ + fn next_back(&mut self) -> Option { + self.inner + .next_back() + .map(&MetadataValue::unchecked_from_mut_header_value_ref) + } +} + +unsafe impl<'a, VE: ValueEncoding> Sync for ValueIterMut<'a, VE> {} +unsafe impl<'a, VE: ValueEncoding> Send for ValueIterMut<'a, VE> {} + +// ===== impl Entry ===== + +impl<'a, VE: ValueEncoding> Entry<'a, VE> { + /// Ensures a value is in the entry by inserting the default if empty. + /// + /// Returns a mutable reference to the **first** value in the entry. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map: MetadataMap = MetadataMap::default(); + /// + /// let keys = &[ + /// "content-length", + /// "x-hello", + /// "Content-Length", + /// "x-world", + /// ]; + /// + /// for &key in keys { + /// let counter = map.entry(key) + /// .expect("valid key names") + /// .or_insert("".parse().unwrap()); + /// *counter = format!("{}{}", counter.to_str().unwrap(), "1").parse().unwrap(); + /// } + /// + /// assert_eq!(map.get("content-length").unwrap(), "11"); + /// assert_eq!(map.get("x-hello").unwrap(), "1"); + /// ``` + pub fn or_insert(self, default: MetadataValue) -> &'a mut MetadataValue { + use self::Entry::*; + + match self { + Occupied(e) => e.into_mut(), + Vacant(e) => e.insert(default), + } + } + + /// Ensures a value is in the entry by inserting the result of the default + /// function if empty. + /// + /// The default function is not called if the entry exists in the map. + /// Returns a mutable reference to the **first** value in the entry. + /// + /// # Examples + /// + /// Basic usage. + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// let res = map.entry("x-hello").unwrap() + /// .or_insert_with(|| "world".parse().unwrap()); + /// + /// assert_eq!(res, "world"); + /// ``` + /// + /// The default function is not called if the entry exists in the map. + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("host", "world".parse().unwrap()); + /// + /// let res = map.entry("host") + /// .expect("host is a valid string") + /// .or_insert_with(|| unreachable!()); + /// + /// + /// assert_eq!(res, "world"); + /// ``` + pub fn or_insert_with MetadataValue>( + self, + default: F, + ) -> &'a mut MetadataValue { + use self::Entry::*; + + match self { + Occupied(e) => e.into_mut(), + Vacant(e) => e.insert(default()), + } + } + + /// Returns a reference to the entry's key + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// assert_eq!(map.entry("x-hello").unwrap().key(), "x-hello"); + /// ``` + pub fn key(&self) -> &MetadataKey { + use self::Entry::*; + + MetadataKey::unchecked_from_header_name_ref(match *self { + Vacant(ref e) => e.inner.key(), + Occupied(ref e) => e.inner.key(), + }) + } +} + +// ===== impl VacantEntry ===== + +impl<'a, VE: ValueEncoding> VacantEntry<'a, VE> { + /// Returns a reference to the entry's key + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// assert_eq!(map.entry("x-hello").unwrap().key(), "x-hello"); + /// ``` + pub fn key(&self) -> &MetadataKey { + MetadataKey::unchecked_from_header_name_ref(self.inner.key()) + } + + /// Take ownership of the key + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// if let Entry::Vacant(v) = map.entry("x-hello").unwrap() { + /// assert_eq!(v.into_key().as_str(), "x-hello"); + /// } + /// ``` + pub fn into_key(self) -> MetadataKey { + MetadataKey::unchecked_from_header_name(self.inner.into_key()) + } + + /// Insert the value into the entry. + /// + /// The value will be associated with this entry's key. A mutable reference + /// to the inserted value will be returned. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// if let Entry::Vacant(v) = map.entry("x-hello").unwrap() { + /// v.insert("world".parse().unwrap()); + /// } + /// + /// assert_eq!(map.get("x-hello").unwrap(), "world"); + /// ``` + pub fn insert(self, value: MetadataValue) -> &'a mut MetadataValue { + MetadataValue::unchecked_from_mut_header_value_ref(self.inner.insert(value.inner)) + } + + /// Insert the value into the entry. + /// + /// The value will be associated with this entry's key. The new + /// `OccupiedEntry` is returned, allowing for further manipulation. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// + /// if let Entry::Vacant(v) = map.entry("x-hello").unwrap() { + /// let mut e = v.insert_entry("world".parse().unwrap()); + /// e.insert("world2".parse().unwrap()); + /// } + /// + /// assert_eq!(map.get("x-hello").unwrap(), "world2"); + /// ``` + pub fn insert_entry(self, value: MetadataValue) -> OccupiedEntry<'a, Ascii> { + OccupiedEntry { + inner: self.inner.insert_entry(value.inner), + phantom: PhantomData, + } + } +} + +// ===== impl OccupiedEntry ===== + +impl<'a, VE: ValueEncoding> OccupiedEntry<'a, VE> { + /// Returns a reference to the entry's key. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("host", "world".parse().unwrap()); + /// + /// if let Entry::Occupied(e) = map.entry("host").unwrap() { + /// assert_eq!("host", e.key()); + /// } + /// ``` + pub fn key(&self) -> &MetadataKey { + MetadataKey::unchecked_from_header_name_ref(self.inner.key()) + } + + /// Get a reference to the first value in the entry. + /// + /// Values are stored in insertion order. + /// + /// # Panics + /// + /// `get` panics if there are no values associated with the entry. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("host", "hello.world".parse().unwrap()); + /// + /// if let Entry::Occupied(mut e) = map.entry("host").unwrap() { + /// assert_eq!(e.get(), &"hello.world"); + /// + /// e.append("hello.earth".parse().unwrap()); + /// + /// assert_eq!(e.get(), &"hello.world"); + /// } + /// ``` + pub fn get(&self) -> &MetadataValue { + MetadataValue::unchecked_from_header_value_ref(self.inner.get()) + } + + /// Get a mutable reference to the first value in the entry. + /// + /// Values are stored in insertion order. + /// + /// # Panics + /// + /// `get_mut` panics if there are no values associated with the entry. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::default(); + /// map.insert("host", "hello.world".parse().unwrap()); + /// + /// if let Entry::Occupied(mut e) = map.entry("host").unwrap() { + /// e.get_mut().set_sensitive(true); + /// assert_eq!(e.get(), &"hello.world"); + /// assert!(e.get().is_sensitive()); + /// } + /// ``` + pub fn get_mut(&mut self) -> &mut MetadataValue { + MetadataValue::unchecked_from_mut_header_value_ref(self.inner.get_mut()) + } + + /// Converts the `OccupiedEntry` into a mutable reference to the **first** + /// value. + /// + /// The lifetime of the returned reference is bound to the original map. + /// + /// # Panics + /// + /// `into_mut` panics if there are no values associated with the entry. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::default(); + /// map.insert("host", "hello.world".parse().unwrap()); + /// map.append("host", "hello.earth".parse().unwrap()); + /// + /// if let Entry::Occupied(e) = map.entry("host").unwrap() { + /// e.into_mut().set_sensitive(true); + /// } + /// + /// assert!(map.get("host").unwrap().is_sensitive()); + /// ``` + pub fn into_mut(self) -> &'a mut MetadataValue { + MetadataValue::unchecked_from_mut_header_value_ref(self.inner.into_mut()) + } + + /// Sets the value of the entry. + /// + /// All previous values associated with the entry are removed and the first + /// one is returned. See `insert_mult` for an API that returns all values. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("host", "hello.world".parse().unwrap()); + /// + /// if let Entry::Occupied(mut e) = map.entry("host").unwrap() { + /// let mut prev = e.insert("earth".parse().unwrap()); + /// assert_eq!("hello.world", prev); + /// } + /// + /// assert_eq!("earth", map.get("host").unwrap()); + /// ``` + pub fn insert(&mut self, value: MetadataValue) -> MetadataValue { + let header_value = self.inner.insert(value.inner); + MetadataValue::unchecked_from_header_value(header_value) + } + + /// Sets the value of the entry. + /// + /// This function does the same as `insert` except it returns an iterator + /// that yields all values previously associated with the key. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("host", "world".parse().unwrap()); + /// map.append("host", "world2".parse().unwrap()); + /// + /// if let Entry::Occupied(mut e) = map.entry("host").unwrap() { + /// let mut prev = e.insert_mult("earth".parse().unwrap()); + /// assert_eq!("world", prev.next().unwrap()); + /// assert_eq!("world2", prev.next().unwrap()); + /// assert!(prev.next().is_none()); + /// } + /// + /// assert_eq!("earth", map.get("host").unwrap()); + /// ``` + pub fn insert_mult(&mut self, value: MetadataValue) -> ValueDrain<'_, VE> { + ValueDrain { + inner: self.inner.insert_mult(value.inner), + phantom: PhantomData, + } + } + + /// Insert the value into the entry. + /// + /// The new value is appended to the end of the entry's value list. All + /// previous values associated with the entry are retained. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("host", "world".parse().unwrap()); + /// + /// if let Entry::Occupied(mut e) = map.entry("host").unwrap() { + /// e.append("earth".parse().unwrap()); + /// } + /// + /// let values = map.get_all("host"); + /// let mut i = values.iter(); + /// assert_eq!("world", *i.next().unwrap()); + /// assert_eq!("earth", *i.next().unwrap()); + /// ``` + pub fn append(&mut self, value: MetadataValue) { + self.inner.append(value.inner) + } + + /// Remove the entry from the map. + /// + /// All values associated with the entry are removed and the first one is + /// returned. See `remove_entry_mult` for an API that returns all values. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("host", "world".parse().unwrap()); + /// + /// if let Entry::Occupied(e) = map.entry("host").unwrap() { + /// let mut prev = e.remove(); + /// assert_eq!("world", prev); + /// } + /// + /// assert!(!map.contains_key("host")); + /// ``` + pub fn remove(self) -> MetadataValue { + let value = self.inner.remove(); + MetadataValue::unchecked_from_header_value(value) + } + + /// Remove the entry from the map. + /// + /// The key and all values associated with the entry are removed and the + /// first one is returned. See `remove_entry_mult` for an API that returns + /// all values. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("host", "world".parse().unwrap()); + /// + /// if let Entry::Occupied(e) = map.entry("host").unwrap() { + /// let (key, mut prev) = e.remove_entry(); + /// assert_eq!("host", key.as_str()); + /// assert_eq!("world", prev); + /// } + /// + /// assert!(!map.contains_key("host")); + /// ``` + pub fn remove_entry(self) -> (MetadataKey, MetadataValue) { + let (name, value) = self.inner.remove_entry(); + ( + MetadataKey::unchecked_from_header_name(name), + MetadataValue::unchecked_from_header_value(value), + ) + } + + /// Remove the entry from the map. + /// + /// The key and all values associated with the entry are removed and + /// returned. + pub fn remove_entry_mult(self) -> (MetadataKey, ValueDrain<'a, VE>) { + let (name, value_drain) = self.inner.remove_entry_mult(); + ( + MetadataKey::unchecked_from_header_name(name), + ValueDrain { + inner: value_drain, + phantom: PhantomData, + }, + ) + } + + /// Returns an iterator visiting all values associated with the entry. + /// + /// Values are iterated in insertion order. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("host", "world".parse().unwrap()); + /// map.append("host", "earth".parse().unwrap()); + /// + /// if let Entry::Occupied(e) = map.entry("host").unwrap() { + /// let mut iter = e.iter(); + /// assert_eq!(&"world", iter.next().unwrap()); + /// assert_eq!(&"earth", iter.next().unwrap()); + /// assert!(iter.next().is_none()); + /// } + /// ``` + pub fn iter(&self) -> ValueIter<'_, VE> { + ValueIter { + inner: Some(self.inner.iter()), + phantom: PhantomData, + } + } + + /// Returns an iterator mutably visiting all values associated with the + /// entry. + /// + /// Values are iterated in insertion order. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::default(); + /// map.insert("host", "world".parse().unwrap()); + /// map.append("host", "earth".parse().unwrap()); + /// + /// if let Entry::Occupied(mut e) = map.entry("host").unwrap() { + /// for e in e.iter_mut() { + /// e.set_sensitive(true); + /// } + /// } + /// + /// let mut values = map.get_all("host"); + /// let mut i = values.iter(); + /// assert!(i.next().unwrap().is_sensitive()); + /// assert!(i.next().unwrap().is_sensitive()); + /// ``` + pub fn iter_mut(&mut self) -> ValueIterMut<'_, VE> { + ValueIterMut { + inner: self.inner.iter_mut(), + phantom: PhantomData, + } + } +} + +impl<'a, VE: ValueEncoding> IntoIterator for OccupiedEntry<'a, VE> +where + VE: 'a, +{ + type Item = &'a mut MetadataValue; + type IntoIter = ValueIterMut<'a, VE>; + + fn into_iter(self) -> ValueIterMut<'a, VE> { + ValueIterMut { + inner: self.inner.into_iter(), + phantom: PhantomData, + } + } +} + +impl<'a, 'b: 'a, VE: ValueEncoding> IntoIterator for &'b OccupiedEntry<'a, VE> { + type Item = &'a MetadataValue; + type IntoIter = ValueIter<'a, VE>; + + fn into_iter(self) -> ValueIter<'a, VE> { + self.iter() + } +} + +impl<'a, 'b: 'a, VE: ValueEncoding> IntoIterator for &'b mut OccupiedEntry<'a, VE> { + type Item = &'a mut MetadataValue; + type IntoIter = ValueIterMut<'a, VE>; + + fn into_iter(self) -> ValueIterMut<'a, VE> { + self.iter_mut() + } +} + +// ===== impl GetAll ===== + +impl<'a, VE: ValueEncoding> GetAll<'a, VE> { + /// Returns an iterator visiting all values associated with the entry. + /// + /// Values are iterated in insertion order. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut map = MetadataMap::new(); + /// map.insert("x-host", "hello.world".parse().unwrap()); + /// map.append("x-host", "hello.earth".parse().unwrap()); + /// + /// let values = map.get_all("x-host"); + /// let mut iter = values.iter(); + /// assert_eq!(&"hello.world", iter.next().unwrap()); + /// assert_eq!(&"hello.earth", iter.next().unwrap()); + /// assert!(iter.next().is_none()); + /// ``` + pub fn iter(&self) -> ValueIter<'a, VE> { + ValueIter { + inner: self.inner.as_ref().map(|inner| inner.iter()), + phantom: PhantomData, + } + } +} + +impl<'a, VE: ValueEncoding> PartialEq for GetAll<'a, VE> { + fn eq(&self, other: &Self) -> bool { + self.inner.iter().eq(other.inner.iter()) + } +} + +impl<'a, VE: ValueEncoding> IntoIterator for GetAll<'a, VE> +where + VE: 'a, +{ + type Item = &'a MetadataValue; + type IntoIter = ValueIter<'a, VE>; + + fn into_iter(self) -> ValueIter<'a, VE> { + ValueIter { + inner: self.inner.map(|inner| inner.into_iter()), + phantom: PhantomData, + } + } +} + +impl<'a, 'b: 'a, VE: ValueEncoding> IntoIterator for &'b GetAll<'a, VE> { + type Item = &'a MetadataValue; + type IntoIter = ValueIter<'a, VE>; + + fn into_iter(self) -> ValueIter<'a, VE> { + ValueIter { + inner: (&self.inner).as_ref().map(|inner| inner.into_iter()), + phantom: PhantomData, + } + } +} + +// ===== impl IntoMetadataKey / AsMetadataKey ===== + +mod into_metadata_key { + use super::{MetadataMap, MetadataValue, ValueEncoding}; + use crate::metadata::key::MetadataKey; + + /// A marker trait used to identify values that can be used as insert keys + /// to a `MetadataMap`. + pub trait IntoMetadataKey: Sealed {} + + // All methods are on this pub(super) trait, instead of `IntoMetadataKey`, + // so that they aren't publicly exposed to the world. + // + // Being on the `IntoMetadataKey` trait would mean users could call + // `"host".insert(&mut map, "localhost")`. + // + // Ultimately, this allows us to adjust the signatures of these methods + // without breaking any external crate. + pub trait Sealed { + #[doc(hidden)] + fn insert(self, map: &mut MetadataMap, val: MetadataValue) + -> Option>; + + #[doc(hidden)] + fn append(self, map: &mut MetadataMap, val: MetadataValue) -> bool; + } + + // ==== impls ==== + + impl Sealed for MetadataKey { + #[doc(hidden)] + #[inline] + fn insert( + self, + map: &mut MetadataMap, + val: MetadataValue, + ) -> Option> { + map.headers + .insert(self.inner, val.inner) + .map(&MetadataValue::unchecked_from_header_value) + } + + #[doc(hidden)] + #[inline] + fn append(self, map: &mut MetadataMap, val: MetadataValue) -> bool { + map.headers.append(self.inner, val.inner) + } + } + + impl IntoMetadataKey for MetadataKey {} + + impl<'a, VE: ValueEncoding> Sealed for &'a MetadataKey { + #[doc(hidden)] + #[inline] + fn insert( + self, + map: &mut MetadataMap, + val: MetadataValue, + ) -> Option> { + map.headers + .insert(&self.inner, val.inner) + .map(&MetadataValue::unchecked_from_header_value) + } + #[doc(hidden)] + #[inline] + fn append(self, map: &mut MetadataMap, val: MetadataValue) -> bool { + map.headers.append(&self.inner, val.inner) + } + } + + impl<'a, VE: ValueEncoding> IntoMetadataKey for &'a MetadataKey {} + + impl Sealed for &'static str { + #[doc(hidden)] + #[inline] + fn insert( + self, + map: &mut MetadataMap, + val: MetadataValue, + ) -> Option> { + // Perform name validation + let key = MetadataKey::::from_static(self); + + map.headers + .insert(key.inner, val.inner) + .map(&MetadataValue::unchecked_from_header_value) + } + #[doc(hidden)] + #[inline] + fn append(self, map: &mut MetadataMap, val: MetadataValue) -> bool { + // Perform name validation + let key = MetadataKey::::from_static(self); + + map.headers.append(key.inner, val.inner) + } + } + + impl IntoMetadataKey for &'static str {} +} + +mod as_metadata_key { + use super::{MetadataMap, MetadataValue, ValueEncoding}; + use crate::metadata::key::{InvalidMetadataKey, MetadataKey}; + use http::header::{Entry, GetAll, HeaderValue}; + + /// A marker trait used to identify values that can be used as search keys + /// to a `MetadataMap`. + pub trait AsMetadataKey: Sealed {} + + // All methods are on this pub(super) trait, instead of `AsMetadataKey`, + // so that they aren't publicly exposed to the world. + // + // Being on the `AsMetadataKey` trait would mean users could call + // `"host".find(&map)`. + // + // Ultimately, this allows us to adjust the signatures of these methods + // without breaking any external crate. + pub trait Sealed { + #[doc(hidden)] + fn get(self, map: &MetadataMap) -> Option<&MetadataValue>; + + #[doc(hidden)] + fn get_mut(self, map: &mut MetadataMap) -> Option<&mut MetadataValue>; + + #[doc(hidden)] + fn get_all(self, map: &MetadataMap) -> Option>; + + #[doc(hidden)] + fn entry(self, map: &mut MetadataMap) + -> Result, InvalidMetadataKey>; + + #[doc(hidden)] + fn remove(self, map: &mut MetadataMap) -> Option>; + } + + // ==== impls ==== + + impl Sealed for MetadataKey { + #[doc(hidden)] + #[inline] + fn get(self, map: &MetadataMap) -> Option<&MetadataValue> { + map.headers + .get(self.inner) + .map(&MetadataValue::unchecked_from_header_value_ref) + } + + #[doc(hidden)] + #[inline] + fn get_mut(self, map: &mut MetadataMap) -> Option<&mut MetadataValue> { + map.headers + .get_mut(self.inner) + .map(&MetadataValue::unchecked_from_mut_header_value_ref) + } + + #[doc(hidden)] + #[inline] + fn get_all(self, map: &MetadataMap) -> Option> { + Some(map.headers.get_all(self.inner)) + } + + #[doc(hidden)] + #[inline] + fn entry( + self, + map: &mut MetadataMap, + ) -> Result, InvalidMetadataKey> { + map.headers + .entry(self.inner) + .map_err(|_| InvalidMetadataKey::new()) + } + + #[doc(hidden)] + #[inline] + fn remove(self, map: &mut MetadataMap) -> Option> { + map.headers + .remove(self.inner) + .map(&MetadataValue::unchecked_from_header_value) + } + } + + impl AsMetadataKey for MetadataKey {} + + impl<'a, VE: ValueEncoding> Sealed for &'a MetadataKey { + #[doc(hidden)] + #[inline] + fn get(self, map: &MetadataMap) -> Option<&MetadataValue> { + map.headers + .get(&self.inner) + .map(&MetadataValue::unchecked_from_header_value_ref) + } + + #[doc(hidden)] + #[inline] + fn get_mut(self, map: &mut MetadataMap) -> Option<&mut MetadataValue> { + map.headers + .get_mut(&self.inner) + .map(&MetadataValue::unchecked_from_mut_header_value_ref) + } + + #[doc(hidden)] + #[inline] + fn get_all(self, map: &MetadataMap) -> Option> { + Some(map.headers.get_all(&self.inner)) + } + + #[doc(hidden)] + #[inline] + fn entry( + self, + map: &mut MetadataMap, + ) -> Result, InvalidMetadataKey> { + map.headers + .entry(&self.inner) + .map_err(|_| InvalidMetadataKey::new()) + } + + #[doc(hidden)] + #[inline] + fn remove(self, map: &mut MetadataMap) -> Option> { + map.headers + .remove(&self.inner) + .map(&MetadataValue::unchecked_from_header_value) + } + } + + impl<'a, VE: ValueEncoding> AsMetadataKey for &'a MetadataKey {} + + impl<'a, VE: ValueEncoding> Sealed for &'a str { + #[doc(hidden)] + #[inline] + fn get(self, map: &MetadataMap) -> Option<&MetadataValue> { + if !VE::is_valid_key(self) { + return None; + } + map.headers + .get(self) + .map(&MetadataValue::unchecked_from_header_value_ref) + } + + #[doc(hidden)] + #[inline] + fn get_mut(self, map: &mut MetadataMap) -> Option<&mut MetadataValue> { + if !VE::is_valid_key(self) { + return None; + } + map.headers + .get_mut(self) + .map(&MetadataValue::unchecked_from_mut_header_value_ref) + } + + #[doc(hidden)] + #[inline] + fn get_all(self, map: &MetadataMap) -> Option> { + if !VE::is_valid_key(self) { + return None; + } + Some(map.headers.get_all(self)) + } + + #[doc(hidden)] + #[inline] + fn entry( + self, + map: &mut MetadataMap, + ) -> Result, InvalidMetadataKey> { + if !VE::is_valid_key(self) { + return Err(InvalidMetadataKey::new()); + } + map.headers + .entry(self) + .map_err(|_| InvalidMetadataKey::new()) + } + + #[doc(hidden)] + #[inline] + fn remove(self, map: &mut MetadataMap) -> Option> { + if !VE::is_valid_key(self) { + return None; + } + map.headers + .remove(self) + .map(&MetadataValue::unchecked_from_header_value) + } + } + + impl<'a, VE: ValueEncoding> AsMetadataKey for &'a str {} + + impl Sealed for String { + #[doc(hidden)] + #[inline] + fn get(self, map: &MetadataMap) -> Option<&MetadataValue> { + if !VE::is_valid_key(self.as_str()) { + return None; + } + map.headers + .get(self.as_str()) + .map(&MetadataValue::unchecked_from_header_value_ref) + } + + #[doc(hidden)] + #[inline] + fn get_mut(self, map: &mut MetadataMap) -> Option<&mut MetadataValue> { + if !VE::is_valid_key(self.as_str()) { + return None; + } + map.headers + .get_mut(self.as_str()) + .map(&MetadataValue::unchecked_from_mut_header_value_ref) + } + + #[doc(hidden)] + #[inline] + fn get_all(self, map: &MetadataMap) -> Option> { + if !VE::is_valid_key(self.as_str()) { + return None; + } + Some(map.headers.get_all(self.as_str())) + } + + #[doc(hidden)] + #[inline] + fn entry( + self, + map: &mut MetadataMap, + ) -> Result, InvalidMetadataKey> { + if !VE::is_valid_key(self.as_str()) { + return Err(InvalidMetadataKey::new()); + } + map.headers + .entry(self.as_str()) + .map_err(|_| InvalidMetadataKey::new()) + } + + #[doc(hidden)] + #[inline] + fn remove(self, map: &mut MetadataMap) -> Option> { + if !VE::is_valid_key(self.as_str()) { + return None; + } + map.headers + .remove(self.as_str()) + .map(&MetadataValue::unchecked_from_header_value) + } + } + + impl AsMetadataKey for String {} + + impl<'a, VE: ValueEncoding> Sealed for &'a String { + #[doc(hidden)] + #[inline] + fn get(self, map: &MetadataMap) -> Option<&MetadataValue> { + if !VE::is_valid_key(self) { + return None; + } + map.headers + .get(self.as_str()) + .map(&MetadataValue::unchecked_from_header_value_ref) + } + + #[doc(hidden)] + #[inline] + fn get_mut(self, map: &mut MetadataMap) -> Option<&mut MetadataValue> { + if !VE::is_valid_key(self) { + return None; + } + map.headers + .get_mut(self.as_str()) + .map(&MetadataValue::unchecked_from_mut_header_value_ref) + } + + #[doc(hidden)] + #[inline] + fn get_all(self, map: &MetadataMap) -> Option> { + if !VE::is_valid_key(self) { + return None; + } + Some(map.headers.get_all(self.as_str())) + } + + #[doc(hidden)] + #[inline] + fn entry( + self, + map: &mut MetadataMap, + ) -> Result, InvalidMetadataKey> { + if !VE::is_valid_key(self) { + return Err(InvalidMetadataKey::new()); + } + map.headers + .entry(self.as_str()) + .map_err(|_| InvalidMetadataKey::new()) + } + + #[doc(hidden)] + #[inline] + fn remove(self, map: &mut MetadataMap) -> Option> { + if !VE::is_valid_key(self) { + return None; + } + map.headers + .remove(self.as_str()) + .map(&MetadataValue::unchecked_from_header_value) + } + } + + impl<'a, VE: ValueEncoding> AsMetadataKey for &'a String {} +} + +mod as_encoding_agnostic_metadata_key { + use super::{MetadataMap, ValueEncoding}; + use crate::metadata::key::MetadataKey; + + /// A marker trait used to identify values that can be used as search keys + /// to a `MetadataMap`, for operations that don't expose the actual value. + pub trait AsEncodingAgnosticMetadataKey: Sealed {} + + // All methods are on this pub(super) trait, instead of + // `AsEncodingAgnosticMetadataKey`, so that they aren't publicly exposed to + // the world. + // + // Being on the `AsEncodingAgnosticMetadataKey` trait would mean users could + // call `"host".contains_key(&map)`. + // + // Ultimately, this allows us to adjust the signatures of these methods + // without breaking any external crate. + pub trait Sealed { + #[doc(hidden)] + fn contains_key(&self, map: &MetadataMap) -> bool; + } + + // ==== impls ==== + + impl Sealed for MetadataKey { + #[doc(hidden)] + #[inline] + fn contains_key(&self, map: &MetadataMap) -> bool { + map.headers.contains_key(&self.inner) + } + } + + impl AsEncodingAgnosticMetadataKey for MetadataKey {} + + impl<'a, VE: ValueEncoding> Sealed for &'a MetadataKey { + #[doc(hidden)] + #[inline] + fn contains_key(&self, map: &MetadataMap) -> bool { + map.headers.contains_key(&self.inner) + } + } + + impl<'a, VE: ValueEncoding> AsEncodingAgnosticMetadataKey for &'a MetadataKey {} + + impl<'a> Sealed for &'a str { + #[doc(hidden)] + #[inline] + fn contains_key(&self, map: &MetadataMap) -> bool { + map.headers.contains_key(*self) + } + } + + impl<'a> AsEncodingAgnosticMetadataKey for &'a str {} + + impl Sealed for String { + #[doc(hidden)] + #[inline] + fn contains_key(&self, map: &MetadataMap) -> bool { + map.headers.contains_key(self.as_str()) + } + } + + impl AsEncodingAgnosticMetadataKey for String {} + + impl<'a> Sealed for &'a String { + #[doc(hidden)] + #[inline] + fn contains_key(&self, map: &MetadataMap) -> bool { + map.headers.contains_key(self.as_str()) + } + } + + impl<'a> AsEncodingAgnosticMetadataKey for &'a String {} +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_from_headers_takes_http_headers() { + let mut http_map = http::HeaderMap::new(); + http_map.insert("x-host", "example.com".parse().unwrap()); + + let map = MetadataMap::from_headers(http_map); + + assert_eq!(map.get("x-host").unwrap(), "example.com"); + } + + #[test] + fn test_to_headers_encoding() { + use crate::Code; + use crate::Status; + let special_char_message = "Beyond ascii \t\n\ršŸŒ¶ļøšŸ’‰šŸ’§šŸ®šŸŗ"; + let s1 = Status::new(Code::Unknown, special_char_message); + + assert_eq!(s1.message(), special_char_message); + + let s1_map = s1.to_header_map().unwrap(); + let s2 = Status::from_header_map(&s1_map).unwrap(); + + assert_eq!(s1.message(), s2.message()); + } + + #[test] + fn test_iter_categorizes_ascii_entries() { + let mut map = MetadataMap::new(); + + map.insert("x-word", "hello".parse().unwrap()); + map.append_bin("x-word-bin", MetadataValue::from_bytes(b"goodbye")); + map.insert_bin("x-number-bin", MetadataValue::from_bytes(b"123")); + + let mut found_x_word = false; + for key_and_value in map.iter() { + if let KeyAndValueRef::Ascii(ref key, ref _value) = key_and_value { + if key.as_str() == "x-word" { + found_x_word = true; + } else { + // Unexpected key + assert!(false); + } + } + } + assert!(found_x_word); + } + + #[test] + fn test_iter_categorizes_binary_entries() { + let mut map = MetadataMap::new(); + + map.insert("x-word", "hello".parse().unwrap()); + map.append_bin("x-word-bin", MetadataValue::from_bytes(b"goodbye")); + + let mut found_x_word_bin = false; + for key_and_value in map.iter() { + if let KeyAndValueRef::Binary(ref key, ref _value) = key_and_value { + if key.as_str() == "x-word-bin" { + found_x_word_bin = true; + } else { + // Unexpected key + assert!(false); + } + } + } + assert!(found_x_word_bin); + } + + #[test] + fn test_iter_mut_categorizes_ascii_entries() { + let mut map = MetadataMap::new(); + + map.insert("x-word", "hello".parse().unwrap()); + map.append_bin("x-word-bin", MetadataValue::from_bytes(b"goodbye")); + map.insert_bin("x-number-bin", MetadataValue::from_bytes(b"123")); + + let mut found_x_word = false; + for key_and_value in map.iter_mut() { + if let KeyAndMutValueRef::Ascii(ref key, ref _value) = key_and_value { + if key.as_str() == "x-word" { + found_x_word = true; + } else { + // Unexpected key + assert!(false); + } + } + } + assert!(found_x_word); + } + + #[test] + fn test_iter_mut_categorizes_binary_entries() { + let mut map = MetadataMap::new(); + + map.insert("x-word", "hello".parse().unwrap()); + map.append_bin("x-word-bin", MetadataValue::from_bytes(b"goodbye")); + + let mut found_x_word_bin = false; + for key_and_value in map.iter_mut() { + if let KeyAndMutValueRef::Binary(ref key, ref _value) = key_and_value { + if key.as_str() == "x-word-bin" { + found_x_word_bin = true; + } else { + // Unexpected key + assert!(false); + } + } + } + assert!(found_x_word_bin); + } + + #[test] + fn test_keys_categorizes_ascii_entries() { + let mut map = MetadataMap::new(); + + map.insert("x-word", "hello".parse().unwrap()); + map.append_bin("x-word-bin", MetadataValue::from_bytes(b"goodbye")); + map.insert_bin("x-number-bin", MetadataValue::from_bytes(b"123")); + + let mut found_x_word = false; + for key in map.keys() { + if let KeyRef::Ascii(key) = key { + if key.as_str() == "x-word" { + found_x_word = true; + } else { + // Unexpected key + assert!(false); + } + } + } + assert!(found_x_word); + } + + #[test] + fn test_keys_categorizes_binary_entries() { + let mut map = MetadataMap::new(); + + map.insert("x-word", "hello".parse().unwrap()); + map.insert_bin("x-number-bin", MetadataValue::from_bytes(b"123")); + + let mut found_x_number_bin = false; + for key in map.keys() { + if let KeyRef::Binary(key) = key { + if key.as_str() == "x-number-bin" { + found_x_number_bin = true; + } else { + // Unexpected key + assert!(false); + } + } + } + assert!(found_x_number_bin); + } + + #[test] + fn test_values_categorizes_ascii_entries() { + let mut map = MetadataMap::new(); + + map.insert("x-word", "hello".parse().unwrap()); + map.append_bin("x-word-bin", MetadataValue::from_bytes(b"goodbye")); + map.insert_bin("x-number-bin", MetadataValue::from_bytes(b"123")); + + let mut found_x_word = false; + for value in map.values() { + if let ValueRef::Ascii(value) = value { + if *value == "hello" { + found_x_word = true; + } else { + // Unexpected key + assert!(false); + } + } + } + assert!(found_x_word); + } + + #[test] + fn test_values_categorizes_binary_entries() { + let mut map = MetadataMap::new(); + + map.insert("x-word", "hello".parse().unwrap()); + map.append_bin("x-word-bin", MetadataValue::from_bytes(b"goodbye")); + + let mut found_x_word_bin = false; + for value_ref in map.values() { + if let ValueRef::Binary(value) = value_ref { + assert_eq!(*value, "goodbye"); + found_x_word_bin = true; + } + } + assert!(found_x_word_bin); + } + + #[test] + fn test_values_mut_categorizes_ascii_entries() { + let mut map = MetadataMap::new(); + + map.insert("x-word", "hello".parse().unwrap()); + map.append_bin("x-word-bin", MetadataValue::from_bytes(b"goodbye")); + map.insert_bin("x-number-bin", MetadataValue::from_bytes(b"123")); + + let mut found_x_word = false; + for value_ref in map.values_mut() { + if let ValueRefMut::Ascii(value) = value_ref { + assert_eq!(*value, "hello"); + found_x_word = true; + } + } + assert!(found_x_word); + } + + #[test] + fn test_values_mut_categorizes_binary_entries() { + let mut map = MetadataMap::new(); + + map.insert("x-word", "hello".parse().unwrap()); + map.append_bin("x-word-bin", MetadataValue::from_bytes(b"goodbye")); + + let mut found_x_word_bin = false; + for value in map.values_mut() { + if let ValueRefMut::Binary(value) = value { + assert_eq!(*value, "goodbye"); + found_x_word_bin = true; + } + } + assert!(found_x_word_bin); + } +} diff --git a/tonic/src/metadata/mod.rs b/tonic/src/metadata/mod.rs new file mode 100644 index 0000000..12c9c53 --- /dev/null +++ b/tonic/src/metadata/mod.rs @@ -0,0 +1,40 @@ +//! The metadata module contains data structures and utilities for handling +//! gRPC custom metadata. + +mod encoding; +mod key; +mod map; +mod value; + +pub use self::encoding::Ascii; +pub use self::encoding::Binary; +pub use self::key::AsciiMetadataKey; +pub use self::key::BinaryMetadataKey; +pub use self::key::MetadataKey; +pub use self::map::Entry; +pub use self::map::GetAll; +pub use self::map::Iter; +pub use self::map::KeyAndMutValueRef; +pub use self::map::KeyAndValueRef; +pub use self::map::KeyRef; +pub use self::map::Keys; +pub use self::map::MetadataMap; +pub use self::map::OccupiedEntry; +pub use self::map::VacantEntry; +pub use self::map::ValueDrain; +pub use self::map::ValueIter; +pub use self::map::ValueRef; +pub use self::map::ValueRefMut; +pub use self::map::Values; +pub use self::value::AsciiMetadataValue; +pub use self::value::BinaryMetadataValue; +pub use self::value::MetadataValue; + +/// The metadata::errors module contains types for errors that can occur +/// while handling gRPC custom metadata. +pub mod errors { + pub use super::encoding::InvalidMetadataValue; + pub use super::encoding::InvalidMetadataValueBytes; + pub use super::key::InvalidMetadataKey; + pub use super::value::ToStrError; +} diff --git a/tonic/src/metadata/value.rs b/tonic/src/metadata/value.rs new file mode 100644 index 0000000..a58af1b --- /dev/null +++ b/tonic/src/metadata/value.rs @@ -0,0 +1,806 @@ +use super::encoding::{ + Ascii, Binary, InvalidMetadataValue, InvalidMetadataValueBytes, ValueEncoding, +}; +use super::key::MetadataKey; + +use bytes::Bytes; +use http::header::HeaderValue; +use std::error::Error; +use std::marker::PhantomData; +use std::str::FromStr; +use std::{cmp, fmt}; + +/// Represents a custom metadata field value. +/// +/// `MetadataValue` is used as the [`MetadataMap`] value. +/// +/// [`HeaderMap`]: struct.HeaderMap.html +#[derive(Clone, Hash)] +#[repr(transparent)] +pub struct MetadataValue { + // Note: There are unsafe transmutes that assume that the memory layout + // of MetadataValue is identical to HeaderValue + pub(crate) inner: HeaderValue, + phantom: PhantomData, +} + +/// A possible error when converting a `MetadataValue` to a string representation. +/// +/// Metadata field values may contain opaque bytes, in which case it is not +/// possible to represent the value as a string. +#[derive(Debug)] +pub struct ToStrError { + _priv: (), +} + +pub type AsciiMetadataValue = MetadataValue; +pub type BinaryMetadataValue = MetadataValue; + +impl MetadataValue { + /// Convert a static string to a `MetadataValue`. + /// + /// This function will not perform any copying, however the string is + /// checked to ensure that no invalid characters are present. + /// + /// For Ascii values, only visible ASCII characters (32-127) are permitted. + /// For Binary values, the string must be valid base64. + /// + /// # Panics + /// + /// This function panics if the argument contains invalid metadata value + /// characters. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = AsciiMetadataValue::from_static("hello"); + /// assert_eq!(val, "hello"); + /// ``` + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = BinaryMetadataValue::from_static("SGVsbG8hIQ=="); + /// assert_eq!(val, "Hello!!"); + /// ``` + #[inline] + pub fn from_static(src: &'static str) -> Self { + MetadataValue { + inner: VE::from_static(src), + 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`. + /// + /// This function is intended to be replaced in the future by a `TryFrom` + /// implementation once the trait is stabilized in std. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = AsciiMetadataValue::try_from_bytes(b"hello\xfa").unwrap(); + /// assert_eq!(val, &b"hello\xfa"[..]); + /// ``` + /// + /// An invalid value + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = AsciiMetadataValue::try_from_bytes(b"\n"); + /// assert!(val.is_err()); + /// ``` + #[inline] + pub fn try_from_bytes(src: &[u8]) -> Result { + VE::from_bytes(src).map(|value| MetadataValue { + inner: value, + phantom: PhantomData, + }) + } + + /// 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. + /// + /// This function is intended to be replaced in the future by a `TryFrom` + /// implementation once the trait is stabilized in std. + #[inline] + pub fn from_shared(src: Bytes) -> Result { + VE::from_shared(src).map(|value| MetadataValue { + inner: value, + phantom: PhantomData, + }) + } + + /// Convert a `Bytes` directly into a `MetadataValue` without validating. + /// For MetadataValue the provided parameter must be base64 + /// encoded without padding bytes at the end. + /// + /// This function does NOT validate that illegal bytes are not contained + /// within the buffer. + #[inline] + pub unsafe fn from_shared_unchecked(src: Bytes) -> Self { + MetadataValue { + inner: HeaderValue::from_shared_unchecked(src), + phantom: PhantomData, + } + } + + /// Returns true if the `MetadataValue` has a length of zero bytes. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = AsciiMetadataValue::from_static(""); + /// assert!(val.is_empty()); + /// + /// let val = AsciiMetadataValue::from_static("hello"); + /// assert!(!val.is_empty()); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + VE::is_empty(self.inner.as_bytes()) + } + + /// Converts a `MetadataValue` to a Bytes buffer. This method cannot + /// fail for Ascii values. For Ascii values, `as_bytes` is more convenient + /// to use. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = AsciiMetadataValue::from_static("hello"); + /// assert_eq!(val.to_bytes().unwrap().as_ref(), b"hello"); + /// ``` + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = BinaryMetadataValue::from_bytes(b"hello"); + /// assert_eq!(val.to_bytes().unwrap().as_ref(), b"hello"); + /// ``` + #[inline] + pub fn to_bytes(&self) -> Result { + VE::decode(self.inner.as_bytes()) + } + + /// Mark that the metadata value represents sensitive information. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut val = AsciiMetadataValue::from_static("my secret"); + /// + /// val.set_sensitive(true); + /// assert!(val.is_sensitive()); + /// + /// val.set_sensitive(false); + /// assert!(!val.is_sensitive()); + /// ``` + #[inline] + pub fn set_sensitive(&mut self, val: bool) { + self.inner.set_sensitive(val); + } + + /// Returns `true` if the value represents sensitive data. + /// + /// Sensitive data could represent passwords or other data that should not + /// be stored on disk or in memory. This setting can be used by components + /// like caches to avoid storing the value. HPACK encoders must set the + /// metadata field to never index when `is_sensitive` returns true. + /// + /// Note that sensitivity is not factored into equality or ordering. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let mut val = AsciiMetadataValue::from_static("my secret"); + /// + /// val.set_sensitive(true); + /// assert!(val.is_sensitive()); + /// + /// val.set_sensitive(false); + /// assert!(!val.is_sensitive()); + /// ``` + #[inline] + pub fn is_sensitive(&self) -> bool { + self.inner.is_sensitive() + } + + /// Converts a `MetadataValue` to a byte slice. For Binary values, the + /// return value is base64 encoded. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = AsciiMetadataValue::from_static("hello"); + /// assert_eq!(val.as_encoded_bytes(), b"hello"); + /// ``` + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = BinaryMetadataValue::from_bytes(b"Hello!"); + /// assert_eq!(val.as_encoded_bytes(), b"SGVsbG8h"); + /// ``` + #[inline] + pub fn as_encoded_bytes(&self) -> &[u8] { + self.inner.as_bytes() + } + + /// Converts a HeaderValue to a MetadataValue. This method assumes that the + /// caller has made sure that the value is of the correct Ascii or Binary + /// value encoding. + #[inline] + pub(crate) fn unchecked_from_header_value(value: HeaderValue) -> Self { + MetadataValue { + inner: value, + phantom: PhantomData, + } + } + + /// Converts a HeaderValue reference to a MetadataValue. This method assumes + /// that the caller has made sure that the value is of the correct Ascii or + /// Binary value encoding. + #[inline] + pub(crate) fn unchecked_from_header_value_ref(header_value: &HeaderValue) -> &Self { + unsafe { &*(header_value as *const HeaderValue as *const Self) } + } + + /// Converts a HeaderValue reference to a MetadataValue. This method assumes + /// that the caller has made sure that the value is of the correct Ascii or + /// Binary value encoding. + #[inline] + pub(crate) fn unchecked_from_mut_header_value_ref(header_value: &mut HeaderValue) -> &mut Self { + unsafe { &mut *(header_value as *mut HeaderValue as *mut Self) } + } +} + +impl MetadataValue { + /// 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). + /// + /// This function is intended to be replaced in the future by a `TryFrom` + /// implementation once the trait is stabilized in std. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = AsciiMetadataValue::from_str("hello").unwrap(); + /// assert_eq!(val, "hello"); + /// ``` + /// + /// An invalid value + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = AsciiMetadataValue::from_str("\n"); + /// assert!(val.is_err()); + /// ``` + #[inline] + pub fn from_str(src: &str) -> Result { + HeaderValue::from_str(src) + .map(|value| MetadataValue { + inner: value, + phantom: PhantomData, + }) + .map_err(|_| InvalidMetadataValue::new()) + } + + /// Converts a MetadataKey into a MetadataValue. + /// + /// Since every valid MetadataKey is a valid MetadataValue this is done + /// infallibly. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = AsciiMetadataValue::from_key::("accept".parse().unwrap()); + /// assert_eq!(val, AsciiMetadataValue::try_from_bytes(b"accept").unwrap()); + /// ``` + #[inline] + pub fn from_key(key: MetadataKey) -> Self { + key.into() + } + + /// Returns the length of `self`, in bytes. + /// + /// This method is not available for MetadataValue because that + /// cannot be implemented in constant time, which most people would probably + /// expect. To get the length of MetadataValue, convert it to a + /// Bytes value and measure its length. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = AsciiMetadataValue::from_static("hello"); + /// assert_eq!(val.len(), 5); + /// ``` + #[inline] + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Yields a `&str` slice if the `MetadataValue` only contains visible ASCII + /// chars. + /// + /// This function will perform a scan of the metadata value, checking all the + /// characters. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = AsciiMetadataValue::from_static("hello"); + /// assert_eq!(val.to_str().unwrap(), "hello"); + /// ``` + pub fn to_str(&self) -> Result<&str, ToStrError> { + return self.inner.to_str().map_err(|_| ToStrError::new()); + } + + /// Converts a `MetadataValue` to a byte slice. For Binary values, use + /// `to_bytes`. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = AsciiMetadataValue::from_static("hello"); + /// assert_eq!(val.as_bytes(), b"hello"); + /// ``` + #[inline] + pub fn as_bytes(&self) -> &[u8] { + self.inner.as_bytes() + } +} + +impl MetadataValue { + /// Convert a byte slice to a `MetadataValue`. + /// + /// # Examples + /// + /// ``` + /// # use tonic::metadata::*; + /// let val = BinaryMetadataValue::from_bytes(b"hello\xfa"); + /// assert_eq!(val, &b"hello\xfa"[..]); + /// ``` + #[inline] + pub fn from_bytes(src: &[u8]) -> Self { + // Only the Ascii version of try_from_bytes can fail. + Self::try_from_bytes(src).unwrap() + } +} + +impl AsRef<[u8]> for MetadataValue { + #[inline] + fn as_ref(&self) -> &[u8] { + self.inner.as_ref() + } +} + +impl fmt::Debug for MetadataValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + VE::fmt(&self.inner, f) + } +} + +impl From> for MetadataValue { + #[inline] + fn from(h: MetadataKey) -> MetadataValue { + MetadataValue { + inner: h.inner.into(), + phantom: PhantomData, + } + } +} + +macro_rules! from_integers { + ($($name:ident: $t:ident => $max_len:expr),*) => {$( + impl From<$t> for MetadataValue { + fn from(num: $t) -> MetadataValue { + MetadataValue { + inner: HeaderValue::from(num), + phantom: PhantomData, + } + } + } + + #[test] + fn $name() { + let n: $t = 55; + let val = AsciiMetadataValue::from(n); + assert_eq!(val, &n.to_string()); + + let n = ::std::$t::MAX; + let val = AsciiMetadataValue::from(n); + assert_eq!(val, &n.to_string()); + } + )*}; +} + +from_integers! { + // integer type => maximum decimal length + + // u8 purposely left off... AsciiMetadataValue::from(b'3') could be confusing + from_u16: u16 => 5, + from_i16: i16 => 6, + from_u32: u32 => 10, + from_i32: i32 => 11, + from_u64: u64 => 20, + from_i64: i64 => 20 +} + +#[cfg(target_pointer_width = "16")] +from_integers! { + from_usize: usize => 5, + from_isize: isize => 6 +} + +#[cfg(target_pointer_width = "32")] +from_integers! { + from_usize: usize => 10, + from_isize: isize => 11 +} + +#[cfg(target_pointer_width = "64")] +from_integers! { + from_usize: usize => 20, + from_isize: isize => 20 +} + +#[cfg(test)] +mod from_metadata_value_tests { + use super::*; + use crate::metadata::map::MetadataMap; + + #[test] + fn it_can_insert_metadata_key_as_metadata_value() { + let mut map = MetadataMap::new(); + map.insert( + "accept", + MetadataKey::::from_bytes(b"hello-world") + .unwrap() + .into(), + ); + + assert_eq!( + map.get("accept").unwrap(), + AsciiMetadataValue::try_from_bytes(b"hello-world").unwrap() + ); + } +} + +impl FromStr for MetadataValue { + type Err = InvalidMetadataValue; + + #[inline] + fn from_str(s: &str) -> Result, Self::Err> { + MetadataValue::::from_str(s) + } +} + +impl From> for Bytes { + #[inline] + fn from(value: MetadataValue) -> Bytes { + Bytes::from(value.inner) + } +} + +impl<'a, VE: ValueEncoding> From<&'a MetadataValue> for MetadataValue { + #[inline] + fn from(t: &'a MetadataValue) -> Self { + t.clone() + } +} + +// ===== ToStrError ===== + +impl ToStrError { + pub(crate) fn new() -> Self { + ToStrError { _priv: () } + } +} + +impl fmt::Display for ToStrError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.description().fmt(f) + } +} + +impl Error for ToStrError { + fn description(&self) -> &str { + "failed to convert metadata to a str" + } +} + +// ===== PartialEq / PartialOrd ===== + +impl PartialEq for MetadataValue { + #[inline] + fn eq(&self, other: &MetadataValue) -> bool { + // Note: Different binary strings that after base64 decoding + // will count as the same value for Binary values. Also, + // different invalid base64 values count as equal for Binary + // values. + VE::values_equal(&self.inner, &other.inner) + } +} + +impl Eq for MetadataValue {} + +impl PartialOrd for MetadataValue { + #[inline] + fn partial_cmp(&self, other: &MetadataValue) -> Option { + self.inner.partial_cmp(&other.inner) + } +} + +impl Ord for MetadataValue { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.inner.cmp(&other.inner) + } +} + +impl PartialEq for MetadataValue { + #[inline] + fn eq(&self, other: &str) -> bool { + VE::equals(&self.inner, other.as_bytes()) + } +} + +impl PartialEq<[u8]> for MetadataValue { + #[inline] + fn eq(&self, other: &[u8]) -> bool { + VE::equals(&self.inner, other) + } +} + +impl PartialOrd for MetadataValue { + #[inline] + fn partial_cmp(&self, other: &str) -> Option { + self.inner.partial_cmp(other.as_bytes()) + } +} + +impl PartialOrd<[u8]> for MetadataValue { + #[inline] + fn partial_cmp(&self, other: &[u8]) -> Option { + self.inner.partial_cmp(other) + } +} + +impl PartialEq> for str { + #[inline] + fn eq(&self, other: &MetadataValue) -> bool { + *other == *self + } +} + +impl PartialEq> for [u8] { + #[inline] + fn eq(&self, other: &MetadataValue) -> bool { + *other == *self + } +} + +impl PartialOrd> for str { + #[inline] + fn partial_cmp(&self, other: &MetadataValue) -> Option { + self.as_bytes().partial_cmp(other.inner.as_bytes()) + } +} + +impl PartialOrd> for [u8] { + #[inline] + fn partial_cmp(&self, other: &MetadataValue) -> Option { + self.partial_cmp(other.inner.as_bytes()) + } +} + +impl PartialEq for MetadataValue { + #[inline] + fn eq(&self, other: &String) -> bool { + *self == &other[..] + } +} + +impl PartialOrd for MetadataValue { + #[inline] + fn partial_cmp(&self, other: &String) -> Option { + self.inner.partial_cmp(other.as_bytes()) + } +} + +impl PartialEq> for String { + #[inline] + fn eq(&self, other: &MetadataValue) -> bool { + *other == *self + } +} + +impl PartialOrd> for String { + #[inline] + fn partial_cmp(&self, other: &MetadataValue) -> Option { + self.as_bytes().partial_cmp(other.inner.as_bytes()) + } +} + +impl<'a, VE: ValueEncoding> PartialEq> for &'a MetadataValue { + #[inline] + fn eq(&self, other: &MetadataValue) -> bool { + **self == *other + } +} + +impl<'a, VE: ValueEncoding> PartialOrd> for &'a MetadataValue { + #[inline] + fn partial_cmp(&self, other: &MetadataValue) -> Option { + (**self).partial_cmp(other) + } +} + +impl<'a, VE: ValueEncoding, T: ?Sized> PartialEq<&'a T> for MetadataValue +where + MetadataValue: PartialEq, +{ + #[inline] + fn eq(&self, other: &&'a T) -> bool { + *self == **other + } +} + +impl<'a, VE: ValueEncoding, T: ?Sized> PartialOrd<&'a T> for MetadataValue +where + MetadataValue: PartialOrd, +{ + #[inline] + fn partial_cmp(&self, other: &&'a T) -> Option { + self.partial_cmp(*other) + } +} + +impl<'a, VE: ValueEncoding> PartialEq> for &'a str { + #[inline] + fn eq(&self, other: &MetadataValue) -> bool { + *other == *self + } +} + +impl<'a, VE: ValueEncoding> PartialOrd> for &'a str { + #[inline] + fn partial_cmp(&self, other: &MetadataValue) -> Option { + self.as_bytes().partial_cmp(other.inner.as_bytes()) + } +} + +#[test] +fn test_debug() { + let cases = &[ + ("hello", "\"hello\""), + ("hello \"world\"", "\"hello \\\"world\\\"\""), + ("\u{7FFF}hello", "\"\\xe7\\xbf\\xbfhello\""), + ]; + + for &(value, expected) in cases { + let val = AsciiMetadataValue::try_from_bytes(value.as_bytes()).unwrap(); + let actual = format!("{:?}", val); + assert_eq!(expected, actual); + } + + let mut sensitive = AsciiMetadataValue::from_static("password"); + sensitive.set_sensitive(true); + assert_eq!("Sensitive", format!("{:?}", sensitive)); +} + +#[test] +fn test_is_empty() { + fn from_str(s: &str) -> MetadataValue { + MetadataValue::::unchecked_from_header_value(s.parse().unwrap()) + } + + assert!(from_str::("").is_empty()); + assert!(from_str::("").is_empty()); + assert!(!from_str::("a").is_empty()); + assert!(!from_str::("a").is_empty()); + assert!(!from_str::("=").is_empty()); + assert!(from_str::("=").is_empty()); + assert!(!from_str::("===").is_empty()); + assert!(from_str::("===").is_empty()); + assert!(!from_str::("=====").is_empty()); + assert!(from_str::("=====").is_empty()); +} + +#[test] +fn test_from_shared_base64_encodes() { + let value = BinaryMetadataValue::from_shared(Bytes::from_static(b"Hello")).unwrap(); + assert_eq!(value.as_encoded_bytes(), b"SGVsbG8"); +} + +#[test] +fn test_value_eq_value() { + type BMV = BinaryMetadataValue; + type AMV = AsciiMetadataValue; + + assert_eq!(AMV::from_static("abc"), AMV::from_static("abc")); + assert!(AMV::from_static("abc") != AMV::from_static("ABC")); + + assert_eq!(BMV::from_bytes(b"abc"), BMV::from_bytes(b"abc")); + assert!(BMV::from_bytes(b"abc") != BMV::from_bytes(b"ABC")); + + // Padding is ignored. + assert_eq!( + BMV::from_static("SGVsbG8hIQ=="), + BMV::from_static("SGVsbG8hIQ") + ); + // Invalid values are all just invalid from this point of view. + unsafe { + assert_eq!( + BMV::from_shared_unchecked(Bytes::from_static(b"..{}")), + BMV::from_shared_unchecked(Bytes::from_static(b"{}..")) + ); + } +} + +#[test] +fn test_value_eq_str() { + type BMV = BinaryMetadataValue; + type AMV = AsciiMetadataValue; + + assert_eq!(AMV::from_static("abc"), "abc"); + assert!(AMV::from_static("abc") != "ABC"); + assert_eq!("abc", AMV::from_static("abc")); + assert!("ABC" != AMV::from_static("abc")); + + assert_eq!(BMV::from_bytes(b"abc"), "abc"); + assert!(BMV::from_bytes(b"abc") != "ABC"); + assert_eq!("abc", BMV::from_bytes(b"abc")); + assert!("ABC" != BMV::from_bytes(b"abc")); + + // Padding is ignored. + assert_eq!(BMV::from_static("SGVsbG8hIQ=="), "Hello!!"); + assert_eq!("Hello!!", BMV::from_static("SGVsbG8hIQ==")); +} + +#[test] +fn test_value_eq_bytes() { + type BMV = BinaryMetadataValue; + type AMV = AsciiMetadataValue; + + assert_eq!(AMV::from_static("abc"), "abc".as_bytes()); + assert!(AMV::from_static("abc") != "ABC".as_bytes()); + assert_eq!(*"abc".as_bytes(), AMV::from_static("abc")); + assert!(*"ABC".as_bytes() != AMV::from_static("abc")); + + assert_eq!(*"abc".as_bytes(), BMV::from_bytes(b"abc")); + assert!(*"ABC".as_bytes() != BMV::from_bytes(b"abc")); + + // Padding is ignored. + assert_eq!(BMV::from_static("SGVsbG8hIQ=="), "Hello!!".as_bytes()); + assert_eq!(*"Hello!!".as_bytes(), BMV::from_static("SGVsbG8hIQ==")); +} diff --git a/tonic/src/request.rs b/tonic/src/request.rs new file mode 100644 index 0000000..b420eb9 --- /dev/null +++ b/tonic/src/request.rs @@ -0,0 +1,74 @@ +use crate::metadata::MetadataMap; + +#[derive(Debug)] +pub struct Request { + metadata: MetadataMap, + message: T, +} + +impl Request { + /// Create a new gRPC request + pub fn new(message: T) -> Self { + Request { + metadata: MetadataMap::new(), + message, + } + } + + /// Get a reference to the message + pub fn get_ref(&self) -> &T { + &self.message + } + + /// Get a mutable reference to the message + pub fn get_mut(&mut self) -> &mut T { + &mut self.message + } + + /// Get a reference to the custom request metadata. + pub fn metadata(&self) -> &MetadataMap { + &self.metadata + } + + /// Get a mutable reference to the request metadata. + pub fn metadata_mut(&mut self) -> &mut MetadataMap { + &mut self.metadata + } + + /// Consumes `self`, returning the message + pub fn into_inner(self) -> T { + self.message + } + + /// Convert an HTTP request to a gRPC request + pub fn from_http(http: http::Request) -> Self { + let (head, message) = http.into_parts(); + Request { + metadata: MetadataMap::from_headers(head.headers), + message, + } + } + + pub fn into_http(self, uri: http::Uri) -> http::Request { + let mut request = http::Request::new(self.message); + + *request.version_mut() = http::Version::HTTP_2; + *request.method_mut() = http::Method::POST; + *request.uri_mut() = uri; + *request.headers_mut() = self.metadata.into_headers(); + + request + } + + pub fn map(self, f: F) -> Request + where + F: FnOnce(T) -> U, + { + let message = f(self.message); + + Request { + metadata: self.metadata, + message, + } + } +} diff --git a/tonic/src/response.rs b/tonic/src/response.rs new file mode 100644 index 0000000..7afca99 --- /dev/null +++ b/tonic/src/response.rs @@ -0,0 +1,75 @@ +use crate::metadata::MetadataMap; + +/// A gRPC response and metadata from an RPC call. +#[derive(Debug)] +pub struct Response { + metadata: MetadataMap, + message: T, +} + +impl Response { + /// Create a new gRPC response. + pub fn new(message: T) -> Self { + Response { + metadata: MetadataMap::new(), + message, + } + } + + /// Get a reference to the message + pub fn get_ref(&self) -> &T { + &self.message + } + + /// Get a mutable reference to the message + pub fn get_mut(&mut self) -> &mut T { + &mut self.message + } + + /// Get a reference to the custom response metadata. + pub fn metadata(&self) -> &MetadataMap { + &self.metadata + } + + /// Get a mutable reference to the response metadata. + pub fn metadata_mut(&mut self) -> &mut MetadataMap { + &mut self.metadata + } + + /// Consumes `self`, returning the message + pub fn into_inner(self) -> T { + self.message + } + + #[allow(dead_code)] + pub(crate) fn from_http(res: http::Response) -> Self { + let (head, message) = res.into_parts(); + Response { + metadata: MetadataMap::from_headers(head.headers), + message, + } + } + + pub fn into_http(self) -> http::Response { + let mut res = http::Response::new(self.message); + + *res.version_mut() = http::Version::HTTP_2; + *res.headers_mut() = self.metadata.into_headers(); + + res + } + + pub fn map(self, f: F) -> Response + where + F: FnOnce(T) -> U, + { + let message = f(self.message); + Response { + metadata: self.metadata, + message, + } + } + + // pub fn metadata() + // pub fn metadata_bin() +} diff --git a/tonic/src/server/mod.rs b/tonic/src/server/mod.rs new file mode 100644 index 0000000..141c468 --- /dev/null +++ b/tonic/src/server/mod.rs @@ -0,0 +1,87 @@ +use crate::{Request, Response, Status}; +use async_stream::stream; +use bytes::{Bytes, BytesMut, IntoBuf}; +use futures_core::TryStream; +use futures_util::{stream, StreamExt, TryStreamExt}; +use std::future::Future; +use tokio_codec::{Decoder, Encoder}; +use tower_service::Service; + +#[allow(dead_code)] +type Result = std::result::Result, Status>; + +pub trait Codec { + type Encode; + type Decode; +} + +pub struct Encode { + inner: T, + source: U, +} + +impl Encode +where + T: Encoder, + U: TryStream + Unpin, +{ + pub fn new(inner: T, source: U) -> Self { + Encode { inner, source } + } + + pub fn encode<'a>( + &'a mut self, + buf: &'a mut BytesMut, + ) -> impl TryStream + 'a { + stream! { + loop { + match self.source.try_next().await { + Ok(Some(item)) => { + self.inner.encode(item, buf).map_err(drop).unwrap(); + let len = buf.len(); + yield Ok(buf.split_to(len).freeze().into_buf()); + }, + Ok(None) => break, + Err(status) => yield Err(status), + } + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::body::AsyncBody; + use crate::server::Encode; + use bytes::Bytes; + use tokio_codec::BytesCodec; + + #[test] + fn body() { + let stream = futures_util::stream::iter(vec![Ok(Bytes::new())]); + let encode = Encode::new(BytesCodec::new(), stream); + } +} + +// impl http_body::Body for Encode + +// pub struct Grpc { +// opdec: T, +// } + +// impl Grpc { +// pub async fn unary(&mut self, message: B) -> Result> { +// self.server_streaming(stream::once(message)).await +// } + +// pub async fn server_streaming( +// &mut self, +// stream: impl Stream, +// ) -> Result> { +// unimplemetned!() +// } + +// fn map_request(&mut self, request: http::Request) -> Request { +// Request::from_http(request) +// } +// } diff --git a/tonic/src/status.rs b/tonic/src/status.rs new file mode 100644 index 0000000..5443534 --- /dev/null +++ b/tonic/src/status.rs @@ -0,0 +1,520 @@ +#![allow(dead_code)] + +use bytes::Bytes; +use http::header::HeaderValue; +use http::{self, HeaderMap}; +use percent_encoding::{percent_decode, percent_encode, EncodeSet, DEFAULT_ENCODE_SET}; +use std::{error::Error, fmt}; +use tracing::{debug, trace, warn}; + +const GRPC_STATUS_HEADER_CODE: &str = "grpc-status"; +const GRPC_STATUS_MESSAGE_HEADER: &str = "grpc-message"; +const GRPC_STATUS_DETAILS_HEADER: &str = "grpc-status-details-bin"; + +/// A gRPC "status" describing the result of an RPC call. +#[derive(Clone)] +pub struct Status { + /// The gRPC status code, found in the `grpc-status` header. + code: Code, + /// A relevant error message, found in the `grpc-message` header. + message: String, + /// Binary opaque details, found in the `grpc-status-details-bin` header. + details: Bytes, +} + +/// gRPC status codes used by `Status`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Code { + Ok = 0, + Cancelled = 1, + Unknown = 2, + InvalidArgument = 3, + DeadlineExceeded = 4, + NotFound = 5, + AlreadyExists = 6, + PermissionDenied = 7, + ResourceExhausted = 8, + FailedPrecondition = 9, + Aborted = 10, + OutOfRange = 11, + Unimplemented = 12, + Internal = 13, + Unavailable = 14, + DataLoss = 15, + Unauthenticated = 16, + + // New Codes may be added in the future, so never exhaustively match! + #[doc(hidden)] + __NonExhaustive, +} + +// ===== impl Status ===== + +impl Status { + /// Create a new `Status` with the associated code and message. + pub fn new(code: Code, message: impl Into) -> Status { + Status { + code, + message: message.into(), + details: Bytes::new(), + } + } + + // Deprecated: this constructor encourages creating statuses with no + // message, hurting later debugging. + #[doc(hidden)] + #[deprecated(note = "use State::new")] + pub fn with_code(code: Code) -> Status { + Status::new(code, String::new()) + } + + // Deprecated: this constructor is overly long. + #[doc(hidden)] + #[deprecated(note = "use State::new")] + pub fn with_code_and_message(code: Code, message: String) -> Status { + Status::new(code, message) + } + + // TODO: This should probably be made public eventually. Need to decide on + // the exact argument type. + #[cfg_attr(not(feature = "h2"), allow(dead_code))] + pub(crate) fn from_error(err: &(dyn Error + 'static)) -> Status { + Status::try_from_error(err).unwrap_or_else(|| Status::new(Code::Unknown, err.to_string())) + } + + fn try_from_error(err: &(dyn Error + 'static)) -> Option { + let mut cause = Some(err); + + while let Some(err) = cause { + if let Some(status) = err.downcast_ref::() { + return Some(Status { + code: status.code, + message: status.message.clone(), + details: status.details.clone(), + }); + } + + #[cfg(feature = "h2")] + { + if let Some(h2) = err.downcast_ref::() { + return Some(Status::from_h2_error(h2)); + } + } + + cause = err.source(); + } + + None + } + + #[cfg(feature = "h2")] + fn from_h2_error(err: &h2::Error) -> Status { + // See https://github.com/grpc/grpc/blob/3977c30/doc/PROTOCOL-HTTP2.md#errors + let code = match err.reason() { + Some(h2::Reason::NO_ERROR) + | Some(h2::Reason::PROTOCOL_ERROR) + | Some(h2::Reason::INTERNAL_ERROR) + | Some(h2::Reason::FLOW_CONTROL_ERROR) + | Some(h2::Reason::SETTINGS_TIMEOUT) + | Some(h2::Reason::COMPRESSION_ERROR) + | Some(h2::Reason::CONNECT_ERROR) => Code::Internal, + Some(h2::Reason::REFUSED_STREAM) => Code::Unavailable, + Some(h2::Reason::CANCEL) => Code::Cancelled, + Some(h2::Reason::ENHANCE_YOUR_CALM) => Code::ResourceExhausted, + Some(h2::Reason::INADEQUATE_SECURITY) => Code::PermissionDenied, + + _ => Code::Unknown, + }; + + Status::new(code, format!("h2 protocol error: {}", err)) + } + + #[cfg(feature = "h2")] + fn to_h2_error(&self) -> h2::Error { + // conservatively transform to h2 error codes... + let reason = match self.code { + Code::Cancelled => h2::Reason::CANCEL, + _ => h2::Reason::INTERNAL_ERROR, + }; + + reason.into() + } + + pub(crate) fn map_error(err: E) -> Status + where + E: Into>, + { + Status::from_error(&*err.into()) + } + + pub(crate) fn from_header_map(header_map: &HeaderMap) -> Option { + header_map.get(GRPC_STATUS_HEADER_CODE).map(|code| { + let code = Code::from_bytes(code.as_ref()); + let error_message = header_map + .get(GRPC_STATUS_MESSAGE_HEADER) + .map(|header| { + percent_decode(header.as_bytes()) + .decode_utf8() + .map(|cow| cow.to_string()) + }) + .unwrap_or_else(|| Ok(String::new())); + let details = header_map + .get(GRPC_STATUS_DETAILS_HEADER) + .map(|h| Bytes::from(h.as_bytes())) + .unwrap_or_else(Bytes::new); + match error_message { + Ok(message) => Status { + code, + message, + details, + }, + Err(err) => { + warn!("Error deserializing status message header: {}", err); + Status { + code: Code::Unknown, + message: format!("Error deserializing status message header: {}", err), + details, + } + } + } + }) + } + + /// Get the gRPC `Code` of this `Status`. + pub fn code(&self) -> Code { + self.code + } + + /// Get the text error message of this `Status`. + pub fn message(&self) -> &str { + &self.message + } + + /// Get the opaque error details of this `Status`. + pub fn details(&self) -> &[u8] { + &self.details + } + + #[doc(hidden)] + #[deprecated(note = "use Status::message")] + pub fn error_message(&self) -> &str { + &self.message + } + + #[doc(hidden)] + #[deprecated(note = "use Status::details")] + pub fn binary_error_details(&self) -> &Bytes { + &self.details + } + + pub(crate) fn to_header_map(&self) -> Result { + let mut header_map = HeaderMap::with_capacity(3); + self.add_header(&mut header_map)?; + Ok(header_map) + } + + pub(crate) fn add_header(&self, header_map: &mut HeaderMap) -> Result<(), Self> { + header_map.insert(GRPC_STATUS_HEADER_CODE, self.code.to_header_value()); + + if !self.message.is_empty() { + let is_need_encode = self + .message + .as_bytes() + .iter() + .any(|&x| DEFAULT_ENCODE_SET.contains(x)); + let to_write = if is_need_encode { + percent_encode(&self.message().as_bytes(), DEFAULT_ENCODE_SET) + .to_string() + .into() + } else { + Bytes::from(self.message().as_bytes()) + }; + + header_map.insert( + GRPC_STATUS_MESSAGE_HEADER, + HeaderValue::from_shared(to_write).map_err(invalid_header_value_byte)?, + ); + } + + if !self.details.is_empty() { + header_map.insert( + GRPC_STATUS_DETAILS_HEADER, + HeaderValue::from_shared(self.details.clone()) + .map_err(invalid_header_value_byte)?, + ); + } + + Ok(()) + } +} + +impl fmt::Debug for Status { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // A manual impl to reduce the noise of frequently empty fields. + let mut builder = f.debug_struct("Status"); + + builder.field("code", &self.code); + + if !self.message.is_empty() { + builder.field("message", &self.message); + } + + if !self.details.is_empty() { + builder.field("details", &self.details); + } + + builder.finish() + } +} + +fn invalid_header_value_byte(err: Error) -> Status { + debug!("Invalid header: {}", err); + Status::new( + Code::Internal, + "Couldn't serialize non-text grpc status header".to_string(), + ) +} + +#[cfg(feature = "h2")] +impl From for Status { + fn from(err: h2::Error) -> Self { + Status::from_h2_error(&err) + } +} + +#[cfg(feature = "h2")] +impl From for h2::Error { + fn from(status: Status) -> Self { + status.to_h2_error() + } +} + +impl fmt::Display for Status { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "grpc-status: {:?}, grpc-message: {:?}", + self.code(), + self.message() + ) + } +} + +impl Error for Status {} + +/// +/// Take the `Status` value from `trailers` if it is available, else from `status_code`. +/// +pub(crate) fn infer_grpc_status( + trailers: Option, + status_code: http::StatusCode, +) -> Result<(), Status> { + if let Some(trailers) = trailers { + if let Some(status) = Status::from_header_map(&trailers) { + if status.code() == Code::Ok { + return Ok(()); + } else { + return Err(status); + } + } + } + trace!("trailers missing grpc-status"); + let code = match status_code { + // Borrowed from https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md + http::StatusCode::BAD_REQUEST => Code::Internal, + http::StatusCode::UNAUTHORIZED => Code::Unauthenticated, + http::StatusCode::FORBIDDEN => Code::PermissionDenied, + http::StatusCode::NOT_FOUND => Code::Unimplemented, + http::StatusCode::TOO_MANY_REQUESTS + | http::StatusCode::BAD_GATEWAY + | http::StatusCode::SERVICE_UNAVAILABLE + | http::StatusCode::GATEWAY_TIMEOUT => Code::Unavailable, + _ => Code::Unknown, + }; + + let msg = format!( + "grpc-status header missing, mapped from HTTP status code {}", + status_code.as_u16(), + ); + let status = Status::new(code, msg); + Err(status) +} + +// ===== impl Code ===== + +impl Code { + /// Get the `Code` that represents the integer, if known. + /// + /// If not known, returns `Code::Unknown` (surprise!). + pub fn from_i32(i: i32) -> Code { + Code::from(i) + } + + pub(crate) fn from_bytes(bytes: &[u8]) -> Code { + match bytes.len() { + 1 => match bytes[0] { + b'0' => Code::Ok, + b'1' => Code::Cancelled, + b'2' => Code::Unknown, + b'3' => Code::InvalidArgument, + b'4' => Code::DeadlineExceeded, + b'5' => Code::NotFound, + b'6' => Code::AlreadyExists, + b'7' => Code::PermissionDenied, + b'8' => Code::ResourceExhausted, + b'9' => Code::FailedPrecondition, + _ => Code::parse_err(), + }, + 2 => match (bytes[0], bytes[1]) { + (b'1', b'0') => Code::Aborted, + (b'1', b'1') => Code::OutOfRange, + (b'1', b'2') => Code::Unimplemented, + (b'1', b'3') => Code::Internal, + (b'1', b'4') => Code::Unavailable, + (b'1', b'5') => Code::DataLoss, + (b'1', b'6') => Code::Unauthenticated, + _ => Code::parse_err(), + }, + _ => Code::parse_err(), + } + } + + fn to_header_value(&self) -> HeaderValue { + match self { + Code::Ok => HeaderValue::from_static("0"), + Code::Cancelled => HeaderValue::from_static("1"), + Code::Unknown => HeaderValue::from_static("2"), + Code::InvalidArgument => HeaderValue::from_static("3"), + Code::DeadlineExceeded => HeaderValue::from_static("4"), + Code::NotFound => HeaderValue::from_static("5"), + Code::AlreadyExists => HeaderValue::from_static("6"), + Code::PermissionDenied => HeaderValue::from_static("7"), + Code::ResourceExhausted => HeaderValue::from_static("8"), + Code::FailedPrecondition => HeaderValue::from_static("9"), + Code::Aborted => HeaderValue::from_static("10"), + Code::OutOfRange => HeaderValue::from_static("11"), + Code::Unimplemented => HeaderValue::from_static("12"), + Code::Internal => HeaderValue::from_static("13"), + Code::Unavailable => HeaderValue::from_static("14"), + Code::DataLoss => HeaderValue::from_static("15"), + Code::Unauthenticated => HeaderValue::from_static("16"), + + Code::__NonExhaustive => unreachable!("Code::__NonExhaustive"), + } + } + + #[allow(dead_code)] + fn parse_err() -> Code { + trace!("error parsing grpc-status"); + Code::Unknown + } +} + +impl From for Code { + fn from(i: i32) -> Self { + match i { + 0 => Code::Ok, + 1 => Code::Cancelled, + 2 => Code::Unknown, + 3 => Code::InvalidArgument, + 4 => Code::DeadlineExceeded, + 5 => Code::NotFound, + 6 => Code::AlreadyExists, + 7 => Code::PermissionDenied, + 8 => Code::ResourceExhausted, + 9 => Code::FailedPrecondition, + 10 => Code::Aborted, + 11 => Code::OutOfRange, + 12 => Code::Unimplemented, + 13 => Code::Internal, + 14 => Code::Unavailable, + 15 => Code::DataLoss, + 16 => Code::Unauthenticated, + + _ => Code::Unknown, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::error::Error; + + #[derive(Debug)] + struct Nested(Error); + + impl fmt::Display for Nested { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "nested error: {}", self.0) + } + } + + impl std::error::Error for Nested { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&*self.0) + } + } + + #[test] + fn from_error_status() { + let orig = Status::new(Code::OutOfRange, "weeaboo"); + let found = Status::from_error(&orig); + + assert_eq!(orig.code(), found.code()); + assert_eq!(orig.message(), found.message()); + } + + #[test] + fn from_error_unknown() { + let orig: Error = "peek-a-boo".into(); + let found = Status::from_error(&*orig); + + assert_eq!(found.code(), Code::Unknown); + assert_eq!(found.message(), orig.to_string()); + } + + #[test] + fn from_error_nested() { + let orig = Nested(Box::new(Status::new(Code::OutOfRange, "weeaboo"))); + let found = Status::from_error(&orig); + + assert_eq!(found.code(), Code::OutOfRange); + assert_eq!(found.message(), "weeaboo"); + } + + #[test] + #[cfg(feature = "h2")] + fn from_error_h2() { + let orig = h2::Error::from(h2::Reason::CANCEL); + let found = Status::from_error(&orig); + + assert_eq!(found.code(), Code::Cancelled); + } + + #[test] + #[cfg(feature = "h2")] + fn to_h2_error() { + let orig = Status::new(Code::Cancelled, "stop eet!"); + let err = orig.to_h2_error(); + + assert_eq!(err.reason(), Some(h2::Reason::CANCEL)); + } + + #[test] + fn code_from_i32() { + // This for loop should catch if we ever add a new variant and don't + // update From. + for i in 0..(Code::__NonExhaustive as i32) { + let code = Code::from(i); + assert_eq!( + i, code as i32, + "Code::from({}) returned {:?} which is {}", + i, code, code as i32, + ); + } + + assert_eq!(Code::from(-1), Code::Unknown); + assert_eq!(Code::from(Code::__NonExhaustive as i32), Code::Unknown); + } +} diff --git a/tower-h2/examples/server.rs b/tower-h2/examples/server.rs index d0859ce..69ffd63 100644 --- a/tower-h2/examples/server.rs +++ b/tower-h2/examples/server.rs @@ -3,10 +3,10 @@ use futures_util::future; use http::{Request, Response}; use std::task::{Context, Poll}; +use tokio::net::TcpListener; use tokio_buf::BufStream; use tower_h2::{RecvBody, Server}; use tower_service::Service; -use tokio::net::TcpListener; const ROOT: &'static str = "/"; diff --git a/tower-h2/src/server.rs b/tower-h2/src/server.rs index 889b717..fed4437 100644 --- a/tower-h2/src/server.rs +++ b/tower-h2/src/server.rs @@ -30,7 +30,7 @@ where Self { maker, builder, - _pd: PhantomData + _pd: PhantomData, } } @@ -79,30 +79,30 @@ where } pub async fn handle_request( - response: Response, - mut send_response: h2::server::SendResponse>, - ) where - B: Body + Send + Unpin + 'static, - B::Data: Unpin, - B::Error: Into>, - { - let (parts, body) = response.into_parts(); + response: Response, + mut send_response: h2::server::SendResponse>, +) where + B: Body + Send + Unpin + 'static, + B::Data: Unpin, + B::Error: Into>, +{ + let (parts, body) = response.into_parts(); - // Check if the response is imemdiately an end-of-stream. - let eos = body.is_end_stream(); + // Check if the response is imemdiately an end-of-stream. + let eos = body.is_end_stream(); - let response = Response::from_parts(parts, ()); + let response = Response::from_parts(parts, ()); - match send_response.send_response(response, eos) { - Ok(sr) => { - if eos { - return; - } - - Flush::new(body, sr).await.unwrap(); - } - Err(e) => { - println!("h2 server ERROR={}", e); + match send_response.send_response(response, eos) { + Ok(sr) => { + if eos { + return; } + + Flush::new(body, sr).await.unwrap(); + } + Err(e) => { + println!("h2 server ERROR={}", e); } } +}