Crypto
WASM
TESTS
This commit is contained in:
Alex Emmet 2026-06-25 22:08:44 +02:00
commit 687e6f9642
49 changed files with 6272 additions and 366 deletions

View file

@ -61,7 +61,7 @@ enum ReceivedFrame {
pub struct Sender {
send_guard: Mutex<()>,
stream_guard: Mutex<Option<wtransport::SendStream>>,
stream_guard: Arc<Mutex<Option<wtransport::SendStream>>>,
handle: Arc<ConnectionHandle>,
connection: Connection,
policy: Arc<Policy>,
@ -71,7 +71,7 @@ impl Sender {
pub fn new(connection: Connection, handle: Arc<ConnectionHandle>, policy: Arc<Policy>) -> Self {
Self {
send_guard: Mutex::new(()),
stream_guard: Mutex::new(None),
stream_guard: Arc::new(Mutex::new(None)),
handle,
connection,
policy,
@ -275,6 +275,18 @@ impl Sender {
}
}
pub async fn finish_stream(&self) -> Result<(), CommunicationError> {
let _send_lock = self.send_guard.lock().await;
let mut stream_opt = self.stream_guard.lock().await;
if let Some(mut stream) = stream_opt.take() {
timeout(self.policy.write_timeout, stream.finish())
.await
.map_err(|_| CommunicationError::StreamError)?
.map_err(|_| CommunicationError::StreamError)?;
}
Ok(())
}
pub fn handle(&self) -> &Arc<ConnectionHandle> {
&self.handle
}
@ -283,6 +295,7 @@ impl Sender {
let connection = self.connection.clone();
let handle = self.handle.clone();
let policy = self.policy.clone();
let stream_guard = self.stream_guard.clone();
tokio::spawn(async move {
if connection.quic_connection().close_reason().is_some() || handle.is_closed() {
@ -290,6 +303,14 @@ impl Sender {
return;
}
if let Some(mut stream) = stream_guard.lock().await.take() {
match timeout(policy.write_timeout, stream.finish()).await {
Ok(Ok(())) => {}
Ok(Err(e)) => log::warn!("[Sender] persistent stream finish failed: {e}"),
Err(_) => log::warn!("[Sender] persistent stream finish timed out"),
}
}
let _ = Self::send_close_frame(&connection, &policy).await;
handle.close(Some(CommunicationError::StreamClosed));
@ -323,6 +344,18 @@ pub struct Receiver {
handle: Arc<ConnectionHandle>,
}
impl Drop for Receiver {
fn drop(&mut self) {
// The accept loop holds clones of the connection and the shared
// ConnectionHandle. Without this, dropping a Receiver without first
// closing the connection would leave that task running forever. Abort
// it directly rather than closing the shared handle, so a still-live
// Sender on the same connection is unaffected. abort() is a no-op if
// the task already finished (e.g. the connection was closed).
self._accept_task.abort();
}
}
impl Receiver {
pub fn new(connection: Connection, handle: Arc<ConnectionHandle>, policy: Arc<Policy>) -> Self {
let (tx, rx) = mpsc::channel::<Result<CommunicationValue, CommunicationError>>(

View file

@ -53,7 +53,7 @@ impl ConnectionHandle {
if self.is_closed() {
return rx.borrow().clone();
}
let _ = rx.changed().await.ok()?;
rx.changed().await.ok()?;
rx.borrow().clone()
}
}

View file

@ -26,6 +26,22 @@ impl Host {
pub fn local_addr(&self) -> std::net::SocketAddr {
self.local_addr
}
/// Stop accepting new connections. Already-accepted connections run on their
/// own spawned tasks and are not affected.
pub fn shutdown(&mut self) {
self._task.abort();
}
}
impl Drop for Host {
fn drop(&mut self) {
// The accept loop runs forever on its own task; dropping the Host
// JoinHandle would only detach it. Abort it so dropping the Host
// actually stops accepting new connections. Per-connection handler
// tasks are spawned independently and keep running.
self._task.abort();
}
}
pub async fn host(