use byteorder::{BigEndian, ReadBytesExt};
use rama_core::bytes::BufMut;
use rama_net::address::HostWithPort;
use std::io::Read;
use tokio::io::{AsyncRead, AsyncReadExt};
use crate::proto::common::write_authority_to_buf;
use super::{
ProtocolError,
common::{authority_length, read_authority, read_authority_sync},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UdpHeader {
pub fragment_number: u8,
pub destination: HostWithPort,
}
impl UdpHeader {
pub async fn read_from<R>(r: &mut R) -> Result<Self, ProtocolError>
where
R: AsyncRead + Unpin,
{
let _rsv = r.read_u16().await?;
let fragment_number = r.read_u8().await?;
let destination = read_authority(r).await?;
Ok(Self {
fragment_number,
destination,
})
}
pub fn read_from_sync<R>(r: &mut R) -> Result<Self, ProtocolError>
where
R: Read,
{
let _rsv = r.read_u16::<BigEndian>()?;
let fragment_number = r.read_u8()?;
let destination = read_authority_sync(r)?;
Ok(Self {
fragment_number,
destination,
})
}
pub fn write_to_buf<B: BufMut>(&self, buf: &mut B) -> Result<(), std::io::Error> {
buf.put_u16(0 );
buf.put_u8(self.fragment_number);
write_authority_to_buf(&self.destination, buf)
}
pub(crate) fn serialized_len(&self) -> usize {
4 + authority_length(&self.destination)
}
}
#[cfg(test)]
mod tests {
use rama_core::bytes::BytesMut;
use std::io::Write;
use tokio::io::{AsyncWrite, AsyncWriteExt};
use super::*;
use crate::proto::{test_write_read_eq, test_write_read_sync_eq};
impl UdpHeader {
pub async fn write_to<W>(&self, w: &mut W) -> Result<(), std::io::Error>
where
W: AsyncWrite + Unpin,
{
let mut buf = BytesMut::with_capacity(self.serialized_len());
self.write_to_buf(&mut buf)?;
w.write_all(&buf).await
}
pub fn write_to_sync<W>(&self, w: &mut W) -> Result<(), std::io::Error>
where
W: Write,
{
let mut buf = BytesMut::with_capacity(self.serialized_len());
self.write_to_buf(&mut buf)?;
w.write_all(&buf)
}
}
#[tokio::test]
async fn test_udp_packet_write_read_eq() {
test_write_read_eq!(
UdpHeader {
fragment_number: 2,
destination: HostWithPort::local_ipv6(45),
},
UdpHeader
);
}
#[test]
fn test_udp_packet_write_read_sync_eq() {
test_write_read_sync_eq!(
UdpHeader {
fragment_number: 2,
destination: HostWithPort::local_ipv6(45),
},
UdpHeader
);
}
#[test]
fn test_serialized_len_matches_write_to_buf() {
use rama_net::address::Host;
let cases: &[UdpHeader] = &[
UdpHeader {
fragment_number: 0,
destination: HostWithPort::local_ipv4(80),
},
UdpHeader {
fragment_number: 7,
destination: HostWithPort::local_ipv6(443),
},
UdpHeader {
fragment_number: 0,
destination: HostWithPort::new(Host::EXAMPLE_NAME, 8080),
},
];
for h in cases {
let mut buf = BytesMut::new();
h.write_to_buf(&mut buf).unwrap();
assert_eq!(
buf.len(),
h.serialized_len(),
"serialized_len() must equal actual bytes written by write_to_buf() for {h:?}",
);
}
}
}