Implement iface event observers and move `Pollee` to them
Finally, this commit implements an iface event observer trait for the
`ConnectingStream`, `ListenStream`, and `ConnectedStream` states in
`StreamSocket`, as well as the `BoundDatagram` state in
`DatagramSocket`. It also moves the `Pollee` from `AnyBoundSocket` to
these observer implementors.
What I have tried to do is minimize the semantic changes. Ideally, this
commit should be a pure refactor commit, meaning that even if the
sematics of the previous code is wrong, the sematics after this commit
should be wrong in the same way. Fixing the wrong sematics should be
done in a separate commit afterwards.
However, keeping exactly the same sematics for `ListenStream` is hard.
We used to maintain a `Pollee` for each `BacklogSocket`, but now we can
just maintain one `Pollee` for the whole `ListenStream`. However,
implementing the correct semantics looks much easier, so we just do it.
For `ConnectingStream`, it used to share the same `Pollee` logic with
`ConnectedStream` (because the `Pollee` was maintained by
`AnyBoundSocket`, which is used by both). Now we write the `Pollee`
logic separately for `ConnectingStream`, so we can just write a correct
one or try to reuse the logic in `ConnectingStream`. This commit does
the former.
There should be no semantic changes for `ConnectedStream` in
`StreamSocket` and `BoundDatagram` in `DatagramSocket`.
2023-11-19 15:54:58 +00:00
|
|
|
use crate::events::Observer;
|
2023-10-08 09:40:58 +00:00
|
|
|
use crate::prelude::*;
|
2023-05-31 02:47:52 +00:00
|
|
|
|
|
|
|
|
use super::Iface;
|
|
|
|
|
use super::{IpAddress, IpEndpoint};
|
|
|
|
|
|
|
|
|
|
pub type RawTcpSocket = smoltcp::socket::tcp::Socket<'static>;
|
|
|
|
|
pub type RawUdpSocket = smoltcp::socket::udp::Socket<'static>;
|
|
|
|
|
pub type RawSocketHandle = smoltcp::iface::SocketHandle;
|
|
|
|
|
|
|
|
|
|
pub struct AnyUnboundSocket {
|
|
|
|
|
socket_family: AnyRawSocket,
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-04 03:04:42 +00:00
|
|
|
#[allow(clippy::large_enum_variant)]
|
2023-05-31 02:47:52 +00:00
|
|
|
pub(super) enum AnyRawSocket {
|
|
|
|
|
Tcp(RawTcpSocket),
|
|
|
|
|
Udp(RawUdpSocket),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(super) enum SocketFamily {
|
|
|
|
|
Tcp,
|
|
|
|
|
Udp,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AnyUnboundSocket {
|
|
|
|
|
pub fn new_tcp() -> Self {
|
|
|
|
|
let raw_tcp_socket = {
|
|
|
|
|
let rx_buffer = smoltcp::socket::tcp::SocketBuffer::new(vec![0u8; RECV_BUF_LEN]);
|
|
|
|
|
let tx_buffer = smoltcp::socket::tcp::SocketBuffer::new(vec![0u8; SEND_BUF_LEN]);
|
|
|
|
|
RawTcpSocket::new(rx_buffer, tx_buffer)
|
|
|
|
|
};
|
|
|
|
|
AnyUnboundSocket {
|
|
|
|
|
socket_family: AnyRawSocket::Tcp(raw_tcp_socket),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn new_udp() -> Self {
|
|
|
|
|
let raw_udp_socket = {
|
|
|
|
|
let metadata = smoltcp::socket::udp::PacketMetadata::EMPTY;
|
|
|
|
|
let rx_buffer = smoltcp::socket::udp::PacketBuffer::new(
|
|
|
|
|
vec![metadata; UDP_METADATA_LEN],
|
|
|
|
|
vec![0u8; UDP_RECEIVE_PAYLOAD_LEN],
|
|
|
|
|
);
|
|
|
|
|
let tx_buffer = smoltcp::socket::udp::PacketBuffer::new(
|
|
|
|
|
vec![metadata; UDP_METADATA_LEN],
|
|
|
|
|
vec![0u8; UDP_RECEIVE_PAYLOAD_LEN],
|
|
|
|
|
);
|
|
|
|
|
RawUdpSocket::new(rx_buffer, tx_buffer)
|
|
|
|
|
};
|
|
|
|
|
AnyUnboundSocket {
|
|
|
|
|
socket_family: AnyRawSocket::Udp(raw_udp_socket),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(super) fn raw_socket_family(self) -> AnyRawSocket {
|
|
|
|
|
self.socket_family
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(super) fn socket_family(&self) -> SocketFamily {
|
|
|
|
|
match &self.socket_family {
|
|
|
|
|
AnyRawSocket::Tcp(_) => SocketFamily::Tcp,
|
|
|
|
|
AnyRawSocket::Udp(_) => SocketFamily::Udp,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct AnyBoundSocket {
|
|
|
|
|
iface: Arc<dyn Iface>,
|
|
|
|
|
handle: smoltcp::iface::SocketHandle,
|
|
|
|
|
port: u16,
|
|
|
|
|
socket_family: SocketFamily,
|
Implement iface event observers and move `Pollee` to them
Finally, this commit implements an iface event observer trait for the
`ConnectingStream`, `ListenStream`, and `ConnectedStream` states in
`StreamSocket`, as well as the `BoundDatagram` state in
`DatagramSocket`. It also moves the `Pollee` from `AnyBoundSocket` to
these observer implementors.
What I have tried to do is minimize the semantic changes. Ideally, this
commit should be a pure refactor commit, meaning that even if the
sematics of the previous code is wrong, the sematics after this commit
should be wrong in the same way. Fixing the wrong sematics should be
done in a separate commit afterwards.
However, keeping exactly the same sematics for `ListenStream` is hard.
We used to maintain a `Pollee` for each `BacklogSocket`, but now we can
just maintain one `Pollee` for the whole `ListenStream`. However,
implementing the correct semantics looks much easier, so we just do it.
For `ConnectingStream`, it used to share the same `Pollee` logic with
`ConnectedStream` (because the `Pollee` was maintained by
`AnyBoundSocket`, which is used by both). Now we write the `Pollee`
logic separately for `ConnectingStream`, so we can just write a correct
one or try to reuse the logic in `ConnectingStream`. This commit does
the former.
There should be no semantic changes for `ConnectedStream` in
`StreamSocket` and `BoundDatagram` in `DatagramSocket`.
2023-11-19 15:54:58 +00:00
|
|
|
observer: RwLock<Weak<dyn Observer<()>>>,
|
2023-05-31 02:47:52 +00:00
|
|
|
weak_self: Weak<Self>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AnyBoundSocket {
|
|
|
|
|
pub(super) fn new(
|
|
|
|
|
iface: Arc<dyn Iface>,
|
|
|
|
|
handle: smoltcp::iface::SocketHandle,
|
|
|
|
|
port: u16,
|
|
|
|
|
socket_family: SocketFamily,
|
|
|
|
|
) -> Arc<Self> {
|
|
|
|
|
Arc::new_cyclic(|weak_self| Self {
|
|
|
|
|
iface,
|
|
|
|
|
handle,
|
|
|
|
|
port,
|
|
|
|
|
socket_family,
|
Implement iface event observers and move `Pollee` to them
Finally, this commit implements an iface event observer trait for the
`ConnectingStream`, `ListenStream`, and `ConnectedStream` states in
`StreamSocket`, as well as the `BoundDatagram` state in
`DatagramSocket`. It also moves the `Pollee` from `AnyBoundSocket` to
these observer implementors.
What I have tried to do is minimize the semantic changes. Ideally, this
commit should be a pure refactor commit, meaning that even if the
sematics of the previous code is wrong, the sematics after this commit
should be wrong in the same way. Fixing the wrong sematics should be
done in a separate commit afterwards.
However, keeping exactly the same sematics for `ListenStream` is hard.
We used to maintain a `Pollee` for each `BacklogSocket`, but now we can
just maintain one `Pollee` for the whole `ListenStream`. However,
implementing the correct semantics looks much easier, so we just do it.
For `ConnectingStream`, it used to share the same `Pollee` logic with
`ConnectedStream` (because the `Pollee` was maintained by
`AnyBoundSocket`, which is used by both). Now we write the `Pollee`
logic separately for `ConnectingStream`, so we can just write a correct
one or try to reuse the logic in `ConnectingStream`. This commit does
the former.
There should be no semantic changes for `ConnectedStream` in
`StreamSocket` and `BoundDatagram` in `DatagramSocket`.
2023-11-19 15:54:58 +00:00
|
|
|
observer: RwLock::new(Weak::<()>::new()),
|
2023-05-31 02:47:52 +00:00
|
|
|
weak_self: weak_self.clone(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
Implement iface event observers and move `Pollee` to them
Finally, this commit implements an iface event observer trait for the
`ConnectingStream`, `ListenStream`, and `ConnectedStream` states in
`StreamSocket`, as well as the `BoundDatagram` state in
`DatagramSocket`. It also moves the `Pollee` from `AnyBoundSocket` to
these observer implementors.
What I have tried to do is minimize the semantic changes. Ideally, this
commit should be a pure refactor commit, meaning that even if the
sematics of the previous code is wrong, the sematics after this commit
should be wrong in the same way. Fixing the wrong sematics should be
done in a separate commit afterwards.
However, keeping exactly the same sematics for `ListenStream` is hard.
We used to maintain a `Pollee` for each `BacklogSocket`, but now we can
just maintain one `Pollee` for the whole `ListenStream`. However,
implementing the correct semantics looks much easier, so we just do it.
For `ConnectingStream`, it used to share the same `Pollee` logic with
`ConnectedStream` (because the `Pollee` was maintained by
`AnyBoundSocket`, which is used by both). Now we write the `Pollee`
logic separately for `ConnectingStream`, so we can just write a correct
one or try to reuse the logic in `ConnectingStream`. This commit does
the former.
There should be no semantic changes for `ConnectedStream` in
`StreamSocket` and `BoundDatagram` in `DatagramSocket`.
2023-11-19 15:54:58 +00:00
|
|
|
pub(super) fn on_iface_events(&self) {
|
|
|
|
|
if let Some(observer) = Weak::upgrade(&*self.observer.read()) {
|
|
|
|
|
observer.on_events(&())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set the observer whose `on_events` will be called when certain iface events happen. After
|
|
|
|
|
/// setting, the new observer will fire once immediately to avoid missing any events.
|
|
|
|
|
///
|
|
|
|
|
/// If there is an existing observer, due to race conditions, this function does not guarentee
|
|
|
|
|
/// that the old observer will never be called after the setting. Users should be aware of this
|
|
|
|
|
/// and proactively handle the race conditions if necessary.
|
|
|
|
|
pub fn set_observer(&self, handler: Weak<dyn Observer<()>>) {
|
|
|
|
|
*self.observer.write() = handler;
|
|
|
|
|
|
|
|
|
|
self.on_iface_events();
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-31 02:47:52 +00:00
|
|
|
pub fn local_endpoint(&self) -> Option<IpEndpoint> {
|
|
|
|
|
let ip_addr = {
|
|
|
|
|
let ipv4_addr = self.iface.ipv4_addr()?;
|
|
|
|
|
IpAddress::Ipv4(ipv4_addr)
|
|
|
|
|
};
|
|
|
|
|
Some(IpEndpoint::new(ip_addr, self.port))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn raw_with<T: smoltcp::socket::AnySocket<'static>, R, F: FnMut(&mut T) -> R>(
|
|
|
|
|
&self,
|
|
|
|
|
mut f: F,
|
|
|
|
|
) -> R {
|
|
|
|
|
let mut sockets = self.iface.sockets();
|
|
|
|
|
let socket = sockets.get_mut::<T>(self.handle);
|
|
|
|
|
f(socket)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Try to connect to a remote endpoint. Tcp socket only.
|
|
|
|
|
pub fn do_connect(&self, remote_endpoint: IpEndpoint) -> Result<()> {
|
|
|
|
|
let mut sockets = self.iface.sockets();
|
|
|
|
|
let socket = sockets.get_mut::<RawTcpSocket>(self.handle);
|
|
|
|
|
let port = self.port;
|
|
|
|
|
let mut iface_inner = self.iface.iface_inner();
|
|
|
|
|
let cx = iface_inner.context();
|
|
|
|
|
socket
|
|
|
|
|
.connect(cx, remote_endpoint, port)
|
|
|
|
|
.map_err(|_| Error::with_message(Errno::ENOBUFS, "send connection request failed"))?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn iface(&self) -> &Arc<dyn Iface> {
|
|
|
|
|
&self.iface
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(super) fn weak_ref(&self) -> Weak<Self> {
|
|
|
|
|
self.weak_self.clone()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn close(&self) {
|
|
|
|
|
match self.socket_family {
|
|
|
|
|
SocketFamily::Tcp => self.raw_with(|socket: &mut RawTcpSocket| socket.close()),
|
|
|
|
|
SocketFamily::Udp => self.raw_with(|socket: &mut RawUdpSocket| socket.close()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for AnyBoundSocket {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
self.close();
|
2023-08-28 09:37:59 +00:00
|
|
|
self.iface.poll();
|
2023-05-31 02:47:52 +00:00
|
|
|
self.iface.common().remove_socket(self.handle);
|
|
|
|
|
self.iface.common().release_port(self.port);
|
|
|
|
|
self.iface.common().remove_bound_socket(self.weak_ref());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For TCP
|
|
|
|
|
const RECV_BUF_LEN: usize = 65536;
|
|
|
|
|
const SEND_BUF_LEN: usize = 65536;
|
|
|
|
|
|
|
|
|
|
// For UDP
|
|
|
|
|
const UDP_METADATA_LEN: usize = 256;
|
|
|
|
|
const UDP_SEND_PAYLOAD_LEN: usize = 65536;
|
|
|
|
|
const UDP_RECEIVE_PAYLOAD_LEN: usize = 65536;
|