use serde::{Deserialize, Serialize};
use super::ControlError;
use crate::codec::MAX_FRAME_SIZE;
pub const CONTROL_GENERATION: u8 = 1;
pub const CONTROL_PROTOCOL: &str = "msb.control";
pub const MAX_HANDSHAKE_FRAME_SIZE: u32 = 4096;
pub const DEFAULT_MAX_IN_FLIGHT: u32 = 64;
pub const DEFAULT_SETUP_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
pub const DEFAULT_REQUEST_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30);
pub const MAX_DISCOVERY_RESPONSE_SIZE: usize = 64 * 1024;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ControlHello {
pub protocol: String,
pub min_generation: u8,
pub max_generation: u8,
pub max_frame_size: u32,
pub max_in_flight: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ControlWelcome {
pub protocol: String,
pub generation: u8,
pub max_frame_size: u32,
pub max_in_flight: u32,
}
impl ControlHello {
pub fn validate(&self) -> Result<(), ControlError> {
if self.protocol != CONTROL_PROTOCOL
|| self.min_generation == 0
|| self.min_generation > self.max_generation
|| !(MAX_HANDSHAKE_FRAME_SIZE..=MAX_FRAME_SIZE).contains(&self.max_frame_size)
|| self.max_in_flight == 0
{
return Err(ControlError::rejected(
"invalid_handshake",
"invalid control handshake",
));
}
Ok(())
}
}
impl ControlWelcome {
pub fn negotiate(hello: &ControlHello, max_in_flight: u32) -> Result<Self, ControlError> {
hello.validate()?;
if max_in_flight == 0 {
return Err(ControlError::rejected(
"internal",
"invalid server admission limit",
));
}
if hello.min_generation > CONTROL_GENERATION {
return Err(ControlError::rejected(
"unsupported_generation",
"no shared control generation",
));
}
Ok(Self {
protocol: CONTROL_PROTOCOL.into(),
generation: CONTROL_GENERATION,
max_frame_size: hello.max_frame_size.min(MAX_FRAME_SIZE),
max_in_flight: hello.max_in_flight.min(max_in_flight),
})
}
pub fn validate_for(&self, hello: &ControlHello) -> Result<(), ControlError> {
hello.validate()?;
if self.protocol != CONTROL_PROTOCOL
|| !(hello.min_generation..=hello.max_generation).contains(&self.generation)
|| !(MAX_HANDSHAKE_FRAME_SIZE..=hello.max_frame_size).contains(&self.max_frame_size)
|| self.max_in_flight == 0
|| self.max_in_flight > hello.max_in_flight
{
return Err(ControlError::rejected(
"invalid_handshake",
"invalid control welcome",
));
}
Ok(())
}
}
impl Default for ControlHello {
fn default() -> Self {
Self {
protocol: CONTROL_PROTOCOL.into(),
min_generation: CONTROL_GENERATION,
max_generation: CONTROL_GENERATION,
max_frame_size: MAX_FRAME_SIZE,
max_in_flight: DEFAULT_MAX_IN_FLIGHT,
}
}
}