use std::{
future::Future,
io,
net::SocketAddr,
num::NonZeroUsize,
pin::Pin,
sync::{Arc, RwLock, RwLockReadGuard, TryLockError, atomic::AtomicBool},
task::{Context, Poll},
};
use atomic_waker::AtomicWaker;
use noq_udp::Transmit;
use tokio::io::Interest;
use tracing::{debug, trace, warn};
use super::IpFamily;
#[derive(Debug)]
pub struct UdpSocket {
socket: RwLock<SocketState>,
recv_waker: AtomicWaker,
send_waker: AtomicWaker,
is_broken: AtomicBool,
}
const SOCKET_BUFFER_SIZE: usize = 7 << 20;
impl UdpSocket {
pub fn bind_v4(port: u16) -> io::Result<Self> {
Self::bind(IpFamily::V4, port)
}
pub fn bind_v6(port: u16) -> io::Result<Self> {
Self::bind(IpFamily::V6, port)
}
pub fn bind_local_v4(port: u16) -> io::Result<Self> {
Self::bind_local(IpFamily::V4, port)
}
pub fn bind_local_v6(port: u16) -> io::Result<Self> {
Self::bind_local(IpFamily::V6, port)
}
pub fn bind_local(network: IpFamily, port: u16) -> io::Result<Self> {
let addr = SocketAddr::new(network.local_addr(), port);
Self::bind_raw(addr)
}
pub fn bind(network: IpFamily, port: u16) -> io::Result<Self> {
let addr = SocketAddr::new(network.unspecified_addr(), port);
Self::bind_raw(addr)
}
pub fn bind_full(addr: impl Into<SocketAddr>) -> io::Result<Self> {
Self::bind_raw(addr)
}
pub fn is_broken(&self) -> bool {
self.is_broken.load(std::sync::atomic::Ordering::Acquire)
}
fn mark_broken(&self) {
self.is_broken
.store(true, std::sync::atomic::Ordering::Release);
}
pub fn rebind(&self) -> io::Result<()> {
{
let mut guard = self.socket.write().unwrap();
guard.rebind()?;
self.is_broken
.store(false, std::sync::atomic::Ordering::Release);
drop(guard);
}
self.wake_all();
Ok(())
}
fn bind_raw(addr: impl Into<SocketAddr>) -> io::Result<Self> {
let socket = SocketState::bind(addr.into())?;
Ok(UdpSocket {
socket: RwLock::new(socket),
recv_waker: AtomicWaker::default(),
send_waker: AtomicWaker::default(),
is_broken: AtomicBool::new(false),
})
}
pub fn recv<'a, 'b>(&'b self, buffer: &'a mut [u8]) -> RecvFut<'a, 'b> {
RecvFut {
socket: self,
buffer,
}
}
pub fn recv_from<'a, 'b>(&'b self, buffer: &'a mut [u8]) -> RecvFromFut<'a, 'b> {
RecvFromFut {
socket: self,
buffer,
}
}
pub fn send<'a, 'b>(&'b self, buffer: &'a [u8]) -> SendFut<'a, 'b> {
SendFut {
socket: self,
buffer,
}
}
pub fn send_to<'a, 'b>(&'b self, buffer: &'a [u8], to: SocketAddr) -> SendToFut<'a, 'b> {
SendToFut {
socket: self,
buffer,
to,
}
}
pub fn connect(&self, addr: SocketAddr) -> io::Result<()> {
trace!(%addr, "connecting");
let guard = self.socket.read().unwrap();
let (socket_tokio, _state) = guard.try_get_connected()?;
let sock_ref = socket2::SockRef::from(&socket_tokio);
sock_ref.connect(&socket2::SockAddr::from(addr))?;
Ok(())
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
let guard = self.socket.read().unwrap();
let (socket, _state) = guard.try_get_connected()?;
socket.local_addr()
}
pub async fn close(&self) {
let socket = self.socket.write().unwrap().close();
self.wake_all();
if let Some((sock, _)) = socket {
let std_sock = sock.into_std();
let res = tokio::runtime::Handle::current()
.spawn_blocking(move || {
drop(std_sock);
})
.await;
if let Err(err) = res {
warn!("failed to close socket: {:?}", err);
}
}
}
pub fn is_closed(&self) -> bool {
self.socket.read().unwrap().is_closed()
}
fn handle_read_error(&self, error: io::Error) -> Option<io::Error> {
match error.kind() {
io::ErrorKind::NotConnected => {
self.mark_broken();
None
}
_ if is_transient_read_error(&error) => None,
_ => Some(error),
}
}
fn handle_write_error(&self, error: io::Error) -> Option<io::Error> {
match error.kind() {
io::ErrorKind::BrokenPipe => {
self.mark_broken();
None
}
_ => Some(error),
}
}
fn poll_read_socket(
&self,
waker: &AtomicWaker,
cx: &mut std::task::Context<'_>,
) -> Poll<RwLockReadGuard<'_, SocketState>> {
let guard = match self.socket.try_read() {
Ok(guard) => guard,
Err(TryLockError::Poisoned(e)) => panic!("socket lock poisoned: {e}"),
Err(TryLockError::WouldBlock) => {
waker.register(cx.waker());
match self.socket.try_read() {
Ok(guard) => {
waker.take();
guard
}
Err(TryLockError::Poisoned(e)) => panic!("socket lock poisoned: {e}"),
Err(TryLockError::WouldBlock) => {
return Poll::Pending;
}
}
}
};
Poll::Ready(guard)
}
fn wake_all(&self) {
self.recv_waker.wake();
self.send_waker.wake();
}
fn maybe_rebind(&self) -> io::Result<()> {
if !self.is_broken() {
return Ok(());
}
let mut guard = self.socket.write().unwrap_or_else(|e| e.into_inner());
if !self.is_broken() {
return Ok(());
}
guard.rebind()?;
self.is_broken
.store(false, std::sync::atomic::Ordering::Release);
drop(guard);
self.wake_all();
Ok(())
}
pub fn poll_writable(&self, cx: &mut std::task::Context<'_>) -> Poll<io::Result<()>> {
loop {
if let Err(err) = self.maybe_rebind() {
return Poll::Ready(Err(err));
}
let guard = std::task::ready!(self.poll_read_socket(&self.send_waker, cx));
let (socket, _state) = guard.try_get_connected()?;
match socket.poll_send_ready(cx) {
Poll::Pending => {
self.send_waker.register(cx.waker());
return Poll::Pending;
}
Poll::Ready(Ok(())) => return Poll::Ready(Ok(())),
Poll::Ready(Err(err)) => {
if let Some(err) = self.handle_write_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
}
}
}
pub fn try_send_noq(&self, transmit: &Transmit<'_>) -> io::Result<()> {
loop {
self.maybe_rebind()?;
let guard = match self.socket.try_read() {
Ok(guard) => guard,
Err(TryLockError::Poisoned(e)) => {
panic!("lock poisoned: {e:?}");
}
Err(TryLockError::WouldBlock) => {
return Err(io::Error::new(io::ErrorKind::WouldBlock, "locked"));
}
};
let (socket, state) = guard.try_get_connected()?;
let res = socket.try_io(Interest::WRITABLE, || state.send(socket.into(), transmit));
match res {
Ok(()) => return Ok(()),
Err(err) => match self.handle_write_error(err) {
Some(err) => return Err(err),
None => {
continue;
}
},
}
}
}
pub fn poll_send_noq(&self, cx: &mut Context, transmit: &Transmit<'_>) -> Poll<io::Result<()>> {
loop {
if let Err(err) = self.maybe_rebind() {
return Poll::Ready(Err(err));
}
let guard = n0_future::ready!(self.poll_read_socket(&self.send_waker, cx));
let (socket, state) = guard.try_get_connected()?;
match socket.poll_send_ready(cx) {
Poll::Pending => {
self.send_waker.register(cx.waker());
return Poll::Pending;
}
Poll::Ready(Ok(())) => {
let res =
socket.try_io(Interest::WRITABLE, || state.send(socket.into(), transmit));
if let Err(err) = res {
if err.kind() == io::ErrorKind::WouldBlock {
continue;
}
if let Some(err) = self.handle_write_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
return Poll::Ready(res);
}
Poll::Ready(Err(err)) => {
if let Some(err) = self.handle_write_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
}
}
}
pub fn poll_recv_noq(
&self,
cx: &mut Context,
bufs: &mut [io::IoSliceMut<'_>],
meta: &mut [noq_udp::RecvMeta],
) -> Poll<io::Result<usize>> {
loop {
if let Err(err) = self.maybe_rebind() {
return Poll::Ready(Err(err));
}
let guard = n0_future::ready!(self.poll_read_socket(&self.recv_waker, cx));
let (socket, state) = guard.try_get_connected()?;
match socket.poll_recv_ready(cx) {
Poll::Pending => {
self.recv_waker.register(cx.waker());
return Poll::Pending;
}
Poll::Ready(Ok(())) => {
}
Poll::Ready(Err(err)) => match self.handle_read_error(err) {
Some(err) => return Poll::Ready(Err(err)),
None => {
continue;
}
},
}
let res = socket.try_io(Interest::READABLE, || state.recv(socket.into(), bufs, meta));
match res {
Ok(count) => {
for meta in meta.iter().take(count) {
trace!(
src = %meta.addr,
len = meta.len,
count = meta.len.checked_div(meta.stride).unwrap_or(0),
dst = %meta.dst_ip.map(|x| x.to_string()).unwrap_or_default(),
"UDP recv"
);
}
return Poll::Ready(Ok(count));
}
Err(err) => {
if err.kind() == io::ErrorKind::WouldBlock {
continue;
}
match self.handle_read_error(err) {
Some(err) => return Poll::Ready(Err(err)),
None => {
continue;
}
}
}
}
}
}
pub fn create_sender(self: Arc<Self>) -> UdpSender {
UdpSender::new(self)
}
pub fn may_fragment(&self) -> bool {
let guard = self.socket.read().unwrap();
guard.may_fragment()
}
pub fn max_gso_segments(&self) -> NonZeroUsize {
let guard = self.socket.read().unwrap();
guard.max_gso_segments()
}
pub fn gro_segments(&self) -> NonZeroUsize {
let guard = self.socket.read().unwrap();
guard.gro_segments()
}
}
#[cfg(windows)]
const WSAENETRESET: i32 = 10052;
fn is_transient_read_error(error: &io::Error) -> bool {
if error.kind() == io::ErrorKind::ConnectionReset {
return true;
}
#[cfg(windows)]
if error.raw_os_error() == Some(WSAENETRESET) {
return true;
}
false
}
#[derive(Debug)]
pub struct RecvFut<'a, 'b> {
socket: &'b UdpSocket,
buffer: &'a mut [u8],
}
impl Future for RecvFut<'_, '_> {
type Output = io::Result<usize>;
fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
let Self { socket, buffer } = &mut *self;
loop {
if let Err(err) = socket.maybe_rebind() {
return Poll::Ready(Err(err));
}
let guard = n0_future::ready!(socket.poll_read_socket(&socket.recv_waker, cx));
let (inner_socket, _state) = guard.try_get_connected()?;
match inner_socket.poll_recv_ready(cx) {
Poll::Pending => {
self.socket.recv_waker.register(cx.waker());
return Poll::Pending;
}
Poll::Ready(Ok(())) => {
let res = inner_socket.try_recv(buffer);
if let Err(err) = res {
if err.kind() == io::ErrorKind::WouldBlock {
continue;
}
if let Some(err) = socket.handle_read_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
return Poll::Ready(res);
}
Poll::Ready(Err(err)) => {
if let Some(err) = socket.handle_read_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
}
}
}
}
#[derive(Debug)]
pub struct RecvFromFut<'a, 'b> {
socket: &'b UdpSocket,
buffer: &'a mut [u8],
}
impl Future for RecvFromFut<'_, '_> {
type Output = io::Result<(usize, SocketAddr)>;
fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
let Self { socket, buffer } = &mut *self;
loop {
if let Err(err) = socket.maybe_rebind() {
return Poll::Ready(Err(err));
}
let guard = n0_future::ready!(socket.poll_read_socket(&socket.recv_waker, cx));
let (inner_socket, _state) = guard.try_get_connected()?;
match inner_socket.poll_recv_ready(cx) {
Poll::Pending => {
self.socket.recv_waker.register(cx.waker());
return Poll::Pending;
}
Poll::Ready(Ok(())) => {
let res = inner_socket.try_recv_from(buffer);
if let Err(err) = res {
if err.kind() == io::ErrorKind::WouldBlock {
continue;
}
if let Some(err) = socket.handle_read_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
return Poll::Ready(res);
}
Poll::Ready(Err(err)) => {
if let Some(err) = socket.handle_read_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
}
}
}
}
#[derive(Debug)]
pub struct SendFut<'a, 'b> {
socket: &'b UdpSocket,
buffer: &'a [u8],
}
impl Future for SendFut<'_, '_> {
type Output = io::Result<usize>;
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
loop {
if let Err(err) = self.socket.maybe_rebind() {
return Poll::Ready(Err(err));
}
let guard =
n0_future::ready!(self.socket.poll_read_socket(&self.socket.send_waker, cx));
let (socket, _state) = guard.try_get_connected()?;
match socket.poll_send_ready(cx) {
Poll::Pending => {
self.socket.send_waker.register(cx.waker());
return Poll::Pending;
}
Poll::Ready(Ok(())) => {
let res = socket.try_send(self.buffer);
if let Err(err) = res {
if err.kind() == io::ErrorKind::WouldBlock {
continue;
}
if let Some(err) = self.socket.handle_write_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
return Poll::Ready(res);
}
Poll::Ready(Err(err)) => {
if let Some(err) = self.socket.handle_write_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
}
}
}
}
#[derive(Debug)]
pub struct SendToFut<'a, 'b> {
socket: &'b UdpSocket,
buffer: &'a [u8],
to: SocketAddr,
}
impl Future for SendToFut<'_, '_> {
type Output = io::Result<usize>;
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
loop {
if let Err(err) = self.socket.maybe_rebind() {
return Poll::Ready(Err(err));
}
let guard =
n0_future::ready!(self.socket.poll_read_socket(&self.socket.send_waker, cx));
let (socket, _state) = guard.try_get_connected()?;
match socket.poll_send_ready(cx) {
Poll::Pending => {
self.socket.send_waker.register(cx.waker());
return Poll::Pending;
}
Poll::Ready(Ok(())) => {
let res = socket.try_send_to(self.buffer, self.to);
if let Err(err) = res {
if err.kind() == io::ErrorKind::WouldBlock {
continue;
}
if let Some(err) = self.socket.handle_write_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
return Poll::Ready(res);
}
Poll::Ready(Err(err)) => {
if let Some(err) = self.socket.handle_write_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
}
}
}
}
#[derive(Debug)]
enum SocketState {
Connected {
socket: tokio::net::UdpSocket,
state: noq_udp::UdpSocketState,
addr: SocketAddr,
},
Closed {
addr: SocketAddr,
last_max_gso_segments: NonZeroUsize,
last_gro_segments: NonZeroUsize,
last_may_fragment: bool,
},
}
impl SocketState {
fn try_get_connected(&self) -> io::Result<(&tokio::net::UdpSocket, &noq_udp::UdpSocketState)> {
match self {
Self::Connected {
socket,
state,
addr: _,
} => Ok((socket, state)),
Self::Closed { .. } => {
warn!("socket closed");
Err(io::Error::new(io::ErrorKind::BrokenPipe, "socket closed"))
}
}
}
fn bind(addr: SocketAddr) -> io::Result<Self> {
let network = IpFamily::from(addr.ip());
let socket = socket2::Socket::new(
network.into(),
socket2::Type::DGRAM,
Some(socket2::Protocol::UDP),
)?;
if let Err(err) = socket.set_recv_buffer_size(SOCKET_BUFFER_SIZE) {
debug!(
"failed to set recv_buffer_size to {}: {:?}",
SOCKET_BUFFER_SIZE, err
);
}
if let Err(err) = socket.set_send_buffer_size(SOCKET_BUFFER_SIZE) {
debug!(
"failed to set send_buffer_size to {}: {:?}",
SOCKET_BUFFER_SIZE, err
);
}
if network == IpFamily::V6 {
socket.set_only_v6(true)?;
}
socket.bind(&addr.into())?;
socket.set_nonblocking(true)?;
let socket: std::net::UdpSocket = socket.into();
let socket = tokio::net::UdpSocket::from_std(socket)?;
let socket_ref = noq_udp::UdpSockRef::from(&socket);
let socket_state = noq_udp::UdpSocketState::new(socket_ref)?;
let local_addr = socket.local_addr()?;
if addr.port() != 0 && local_addr.port() != addr.port() {
return Err(io::Error::other(format!(
"wrong port bound: {:?}: wanted: {} got {}",
network,
addr.port(),
local_addr.port(),
)));
}
Ok(Self::Connected {
socket,
state: socket_state,
addr: local_addr,
})
}
fn rebind(&mut self) -> io::Result<()> {
let addr = match self {
Self::Connected { addr, .. } => *addr,
Self::Closed { addr, .. } => *addr,
};
debug!("rebinding {}", addr);
if let Self::Connected { state, .. } = self {
*self = SocketState::Closed {
addr,
last_max_gso_segments: state.max_gso_segments(),
last_gro_segments: state.gro_segments(),
last_may_fragment: state.may_fragment(),
};
}
match Self::bind(addr) {
Ok(new_state) => {
*self = new_state;
Ok(())
}
Err(err) => {
debug!("rebind failed, will retry on next attempt: {}", err);
Err(err)
}
}
}
fn is_closed(&self) -> bool {
matches!(self, Self::Closed { .. })
}
fn close(&mut self) -> Option<(tokio::net::UdpSocket, noq_udp::UdpSocketState)> {
match self {
Self::Connected { state, addr, .. } => {
let s = SocketState::Closed {
addr: *addr,
last_max_gso_segments: state.max_gso_segments(),
last_gro_segments: state.gro_segments(),
last_may_fragment: state.may_fragment(),
};
let Self::Connected { socket, state, .. } = std::mem::replace(self, s) else {
unreachable!("just checked");
};
Some((socket, state))
}
Self::Closed { .. } => None,
}
}
fn may_fragment(&self) -> bool {
match self {
Self::Connected { state, .. } => state.may_fragment(),
Self::Closed {
last_may_fragment, ..
} => *last_may_fragment,
}
}
fn max_gso_segments(&self) -> NonZeroUsize {
match self {
Self::Connected { state, .. } => state.max_gso_segments(),
Self::Closed {
last_max_gso_segments,
..
} => *last_max_gso_segments,
}
}
fn gro_segments(&self) -> NonZeroUsize {
match self {
Self::Connected { state, .. } => state.gro_segments(),
Self::Closed {
last_gro_segments, ..
} => *last_gro_segments,
}
}
}
impl Drop for UdpSocket {
fn drop(&mut self) {
if let Some((socket, _)) = self.socket.write().unwrap().close()
&& let Ok(handle) = tokio::runtime::Handle::try_current()
{
let std_sock = socket.into_std();
handle.spawn_blocking(move || {
drop(std_sock);
});
}
}
}
pin_project_lite::pin_project! {
pub struct UdpSender {
socket: Arc<UdpSocket>,
#[pin]
fut: Option<Pin<Box<dyn Future<Output = io::Result<()>> + Send + Sync + 'static>>>,
}
}
impl Clone for UdpSender {
fn clone(&self) -> Self {
self.socket.clone().create_sender()
}
}
impl std::fmt::Debug for UdpSender {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("UdpSender")
}
}
impl UdpSender {
fn new(socket: Arc<UdpSocket>) -> Self {
Self { socket, fut: None }
}
pub fn send<'a, 'b>(&self, transmit: &'a noq_udp::Transmit<'b>) -> SendFutNoq<'a, 'b> {
SendFutNoq {
socket: self.socket.clone(),
transmit,
}
}
pub fn poll_send(
self: Pin<&mut Self>,
transmit: &noq_udp::Transmit,
cx: &mut Context,
) -> Poll<io::Result<()>> {
let mut this = self.project();
loop {
if let Err(err) = this.socket.maybe_rebind() {
return Poll::Ready(Err(err));
}
let guard =
n0_future::ready!(this.socket.poll_read_socket(&this.socket.send_waker, cx));
if this.fut.is_none() {
let socket = this.socket.clone();
this.fut.set(Some(Box::pin(async move {
n0_future::future::poll_fn(|cx| socket.poll_writable(cx)).await
})));
}
let result = n0_future::ready!(this.fut.as_mut().as_pin_mut().unwrap().poll(cx));
this.fut.set(None);
result?;
let (socket, state) = guard.try_get_connected()?;
let result = socket.try_io(Interest::WRITABLE, || state.send(socket.into(), transmit));
match result {
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue,
_ => return Poll::Ready(result),
}
}
}
pub fn try_send(&self, transmit: &noq_udp::Transmit) -> io::Result<()> {
self.socket.maybe_rebind()?;
match self.socket.socket.try_read() {
Ok(guard) => {
let (socket, state) = guard.try_get_connected()?;
socket.try_io(Interest::WRITABLE, || state.send(socket.into(), transmit))
}
Err(TryLockError::Poisoned(e)) => panic!("socket lock poisoned: {e}"),
Err(TryLockError::WouldBlock) => {
Err(io::Error::new(io::ErrorKind::WouldBlock, "locked"))
}
}
}
}
#[derive(Debug)]
pub struct SendFutNoq<'a, 'b> {
socket: Arc<UdpSocket>,
transmit: &'a noq_udp::Transmit<'b>,
}
impl Future for SendFutNoq<'_, '_> {
type Output = io::Result<()>;
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
loop {
if let Err(err) = self.socket.maybe_rebind() {
return Poll::Ready(Err(err));
}
let guard =
n0_future::ready!(self.socket.poll_read_socket(&self.socket.send_waker, cx));
let (socket, state) = guard.try_get_connected()?;
match socket.poll_send_ready(cx) {
Poll::Pending => {
self.socket.send_waker.register(cx.waker());
return Poll::Pending;
}
Poll::Ready(Ok(())) => {
let res = socket.try_io(Interest::WRITABLE, || {
state.send(socket.into(), self.transmit)
});
if let Err(err) = res {
if err.kind() == io::ErrorKind::WouldBlock {
continue;
}
if let Some(err) = self.socket.handle_write_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
return Poll::Ready(res);
}
Poll::Ready(Err(err)) => {
if let Some(err) = self.socket.handle_write_error(err) {
return Poll::Ready(Err(err));
}
continue;
}
}
}
}
}
#[cfg(test)]
mod tests {
use testresult::TestResult;
use super::*;
#[tokio::test]
async fn test_reconnect() -> TestResult {
let (s_b, mut r_b) = tokio::sync::mpsc::channel(16);
let handle_a = tokio::task::spawn(async move {
let socket = UdpSocket::bind_local(IpFamily::V4, 0)?;
let addr = socket.local_addr()?;
s_b.send(addr).await?;
println!("socket bound to {addr:?}");
let mut buffer = [0u8; 16];
for i in 0..100 {
println!("-- tick {i}");
let read = socket.recv_from(&mut buffer).await;
match read {
Ok((count, addr)) => {
println!("got {:?}", &buffer[..count]);
println!("sending {:?} to {:?}", &buffer[..count], addr);
socket.send_to(&buffer[..count], addr).await?;
}
Err(err) => {
eprintln!("error reading: {err:?}");
}
}
}
socket.close().await;
Ok::<_, testresult::TestError>(())
});
let socket = UdpSocket::bind_local(IpFamily::V4, 0)?;
let first_addr = socket.local_addr()?;
println!("socket2 bound to {:?}", socket.local_addr()?);
let addr = r_b.recv().await.unwrap();
let mut buffer = [0u8; 16];
for i in 0u8..100 {
println!("round one - {i}");
socket.send_to(&[i][..], addr).await?;
let (count, from) = socket.recv_from(&mut buffer).await?;
assert_eq!(addr, from);
assert_eq!(count, 1);
assert_eq!(buffer[0], i);
assert!(!socket.is_broken());
socket.rebind()?;
assert_eq!(socket.local_addr()?, first_addr);
}
handle_a.await.ok();
Ok(())
}
#[tokio::test]
async fn test_udp_mark_broken() -> TestResult {
let socket_a = UdpSocket::bind_local(IpFamily::V4, 0)?;
let addr_a = socket_a.local_addr()?;
println!("socket bound to {addr_a:?}");
let socket_b = UdpSocket::bind_local(IpFamily::V4, 0)?;
let addr_b = socket_b.local_addr()?;
println!("socket bound to {addr_b:?}");
let handle = tokio::task::spawn(async move {
let mut buffer = [0u8; 16];
for _ in 0..2 {
match socket_b.recv_from(&mut buffer).await {
Ok((count, addr)) => {
println!("got {:?} from {:?}", &buffer[..count], addr);
}
Err(err) => {
eprintln!("error recv: {err:?}");
}
}
}
});
socket_a.send_to(&[0][..], addr_b).await?;
socket_a.mark_broken();
assert!(socket_a.is_broken());
socket_a.send_to(&[0][..], addr_b).await?;
assert!(!socket_a.is_broken());
handle.await?;
Ok(())
}
#[tokio::test]
async fn test_recv_survives_icmp_unreachable_from_prior_send() -> TestResult {
use std::time::Duration;
let receiver = UdpSocket::bind_local(IpFamily::V4, 0)?;
let receiver_addr = receiver.local_addr()?;
let sender = UdpSocket::bind_local(IpFamily::V4, 0)?;
let sender_addr = sender.local_addr()?;
let closed_addr = {
let tmp = UdpSocket::bind_local(IpFamily::V4, 0)?;
let addr = tmp.local_addr()?;
tmp.close().await;
addr
};
receiver.send_to(b"void", closed_addr).await?;
tokio::time::sleep(Duration::from_millis(100)).await;
sender.send_to(b"hello", receiver_addr).await?;
let mut buf = [0u8; 16];
let (n, from) = tokio::time::timeout(Duration::from_secs(5), receiver.recv_from(&mut buf))
.await
.expect("recv must not hang")?;
assert_eq!(&buf[..n], b"hello");
assert_eq!(from, sender_addr);
Ok(())
}
}