mod timeout;
pub use timeout::{
decode_grpc_timeout, decode_grpc_timeout_connect, encode_grpc_timeout, TimeoutError,
};
use std::collections::HashMap;
use std::error::Error as StdError;
use std::io::Read;
use std::sync::Arc;
use bytes::Bytes;
use foundation_core::io::{
read_frame_blocking, AccumulatingBuffer, DecodeError, DecodeStep, IncrementalDecoder,
};
use foundation_errstacks::ErrorTrace;
use foundation_netio::shared::http::SimpleHeaders;
use crate::shared::compression::Compressor;
use crate::shared::error::{Code, ConnectError, ConnectResult, EndStreamResponse};
pub const ENVELOPE_HEADER_LEN: usize = 5;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Envelope {
pub flags: u8,
pub data: Bytes,
}
impl Envelope {
pub const FLAG_COMPRESSED: u8 = 0x01;
pub const FLAG_END_STREAM: u8 = 0x02;
pub const FLAG_TRAILER: u8 = 0x80;
#[must_use]
pub fn is_compressed(&self) -> bool {
self.flags & Self::FLAG_COMPRESSED != 0
}
#[must_use]
pub fn is_end_stream(&self) -> bool {
self.flags & Self::FLAG_END_STREAM != 0
}
#[must_use]
pub fn is_trailer(&self) -> bool {
self.flags & Self::FLAG_TRAILER != 0
}
pub fn encode_into(&self, buf: &mut Vec<u8>) {
buf.reserve(ENVELOPE_HEADER_LEN + self.data.len());
buf.push(self.flags);
buf.extend_from_slice(&(self.data.len() as u32).to_be_bytes());
buf.extend_from_slice(&self.data);
}
pub fn decode(data: &Bytes) -> Result<(Envelope, usize), EnvelopeError> {
if data.len() < ENVELOPE_HEADER_LEN {
return Err(EnvelopeError::Incomplete);
}
let flags = data[0];
let len = u32::from_be_bytes([data[1], data[2], data[3], data[4]]) as usize;
let total = ENVELOPE_HEADER_LEN + len;
if data.len() < total {
return Err(EnvelopeError::Incomplete);
}
let payload = data.slice(ENVELOPE_HEADER_LEN..total);
Ok((Envelope { flags, data: payload }, total))
}
}
#[derive(Debug)]
pub enum EnvelopeError {
Incomplete,
TooLarge {
length: usize,
limit: usize,
},
CompressedWithoutDecompressor,
}
impl EnvelopeError {
fn code(&self) -> Code {
match self {
EnvelopeError::TooLarge { .. } => Code::ResourceExhausted,
_ => Code::InvalidArgument,
}
}
}
impl core::fmt::Display for EnvelopeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
EnvelopeError::Incomplete => write!(f, "incomplete envelope frame"),
EnvelopeError::TooLarge { length, limit } => {
write!(f, "envelope length {length} exceeds read_max_bytes {limit}")
}
EnvelopeError::CompressedWithoutDecompressor => {
write!(f, "compressed frame received but no decompressor was negotiated")
}
}
}
}
impl StdError for EnvelopeError {}
impl From<EnvelopeError> for ConnectError {
fn from(err: EnvelopeError) -> Self {
ConnectError::new(err.code(), err.to_string())
}
}
impl From<EnvelopeError> for ErrorTrace<ConnectError> {
fn from(err: EnvelopeError) -> Self {
let connect = ConnectError::from(&err);
ErrorTrace::new(err).change_context(connect)
}
}
impl From<&EnvelopeError> for ConnectError {
fn from(err: &EnvelopeError) -> Self {
ConnectError::new(err.code(), err.to_string())
}
}
struct EnvelopeFrameDecoder {
buffer: AccumulatingBuffer,
decompressor: Option<Arc<dyn Compressor>>,
read_max_bytes: usize,
}
impl IncrementalDecoder for EnvelopeFrameDecoder {
type Frame = Envelope;
fn step(&mut self, src: &mut impl Read) -> Result<DecodeStep<Envelope>, DecodeError> {
self.buffer.fill_from(src)?;
let view = self.buffer.view();
if view.len() < ENVELOPE_HEADER_LEN {
return Ok(DecodeStep::Pending);
}
let flags = view[0];
let len = u32::from_be_bytes([view[1], view[2], view[3], view[4]]) as usize;
if self.read_max_bytes != 0 && len > self.read_max_bytes {
return Err(DecodeError::protocol(format!(
"envelope length {len} exceeds read_max_bytes {}",
self.read_max_bytes
)));
}
if view.len() < ENVELOPE_HEADER_LEN + len {
return Ok(DecodeStep::Pending);
}
self.buffer.advance(ENVELOPE_HEADER_LEN);
let payload = self.buffer.split_to(len);
let data = if flags & Envelope::FLAG_COMPRESSED != 0 {
match &self.decompressor {
Some(c) => Bytes::from(
c.decompress(&payload, self.read_max_bytes)
.map_err(|e| DecodeError::protocol(e.to_string()))?,
),
None => {
return Err(DecodeError::protocol(
"compressed frame received but no decompressor was negotiated",
));
}
}
} else {
payload
};
Ok(DecodeStep::Frame(Envelope { flags, data }))
}
fn has_partial(&self) -> bool {
!self.buffer.is_empty()
}
}
pub struct EnvelopeReader<S> {
source: S,
decoder: EnvelopeFrameDecoder,
}
impl<S: Read> EnvelopeReader<S> {
pub fn new(source: S, decompressor: Option<Arc<dyn Compressor>>, read_max_bytes: usize) -> Self {
Self {
source,
decoder: EnvelopeFrameDecoder {
buffer: AccumulatingBuffer::new(),
decompressor,
read_max_bytes,
},
}
}
pub fn read(&mut self) -> ConnectResult<Option<Envelope>> {
read_frame_blocking(&mut self.decoder, &mut self.source).map_err(map_decode_error)
}
pub fn step(&mut self) -> ConnectResult<DecodeStep<Envelope>> {
self.decoder.step(&mut self.source).map_err(map_decode_error)
}
#[must_use]
pub fn has_partial(&self) -> bool {
self.decoder.has_partial()
}
}
fn map_decode_error(err: DecodeError) -> ErrorTrace<ConnectError> {
let connect = match &err {
DecodeError::Io(_) => ConnectError::unavailable(err.to_string()),
DecodeError::Protocol(_) => ConnectError::invalid_argument(err.to_string()),
};
ErrorTrace::new(err).change_context(connect)
}
pub struct EnvelopeWriter {
compressor: Option<Arc<dyn Compressor>>,
compress_min_bytes: usize,
send_max_bytes: usize,
}
impl EnvelopeWriter {
#[must_use]
pub fn new(
compressor: Option<Arc<dyn Compressor>>,
compress_min_bytes: usize,
send_max_bytes: usize,
) -> Self {
Self {
compressor,
compress_min_bytes,
send_max_bytes,
}
}
pub fn write(&self, frame: Bytes) -> ConnectResult<Vec<u8>> {
self.frame_with_flags(&frame, 0)
}
pub fn write_end_stream(
&self,
error: Option<&ErrorTrace<ConnectError>>,
trailers: &SimpleHeaders,
) -> ConnectResult<Vec<u8>> {
let end = EndStreamResponse {
error: error.map(|t| t.current_context().to_wire()),
metadata: headers_to_map(trailers),
};
let payload = serde_json::to_vec(&end).map_err(|e| {
ConnectError::internal(format!("failed to encode EndStreamResponse: {e}"))
})?;
self.frame_with_flags(&payload, Envelope::FLAG_END_STREAM)
}
pub fn write_trailer_frame(&self, trailers: &SimpleHeaders) -> ConnectResult<Vec<u8>> {
let mut payload = Vec::new();
for (name, values) in trailers {
for value in values {
payload.extend_from_slice(name.to_string().to_ascii_lowercase().as_bytes());
payload.extend_from_slice(b": ");
payload.extend_from_slice(value.as_bytes());
payload.extend_from_slice(b"\r\n");
}
}
self.frame_with_flags(&payload, Envelope::FLAG_TRAILER)
}
fn frame_with_flags(&self, payload: &[u8], extra_flags: u8) -> ConnectResult<Vec<u8>> {
let mut flags = extra_flags;
let body = match &self.compressor {
Some(compressor) if payload.len() >= self.compress_min_bytes => {
flags |= Envelope::FLAG_COMPRESSED;
compressor.compress(payload)?
}
_ => payload.to_vec(),
};
if self.send_max_bytes != 0 && body.len() > self.send_max_bytes {
return Err(ConnectError::resource_exhausted(format!(
"message of {} bytes exceeds send_max_bytes of {}",
body.len(),
self.send_max_bytes
))
.into());
}
let mut out = Vec::with_capacity(ENVELOPE_HEADER_LEN + body.len());
out.push(flags);
out.extend_from_slice(&(body.len() as u32).to_be_bytes());
out.extend_from_slice(&body);
Ok(out)
}
}
fn headers_to_map(headers: &SimpleHeaders) -> Option<HashMap<String, Vec<String>>> {
if headers.is_empty() {
return None;
}
Some(
headers
.iter()
.map(|(name, values)| (name.to_string().to_ascii_lowercase(), values.clone()))
.collect(),
)
}