use foundation_netio::shared::http::Proto;
use crate::shared::context::StreamType;
use crate::shared::error::{ConnectError, ConnectResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ProtocolKind {
Connect,
Grpc,
GrpcWeb,
}
#[derive(Debug, Clone)]
pub struct TransportCapabilities {
pub request_streaming: bool,
pub full_duplex: bool,
pub h2_trailers: bool,
pub http_versions: &'static [Proto],
pub multiplexed: bool,
}
#[derive(Debug, Clone)]
pub struct CallRequirements {
pub request_streaming: bool,
pub full_duplex: bool,
pub h2_trailers: bool,
pub min_http_version: Proto,
}
#[must_use]
pub fn requirements(protocol: ProtocolKind, stream_type: StreamType) -> CallRequirements {
let request_streaming = matches!(stream_type, StreamType::ClientStream | StreamType::BidiStream);
let full_duplex = matches!(stream_type, StreamType::BidiStream);
let h2_trailers = matches!(protocol, ProtocolKind::Grpc);
let min_http_version = if matches!(protocol, ProtocolKind::Grpc) {
Proto::HTTP20
} else {
Proto::HTTP11
};
CallRequirements {
request_streaming,
full_duplex,
h2_trailers,
min_http_version,
}
}
pub fn check_compatible(
req: &CallRequirements,
cap: &TransportCapabilities,
) -> ConnectResult<()> {
if req.request_streaming && !cap.request_streaming {
return Err(ConnectError::unimplemented(
"this transport cannot stream the request body (client/bidi streaming unsupported)",
)
.into());
}
if req.full_duplex && !cap.full_duplex {
return Err(ConnectError::unimplemented(
"this transport is not full-duplex; bidi streaming requires HTTP/2+ (505)",
)
.into());
}
if req.h2_trailers && !cap.h2_trailers {
return Err(ConnectError::unimplemented(
"this transport lacks HTTP/2 trailing HEADERS required by gRPC",
)
.into());
}
if !cap
.http_versions
.iter()
.any(|v| *v >= req.min_http_version)
{
return Err(ConnectError::unimplemented(format!(
"this transport does not offer HTTP version >= {:?} required by the protocol",
req.min_http_version
))
.into());
}
Ok(())
}