diff --git a/tonic-web/Cargo.toml b/tonic-web/Cargo.toml index 26549bc..82bbb97 100644 --- a/tonic-web/Cargo.toml +++ b/tonic-web/Cargo.toml @@ -15,7 +15,7 @@ repository = "https://github.com/hyperium/tonic" version = "0.5.0" [dependencies] -base64 = "0.13" +base64 = "0.21" bytes = "1.0" futures-core = "0.3" http = "0.2" diff --git a/tonic-web/src/call.rs b/tonic-web/src/call.rs index c2af867..7ace753 100644 --- a/tonic-web/src/call.rs +++ b/tonic-web/src/call.rs @@ -2,6 +2,7 @@ use std::error::Error; use std::pin::Pin; use std::task::{Context, Poll}; +use base64::Engine as _; use bytes::{Buf, BufMut, Bytes, BytesMut}; use futures_core::{ready, Stream}; use http::{header, HeaderMap, HeaderValue}; @@ -100,7 +101,8 @@ impl GrpcWebCall { // returned `Bytes`, keeping the rest for the next attempt to decode. let index = self.max_decodable(); - base64::decode(self.as_mut().project().buf.split_to(index)) + crate::util::base64::STANDARD + .decode(self.as_mut().project().buf.split_to(index)) .map(|decoded| Some(Bytes::from(decoded))) .map_err(internal_error) } @@ -151,7 +153,7 @@ where if let Some(mut res) = ready!(this.inner.as_mut().poll_data(cx)) { if *this.encoding == Encoding::Base64 { - res = res.map(|b| base64::encode(b).into()) + res = res.map(|b| crate::util::base64::STANDARD.encode(b).into()) } return Poll::Ready(Some(res.map_err(internal_error))); @@ -165,7 +167,7 @@ where let mut frame = make_trailers_frame(map); if *this.encoding == Encoding::Base64 { - frame = base64::encode(frame).into_bytes(); + frame = crate::util::base64::STANDARD.encode(frame).into_bytes(); } *this.poll_trailers = false; diff --git a/tonic-web/src/lib.rs b/tonic-web/src/lib.rs index 4adeedd..00c2326 100644 --- a/tonic-web/src/lib.rs +++ b/tonic-web/src/lib.rs @@ -139,3 +139,22 @@ where ) .layer(GrpcWebService::new(service)) } + +pub(crate) mod util { + pub(crate) mod base64 { + use base64::{ + alphabet, + engine::{ + general_purpose::{GeneralPurpose, GeneralPurposeConfig}, + DecodePaddingMode, + }, + }; + + pub(crate) const STANDARD: GeneralPurpose = GeneralPurpose::new( + &alphabet::STANDARD, + GeneralPurposeConfig::new() + .with_encode_padding(true) + .with_decode_padding_mode(DecodePaddingMode::Indifferent), + ); + } +} diff --git a/tonic/Cargo.toml b/tonic/Cargo.toml index d660777..0aefcfd 100644 --- a/tonic/Cargo.toml +++ b/tonic/Cargo.toml @@ -48,7 +48,7 @@ channel = [ # harness = false [dependencies] -base64 = "0.13" +base64 = "0.21" bytes = "1.0" futures-core = {version = "0.3", default-features = false} futures-util = {version = "0.3", default-features = false} diff --git a/tonic/src/metadata/encoding.rs b/tonic/src/metadata/encoding.rs index 657b2bf..cf2c38a 100644 --- a/tonic/src/metadata/encoding.rs +++ b/tonic/src/metadata/encoding.rs @@ -1,3 +1,4 @@ +use base64::Engine as _; use bytes::Bytes; use http::header::HeaderValue; use std::error::Error; @@ -127,7 +128,7 @@ impl self::value_encoding::Sealed for Binary { } fn from_bytes(value: &[u8]) -> Result { - let encoded_value: String = base64::encode_config(value, base64::STANDARD_NO_PAD); + let encoded_value: String = crate::util::base64::STANDARD_NO_PAD.encode(value); HeaderValue::from_maybe_shared(Bytes::from(encoded_value)) .map_err(|_| InvalidMetadataValueBytes::new()) } @@ -137,7 +138,7 @@ impl self::value_encoding::Sealed for Binary { } fn from_static(value: &'static str) -> HeaderValue { - if base64::decode(value).is_err() { + if crate::util::base64::STANDARD.decode(value).is_err() { panic!("Invalid base64 passed to from_static: {}", value); } unsafe { @@ -148,13 +149,14 @@ impl self::value_encoding::Sealed for Binary { } fn decode(value: &[u8]) -> Result { - base64::decode(value) + crate::util::base64::STANDARD + .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()) { + if let Ok(decoded) = crate::util::base64::STANDARD.decode(a.as_bytes()) { decoded == b } else { a.as_bytes() == b diff --git a/tonic/src/status.rs b/tonic/src/status.rs index 0c34160..fb73863 100644 --- a/tonic/src/status.rs +++ b/tonic/src/status.rs @@ -1,5 +1,6 @@ use crate::body::BoxBody; use crate::metadata::MetadataMap; +use base64::Engine as _; use bytes::Bytes; use http::header::{HeaderMap, HeaderValue}; use percent_encoding::{percent_decode, percent_encode, AsciiSet, CONTROLS}; @@ -435,7 +436,8 @@ impl Status { let details = header_map .get(GRPC_STATUS_DETAILS_HEADER) .map(|h| { - base64::decode(h.as_bytes()) + crate::util::base64::STANDARD + .decode(h.as_bytes()) .expect("Invalid status header, expected base64 encoded value") }) .map(Bytes::from) @@ -516,7 +518,7 @@ impl Status { } if !self.details.is_empty() { - let details = base64::encode_config(&self.details[..], base64::STANDARD_NO_PAD); + let details = crate::util::base64::STANDARD_NO_PAD.encode(&self.details[..]); header_map.insert( GRPC_STATUS_DETAILS_HEADER, @@ -971,7 +973,7 @@ mod tests { let header_map = status.to_header_map().unwrap(); - let b64_details = base64::encode_config(DETAILS, base64::STANDARD_NO_PAD); + let b64_details = crate::util::base64::STANDARD_NO_PAD.encode(DETAILS); assert_eq!(header_map[super::GRPC_STATUS_DETAILS_HEADER], b64_details); diff --git a/tonic/src/util.rs b/tonic/src/util.rs index 2dd303d..17c82f4 100644 --- a/tonic/src/util.rs +++ b/tonic/src/util.rs @@ -11,3 +11,27 @@ pub(crate) enum OptionPin { Some(#[pin] T), None, } + +pub(crate) mod base64 { + use base64::{ + alphabet, + engine::{ + general_purpose::{GeneralPurpose, GeneralPurposeConfig}, + DecodePaddingMode, + }, + }; + + pub(crate) const STANDARD: GeneralPurpose = GeneralPurpose::new( + &alphabet::STANDARD, + GeneralPurposeConfig::new() + .with_encode_padding(true) + .with_decode_padding_mode(DecodePaddingMode::Indifferent), + ); + + pub(crate) const STANDARD_NO_PAD: GeneralPurpose = GeneralPurpose::new( + &alphabet::STANDARD, + GeneralPurposeConfig::new() + .with_encode_padding(false) + .with_decode_padding_mode(DecodePaddingMode::Indifferent), + ); +}