-
Notifications
You must be signed in to change notification settings - Fork 39
Treat UnexpectedEnd as graceful session closure #130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Changed logging to display the actual wire-format QUIC stream ID (u64) instead of just the stream index. This helps debug stream numbering issues where server-initiated streams should use odd IDs (1, 5, 9...) and client-initiated streams use even IDs (0, 4, 8...).
These constructors need to be public to allow external code to wrap raw Quinn streams in WebTransport stream types when bypassing the Session::open_bi() method for atomic header+data writes.
WalkthroughThis pull request makes the Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
web-transport-quinn/src/session.rs (4)
96-125: Graceful handling ofUnexpectedEndis correct, but detection via string match is brittleThe behavioral change to treat EOF on the CONNECT capsule stream as a graceful WebTransport session closure (returning
(0, "session closed")) matches the intent of mappingCapsuleError::UnexpectedEndto a clean shutdown, and the_send_keep_alivebinding correctly keeps the send half alive to avoid sending a FIN prematurely.However, detecting
UnexpectedEndby checkingformat!("{e:?}").contains("UnexpectedEnd")is fragile and could break if theDebugrepresentation of the error ever changes, or if other error variants happen to include the same substring.If possible, consider matching the concrete error variant instead of relying on its string form, e.g.:
match web_transport_proto::Capsule::read(&mut recv).await { // ... - Err(e) => { - let error_str = format!("{e:?}"); - if error_str.contains("UnexpectedEnd") { - log::info!("WebTransport CONNECT stream closed by peer (graceful)"); - return (0, "session closed".to_string()); - } else { - log::error!("WebTransport CONNECT stream capsule read error: {e:?}"); - return (1, format!("capsule error: {e:?}")); - } - } + Err(web_transport_proto::CapsuleError::UnexpectedEnd) => { + log::info!("WebTransport CONNECT stream closed by peer (graceful)"); + return (0, "session closed".to_string()); + } + Err(e) => { + log::error!("WebTransport CONNECT stream capsule read error: {e:?}"); + return (1, format!("capsule error: {e:?}")); + } }(or whatever concrete path to the error type is exposed by
web_transport_proto).This keeps the new semantics while making the match robust to formatting changes.
195-201: Replaceprintln!inopen_biwith structured logging or a debug-only pathThe detailed diagnostics for
open_bi(stream ID, index, header bytes in hex) are useful, but unconditionalprintln!calls in a library will spam stdout and add allocation/formatting overhead for every bidirectional stream opened.Consider switching to the existing
logmacros at an appropriate level (e.g.log::debug!orlog::trace!) or gating this behind a feature/compile-time flag:let hex_str: String = self.header_bi.iter().map(|b| format!("{:02x}", b)).collect::<Vec<_>>().join(" "); let stream_id_u64: u64 = send.id().into(); log::debug!( "open_bi: QUIC stream ID: {} (index={}, {}), writing header ({} bytes): [{}]", stream_id_u64, send.id().index(), send.id(), self.header_bi.len(), hex_str, ); // ... log::debug!("open_bi: QUIC stream ID {} header written successfully", stream_id_u64);This keeps the observability benefit while respecting caller control over logging output.
473-500: Avoidprintln!inpoll_accept_biin favor of existing logging infrastructureThe
println!calls that log accepted and validated stream IDs are helpful for debugging, but they unconditionally write to stdout in all builds, which is undesirable for a reusable transport library.Prefer using
log::debug!orlog::trace!here so that applications can control verbosity via logger configuration:let (send, recv) = res?; log::debug!("poll_accept_bi: accepted stream ID: {}, queuing for decode", recv.id()); let pending = Self::decode_bi(send, recv, self.session_id); // ... if let Some((send, recv)) = res { log::debug!("poll_accept_bi: returning validated stream ID: {} to application", recv.id()); // wrap and return }This keeps the tracing detail without hard-wiring I/O to stdout.
512-535: Consolidatedecode_bidiagnostics under the logging frameworkThe new
println!calls indecode_bi(start marker, type byte, session ID, mismatch, success) provide excellent visibility into bidirectional stream decoding, but for a library they should ideally go through the logging system rather than directprintln!s, and possibly atdebug/tracelevel:log::debug!("decode_bi: start - stream ID: {}", recv.id()); // ... log::debug!( "decode_bi: stream {} - read type byte: 0x{:02x} (expected WEBTRANSPORT = 0x41)", recv.id(), typ.into_inner(), ); // and similar for mismatch, session_id, successThat way users can enable this rich tracing when needed without incurring unconditional stdout noise and overhead in all environments.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
web-transport-quinn/src/recv.rs(1 hunks)web-transport-quinn/src/send.rs(1 hunks)web-transport-quinn/src/session.rs(6 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
web-transport-quinn/src/recv.rs (1)
web-transport-quinn/src/send.rs (1)
new(21-23)
web-transport-quinn/src/send.rs (1)
web-transport-quinn/src/recv.rs (1)
new(18-20)
web-transport-quinn/src/session.rs (3)
web-transport-quinn/src/settings.rs (1)
connect(37-44)web-transport-proto/src/capsule.rs (1)
read(74-88)web-transport-quinn/src/connect.rs (2)
into_inner(103-105)session_id(91-96)
🔇 Additional comments (2)
web-transport-quinn/src/send.rs (1)
20-23: PublicSendStream::newlooks reasonable and consistentMaking
SendStream::newpublic aligns withRecvStream::newand gives consumers a straightforward way to wrap rawquinn::SendStreams. No hidden invariants appear to be violated here.web-transport-quinn/src/recv.rs (1)
17-20: PublicRecvStream::newis consistent with the send-side APIExposing
RecvStream::newpublicly mirrorsSendStream::newand preserves all existing behavior; the wrapper remains a thin newtype aroundquinn::RecvStream.
Problem
When the CONNECT stream receives
UnexpectedEndduring capsule reading,the session closes with error code 1. However,
UnexpectedEndtypicallymeans the peer closed their end of the stream normally, which is a valid
way to end a WebTransport session.
This causes issues when integrating with clients that close the CONNECT
stream to signal session end rather than sending a
CloseWebTransportSessioncapsule.
Solution
UnexpectedEnd(graceful, code 0) and other errors (code 1)Changes
UnexpectedEndnow returns code 0 ("session closed") instead of code 1_sendto_send_keep_alivefor clarity (documents why we keep it)