use bytes::{Buf, BufMut, Bytes, BytesMut};
use super::types::{PKT_LINE_END_MARKER, TransportProtocol};
pub fn read_pkt_line(bytes: &mut Bytes) -> (usize, Bytes) {
if bytes.is_empty() {
return (0, Bytes::new());
}
if bytes.len() < 4 {
return (0, Bytes::new());
}
let pkt_length = bytes.slice(0..4);
let pkt_length_str = match core::str::from_utf8(&pkt_length) {
Ok(s) => s,
Err(_) => {
tracing::warn!("Invalid UTF-8 in packet length: {:?}", pkt_length);
return (0, Bytes::new());
}
};
let pkt_length = match usize::from_str_radix(pkt_length_str, 16) {
Ok(len) => len,
Err(_) => {
tracing::warn!("Invalid hex packet length: {:?}", pkt_length_str);
return (0, Bytes::new());
}
};
if pkt_length == 0 {
bytes.advance(4);
return (4, Bytes::new()); }
if pkt_length < 4 {
tracing::warn!("Invalid packet length: {} (must be >= 4)", pkt_length);
return (0, Bytes::new());
}
if bytes.len() < pkt_length {
tracing::warn!(
"Insufficient data: need {} bytes, have {}",
pkt_length,
bytes.len()
);
return (0, Bytes::new());
}
bytes.advance(4);
let data_length = pkt_length - 4;
let pkt_line = bytes.copy_to_bytes(data_length);
tracing::debug!("pkt line: {:?}", pkt_line);
(pkt_length, pkt_line)
}
pub fn add_pkt_line_string(pkt_line_stream: &mut BytesMut, buf_str: String) {
let buf_str_length = buf_str.len() + 4;
pkt_line_stream.put(Bytes::from(format!("{buf_str_length:04x}")));
pkt_line_stream.put(buf_str.as_bytes());
}
pub fn read_until_white_space(bytes: &mut Bytes) -> String {
let mut buf = Vec::new();
while bytes.has_remaining() {
let c = bytes.get_u8();
if c.is_ascii_whitespace() || c == 0 {
break;
}
buf.push(c);
}
match String::from_utf8(buf) {
Ok(s) => s,
Err(e) => {
tracing::warn!("Invalid UTF-8 in protocol data: {}", e);
String::new() }
}
}
pub fn build_smart_reply(
transport_protocol: TransportProtocol,
ref_list: &[String],
service: String,
) -> BytesMut {
let mut pkt_line_stream = BytesMut::new();
if transport_protocol == TransportProtocol::Http {
add_pkt_line_string(&mut pkt_line_stream, format!("# service={service}\n"));
pkt_line_stream.put(&PKT_LINE_END_MARKER[..]);
}
for ref_line in ref_list {
add_pkt_line_string(&mut pkt_line_stream, ref_line.to_string());
}
pkt_line_stream.put(&PKT_LINE_END_MARKER[..]);
pkt_line_stream
}
pub fn search_subsequence(haystack: &[u8], needle: &[u8]) -> Option<usize> {
haystack
.windows(needle.len())
.position(|window| window == needle)
}
#[cfg(test)]
mod tests {
use bytes::Bytes;
use super::*;
#[test]
fn read_pkt_line_incomplete_does_not_consume() {
let mut buf = Bytes::from_static(b"0009do");
let before = buf.len();
let (len, data) = read_pkt_line(&mut buf);
assert_eq!(len, 0);
assert!(data.is_empty());
assert_eq!(buf.len(), before);
}
}