fix(client): reject invalid timing parameters earlier
This commit is contained in:
1 file changed
+33
-12
@@ -585,14 +585,11 @@ impl Client {
|
|||||||
self.recv_buffer.resize(recv_buffer_size, 0);
|
self.recv_buffer.resize(recv_buffer_size, 0);
|
||||||
self.phase = ClientPhase::Open { token: reply.token };
|
self.phase = ClientPhase::Open { token: reply.token };
|
||||||
|
|
||||||
let duration_ns = negotiated.params.duration_ns;
|
let end_mono = if negotiated.params.duration_ns > 0 {
|
||||||
let end_mono = if duration_ns > 0 {
|
Some(negotiated_end_mono(
|
||||||
let duration = Duration::from_nanos(duration_ns as u64);
|
now.mono,
|
||||||
Some(
|
negotiated.params.duration_ns,
|
||||||
now.mono
|
)?)
|
||||||
.checked_add(duration)
|
|
||||||
.ok_or(ClientError::DurationOverflow)?,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -829,8 +826,11 @@ fn params_from_config(config: &ClientConfig) -> Result<Params, ClientError> {
|
|||||||
validate_protocol_config(config)?;
|
validate_protocol_config(config)?;
|
||||||
Ok(Params {
|
Ok(Params {
|
||||||
protocol_version: PROTOCOL_VERSION,
|
protocol_version: PROTOCOL_VERSION,
|
||||||
duration_ns: duration_to_ns(config.duration.unwrap_or_default())?,
|
duration_ns: match config.duration {
|
||||||
interval_ns: duration_to_ns(config.interval)?,
|
Some(duration) => config_duration_to_ns("duration", duration)?,
|
||||||
|
None => 0,
|
||||||
|
},
|
||||||
|
interval_ns: config_duration_to_ns("interval", config.interval)?,
|
||||||
length: i64::from(config.length),
|
length: i64::from(config.length),
|
||||||
received_stats: config.received_stats,
|
received_stats: config.received_stats,
|
||||||
stamp_at: config.stamp_at,
|
stamp_at: config.stamp_at,
|
||||||
@@ -841,6 +841,16 @@ fn params_from_config(config: &ClientConfig) -> Result<Params, ClientError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate_protocol_config(config: &ClientConfig) -> Result<(), ClientError> {
|
fn validate_protocol_config(config: &ClientConfig) -> Result<(), ClientError> {
|
||||||
|
if config.duration == Some(Duration::ZERO) {
|
||||||
|
return Err(ClientError::InvalidConfig {
|
||||||
|
reason: "duration must be greater than zero; use None for continuous mode".to_owned(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if config.interval == Duration::ZERO {
|
||||||
|
return Err(ClientError::InvalidConfig {
|
||||||
|
reason: "interval must be greater than zero".to_owned(),
|
||||||
|
});
|
||||||
|
}
|
||||||
if config.dscp > MAX_DSCP_CODEPOINT {
|
if config.dscp > MAX_DSCP_CODEPOINT {
|
||||||
return Err(ClientError::InvalidConfig {
|
return Err(ClientError::InvalidConfig {
|
||||||
reason: format!("dscp must be <= {MAX_DSCP_CODEPOINT}"),
|
reason: format!("dscp must be <= {MAX_DSCP_CODEPOINT}"),
|
||||||
@@ -869,8 +879,19 @@ fn validate_protocol_config(config: &ClientConfig) -> Result<(), ClientError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn duration_to_ns(duration: Duration) -> Result<i64, ClientError> {
|
fn config_duration_to_ns(field: &str, duration: Duration) -> Result<i64, ClientError> {
|
||||||
i64::try_from(duration.as_nanos()).map_err(|_| ClientError::DurationOverflow)
|
i64::try_from(duration.as_nanos()).map_err(|_| ClientError::InvalidConfig {
|
||||||
|
reason: format!("{field} is too large to encode as nanoseconds"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negotiated_end_mono(start: Instant, duration_ns: i64) -> Result<Instant, ClientError> {
|
||||||
|
debug_assert!(duration_ns > 0);
|
||||||
|
start
|
||||||
|
.checked_add(Duration::from_nanos(duration_ns as u64))
|
||||||
|
.ok_or_else(|| ClientError::NegotiationRejected {
|
||||||
|
reason: "duration is too large to schedule".to_owned(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
Reference in new issue
Block a user