use super::{chunk_header::*, chunk_type::*, *};
use crate::param::param_supported_extensions::ParamSupportedExtensions;
use crate::param::{param_header::*, *};
use crate::util::get_padding_size;
#[derive(Default, Debug, Clone)]
pub(crate) struct ChunkInit {
pub(crate) is_ack: bool,
pub(crate) initiate_tag: u32,
pub(crate) advertised_receiver_window_credit: u32,
pub(crate) num_outbound_streams: u16,
pub(crate) num_inbound_streams: u16,
pub(crate) initial_tsn: u32,
pub(crate) params: Vec<Box<dyn Param + Send + Sync>>,
}
pub(crate) type ChunkInitAck = ChunkInit;
pub(crate) const INIT_CHUNK_MIN_LENGTH: usize = 16;
pub(crate) const INIT_OPTIONAL_VAR_HEADER_LENGTH: usize = 4;
impl fmt::Display for ChunkInit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut res = format!(
"is_ack: {}
initiate_tag: {}
advertised_receiver_window_credit: {}
num_outbound_streams: {}
num_inbound_streams: {}
initial_tsn: {}",
self.is_ack,
self.initiate_tag,
self.advertised_receiver_window_credit,
self.num_outbound_streams,
self.num_inbound_streams,
self.initial_tsn,
);
for (i, param) in self.params.iter().enumerate() {
res += format!("Param {}:\n {}", i, param).as_str();
}
write!(f, "{} {}", self.header(), res)
}
}
impl Chunk for ChunkInit {
fn header(&self) -> ChunkHeader {
ChunkHeader {
typ: if self.is_ack { CT_INIT_ACK } else { CT_INIT },
flags: 0,
value_length: self.value_length() as u16,
}
}
fn unmarshal(raw: &Bytes) -> Result<Self> {
let header = ChunkHeader::unmarshal(raw)?;
if !(header.typ == CT_INIT || header.typ == CT_INIT_ACK) {
return Err(Error::ErrChunkTypeNotTypeInit);
} else if header.value_length() < INIT_CHUNK_MIN_LENGTH {
return Err(Error::ErrChunkValueNotLongEnough);
}
if header.flags != 0 {
return Err(Error::ErrChunkTypeInitFlagZero);
}
let reader = &mut raw.slice(CHUNK_HEADER_SIZE..CHUNK_HEADER_SIZE + header.value_length());
let initiate_tag = reader.get_u32();
let advertised_receiver_window_credit = reader.get_u32();
let num_outbound_streams = reader.get_u16();
let num_inbound_streams = reader.get_u16();
let initial_tsn = reader.get_u32();
let mut params = vec![];
let mut offset = CHUNK_HEADER_SIZE + INIT_CHUNK_MIN_LENGTH;
let value_end = CHUNK_HEADER_SIZE + header.value_length();
let mut remaining = value_end as isize - offset as isize;
while remaining > INIT_OPTIONAL_VAR_HEADER_LENGTH as isize {
let p = build_param(&raw.slice(offset..CHUNK_HEADER_SIZE + header.value_length()))?;
let p_len = PARAM_HEADER_LENGTH + p.value_length();
let len_plus_padding = p_len + get_padding_size(p_len);
params.push(p);
offset += len_plus_padding;
remaining -= len_plus_padding as isize;
}
Ok(ChunkInit {
is_ack: header.typ == CT_INIT_ACK,
initiate_tag,
advertised_receiver_window_credit,
num_outbound_streams,
num_inbound_streams,
initial_tsn,
params,
})
}
fn marshal_to(&self, writer: &mut BytesMut) -> Result<usize> {
self.header().marshal_to(writer)?;
writer.put_u32(self.initiate_tag);
writer.put_u32(self.advertised_receiver_window_credit);
writer.put_u16(self.num_outbound_streams);
writer.put_u16(self.num_inbound_streams);
writer.put_u32(self.initial_tsn);
for (idx, p) in self.params.iter().enumerate() {
let pp = p.marshal()?;
let pp_len = pp.len();
writer.extend(pp);
if idx != self.params.len() - 1 {
let cnt = get_padding_size(pp_len);
writer.extend(vec![0u8; cnt]);
}
}
Ok(writer.len())
}
fn check(&self) -> Result<()> {
if self.initiate_tag == 0 {
return Err(Error::ErrChunkTypeInitInitiateTagZero);
}
if self.num_inbound_streams == 0 {
return Err(Error::ErrInitInboundStreamRequestZero);
}
if self.num_outbound_streams == 0 {
return Err(Error::ErrInitOutboundStreamRequestZero);
}
if self.advertised_receiver_window_credit < 1500 {
return Err(Error::ErrInitAdvertisedReceiver1500);
}
Ok(())
}
fn value_length(&self) -> usize {
let mut l = 4 + 4 + 2 + 2 + 4;
for (idx, p) in self.params.iter().enumerate() {
let p_len = PARAM_HEADER_LENGTH + p.value_length();
l += p_len;
if idx != self.params.len() - 1 {
l += get_padding_size(p_len);
}
}
l
}
fn as_any(&self) -> &(dyn Any + Send + Sync) {
self
}
}
impl ChunkInit {
pub(crate) fn set_supported_extensions(&mut self) {
self.params.push(Box::new(ParamSupportedExtensions {
chunk_types: vec![CT_RECONFIG, CT_FORWARD_TSN],
}));
}
}