use std::time::Duration;
use crate::throttle::types::AdaptiveThrottleConfig;
use crate::{Blob, CoreState, ZmqError};
pub const DEFAULT_SNDBATCH_COUNT: usize = 128;
pub const DEFAULT_SNDBATCH_BYTES: usize = 256 * 1024; pub const DEFAULT_RCVBATCH_COUNT: usize = 128;
pub const DEFAULT_RCVBATCH_BYTES: usize = 256 * 1024;
pub const SNDBUF: i32 = 11;
pub const RCVBUF: i32 = 12;
pub const SNDHWM: i32 = 23;
pub const RCVHWM: i32 = 24;
pub const LINGER: i32 = 17;
pub const SUBSCRIBE: i32 = 6;
pub const UNSUBSCRIBE: i32 = 7;
pub const ROUTING_ID: i32 = 5; pub const RECONNECT_IVL: i32 = 18; pub const RECONNECT_IVL_MAX: i32 = 21; pub const RCVTIMEO: i32 = 27;
pub const SNDTIMEO: i32 = 28;
pub const LAST_ENDPOINT: i32 = 32;
pub const TCP_KEEPALIVE: i32 = 34;
pub const TCP_KEEPALIVE_IDLE: i32 = 35;
pub const TCP_KEEPALIVE_CNT: i32 = 36;
pub const TCP_KEEPALIVE_INTVL: i32 = 37;
pub const HEARTBEAT_IVL: i32 = 38; pub const HEARTBEAT_TIMEOUT: i32 = 39; pub const HEARTBEAT_TTL: i32 = 40; pub const HANDSHAKE_IVL: i32 = 41;
pub const ROUTER_MANDATORY: i32 = 33;
pub const AUTO_DELIMITER: i32 = 42;
pub const ZAP_DOMAIN: i32 = 55; pub const PLAIN_SERVER: i32 = 44;
pub const PLAIN_USERNAME: i32 = 45;
pub const PLAIN_PASSWORD: i32 = 46;
pub const NOISE_XX_ENABLED: i32 = 1202; pub const NOISE_XX_STATIC_SECRET_KEY: i32 = 1200; pub const NOISE_XX_REMOTE_STATIC_PUBLIC_KEY: i32 = 1201;
pub const CURVE_SERVER: i32 = 47; pub const CURVE_SECRET_KEY: i32 = 49; pub const CURVE_SERVER_KEY: i32 = 48;
pub const MAXMSGSIZE: i32 = 22;
pub const MAX_CONNECTIONS: i32 = 1000;
#[cfg(feature = "io-uring")]
pub const IO_URING_SNDZEROCOPY: i32 = 1170;
#[cfg(feature = "io-uring")]
pub const IO_URING_RCVMULTISHOT: i32 = 1171;
pub const TCP_CORK: i32 = 1172;
pub const IO_URING_SESSION_ENABLED: i32 = 1175;
#[cfg(feature = "io-uring")]
pub const IO_URING_ZC_SEND_THRESHOLD: i32 = 1176;
pub const ADAPTIVE_THROTTLE: i32 = 1210;
pub const ALLOW_ZMTP2: i32 = 1220;
pub const SNDBATCH_COUNT: i32 = 1215; pub const SNDBATCH_BYTES: i32 = 1216; pub const RCVBATCH_COUNT: i32 = 1217; pub const RCVBATCH_BYTES: i32 = 1218;
pub const DEFAULT_RECONNECT_IVL_MS: u64 = 1000;
#[derive(Debug, Clone)]
pub(crate) struct SocketOptions {
pub rcvhwm: usize,
pub sndhwm: usize,
pub rcvtimeo: Option<Duration>,
pub sndtimeo: Option<Duration>,
pub linger: Option<Duration>, pub reconnect_ivl: Option<Duration>, pub reconnect_ivl_max: Option<Duration>, pub backlog: Option<u32>,
pub routing_id: Option<Blob>,
pub socket_type_name: String, pub tcp_keepalive_enabled: i32, pub tcp_keepalive_idle: Option<Duration>,
pub tcp_keepalive_count: Option<u32>,
pub tcp_keepalive_interval: Option<Duration>,
pub tcp_nodelay: bool, pub max_connections: Option<usize>,
pub maxmsgsize: i64,
pub heartbeat_ivl: Option<Duration>,
pub heartbeat_timeout: Option<Duration>,
pub handshake_ivl: Option<Duration>,
pub router_mandatory: bool,
pub allow_zmtp2: bool,
pub tcp_cork: bool,
pub sndbuf: Option<usize>,
pub rcvbuf: Option<usize>,
pub io_uring: IOURingSocketOptions,
pub zap_domain: Option<String>, #[cfg(feature = "plain")]
pub plain_options: PlainMechanismSocketOptions,
#[cfg(feature = "curve")]
pub curve_options: CurveMechanismSocketOptions,
#[cfg(feature = "noise_xx")]
pub noise_xx_options: NoiseXxSocketOptions,
pub throttle_config: AdaptiveThrottleConfig,
pub sndbatch_count: usize,
pub sndbatch_bytes: usize,
pub rcvbatch_count: usize,
pub rcvbatch_bytes: usize,
}
impl Default for SocketOptions {
fn default() -> Self {
Self {
rcvhwm: 256,
sndhwm: 256,
rcvtimeo: None, sndtimeo: None, linger: Some(Duration::ZERO), reconnect_ivl: Some(Duration::from_millis(DEFAULT_RECONNECT_IVL_MS)),
reconnect_ivl_max: Some(Duration::ZERO), backlog: None,
routing_id: None,
socket_type_name: "UNKNOWN".to_string(), tcp_keepalive_enabled: 0, tcp_keepalive_idle: None,
tcp_keepalive_count: None,
tcp_keepalive_interval: None,
tcp_nodelay: true, max_connections: Some(1024),
maxmsgsize: -1, heartbeat_ivl: None, heartbeat_timeout: None,
handshake_ivl: None,
router_mandatory: false, allow_zmtp2: true, tcp_cork: false,
sndbuf: None,
rcvbuf: None,
io_uring: Default::default(),
zap_domain: None,
#[cfg(feature = "plain")]
plain_options: Default::default(),
#[cfg(feature = "noise_xx")]
noise_xx_options: NoiseXxSocketOptions::default(),
#[cfg(feature = "curve")]
curve_options: CurveMechanismSocketOptions::default(),
throttle_config: {
let mut c = AdaptiveThrottleConfig::default();
c.credit_per_message = 5;
c.healthy_balance_width = 1024000;
c.max_imbalance = 6553600;
c.yield_after_n_consecutive = 256;
c.priority_boost_factor = 5.0;
c
},
sndbatch_count: DEFAULT_SNDBATCH_COUNT,
sndbatch_bytes: DEFAULT_SNDBATCH_BYTES,
rcvbatch_count: DEFAULT_RCVBATCH_COUNT,
rcvbatch_bytes: DEFAULT_RCVBATCH_BYTES,
}
}
}
#[derive(Debug, Clone)]
pub struct IOURingSocketOptions {
pub send_zerocopy: bool,
pub recv_multishot: bool,
pub session_enabled: bool,
pub zc_send_threshold: usize,
}
impl Default for IOURingSocketOptions {
fn default() -> Self {
Self {
session_enabled: false,
send_zerocopy: false,
recv_multishot: false,
zc_send_threshold: 16384,
}
}
}
#[cfg(feature = "curve")]
#[derive(Debug, Clone, Default)]
pub struct CurveMechanismSocketOptions {
pub enabled: bool,
pub server_role: bool, pub secret_key: Option<[u8; 32]>,
pub server_public_key: Option<[u8; 32]>, }
#[cfg(feature = "noise_xx")]
#[derive(Debug, Clone, Default)]
pub struct NoiseXxSocketOptions {
pub enabled: bool,
pub static_secret_key_bytes: Option<[u8; 32]>,
pub remote_static_public_key_bytes: Option<[u8; 32]>,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct TcpTransportConfig {
pub tcp_nodelay: bool,
pub keepalive_time: Option<Duration>,
pub keepalive_interval: Option<Duration>,
pub keepalive_count: Option<u32>,
pub sndbuf: Option<usize>,
pub rcvbuf: Option<usize>,
}
#[cfg(feature = "plain")]
#[derive(Debug, Clone, Default)]
pub struct PlainMechanismSocketOptions {
pub enabled: bool,
pub server_role: Option<bool>,
pub username: Option<String>,
pub password: Option<String>,
}
#[derive(Debug, Clone)]
pub(crate) struct ZmtpEngineConfig {
pub routing_id: Option<Blob>,
pub socket_type_name: String,
pub security_enabled: bool,
pub allow_zmtp2: bool,
pub heartbeat_ivl: Option<Duration>,
pub heartbeat_timeout: Option<Duration>,
pub handshake_timeout: Option<Duration>,
pub rcvtimeo: Option<Duration>,
pub sndtimeo: Option<Duration>,
pub use_send_zerocopy: bool,
pub use_recv_multishot: bool,
pub use_cork: bool,
#[cfg(feature = "noise_xx")]
pub use_noise_xx: bool,
#[cfg(feature = "noise_xx")]
pub noise_xx_local_sk_bytes_for_engine: Option<[u8; 32]>,
#[cfg(feature = "noise_xx")]
pub noise_xx_remote_pk_bytes_for_engine: Option<[u8; 32]>,
#[cfg(feature = "curve")]
pub use_curve: bool,
#[cfg(feature = "curve")]
pub curve_local_secret_key: Option<[u8; 32]>,
#[cfg(feature = "curve")]
pub curve_remote_public_key: Option<[u8; 32]>,
#[cfg(feature = "plain")]
pub use_plain: bool,
#[cfg(feature = "plain")]
pub plain_username_for_engine: Option<String>,
#[cfg(feature = "plain")]
pub plain_password_for_engine: Option<String>,
pub max_msg_size: i64,
pub throttle_config: AdaptiveThrottleConfig,
pub sndhwm: usize,
pub rcvhwm: usize,
pub sndbatch_count: usize,
pub sndbatch_bytes: usize,
pub sndbatch_bytes_physical: usize,
pub rcvbatch_count: usize,
pub rcvbatch_bytes: usize,
pub rcvbuf: Option<usize>,
#[cfg(feature = "io-uring")]
pub zc_send_threshold: usize,
}
impl Default for ZmtpEngineConfig {
fn default() -> Self {
Self {
routing_id: None,
socket_type_name: String::new(),
security_enabled: false,
allow_zmtp2: true,
heartbeat_ivl: None,
heartbeat_timeout: None,
handshake_timeout: None,
rcvtimeo: None,
sndtimeo: None,
use_send_zerocopy: false,
use_recv_multishot: false,
use_cork: false,
#[cfg(feature = "noise_xx")]
use_noise_xx: false,
#[cfg(feature = "noise_xx")]
noise_xx_local_sk_bytes_for_engine: None,
#[cfg(feature = "noise_xx")]
noise_xx_remote_pk_bytes_for_engine: None,
#[cfg(feature = "curve")]
use_curve: false,
#[cfg(feature = "curve")]
curve_local_secret_key: None,
#[cfg(feature = "curve")]
curve_remote_public_key: None,
#[cfg(feature = "plain")]
use_plain: false,
#[cfg(feature = "plain")]
plain_username_for_engine: None,
#[cfg(feature = "plain")]
plain_password_for_engine: None,
max_msg_size: -1, throttle_config: AdaptiveThrottleConfig::default(),
sndhwm: 256,
rcvhwm: 256,
sndbatch_count: DEFAULT_SNDBATCH_COUNT,
sndbatch_bytes: DEFAULT_SNDBATCH_BYTES,
sndbatch_bytes_physical: DEFAULT_SNDBATCH_BYTES + (DEFAULT_SNDBATCH_COUNT * 9),
rcvbatch_count: DEFAULT_RCVBATCH_COUNT,
rcvbatch_bytes: DEFAULT_RCVBATCH_BYTES,
rcvbuf: None,
#[cfg(feature = "io-uring")]
zc_send_threshold: 16384,
}
}
}
impl From<&SocketOptions> for ZmtpEngineConfig {
fn from(options: &SocketOptions) -> Self {
let security_enabled = {
#[cfg(feature = "plain")]
{
options.plain_options.enabled
}
#[cfg(not(feature = "plain"))]
{
false
}
} || {
#[cfg(feature = "noise_xx")]
{
options.noise_xx_options.enabled
}
#[cfg(not(feature = "noise_xx"))]
{
false
}
} || {
#[cfg(feature = "curve")]
{
options.curve_options.enabled
}
#[cfg(not(feature = "curve"))]
{
false
}
};
#[cfg(feature = "io-uring")]
let sndbatch_bytes = if options.io_uring.send_zerocopy {
let ceiling = crate::uring::DEFAULT_IO_URING_SND_BUFFER_SIZE;
if options.sndbatch_bytes > ceiling {
tracing::warn!(
sndbatch_bytes = options.sndbatch_bytes,
ceiling,
"sndbatch_bytes clamped to io_uring logical payload ceiling to prevent ZC slot overflow"
);
ceiling
} else {
options.sndbatch_bytes
}
} else {
options.sndbatch_bytes
};
#[cfg(not(feature = "io-uring"))]
let sndbatch_bytes = options.sndbatch_bytes;
let sndbatch_bytes_physical =
calculate_required_slot_size(sndbatch_bytes, options.sndbatch_count);
ZmtpEngineConfig {
routing_id: options.routing_id.clone(),
socket_type_name: options.socket_type_name.clone(),
security_enabled,
allow_zmtp2: options.allow_zmtp2,
heartbeat_ivl: options.heartbeat_ivl,
heartbeat_timeout: options.heartbeat_timeout,
handshake_timeout: options.handshake_ivl,
rcvtimeo: options.rcvtimeo,
sndtimeo: options.sndtimeo,
use_send_zerocopy: options.io_uring.send_zerocopy,
use_recv_multishot: options.io_uring.recv_multishot,
use_cork: options.tcp_cork,
#[cfg(feature = "noise_xx")]
use_noise_xx: options.noise_xx_options.enabled,
#[cfg(feature = "noise_xx")]
noise_xx_local_sk_bytes_for_engine: options.noise_xx_options.static_secret_key_bytes,
#[cfg(feature = "noise_xx")]
noise_xx_remote_pk_bytes_for_engine: options.noise_xx_options.remote_static_public_key_bytes,
#[cfg(feature = "curve")]
use_curve: options.curve_options.enabled,
#[cfg(feature = "curve")]
curve_local_secret_key: options.curve_options.secret_key,
#[cfg(feature = "curve")]
curve_remote_public_key: options.curve_options.server_public_key,
#[cfg(feature = "plain")]
use_plain: options.plain_options.enabled,
#[cfg(feature = "plain")]
plain_username_for_engine: options.plain_options.username.clone(),
#[cfg(feature = "plain")]
plain_password_for_engine: options.plain_options.password.clone(),
max_msg_size: options.maxmsgsize,
throttle_config: options.throttle_config.clone(),
sndhwm: options.sndhwm,
rcvhwm: options.rcvhwm,
sndbatch_count: options.sndbatch_count,
sndbatch_bytes,
sndbatch_bytes_physical,
rcvbatch_count: options.rcvbatch_count,
rcvbatch_bytes: options.rcvbatch_bytes,
rcvbuf: options.rcvbuf,
#[cfg(feature = "io-uring")]
zc_send_threshold: options.io_uring.zc_send_threshold,
}
}
}
pub(crate) fn parse_i32_option(value: &[u8]) -> Result<i32, ZmqError> {
let arr: [u8; 4] = value
.try_into()
.map_err(|_| ZmqError::InvalidOptionValue(0))?;
Ok(i32::from_ne_bytes(arr)) }
pub(crate) fn parse_bool_option(value: &[u8]) -> Result<bool, ZmqError> {
Ok(parse_i32_option(value)? == 1)
}
pub(crate) fn parse_duration_ms_option(value: &[u8]) -> Result<Option<Duration>, ZmqError> {
let val = parse_i32_option(value)?;
match val {
-1 => Ok(None), 0.. => Ok(Some(Duration::from_millis(val as u64))), _ => Err(ZmqError::InvalidOptionValue(0)), }
}
pub(crate) fn parse_secs_duration_option(value: &[u8]) -> Result<Option<Duration>, ZmqError> {
let val = parse_i32_option(value)?;
match val {
0..=i32::MAX => Ok(Some(Duration::from_secs(val as u64))),
_ => Err(ZmqError::InvalidOptionValue(0)),
}
}
pub(crate) fn parse_timeout_option(
value: &[u8],
option_id: i32,
) -> Result<Option<Duration>, ZmqError> {
let val = parse_i32_option(value).map_err(|_| ZmqError::InvalidOptionValue(option_id))?;
match val {
-1 => Ok(None), 0 => Ok(Some(Duration::ZERO)), 1.. => Ok(Some(Duration::from_millis(val as u64))), _ => Err(ZmqError::InvalidOptionValue(option_id)), }
}
pub(crate) fn parse_linger_option(value: &[u8]) -> Result<Option<Duration>, ZmqError> {
let val = parse_i32_option(value)?;
match val {
-1 => Ok(None), 0.. => Ok(Some(Duration::from_millis(val as u64))), _ => Err(ZmqError::InvalidOptionValue(LINGER)), }
}
pub(crate) fn parse_u32_option(value: &[u8]) -> Result<Option<u32>, ZmqError> {
let val = parse_i32_option(value)?; match val {
0..=i32::MAX => Ok(Some(val as u32)),
_ => Err(ZmqError::InvalidOptionValue(0)),
}
}
pub(crate) fn parse_keepalive_mode_option(value: &[u8]) -> Result<i32, ZmqError> {
let val = parse_i32_option(value)?;
if val >= -1 && val <= 1 {
Ok(val)
} else {
Err(ZmqError::InvalidOptionValue(TCP_KEEPALIVE))
}
}
pub(crate) fn parse_blob_option(value: &[u8]) -> Result<Blob, ZmqError> {
if value.len() > 255 {
Err(ZmqError::InvalidOptionValue(ROUTING_ID)) } else {
Ok(Blob::from(value.to_vec())) }
}
pub(crate) fn parse_heartbeat_option(
value: &[u8],
option_id: i32,
) -> Result<Option<Duration>, ZmqError> {
let val = parse_i32_option(value).map_err(|_| ZmqError::InvalidOptionValue(option_id))?;
match val {
0 => Ok(None), 1.. => Ok(Some(Duration::from_millis(val as u64))), _ => Err(ZmqError::InvalidOptionValue(option_id)), }
}
pub(crate) fn parse_handshake_option(
value: &[u8],
option_id: i32,
) -> Result<Option<Duration>, ZmqError> {
let val = parse_i32_option(value).map_err(|_| ZmqError::InvalidOptionValue(option_id))?;
match val {
0 => Ok(None),
1.. => Ok(Some(Duration::from_millis(val as u64))), _ => Err(ZmqError::InvalidOptionValue(option_id)), }
}
pub(crate) fn parse_reconnect_ivl_option(value: &[u8]) -> Result<Option<Duration>, ZmqError> {
let val = parse_i32_option(value)?;
match val {
-1 => Ok(None), 0 => Ok(None), 1.. => Ok(Some(Duration::from_millis(val as u64))),
_ => Err(ZmqError::InvalidOptionValue(RECONNECT_IVL)),
}
}
pub(crate) fn parse_reconnect_ivl_max_option(value: &[u8]) -> Result<Option<Duration>, ZmqError> {
let val = parse_i32_option(value)?;
match val {
0 => Ok(Some(Duration::ZERO)), 1.. => Ok(Some(Duration::from_millis(val as u64))),
_ => Err(ZmqError::InvalidOptionValue(RECONNECT_IVL_MAX)),
}
}
pub(crate) fn parse_maxmsgsize_option(value: &[u8]) -> Result<i64, ZmqError> {
let arr: [u8; 8] = value
.try_into()
.map_err(|_| ZmqError::InvalidOptionValue(MAXMSGSIZE))?;
let v = i64::from_ne_bytes(arr);
if v < -1 {
return Err(ZmqError::InvalidOptionValue(MAXMSGSIZE));
}
Ok(v)
}
pub(crate) fn parse_max_connections_option(
value: &[u8],
option_id: i32,
) -> Result<Option<usize>, ZmqError> {
let val = parse_i32_option(value).map_err(|_| ZmqError::InvalidOptionValue(option_id))?;
match val {
-1 => Ok(None), 0 => Err(ZmqError::InvalidOptionValue(option_id)), 1.. => Ok(Some(val as usize)),
_ => Err(ZmqError::InvalidOptionValue(option_id)),
}
}
pub(crate) fn parse_key_option<const N: usize>(
value: &[u8],
option_id: i32,
) -> Result<[u8; N], ZmqError> {
value.try_into().map_err(|_e| {
tracing::error!(
option_id = option_id,
expected_len = N,
actual_len = value.len(),
"Invalid key length provided for socket option."
);
ZmqError::InvalidOptionValue(option_id) })
}
pub(crate) fn parse_string_option(value: &[u8], option_id: i32) -> Result<String, ZmqError> {
String::from_utf8(value.to_vec()).map_err(|_| ZmqError::InvalidOptionValue(option_id))
}
pub(crate) fn apply_core_option_value(
options: &mut SocketOptions, option_id: i32,
value: &[u8],
) -> Result<(), ZmqError> {
tracing::debug!(
option_id,
value_len = value.len(),
"Applying core socket option"
);
match option_id {
SNDBUF => options.sndbuf = Some(parse_i32_option(value)?.max(0) as usize),
RCVBUF => options.rcvbuf = Some(parse_i32_option(value)?.max(0) as usize),
SNDHWM => options.sndhwm = parse_i32_option(value)?.max(0) as usize,
RCVHWM => options.rcvhwm = parse_i32_option(value)?.max(0) as usize,
LINGER => options.linger = parse_linger_option(value)?,
ROUTING_ID => options.routing_id = Some(parse_blob_option(value)?),
RECONNECT_IVL => options.reconnect_ivl = parse_reconnect_ivl_option(value)?,
RECONNECT_IVL_MAX => options.reconnect_ivl_max = parse_reconnect_ivl_max_option(value)?,
RCVTIMEO => options.rcvtimeo = parse_timeout_option(value, option_id)?,
SNDTIMEO => options.sndtimeo = parse_timeout_option(value, option_id)?,
TCP_KEEPALIVE => options.tcp_keepalive_enabled = parse_keepalive_mode_option(value)?,
TCP_KEEPALIVE_IDLE => options.tcp_keepalive_idle = parse_secs_duration_option(value)?,
TCP_KEEPALIVE_CNT => options.tcp_keepalive_count = parse_u32_option(value)?,
TCP_KEEPALIVE_INTVL => options.tcp_keepalive_interval = parse_secs_duration_option(value)?,
HEARTBEAT_IVL => options.heartbeat_ivl = parse_heartbeat_option(value, option_id)?,
HEARTBEAT_TIMEOUT => options.heartbeat_timeout = parse_heartbeat_option(value, option_id)?,
HANDSHAKE_IVL => options.handshake_ivl = parse_handshake_option(value, option_id)?,
MAXMSGSIZE => options.maxmsgsize = parse_maxmsgsize_option(value)?,
MAX_CONNECTIONS => options.max_connections = parse_max_connections_option(value, option_id)?,
TCP_CORK => options.tcp_cork = parse_bool_option(value)?,
ALLOW_ZMTP2 => options.allow_zmtp2 = parse_bool_option(value)?,
ZAP_DOMAIN => options.zap_domain = Some(parse_string_option(value, option_id)?),
#[cfg(feature = "plain")]
PLAIN_SERVER => {
options.plain_options.server_role = Some(parse_bool_option(value)?);
options.plain_options.enabled = true;
}
#[cfg(feature = "plain")]
PLAIN_USERNAME => {
options.plain_options.username = Some(parse_string_option(value, option_id)?);
options.plain_options.enabled = true;
}
#[cfg(feature = "plain")]
PLAIN_PASSWORD => {
options.plain_options.password = Some(parse_string_option(value, option_id)?);
options.plain_options.enabled = true;
}
#[cfg(feature = "curve")]
CURVE_SERVER => {
options.curve_options.server_role = parse_bool_option(value)?;
options.curve_options.enabled = true; }
#[cfg(feature = "curve")]
CURVE_SECRET_KEY => {
options.curve_options.secret_key = Some(parse_key_option::<32>(value, option_id)?);
options.curve_options.enabled = true;
}
#[cfg(feature = "curve")]
CURVE_SERVER_KEY => {
options.curve_options.server_public_key = Some(parse_key_option::<32>(value, option_id)?);
options.curve_options.enabled = true;
}
#[cfg(feature = "noise_xx")]
NOISE_XX_ENABLED => options.noise_xx_options.enabled = parse_bool_option(value)?,
#[cfg(feature = "noise_xx")]
NOISE_XX_STATIC_SECRET_KEY => options.noise_xx_options.static_secret_key_bytes = Some(parse_key_option::<32>(value, option_id)?),
#[cfg(feature = "noise_xx")]
NOISE_XX_REMOTE_STATIC_PUBLIC_KEY => options.noise_xx_options.remote_static_public_key_bytes = Some(parse_key_option::<32>(value, option_id)?),
#[cfg(feature = "io-uring")]
IO_URING_SESSION_ENABLED => options.io_uring.session_enabled = parse_bool_option(value)?,
#[cfg(feature = "io-uring")]
IO_URING_SNDZEROCOPY => options.io_uring.send_zerocopy = parse_bool_option(value)?,
#[cfg(feature = "io-uring")]
IO_URING_RCVMULTISHOT => options.io_uring.recv_multishot = parse_bool_option(value)?,
#[cfg(feature = "io-uring")]
IO_URING_ZC_SEND_THRESHOLD => options.io_uring.zc_send_threshold = parse_i32_option(value)?.max(1) as usize,
ADAPTIVE_THROTTLE => options.throttle_config.enabled = parse_bool_option(value)?,
SNDBATCH_COUNT => options.sndbatch_count = parse_i32_option(value)?.max(1) as usize,
SNDBATCH_BYTES => options.sndbatch_bytes = parse_i32_option(value)?.max(1) as usize,
RCVBATCH_COUNT => options.rcvbatch_count = parse_i32_option(value)?.max(1) as usize,
RCVBATCH_BYTES => options.rcvbatch_bytes = parse_i32_option(value)?.max(1) as usize,
SUBSCRIBE | UNSUBSCRIBE | LAST_ENDPOINT | ROUTER_MANDATORY |
AUTO_DELIMITER | 16 => return Err(ZmqError::UnsupportedOption(option_id)),
_ => return Err(ZmqError::InvalidOption(option_id)), }
Ok(())
}
pub(crate) fn retrieve_core_option_value(
options: &SocketOptions, core_s_reader: &CoreState, option_id: i32,
) -> Result<Vec<u8>, ZmqError> {
match option_id {
SNDBUF => Ok(options.sndbuf.map_or(0, |v| v as i32).to_ne_bytes().to_vec()),
RCVBUF => Ok(options.rcvbuf.map_or(0, |v| v as i32).to_ne_bytes().to_vec()),
SNDHWM => Ok((options.sndhwm as i32).to_ne_bytes().to_vec()),
RCVHWM => Ok((options.rcvhwm as i32).to_ne_bytes().to_vec()),
LINGER => Ok(options.linger.map_or(-1, |d| d.as_millis().try_into().unwrap_or(i32::MAX)).to_ne_bytes().to_vec()),
ROUTING_ID => options.routing_id.as_ref().map(|b| b.to_vec()).ok_or(ZmqError::Internal("Option ROUTING_ID not set".into())),
RECONNECT_IVL => Ok(options.reconnect_ivl.map_or(0, |d| d.as_millis() as i32).to_ne_bytes().to_vec()), RECONNECT_IVL_MAX => Ok(options.reconnect_ivl_max.map_or(0, |d| d.as_millis() as i32).to_ne_bytes().to_vec()), RCVTIMEO => Ok(options.rcvtimeo.map_or(-1, |d| d.as_millis().try_into().unwrap_or(i32::MAX)).to_ne_bytes().to_vec()),
SNDTIMEO => Ok(options.sndtimeo.map_or(-1, |d| d.as_millis().try_into().unwrap_or(i32::MAX)).to_ne_bytes().to_vec()),
LAST_ENDPOINT => Ok(core_s_reader.last_bound_endpoint.as_deref().unwrap_or("").as_bytes().to_vec()),
TCP_KEEPALIVE => Ok(options.tcp_keepalive_enabled.to_ne_bytes().to_vec()),
TCP_KEEPALIVE_IDLE => Ok(options.tcp_keepalive_idle.map_or(0, |d| d.as_secs() as i32).to_ne_bytes().to_vec()),
TCP_KEEPALIVE_CNT => Ok(options.tcp_keepalive_count.map_or(0, |c| c as i32).to_ne_bytes().to_vec()),
TCP_KEEPALIVE_INTVL => Ok(options.tcp_keepalive_interval.map_or(0, |d| d.as_secs() as i32).to_ne_bytes().to_vec()),
HEARTBEAT_IVL => Ok(options.heartbeat_ivl.map_or(0, |d| d.as_millis() as i32).to_ne_bytes().to_vec()),
HEARTBEAT_TIMEOUT => Ok(options.heartbeat_timeout.map_or(0, |d| d.as_millis() as i32).to_ne_bytes().to_vec()),
HANDSHAKE_IVL => Ok(options.handshake_ivl.map_or(0, |d| d.as_millis() as i32).to_ne_bytes().to_vec()),
MAXMSGSIZE => Ok(options.maxmsgsize.to_ne_bytes().to_vec()),
MAX_CONNECTIONS => Ok(options.max_connections.map_or(-1, |v| v as i32).to_ne_bytes().to_vec()),
TCP_CORK => Ok((options.tcp_cork as i32).to_ne_bytes().to_vec()),
ALLOW_ZMTP2 => Ok((options.allow_zmtp2 as i32).to_ne_bytes().to_vec()),
ZAP_DOMAIN => options.zap_domain.as_ref().map(|s| s.as_bytes().to_vec()).ok_or(ZmqError::Internal("Option ZAP_DOMAIN not set".into())),
#[cfg(feature = "plain")]
PLAIN_SERVER => options.plain_options.server_role.map(|b| (b as i32).to_ne_bytes().to_vec()).ok_or(ZmqError::Internal("Option PLAIN_SERVER not set".into())),
#[cfg(feature = "plain")]
PLAIN_USERNAME => options.plain_options.username.as_ref().map(|s| s.as_bytes().to_vec()).ok_or(ZmqError::Internal("Option PLAIN_USERNAME not set".into())),
#[cfg(feature = "plain")]
PLAIN_PASSWORD => Err(ZmqError::PermissionDenied("PLAIN_PASSWORD is write-only".into())),
#[cfg(feature = "noise_xx")]
NOISE_XX_ENABLED => Ok((options.noise_xx_options.enabled as i32).to_ne_bytes().to_vec()),
#[cfg(feature = "noise_xx")]
NOISE_XX_STATIC_SECRET_KEY => Err(ZmqError::PermissionDenied("NOISE_XX_STATIC_SECRET_KEY is write-only".into())),
#[cfg(feature = "noise_xx")]
NOISE_XX_REMOTE_STATIC_PUBLIC_KEY => options.noise_xx_options.remote_static_public_key_bytes.map(|k| k.to_vec()).ok_or(ZmqError::Internal("Option NOISE_XX_REMOTE_STATIC_PUBLIC_KEY not set".into())),
#[cfg(feature = "io-uring")]
IO_URING_SESSION_ENABLED => Ok((options.io_uring.session_enabled as i32).to_ne_bytes().to_vec()),
#[cfg(feature = "io-uring")]
IO_URING_SNDZEROCOPY => Ok((options.io_uring.send_zerocopy as i32).to_ne_bytes().to_vec()),
#[cfg(feature = "io-uring")]
IO_URING_RCVMULTISHOT => Ok((options.io_uring.recv_multishot as i32).to_ne_bytes().to_vec()),
#[cfg(feature = "io-uring")]
IO_URING_ZC_SEND_THRESHOLD => Ok((options.io_uring.zc_send_threshold as i32).to_ne_bytes().to_vec()),
ADAPTIVE_THROTTLE => Ok((options.throttle_config.enabled as i32).to_ne_bytes().to_vec()),
SNDBATCH_COUNT => Ok((options.sndbatch_count as i32).to_ne_bytes().to_vec()),
SNDBATCH_BYTES => Ok((options.sndbatch_bytes as i32).to_ne_bytes().to_vec()),
RCVBATCH_COUNT => Ok((options.rcvbatch_count as i32).to_ne_bytes().to_vec()),
RCVBATCH_BYTES => Ok((options.rcvbatch_bytes as i32).to_ne_bytes().to_vec()),
16 => Ok((core_s_reader.socket_type as i32).to_ne_bytes().to_vec()),
SUBSCRIBE | UNSUBSCRIBE | ROUTER_MANDATORY | AUTO_DELIMITER => Err(ZmqError::UnsupportedOption(option_id)),
_ => Err(ZmqError::InvalidOption(option_id)),
}
}
pub fn calculate_required_slot_size(target_payload_bytes: usize, max_batch_count: usize) -> usize {
let max_long_frames = std::cmp::min(max_batch_count, target_payload_bytes / 256);
let long_frame_overhead = max_long_frames * 9;
let short_frame_overhead = max_batch_count.saturating_sub(max_long_frames) * 2;
let raw_physical_size = target_payload_bytes + long_frame_overhead + short_frame_overhead;
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
((raw_physical_size + page_size - 1) / page_size) * page_size
}
#[cfg(test)]
mod allow_zmtp2_tests {
use super::*;
#[test]
fn test_allow_zmtp2_default_true() {
assert!(SocketOptions::default().allow_zmtp2);
assert!(ZmtpEngineConfig::default().allow_zmtp2);
}
#[test]
fn test_allow_zmtp2_apply_and_map_through() {
let mut opts = SocketOptions::default();
apply_core_option_value(&mut opts, ALLOW_ZMTP2, &0i32.to_ne_bytes()).unwrap();
assert!(!opts.allow_zmtp2);
let cfg = ZmtpEngineConfig::from(&opts);
assert!(!cfg.allow_zmtp2, "From<&SocketOptions> must carry allow_zmtp2 through");
apply_core_option_value(&mut opts, ALLOW_ZMTP2, &1i32.to_ne_bytes()).unwrap();
assert!(opts.allow_zmtp2);
assert!(ZmtpEngineConfig::from(&opts).allow_zmtp2);
}
}