From 3ace9c6528e4e5bd11808875567e513991cf86f5 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 14 Nov 2025 13:30:13 -0800 Subject: [PATCH 1/2] Revert some of the trait changes; match Quinn's naming. No need to confuse people. --- web-transport-quiche/src/ez/recv.rs | 6 +++--- web-transport-quiche/src/ez/send.rs | 15 ++++++++------- web-transport-quiche/src/recv.rs | 12 ++++++------ web-transport-quiche/src/send.rs | 17 +++++++---------- web-transport-quinn/src/recv.rs | 2 +- web-transport-quinn/src/send.rs | 2 +- web-transport-trait/src/lib.rs | 16 ++++++++-------- web-transport-ws/src/session.rs | 8 ++++---- 8 files changed, 38 insertions(+), 40 deletions(-) diff --git a/web-transport-quiche/src/ez/recv.rs b/web-transport-quiche/src/ez/recv.rs index 69dc199..d4692f6 100644 --- a/web-transport-quiche/src/ez/recv.rs +++ b/web-transport-quiche/src/ez/recv.rs @@ -301,7 +301,7 @@ impl RecvStream { /// Tell the other end to stop sending data with the given error code. /// /// This sends a STOP_SENDING frame to the remote. - pub fn close(&mut self, code: u64) { + pub fn stop(&mut self, code: u64) { self.state.lock().stop = Some(code); let waker = self.driver.lock().recv(self.id); @@ -313,7 +313,7 @@ impl RecvStream { /// Returns true if the stream is closed by either side. /// /// This includes: - /// - We sent a STOP_SENDING via [RecvStream::close] + /// - We sent a STOP_SENDING via [RecvStream::stop] /// - We received a RESET_STREAM from the remote /// - We received a FIN from the remote pub fn is_closed(&self) -> bool { @@ -335,7 +335,7 @@ impl RecvStream { /// Wait until the stream is closed by either side. /// /// This includes: - /// - We sent a STOP_SENDING via [RecvStream::close] + /// - We sent a STOP_SENDING via [RecvStream::stop] /// - We received a RESET_STREAM from the remote /// - We received a FIN from the remote /// diff --git a/web-transport-quiche/src/ez/send.rs b/web-transport-quiche/src/ez/send.rs index 262f436..c195822 100644 --- a/web-transport-quiche/src/ez/send.rs +++ b/web-transport-quiche/src/ez/send.rs @@ -281,8 +281,9 @@ impl SendStream { /// Mark the stream as finished, such that no more data can be written. /// - /// **WARN**: If this is not called explicitly, [SendStream::close] will be called on [Drop]. - /// **NOTE**: [SendStream::closed] will block until the FIN has been sent. + /// [SendStream::closed] will block until the FIN has been sent. + /// + /// **WARN**: If this is not called explicitly, [SendStream::reset] will be called on [Drop]. pub fn finish(&mut self) -> Result<(), StreamError> { { let mut state = self.state.lock(); @@ -313,7 +314,7 @@ impl SendStream { /// Abruptly reset the stream with the provided error code. /// /// This sends a RESET_STREAM frame to the remote. - pub fn close(&mut self, code: u64) { + pub fn reset(&mut self, code: u64) { self.state.lock().reset = Some(code); let waker = self.driver.lock().send(self.id); @@ -325,8 +326,8 @@ impl SendStream { /// Returns true if the stream is closed by either side. /// /// This includes: - /// - We sent a RESET_STREAM via [SendStream::close] - /// - We received a STOP_SENDING via [RecvStream::close] + /// - We sent a RESET_STREAM via [SendStream::reset] + /// - We received a STOP_SENDING via [super::RecvStream::stop] /// - We sent a FIN via [SendStream::finish] pub fn is_closed(&self) -> bool { self.state.lock().is_closed() @@ -347,8 +348,8 @@ impl SendStream { /// Wait until the stream is closed by either side. /// /// This includes: - /// - We sent a RESET_STREAM via [SendStream::close] - /// - We received a STOP_SENDING via [RecvStream::close] + /// - We sent a RESET_STREAM via [SendStream::reset] + /// - We received a STOP_SENDING via [super::RecvStream::stop] /// - We sent a FIN via [SendStream::finish] /// /// Note: This takes `&mut` to match quiche and to simplify the implementation. diff --git a/web-transport-quiche/src/recv.rs b/web-transport-quiche/src/recv.rs index fc81d5d..dfde6be 100644 --- a/web-transport-quiche/src/recv.rs +++ b/web-transport-quiche/src/recv.rs @@ -52,8 +52,8 @@ impl RecvStream { /// Tell the other end to stop sending data with the given error code. /// /// This is a u32 with WebTransport since it shares the error space with HTTP/3. - pub fn close(&mut self, code: u32) { - self.inner.close(web_transport_proto::error_to_http3(code)); + pub fn stop(&mut self, code: u32) { + self.inner.stop(web_transport_proto::error_to_http3(code)); } /// Block until the stream has been reset and return the error code. @@ -65,8 +65,8 @@ impl RecvStream { impl Drop for RecvStream { fn drop(&mut self) { if !self.inner.is_closed() { - tracing::warn!("stream dropped without `close` or reading all contents"); - self.inner.close(DROP_CODE) + tracing::warn!("stream dropped without `stop` or reading all contents"); + self.inner.stop(DROP_CODE) } } } @@ -94,8 +94,8 @@ impl web_transport_trait::RecvStream for RecvStream { self.read_chunk(max).await } - fn close(&mut self, code: u32) { - self.close(code); + fn stop(&mut self, code: u32) { + self.stop(code); } async fn closed(&mut self) -> Result<(), Self::Error> { diff --git a/web-transport-quiche/src/send.rs b/web-transport-quiche/src/send.rs index 7239491..f5ffb08 100644 --- a/web-transport-quiche/src/send.rs +++ b/web-transport-quiche/src/send.rs @@ -48,9 +48,6 @@ impl SendStream { } /// Mark the stream as finished, such that no more data can be written. - /// - /// **WARNING**: This is implicitly called on Drop, but it's a common footgun. - /// If you cancel futures by dropping them you'll get incomplete writes. pub fn finish(&mut self) -> Result<(), StreamError> { self.inner.finish().map_err(Into::into) } @@ -65,9 +62,9 @@ impl SendStream { /// Abruptly reset the stream with the provided error code. /// /// This is a u32 with WebTransport because it shares the error space with HTTP/3. - pub fn close(&mut self, code: u32) { + pub fn reset(&mut self, code: u32) { let code = web_transport_proto::error_to_http3(code); - self.inner.close(code) + self.inner.reset(code) } /// Wait until the stream has been stopped and return the error code. @@ -78,10 +75,10 @@ impl SendStream { impl Drop for SendStream { fn drop(&mut self) { - // Reset the stream if we dropped without calling `close` or `finish` + // Reset the stream if we dropped without calling `close` or `reset` if !self.inner.is_finished().unwrap_or(true) { - tracing::warn!("stream dropped without `close` or `finish`"); - self.inner.close(DROP_CODE) + tracing::warn!("stream dropped without `close` or `reset`"); + self.inner.reset(DROP_CODE) } } } @@ -121,8 +118,8 @@ impl web_transport_trait::SendStream for SendStream { self.set_priority(order) } - fn close(&mut self, code: u32) { - self.close(code) + fn reset(&mut self, code: u32) { + self.reset(code) } fn finish(&mut self) -> Result<(), Self::Error> { diff --git a/web-transport-quinn/src/recv.rs b/web-transport-quinn/src/recv.rs index 877495e..6734c20 100644 --- a/web-transport-quinn/src/recv.rs +++ b/web-transport-quinn/src/recv.rs @@ -102,7 +102,7 @@ impl tokio::io::AsyncRead for RecvStream { impl web_transport_trait::RecvStream for RecvStream { type Error = ReadError; - fn close(&mut self, code: u32) { + fn stop(&mut self, code: u32) { Self::stop(self, code).ok(); } diff --git a/web-transport-quinn/src/send.rs b/web-transport-quinn/src/send.rs index aa35c87..836490f 100644 --- a/web-transport-quinn/src/send.rs +++ b/web-transport-quinn/src/send.rs @@ -124,7 +124,7 @@ impl web_transport_trait::SendStream for SendStream { Self::set_priority(self, order.into()).ok(); } - fn close(&mut self, code: u32) { + fn reset(&mut self, code: u32) { Self::reset(self, code).ok(); } diff --git a/web-transport-trait/src/lib.rs b/web-transport-trait/src/lib.rs index a0ab34d..94bfb09 100644 --- a/web-transport-trait/src/lib.rs +++ b/web-transport-trait/src/lib.rs @@ -141,24 +141,24 @@ pub trait SendStream: MaybeSend { /// Mark the stream as finished, erroring on any future writes. /// - /// [SendStream::close] can still be called to abandon any queued data. + /// [SendStream::reset] can still be called to abandon any queued data. /// [SendStream::closed] should return when the FIN is acknowledged by the peer. /// /// NOTE: Quinn implicitly calls this on Drop, but it's a common footgun. - /// Implementations SHOULD [SendStream::close] on Drop instead. + /// Implementations SHOULD [SendStream::reset] on Drop instead. fn finish(&mut self) -> Result<(), Self::Error>; /// Immediately closes the stream and discards any remaining data. /// /// This translates into a RESET_STREAM QUIC code. /// The peer may not receive the reset code if the stream is already closed. - fn close(&mut self, code: u32); + fn reset(&mut self, code: u32); /// Block until the stream is closed by either side. /// /// This includes: - /// - We sent a RESET_STREAM via [SendStream::close] - /// - We received a STOP_SENDING via [RecvStream::close] + /// - We sent a RESET_STREAM via [SendStream::reset] + /// - We received a STOP_SENDING via [RecvStream::stop] /// - A FIN is acknowledged by the peer via [SendStream::finish] /// /// Some implementations do not support FIN acknowledgement, in which case this will block until the FIN is sent. @@ -225,13 +225,13 @@ pub trait RecvStream: MaybeSend { /// /// An implementation MUST do this on Drop otherwise flow control will be leaked. /// Call this method manually if you want to specify a code yourself. - fn close(&mut self, code: u32); + fn stop(&mut self, code: u32); /// Block until the stream has been closed by either side. /// /// This includes: - /// - We received a RESET_STREAM via [SendStream::close] - /// - We sent a STOP_SENDING via [RecvStream::close] + /// - We received a RESET_STREAM via [SendStream::reset] + /// - We sent a STOP_SENDING via [RecvStream::stop] /// - We received a FIN via [SendStream::finish] and read all data. fn closed(&mut self) -> impl Future> + MaybeSend; diff --git a/web-transport-ws/src/session.rs b/web-transport-ws/src/session.rs index 9469e70..0fba9a6 100644 --- a/web-transport-ws/src/session.rs +++ b/web-transport-ws/src/session.rs @@ -521,7 +521,7 @@ impl SendStream { impl Drop for SendStream { fn drop(&mut self) { if !self.fin && self.closed.is_none() { - generic::SendStream::close(self, 0); + generic::SendStream::reset(self, 0); } } } @@ -570,7 +570,7 @@ impl generic::SendStream for SendStream { // Priority not implemented in this version } - fn close(&mut self, code: u32) { + fn reset(&mut self, code: u32) { if self.fin || self.closed.is_some() { return; } @@ -653,7 +653,7 @@ impl RecvStream { impl Drop for RecvStream { fn drop(&mut self) { if !self.fin && self.closed.is_none() { - generic::RecvStream::close(self, 0); + generic::RecvStream::stop(self, 0); } } } @@ -714,7 +714,7 @@ impl generic::RecvStream for RecvStream { self.read_buf(&mut buf).await } - fn close(&mut self, code: u32) { + fn stop(&mut self, code: u32) { let code = VarInt::from(code); let frame = StopSending { id: self.id, code }; From f5d7ef894ad1e67baf973f26376f227f6556a0fa Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 14 Nov 2025 14:18:02 -0800 Subject: [PATCH 2/2] Revert some semver changes. --- Cargo.toml | 4 ++-- web-transport-proto/Cargo.toml | 2 +- web-transport-quiche/Cargo.toml | 2 +- web-transport-quiche/src/ez/send.rs | 2 +- web-transport-quinn/src/error.rs | 18 +++++------------- web-transport-quinn/src/session.rs | 4 ++-- web-transport-trait/Cargo.toml | 2 +- web-transport/Cargo.toml | 2 +- 8 files changed, 14 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b83a555..e597fa5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,5 @@ members = [ resolver = "2" [workspace.dependencies] -web-transport-proto = { path = "web-transport-proto", version = "0.2.8" } -web-transport-trait = { path = "web-transport-trait", version = "0.2" } +web-transport-proto = { path = "web-transport-proto", version = "0.3" } +web-transport-trait = { path = "web-transport-trait", version = "0.3" } diff --git a/web-transport-proto/Cargo.toml b/web-transport-proto/Cargo.toml index 5d4bb30..2475a57 100644 --- a/web-transport-proto/Cargo.toml +++ b/web-transport-proto/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Luke Curley"] repository = "https://github.com/kixelated/web-transport" license = "MIT OR Apache-2.0" -version = "0.2.8" +version = "0.3.0" edition = "2021" keywords = ["quic", "http3", "webtransport"] diff --git a/web-transport-quiche/Cargo.toml b/web-transport-quiche/Cargo.toml index bf402a5..d861345 100644 --- a/web-transport-quiche/Cargo.toml +++ b/web-transport-quiche/Cargo.toml @@ -29,7 +29,7 @@ tokio = { version = "1", default-features = false, features = [ "time", ] } -tokio-quiche = "0.10" +tokio-quiche = "0.12" tracing = "0.1" url = "2" web-transport-proto = { workspace = true } diff --git a/web-transport-quiche/src/ez/send.rs b/web-transport-quiche/src/ez/send.rs index c195822..51021f1 100644 --- a/web-transport-quiche/src/ez/send.rs +++ b/web-transport-quiche/src/ez/send.rs @@ -5,7 +5,7 @@ use std::{ pin::Pin, task::{ready, Context, Poll, Waker}, }; -use tokio_quiche::quiche; +use tokio_quiche::quiche::{self}; use bytes::{Buf, Bytes}; use tokio::io::AsyncWrite; diff --git a/web-transport-quinn/src/error.rs b/web-transport-quinn/src/error.rs index 7beb165..3ce3e80 100644 --- a/web-transport-quinn/src/error.rs +++ b/web-transport-quinn/src/error.rs @@ -43,7 +43,7 @@ pub enum SessionError { ConnectionError(quinn::ConnectionError), #[error("webtransport error: {0}")] - WebTransport(#[from] WebTransportError), + WebTransportError(#[from] WebTransportError), #[error("send datagram error: {0}")] SendDatagramError(#[from] quinn::SendDatagramError), @@ -54,7 +54,7 @@ impl From for SessionError { match &e { quinn::ConnectionError::ApplicationClosed(close) => { match web_transport_proto::error_from_http3(close.error_code.into_inner()) { - Some(code) => WebTransportError::ApplicationClosed( + Some(code) => WebTransportError::Closed( code, String::from_utf8_lossy(&close.reason).into_owned(), ) @@ -62,7 +62,6 @@ impl From for SessionError { None => SessionError::ConnectionError(e), } } - quinn::ConnectionError::LocallyClosed => WebTransportError::LocallyClosed.into(), _ => SessionError::ConnectionError(e), } } @@ -71,18 +70,12 @@ impl From for SessionError { /// An error that can occur when reading/writing the WebTransport stream header. #[derive(Clone, Error, Debug)] pub enum WebTransportError { - #[error("application closed: code={0} reason={1}")] - ApplicationClosed(u32, String), - - #[error("locally closed")] - LocallyClosed, + #[error("closed: code={0} reason={1}")] + Closed(u32, String), #[error("unknown session")] UnknownSession, - #[error("unknown stream")] - UnknownStream, - #[error("read error: {0}")] ReadError(#[from] quinn::ReadExactError), @@ -264,8 +257,7 @@ pub enum ServerError { impl web_transport_trait::Error for SessionError { fn session_error(&self) -> Option<(u32, String)> { - if let SessionError::WebTransport(WebTransportError::ApplicationClosed(code, reason)) = self - { + if let SessionError::WebTransportError(WebTransportError::Closed(code, reason)) = self { return Some((*code, reason.to_string())); } diff --git a/web-transport-quinn/src/session.rs b/web-transport-quinn/src/session.rs index 4a789ab..d91d868 100644 --- a/web-transport-quinn/src/session.rs +++ b/web-transport-quinn/src/session.rs @@ -426,7 +426,7 @@ impl SessionAccept { // Read the VarInt at the start of the stream. let typ = VarInt::read(&mut recv) .await - .map_err(|_| WebTransportError::UnknownStream)?; + .map_err(|_| WebTransportError::UnknownSession)?; let typ = StreamUni(typ); if typ == StreamUni::WEBTRANSPORT { @@ -488,7 +488,7 @@ impl SessionAccept { ) -> Result, SessionError> { let typ = VarInt::read(&mut recv) .await - .map_err(|_| WebTransportError::UnknownStream)?; + .map_err(|_| WebTransportError::UnknownSession)?; if Frame(typ) != Frame::WEBTRANSPORT { log::debug!("ignoring unknown bidirectional stream: {typ:?}"); return Ok(None); diff --git a/web-transport-trait/Cargo.toml b/web-transport-trait/Cargo.toml index 71780e4..8c1f01e 100644 --- a/web-transport-trait/Cargo.toml +++ b/web-transport-trait/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Luke Curley"] repository = "https://github.com/kixelated/web-transport" license = "MIT OR Apache-2.0" -version = "0.2.0" +version = "0.3.0" edition = "2021" keywords = ["quic", "http3", "webtransport"] diff --git a/web-transport/Cargo.toml b/web-transport/Cargo.toml index 0dceb92..81ffe13 100644 --- a/web-transport/Cargo.toml +++ b/web-transport/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Luke Curley"] repository = "https://github.com/kixelated/web-transport" license = "MIT OR Apache-2.0" -version = "0.9.7" +version = "0.10.0" edition = "2021" keywords = ["quic", "http3", "webtransport"]