use anyhow::{anyhow, Context}; use http::{header, Extensions, HeaderMap, HeaderValue}; use tonic::metadata::MetadataMap; use crate::{ codec::MessageCodec, error::Error, request::{maybe_streaming_header, CONTENT_ENCODING_HEADERS}, }; pub struct ResponseEnvelope { pub metadata: MetadataMap, pub is_streaming: bool, pub content_type: Option, pub content_encoding: Option, pub extensions: Extensions, } impl ResponseEnvelope { /// Parses a _successful_ Connect response. /// /// Failures should be parsed with [`ConnectStatus::from_connect_response`]. pub fn from_connect_response(resp: http::Response) -> Result<(Self, T), Error> { let ( http::response::Parts { status, version, headers, mut extensions, .. }, body, ) = resp.into_parts(); if !status.is_success() { // Failures should be parsed via ConnectStatus::from_connect_response return Err(Error::InvalidResponse(anyhow!( "failure status code {status}" ))); } extensions.insert(version); Ok(( Self::from_connect_response_parts(headers, extensions)?, body, )) } pub fn from_connect_response_parts( mut headers: HeaderMap, mut extensions: Extensions, ) -> Result { let content_type = headers .remove(header::CONTENT_TYPE) .context("missing content-type") .map_err(Error::InvalidResponse)?; let (_, is_streaming) = MessageCodec::from_content_type(&content_type)?; let content_encoding = maybe_streaming_header(&mut headers, &CONTENT_ENCODING_HEADERS, is_streaming) .map_err(Error::InvalidRequest)? .map(|entry| entry.remove()); let headers_info = extensions.get_or_insert_default(); let metadata = crate::metadata::from_headers(headers, Some(headers_info)); Ok(Self { metadata, is_streaming, content_type: Some(content_type), content_encoding, extensions, }) } }