use crate::api::ErrorKind;
use crate::api::SocketEvent;
use crate::api::SocketTime;
use crate::packet::chunk::Chunk;
use crate::packet::heartbeat_ack_chunk::HeartbeatAckChunk;
use crate::packet::heartbeat_info_parameter::HeartbeatInfoParameter;
use crate::packet::heartbeat_request_chunk::HeartbeatRequestChunk;
use crate::packet::parameter::Parameter;
use crate::packet::read_u32_be;
use crate::packet::write_u32_be;
use crate::socket::context::Context;
use crate::socket::state::State;
pub(crate) fn handle_heartbeat_req(
state: &mut State,
ctx: &mut Context,
chunk: HeartbeatRequestChunk,
) {
if let Some(tcb) = state.tcb_mut() {
ctx.events.borrow_mut().add(SocketEvent::SendPacket(
tcb.new_packet()
.add(&Chunk::HeartbeatAck(HeartbeatAckChunk { parameters: chunk.parameters }))
.build(),
));
ctx.tx_packets_count += 1;
}
}
pub(crate) fn handle_heartbeat_ack(ctx: &mut Context, now: SocketTime, chunk: HeartbeatAckChunk) {
ctx.heartbeat_timeout.stop();
match chunk.parameters.iter().find_map(|p| match p {
Parameter::HeartbeatInfo(HeartbeatInfoParameter { info }) => Some(info),
_ => None,
}) {
Some(info) if info.len() == 4 => {
let counter = read_u32_be!(&info);
if counter == ctx.heartbeat_counter {
let _rtt = now - ctx.heartbeat_sent_time;
ctx.tx_error_counter.reset();
}
}
_ => {
ctx.events.borrow_mut().add(SocketEvent::OnError(
ErrorKind::ParseFailed,
"Failed to parse HEARTBEAT-ACK; Invalid info parameter".into(),
));
}
}
}
pub(crate) fn handle_heartbeat_timeouts(
state: &mut State,
ctx: &mut Context,
now: SocketTime,
) -> bool {
let interval_expired = ctx.heartbeat_interval.expire(now);
if interval_expired {
if let Some(tcb) = state.tcb() {
ctx.heartbeat_timeout.set_duration(ctx.options.rto_initial);
ctx.heartbeat_timeout.start(now);
ctx.heartbeat_counter = ctx.heartbeat_counter.wrapping_add(1);
ctx.heartbeat_sent_time = now;
let mut info = vec![0; 4];
write_u32_be!(&mut info, ctx.heartbeat_counter);
ctx.events.borrow_mut().add(SocketEvent::SendPacket(
tcb.new_packet()
.add(&Chunk::HeartbeatRequest(HeartbeatRequestChunk {
parameters: vec![Parameter::HeartbeatInfo(HeartbeatInfoParameter { info })],
}))
.build(),
));
ctx.tx_packets_count += 1;
}
}
let timeout_expired = ctx.heartbeat_timeout.expire(now);
if timeout_expired {
debug_assert!(!ctx.heartbeat_timeout.is_running());
ctx.tx_error_counter.increment();
}
interval_expired || timeout_expired
}