use crate::codes::{LOGIN_REGISTER_CODE, LOGIN_REGISTER_WITH_PAT_CODE, LOGOUT_USER_CODE};
use crate::consensus::{Command, HEADER_SIZE, Operation, RequestHeader};
use crate::error::WireError;
use std::ops::Range;
use twox_hash::XxHash3_64;
pub const NON_REPLICATED_CODE_RANGE: Range<usize> = 0..4;
#[must_use]
pub fn operation_for_code(code: u32) -> Operation {
match code {
LOGIN_REGISTER_CODE | LOGIN_REGISTER_WITH_PAT_CODE => Operation::Register,
LOGOUT_USER_CODE => Operation::Logout,
_ => Operation::from_command_code(code).unwrap_or(Operation::NonReplicated),
}
}
impl RequestHeader {
pub fn for_request(
code: u32,
client: u128,
request: u64,
session: u64,
payload: &[u8],
) -> Result<Self, WireError> {
let operation = operation_for_code(code);
let total_size = HEADER_SIZE.saturating_add(payload.len());
let size = u32::try_from(total_size).map_err(|_| WireError::PayloadTooLarge {
size: payload.len(),
max: u32::MAX as usize - HEADER_SIZE,
})?;
let request_checksum = if operation.is_partition() || operation == Operation::NonReplicated
{
0
} else {
u128::from(XxHash3_64::oneshot(payload))
};
let mut reserved = [0; 60];
if operation == Operation::NonReplicated {
reserved[NON_REPLICATED_CODE_RANGE].copy_from_slice(&code.to_le_bytes());
}
Ok(Self {
command: Command::Request,
operation,
size,
client,
request,
session,
request_checksum,
timestamp: 0,
reserved,
..Default::default()
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codes::{CREATE_STREAM_CODE, PING_CODE, SEND_MESSAGES_CODE};
#[test]
fn login_codes_register_and_logout_logs_out() {
assert_eq!(operation_for_code(LOGIN_REGISTER_CODE), Operation::Register);
assert_eq!(
operation_for_code(LOGIN_REGISTER_WITH_PAT_CODE),
Operation::Register
);
assert_eq!(operation_for_code(LOGOUT_USER_CODE), Operation::Logout);
assert_eq!(operation_for_code(u32::MAX), Operation::NonReplicated);
}
#[test]
fn metadata_requests_are_stamped_with_the_payload_hash() {
let payload = [1, 2, 3];
let header = RequestHeader::for_request(CREATE_STREAM_CODE, 7, 8, 9, &payload).unwrap();
assert_eq!(header.command, Command::Request);
assert_eq!(header.size as usize, HEADER_SIZE + payload.len());
assert_eq!((header.client, header.request, header.session), (7, 8, 9));
assert_eq!(
header.request_checksum,
u128::from(XxHash3_64::oneshot(&payload))
);
assert_eq!(header.reserved, [0; 60]);
}
#[test]
fn partition_and_non_replicated_requests_leave_the_stamp_zero() {
let send = RequestHeader::for_request(SEND_MESSAGES_CODE, 1, 2, 3, &[9; 16]).unwrap();
assert_eq!(send.request_checksum, 0);
assert!(send.operation.is_partition());
let ping = RequestHeader::for_request(PING_CODE, 1, 2, 3, &[]).unwrap();
assert_eq!(ping.operation, Operation::NonReplicated);
assert_eq!(ping.request_checksum, 0);
assert_eq!(
ping.reserved[NON_REPLICATED_CODE_RANGE],
PING_CODE.to_le_bytes()
);
}
}