#![allow(clippy::expect_used)]
use std::io;
use std::net::SocketAddr;
use crate::connection::{ConnectionConfig, ConnectionError, IngressStats, State};
use crate::proactor::{
BufferRing, Completion, Domain, OpKind, Proactor, SockAddr, SqeFlags, TcpSocket, UserData,
};
use crate::tls::TlsAdapter;
use crate::ws::{ConnState as WsConnState, DataEvent as WsDataEvent, WsClient, WsConfig};
#[derive(Clone, Copy, Eq, PartialEq)]
enum WsIngressState {
Clean,
Dirty,
}
pub(crate) struct ConnectionState {
pub(crate) socket: TcpSocket,
pub(crate) addr: SockAddr,
pub(crate) tls: Option<TlsAdapter>,
pub(crate) ws: WsClient,
pub(crate) state: State,
pub(crate) buf_ring: Option<BufferRing>,
pub(crate) send_buf: Vec<u8>,
pub(crate) send_head: usize,
pub(crate) tls_pending_out: Vec<u8>,
pub(crate) send_inflight: bool,
pub(crate) multishot_armed: bool,
pub(crate) ws_handshake_begun: bool,
ws_ingress: WsIngressState,
pub(crate) ingress_stats: IngressStats,
pub(crate) cfg: ConnectionConfig,
}
impl ConnectionState {
pub(crate) fn new(cfg: ConnectionConfig, addr: SocketAddr) -> Result<Self, ConnectionError> {
let sock_addr = SockAddr::from_std(addr);
let domain = match addr {
SocketAddr::V4(_) => Domain::V4,
SocketAddr::V6(_) => Domain::V6,
};
let socket = TcpSocket::new(domain)?;
socket.set_nodelay(true)?;
let tls = if cfg.use_tls {
Some(match cfg.tls_config.clone() {
Some(config) => TlsAdapter::new_client_with_config(&cfg.host, config)?,
None => TlsAdapter::new_client(&cfg.host)?,
})
} else {
None
};
let mut ws_cfg = cfg
.ws_config
.clone()
.unwrap_or_else(|| WsConfig::new(cfg.host.clone(), cfg.path.clone()));
ws_cfg.host.clone_from(&cfg.host);
ws_cfg.path.clone_from(&cfg.path);
let ws = WsClient::new_client(ws_cfg)?;
let init_cap = cfg.buf_ring_slot_size as usize;
let send_cap = cfg.send_buffer_initial_capacity.unwrap_or(init_cap);
let tls_pending_out_cap = cfg.tls_pending_out_initial_capacity.unwrap_or(init_cap);
Ok(Self {
socket,
addr: sock_addr,
tls,
ws,
state: State::Init,
buf_ring: None,
send_buf: Vec::with_capacity(send_cap),
send_head: 0,
tls_pending_out: Vec::with_capacity(tls_pending_out_cap),
send_inflight: false,
multishot_armed: false,
ws_handshake_begun: false,
ws_ingress: WsIngressState::Clean,
ingress_stats: IngressStats::default(),
cfg,
})
}
#[inline]
pub(crate) const fn conn_id(&self) -> u32 {
self.cfg.conn_id
}
#[inline]
pub(crate) const fn state(&self) -> State {
self.state
}
#[inline]
pub(crate) const fn ingress_stats(&self) -> IngressStats {
self.ingress_stats
}
pub(crate) fn assert_open(&self) -> Result<(), ConnectionError> {
if matches!(self.state, State::Open) {
Ok(())
} else {
Err(ConnectionError::InvalidState(self.state))
}
}
pub(crate) fn submit_connect(
&mut self,
proactor: &mut Proactor,
) -> Result<(), ConnectionError> {
let ud = UserData::new(OpKind::Connect, u64::from(self.cfg.conn_id));
unsafe {
proactor.submit_connect(self.socket.as_raw_fd(), &self.addr, ud, SqeFlags::NONE)?;
}
self.state = State::Connecting;
Ok(())
}
pub(crate) fn try_submit_send(
&mut self,
proactor: &mut Proactor,
) -> Result<(), ConnectionError> {
if self.send_inflight {
return Ok(());
}
if matches!(self.state, State::Init | State::Connecting | State::Closed) {
return Ok(());
}
self.compact_send_buf_if_needed();
if !self.tls_pending_out.is_empty() {
self.send_buf.extend_from_slice(&self.tls_pending_out);
self.tls_pending_out.clear();
}
let ws_tx_len = self.ws.pending_tx().len();
if ws_tx_len > 0 {
if let Some(tls) = &mut self.tls {
tls.egress_plaintext(self.ws.pending_tx(), &mut self.send_buf)?;
} else {
self.send_buf.extend_from_slice(self.ws.pending_tx());
}
self.ws.ack_tx(ws_tx_len);
}
if let Some(tls) = &mut self.tls {
tls.egress_plaintext(&[], &mut self.send_buf)?;
}
let pending = self.send_buf.len().saturating_sub(self.send_head);
if pending == 0 {
return Ok(());
}
let ud = UserData::new(OpKind::Send, u64::from(self.cfg.conn_id));
let len = u32::try_from(pending).unwrap_or(u32::MAX);
unsafe {
proactor.submit_send(
self.socket.as_raw_fd(),
self.send_buf.as_ptr().add(self.send_head),
len,
ud,
SqeFlags::NONE,
)?;
}
self.send_inflight = true;
Ok(())
}
fn compact_send_buf_if_needed(&mut self) {
debug_assert!(!self.send_inflight);
if self.send_head == 0 {
return;
}
if self.send_head == self.send_buf.len() {
self.send_buf.clear();
} else {
self.send_buf.drain(..self.send_head);
}
self.send_head = 0;
}
pub(crate) fn try_rearm_multishot(
&mut self,
proactor: &mut Proactor,
) -> Result<(), ConnectionError> {
if self.multishot_armed {
return Ok(());
}
if matches!(self.state, State::Init | State::Connecting | State::Closed) {
return Ok(());
}
let Some(ring) = self.buf_ring.as_ref() else {
return Ok(());
};
let bgid = ring.bgid();
unsafe {
proactor.submit_recv_multishot(
self.socket.as_raw_fd(),
bgid,
UserData::new(OpKind::Recv, u64::from(self.cfg.conn_id)),
)?;
}
self.multishot_armed = true;
Ok(())
}
pub(crate) fn handle_completion(
&mut self,
proactor: &mut Proactor,
c: Completion,
) -> Result<(), ConnectionError> {
let kind = c
.user_data
.kind()
.ok_or_else(|| ConnectionError::UnknownOpKind(c.user_data.raw()))?;
match kind {
OpKind::Connect => self.on_connect_cqe(proactor, c),
OpKind::Send => self.on_send_cqe(c),
OpKind::Recv => self.on_recv_cqe(c),
OpKind::Close => {
self.state = State::Closed;
Ok(())
}
OpKind::Nop => Ok(()),
}
}
pub(crate) fn handle_completion_data<F>(
&mut self,
proactor: &mut Proactor,
c: Completion,
mut sink: F,
) -> Result<usize, ConnectionError>
where
F: for<'a> FnMut(WsDataEvent<'a>),
{
let kind = c
.user_data
.kind()
.ok_or_else(|| ConnectionError::UnknownOpKind(c.user_data.raw()))?;
match kind {
OpKind::Connect => {
self.on_connect_cqe(proactor, c)?;
Ok(0)
}
OpKind::Send => {
self.on_send_cqe(c)?;
Ok(0)
}
OpKind::Recv => self.on_recv_cqe_data(c, &mut sink),
OpKind::Close => {
self.state = State::Closed;
Ok(0)
}
OpKind::Nop => Ok(0),
}
}
fn on_connect_cqe(
&mut self,
proactor: &mut Proactor,
c: Completion,
) -> Result<(), ConnectionError> {
c.to_result().map_err(ConnectionError::ConnectFailed)?;
let ring = BufferRing::new(
proactor,
self.cfg.bgid,
self.cfg.buf_ring_entries,
self.cfg.buf_ring_slot_size,
)?;
let bgid = ring.bgid();
self.buf_ring = Some(ring);
unsafe {
proactor.submit_recv_multishot(
self.socket.as_raw_fd(),
bgid,
UserData::new(OpKind::Recv, u64::from(self.cfg.conn_id)),
)?;
}
self.multishot_armed = true;
self.state = if self.tls.is_some() {
State::TlsHandshake
} else {
State::WsHandshake
};
if self.tls.is_none() && !self.ws_handshake_begun {
self.ws.begin_handshake()?;
self.ws_handshake_begun = true;
}
Ok(())
}
fn on_send_cqe(&mut self, c: Completion) -> Result<(), ConnectionError> {
self.send_inflight = false;
let n = c.to_result().map_err(ConnectionError::SendFailed)?;
self.send_head += n;
if self.send_head >= self.send_buf.len() {
self.send_buf.clear();
self.send_head = 0;
}
Ok(())
}
fn on_recv_cqe(&mut self, c: Completion) -> Result<(), ConnectionError> {
if !c.has_more() {
self.multishot_armed = false;
}
let Some(bid) = c.buffer_id() else {
return match c.to_result() {
Ok(0) => {
self.state = State::Closed;
Err(ConnectionError::PeerClosed)
}
Ok(_) => Ok(()),
Err(e) if is_recv_buffer_ring_exhausted(&e) => {
self.record_recv_ring_exhaustion();
self.multishot_armed = false;
tracing::warn!(
conn_id = self.cfg.conn_id,
bgid = self.buf_ring.as_ref().map_or(0, BufferRing::bgid),
"recv multishot provided-buffer ring exhausted; will rearm next pump"
);
Ok(())
}
Err(e) => Err(ConnectionError::RecvFailed(e)),
};
};
let n = c.to_result().map_err(ConnectionError::RecvFailed)?;
if n == 0 {
self.buf_ring
.as_mut()
.expect("buf_ring 应在 on_connect_cqe 注册")
.recycle(bid);
self.state = State::Closed;
return Err(ConnectionError::PeerClosed);
}
self.record_recv_data(n);
let bytes_ptr = self
.buf_ring
.as_ref()
.expect("buf_ring")
.buffer(bid)
.as_ptr();
let bytes: &[u8] = unsafe { std::slice::from_raw_parts(bytes_ptr, n) };
let recv_result = if let Some(tls) = &mut self.tls {
let ws = &mut self.ws;
let mut fed_plaintext = false;
tls.ingest_ciphertext(bytes, &mut self.tls_pending_out, |plaintext| {
ws.feed_recv(plaintext);
fed_plaintext = true;
})?;
if fed_plaintext {
self.ws_ingress = WsIngressState::Dirty;
}
if !tls.is_handshaking()
&& matches!(self.state, State::TlsHandshake)
&& !self.ws_handshake_begun
{
tls.verify_alpn()?;
self.state = State::WsHandshake;
self.ws.begin_handshake()?;
self.ws_handshake_begun = true;
}
if tls.received_close_notify() && !matches!(self.state, State::Closed | State::Closing)
{
self.state = State::Closing;
}
Ok(())
} else {
self.ws.feed_recv(bytes);
self.ws_ingress = WsIngressState::Dirty;
Ok::<_, ConnectionError>(())
};
self.buf_ring.as_mut().expect("buf_ring").recycle(bid);
recv_result
}
fn on_recv_cqe_data<F>(&mut self, c: Completion, sink: &mut F) -> Result<usize, ConnectionError>
where
F: for<'a> FnMut(WsDataEvent<'a>),
{
if !c.has_more() {
self.multishot_armed = false;
}
let Some(bid) = c.buffer_id() else {
return match c.to_result() {
Ok(0) => {
self.state = State::Closed;
Err(ConnectionError::PeerClosed)
}
Ok(_) => Ok(0),
Err(e) if is_recv_buffer_ring_exhausted(&e) => {
self.record_recv_ring_exhaustion();
self.multishot_armed = false;
tracing::warn!(
conn_id = self.cfg.conn_id,
bgid = self.buf_ring.as_ref().map_or(0, BufferRing::bgid),
"recv multishot provided-buffer ring exhausted; will rearm next pump"
);
Ok(0)
}
Err(e) => Err(ConnectionError::RecvFailed(e)),
};
};
let n = c.to_result().map_err(ConnectionError::RecvFailed)?;
if n == 0 {
self.buf_ring
.as_mut()
.expect("buf_ring 应在 on_connect_cqe 注册")
.recycle(bid);
self.state = State::Closed;
return Err(ConnectionError::PeerClosed);
}
self.record_recv_data(n);
let bytes_ptr = self
.buf_ring
.as_ref()
.expect("buf_ring")
.buffer(bid)
.as_ptr();
let bytes: &[u8] = unsafe { std::slice::from_raw_parts(bytes_ptr, n) };
let recv_result = if let Some(tls) = &mut self.tls {
let ws = &mut self.ws;
let mut fed_plaintext = false;
let mut drained_events = 0_usize;
let mut ws_error = None;
tls.ingest_ciphertext(bytes, &mut self.tls_pending_out, |plaintext| {
fed_plaintext = true;
match ws.drain_data_events_from_ingress(plaintext, |ev| {
drained_events = drained_events.saturating_add(1);
sink(ev);
}) {
Ok(_) => {}
Err(e) if ws_error.is_none() => {
ws_error = Some(e);
}
Err(_) => {}
}
})?;
if !tls.is_handshaking()
&& matches!(self.state, State::TlsHandshake)
&& !self.ws_handshake_begun
{
tls.verify_alpn()?;
self.state = State::WsHandshake;
self.ws.begin_handshake()?;
self.ws_handshake_begun = true;
}
if tls.received_close_notify() && !matches!(self.state, State::Closed | State::Closing)
{
self.state = State::Closing;
}
self.record_ws_data_drain_attempt(fed_plaintext);
match ws_error {
Some(e) => Err(ConnectionError::Ws(e)),
None => Ok(drained_events),
}
} else {
let mut drained_events = 0_usize;
let result = self
.ws
.drain_data_events_from_ingress(bytes, |ev| {
drained_events = drained_events.saturating_add(1);
sink(ev);
})
.map(|_| drained_events)
.map_err(ConnectionError::Ws);
self.record_ws_data_drain_attempt(true);
result
};
self.buf_ring.as_mut().expect("buf_ring").recycle(bid);
recv_result
}
#[inline]
fn record_recv_data(&mut self, bytes: usize) {
if !self.cfg.track_ingress_stats {
return;
}
self.ingress_stats.recv_data_cqes = self.ingress_stats.recv_data_cqes.saturating_add(1);
self.ingress_stats.recv_bytes = self.ingress_stats.recv_bytes.saturating_add(bytes as u64);
}
#[inline]
fn record_recv_ring_exhaustion(&mut self) {
if self.cfg.track_ingress_stats {
self.ingress_stats.recv_ring_exhaustions =
self.ingress_stats.recv_ring_exhaustions.saturating_add(1);
}
}
#[inline]
fn record_ws_data_drain_attempt(&mut self, dirty: bool) {
if self.cfg.track_ingress_stats {
if dirty {
self.ingress_stats.ws_data_drains =
self.ingress_stats.ws_data_drains.saturating_add(1);
} else {
self.ingress_stats.ws_data_drain_skips =
self.ingress_stats.ws_data_drain_skips.saturating_add(1);
}
}
}
#[inline]
pub(crate) fn clear_ws_ingress_dirty(&mut self) {
self.ws_ingress = WsIngressState::Clean;
}
pub(crate) fn sync_ws_close_state(&mut self) {
if matches!(self.ws.state(), WsConnState::Closed)
&& !matches!(self.state, State::Closing | State::Closed)
{
self.state = State::Closing;
}
}
pub(crate) fn sync_ws_open_state(&mut self) {
if matches!(self.ws.state(), WsConnState::Open) && !matches!(self.state, State::Open) {
self.state = State::Open;
}
}
}
fn is_recv_buffer_ring_exhausted(err: &io::Error) -> bool {
err.raw_os_error() == Some(libc::ENOBUFS)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detects_recv_buffer_ring_exhaustion() {
let err = io::Error::from_raw_os_error(libc::ENOBUFS);
assert!(is_recv_buffer_ring_exhausted(&err));
let err = io::Error::from_raw_os_error(libc::ECONNRESET);
assert!(!is_recv_buffer_ring_exhausted(&err));
}
}