Skip to content

Conversation

@jhiatt-verkada
Copy link
Contributor

This is an alternate approach to #808 . In this version the return type of connect_with_fallback() is an enum with Quinn + WebSocket variants, and it's up to the caller to explicitly dispatch to moq_lite over the different types.

Advantages

  • Overall fewer lines of new code required
  • No need to synchronize with changes in other repository
  • Better performance, as other version introduced indirection in trait implementation

Disadvantages

  • moq_native::Client::connect_with_fallback() is not a simple replacement for moq_native::Client::connect() as the resulting moq_lite::Session instances are distinct types
  • Future modifications to fallback logic (e.g. introducing another possible transport) could require changes at the callsites

We'll get a cleaner diff when we add another connection method to
the struct in a future commit.
default_value = "200ms",
value_parser = humantime::parse_duration,
)]
#[serde(with = "humantime_serde")]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd initially avoided this crate because it's not clear that it's still maintained. It hasn't been updated in about 4 years so I wasn't sure if people would look askance at taking that dependency. However it does save a few lines of code here and in general I think humantime has nice ergonomics.

Comment on lines 119 to 128
match session {
moq_native::WebTransportSessionAny::Quinn(session) => {
let session = moq_lite::Session::connect(session, publish, subscribe).await?;
session.closed().await
}
moq_native::WebTransportSessionAny::WebSocket(session) => {
let session = moq_lite::Session::connect(session, publish, subscribe).await?;
session.closed().await
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's where we need to explicitly handle the possible transport implementations.

@jhiatt-verkada jhiatt-verkada force-pushed the websocket-fallback-with-explicit-enum branch 2 times, most recently from 8cad64a to ddf53c2 Compare January 2, 2026 21:06
If desired, use Client::connect_with_fallback() to use a
WebSocket-based transport when the QUIC connection fails. The
logic mirrors the TypeScript implementation by affording the
QUIC codepath a small (configurable) headstart.
@jhiatt-verkada jhiatt-verkada force-pushed the websocket-fallback-with-explicit-enum branch from ddf53c2 to 0f07786 Compare January 2, 2026 22:11
And remove <S> from Session so it's muuuch easier to use.
@kixelated
Copy link
Collaborator

Heh I probably could have make this into a separate PR. I haven't tested this outside of running just dev.

@jhiatt-verkada By removing <S> from Session with another trait, it's sooo much easier to return a Session with an arbitrary backend. There's no reason why the QUIC and moq-lite connection need to be performed separately (more confusing IMO) so this is generally an API improvement too.

@kixelated kixelated marked this pull request as ready for review January 3, 2026 06:19
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 3, 2026

Walkthrough

The PR overhauls connection/session flows and client/server APIs across crates. Clients gain new connect and connect_with_fallback methods that return a ready moq_lite::Session and implement a QUIC vs WebSocket race with tracked WebSocket wins. moq_lite::Session is converted from a generic wrapper to a non-generic type backed by a SessionInner trait. Server-side Request::close was renamed to reject and a new Request::accept performs the MoQ handshake and returns a Session. New dependencies for WebSocket support were added.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.27% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'moq-native: WebSocket fallback with explicit enum' is clear, concise, and accurately summarizes the main change—implementing a WebSocket fallback mechanism with an explicit enum approach in the moq-native module.
Description check ✅ Passed The description is well-related to the changeset, explaining the alternate approach to PR #808, the enum-based return type for connect_with_fallback(), and listing specific advantages and disadvantages of this implementation strategy.
✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
rs/moq-native/src/client.rs (2)

11-12: Consider bounded cache or TTL for WEBSOCKET_WON.

The WEBSOCKET_WON set grows unboundedly as the client connects to different servers. For long-running applications connecting to many unique servers, this could accumulate memory indefinitely. Consider adding a TTL or using an LRU cache with a bounded size.


304-310: Consider handling poisoned mutex gracefully.

Using .unwrap() on Mutex::lock() will panic if the mutex is poisoned (e.g., a thread panicked while holding it). While rare, this could crash the application. Consider using lock().unwrap_or_else(|e| e.into_inner()) to recover from poisoned state, or propagate as an error.

🔎 Proposed fix
-		match self.websocket_delay {
-			Some(delay) if !WEBSOCKET_WON.lock().unwrap().contains(&key) => {
+		match self.websocket_delay {
+			Some(delay) if !WEBSOCKET_WON.lock().unwrap_or_else(|e| e.into_inner()).contains(&key) => {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 34c3f80 and 79364b4.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • rs/hang-cli/src/client.rs
  • rs/hang-cli/src/server.rs
  • rs/hang/examples/video.rs
  • rs/libmoq/src/session.rs
  • rs/moq-clock/src/main.rs
  • rs/moq-lite/src/session.rs
  • rs/moq-native/Cargo.toml
  • rs/moq-native/examples/chat.rs
  • rs/moq-native/src/client.rs
  • rs/moq-native/src/server.rs
  • rs/moq-relay/src/cluster.rs
  • rs/moq-relay/src/connection.rs
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

In Rust crates, tests should be integrated within source files using inline test modules

Files:

  • rs/moq-relay/src/connection.rs
  • rs/moq-relay/src/cluster.rs
  • rs/moq-native/examples/chat.rs
  • rs/hang-cli/src/server.rs
  • rs/moq-clock/src/main.rs
  • rs/moq-lite/src/session.rs
  • rs/hang-cli/src/client.rs
  • rs/hang/examples/video.rs
  • rs/moq-native/src/client.rs
  • rs/moq-native/src/server.rs
  • rs/libmoq/src/session.rs
rs/**/Cargo.toml

📄 CodeRabbit inference engine (CLAUDE.md)

For Rust development, use the workspace configuration in rs/Cargo.toml

Files:

  • rs/moq-native/Cargo.toml
🧠 Learnings (3)
📚 Learning: 2025-12-20T13:14:11.182Z
Learnt from: Frando
Repo: moq-dev/moq PR: 794
File: rs/moq-native/src/iroh/client.rs:40-42
Timestamp: 2025-12-20T13:14:11.182Z
Learning: In rs/moq-native/src/iroh/server.rs, the Server::close method uses `&mut self` instead of `&self` (unlike Client::close) because the Server struct contains `FuturesUnordered<BoxFuture<...>>` which is Send but not Sync, and using `&self` would require Self: Sync. This is documented in the code and is an acceptable trade-off.

Applied to files:

  • rs/moq-relay/src/connection.rs
  • rs/moq-native/examples/chat.rs
  • rs/hang-cli/src/server.rs
  • rs/moq-lite/src/session.rs
  • rs/moq-native/src/server.rs
📚 Learning: 2025-12-20T13:22:14.684Z
Learnt from: Frando
Repo: moq-dev/moq PR: 794
File: justfile:150-156
Timestamp: 2025-12-20T13:22:14.684Z
Learning: In moq-relay, WebTransport connections automatically prepend the URL path (e.g., `/anon`) as a prefix to all broadcasts. However, raw QUIC and iroh connections have no HTTP layer, so there's no way to send a path. To match the expected broadcast path for the web UI, the prefix must be manually added to the broadcast name (e.g., `anon/{{name}}`) when publishing via iroh or raw QUIC.

Applied to files:

  • rs/moq-native/examples/chat.rs
  • rs/hang-cli/src/server.rs
📚 Learning: 2025-12-10T04:00:14.871Z
Learnt from: CR
Repo: moq-dev/moq PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-10T04:00:14.871Z
Learning: Applies to rs/**/Cargo.toml : For Rust development, use the workspace configuration in `rs/Cargo.toml`

Applied to files:

  • rs/moq-native/Cargo.toml
🧬 Code graph analysis (6)
rs/moq-relay/src/connection.rs (1)
rs/hang/src/model/broadcast.rs (1)
  • subscribe (82-84)
rs/moq-native/examples/chat.rs (1)
rs/hang-cli/src/client.rs (1)
  • client (6-31)
rs/moq-lite/src/session.rs (1)
rs/libmoq/src/session.rs (2)
  • connect (15-40)
  • close (60-63)
rs/hang/examples/video.rs (1)
rs/hang-cli/src/client.rs (1)
  • client (6-31)
rs/moq-native/src/client.rs (1)
rs/moq-lite/src/session.rs (2)
  • new (25-29)
  • connect (35-90)
rs/moq-native/src/server.rs (1)
rs/hang-cli/src/server.rs (1)
  • accept (43-68)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Check
🔇 Additional comments (21)
rs/hang/examples/video.rs (1)

34-37: LGTM!

The simplified single-step connection flow is clean and consistent with the broader API refactoring. The comment helpfully mentions connect_with_fallback as an alternative for WebSocket support.

rs/moq-native/src/server.rs (2)

248-256: Good rename from close to reject.

The new name better conveys the semantic intent—rejecting a request with an HTTP status code rather than closing an established connection.


258-270: Clean consolidation of transport and MoQ handshakes.

The accept method correctly handles both transport types:

  • WebTransport requires async .ok().await
  • QUIC is synchronous .ok()

Then delegates to moq_lite::Session::accept for the MoQ-level handshake. This aligns well with the client-side connect consolidation.

rs/moq-lite/src/session.rs (3)

8-10: Good use of trait objects to simplify the public API.

Erasing the generic via Arc<dyn SessionInner> allows Session to be returned uniformly from different transport backends (QUIC, WebSocket) without exposing generics to callers. This enables the connect_with_fallback pattern where the transport type is determined at runtime.

Also applies to: 24-29


167-170: Verify: closed() now always returns Err.

The method now unconditionally wraps the underlying error in Error::Transport and returns it as an error. Previously, clean session closures might have been distinguishable from error cases. Confirm this is the intended behavior—the TODO comment on line 166 suggests this area is pending refinement.


173-186: SessionInner trait implementation is sound.

The trait is correctly marked Send + Sync for use with Arc<dyn SessionInner>. The blanket implementation for S: web_transport_trait::Session properly delegates to the underlying session methods.

One observation: the closed() method boxes both the future and the error. This adds slight overhead but is necessary for type erasure. Given that session closure is not a hot path, this is an acceptable trade-off.

rs/moq-native/examples/chat.rs (1)

33-36: LGTM!

The simplified connection flow is consistent with the video.rs example and the broader API refactoring. The comments clearly explain the options available.

rs/hang-cli/src/server.rs (1)

81-82: LGTM!

The server-side flow now mirrors the client-side simplification. The Request::accept method handles both the transport-level handshake and MoQ session establishment in a single call.

rs/moq-relay/src/cluster.rs (1)

270-274: LGTM!

The cluster connection now uses the consolidated client.connect() API. The error context appropriately reflects that this is a connection failure to a remote node.

rs/hang-cli/src/client.rs (1)

13-16: LGTM!

The transition to connect_with_fallback is clean and correct. The logging placement before the connection attempt is appropriate for debugging, and passing origin.consumer for publishing aligns with the updated API semantics.

rs/moq-relay/src/connection.rs (2)

32-34: LGTM!

The rename from close to reject is semantically clearer for the error case.


53-60: LGTM!

The unified accept() API simplifies the flow by encapsulating session creation. The comment explaining the reversed subscribe/publish semantics for relays is helpful.

rs/libmoq/src/session.rs (1)

50-53: LGTM!

The transition to the unified client.connect(url, publish, consume) API simplifies the flow and maintains proper error handling with Arc-wrapped errors.

rs/moq-clock/src/main.rs (3)

56-57: LGTM!

Creating the origin before the match is clean, as only one branch will execute and consume its parts.


66-71: LGTM!

The connect_with_fallback usage for publishing is correct: Some(origin.consumer) for publishing tracks, None for subscribing. The .context("session closed") provides clearer error messages than the previous map_err.


76-76: LGTM!

Correct API usage for subscribing: None for publish, origin.producer for consume.

rs/moq-native/src/client.rs (5)

38-53: LGTM!

The ClientWebSocket configuration struct is well-designed with sensible defaults (200ms delay) and proper CLI/serde integration using humantime for duration parsing.


174-184: LGTM!

The unified connect() method cleanly combines QUIC connection and MoQ handshake into a single call.


217-223: Race logic handles failure cases correctly.

The tokio::select! with pattern matching works as intended: when a branch's future completes but its pattern doesn't match (returns None), that branch is disabled and select! waits for the remaining branch. The else only triggers when all branches complete without matching.


340-348: LGTM with note on state update.

The WebSocket session creation and WEBSOCKET_WON update logic is correct. The warning log level appropriately signals fallback usage for debugging.


327-338: No action needed. The third parameter to connect_async_with_config is correctly identified as disable_nagle in tokio-tungstenite 0.24. The code comment is accurate.

Comment on lines +26 to +27
humantime = "2.3"
humantime-serde = "1.1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check the workspace configuration
head -30 rs/Cargo.toml

Repository: moq-dev/moq

Length of output: 127


🏁 Script executed:

# Read the full rs/moq-native/Cargo.toml to verify the dependencies
cat -n rs/moq-native/Cargo.toml

Repository: moq-dev/moq

Length of output: 2209


🏁 Script executed:

# Check if web-transport-ws is defined in workspace dependencies
grep -A 20 "\[workspace.dependencies\]" rs/Cargo.toml

Repository: moq-dev/moq

Length of output: 101


🏁 Script executed:

# Find all Cargo.toml files in the repository
find . -name "Cargo.toml" -type f

Repository: moq-dev/moq

Length of output: 296


🏁 Script executed:

# Check if there's a root Cargo.toml with workspace definition
git ls-files | grep -i cargo

Repository: moq-dev/moq

Length of output: 306


🏁 Script executed:

# Check the root Cargo.toml for workspace dependencies
cat -n Cargo.toml

Repository: moq-dev/moq

Length of output: 1003


🌐 Web query:

tokio-tungstenite latest version crate

💡 Result:

The latest released version is tokio-tungstenite 0.28.0. [1][2]

Sources:

  • docs.rs documentation for tokio-tungstenite (0.28.0). [1]
  • tokio-tungstenite GitHub repository / project pages. [2]

🌐 Web query:

humantime crate version 2.3 latest

💡 Result:

Yes — the latest published humantime release is 2.3.0 (current as of Jan 3, 2026). [1][2]

Sources:
[1] humantime 2.3.0 documentation (docs.rs).
[2] Fedora package listing showing humantime 2.3.0.


🌐 Web query:

humantime-serde crate version 1.1 latest

💡 Result:

The latest published version of the humantime-serde crate is 1.1.1. [1][2]

Sources:

  • docs.rs listing for humantime-serde (v1.1.1). [1]
  • Fedora/crates packaging information showing humantime-serde 1.1.1. [2]

Update tokio-tungstenite to the latest version.

While humantime (2.3.0) and web-transport-ws (workspace reference) are correctly pinned, tokio-tungstenite = "0.24" is outdated. The latest version is 0.28.0. Update to tokio-tungstenite = "0.28" to align with current releases and security updates.

humantime-serde = "1.1" could also be more specific (latest is 1.1.1), though the semantic versioning allows the current specification.

🤖 Prompt for AI Agents
In rs/moq-native/Cargo.toml around lines 26 to 27, update the outdated
dependency version: change tokio-tungstenite from "0.24" to "0.28" to use the
latest release (0.28.0), and optionally pin humantime-serde to "1.1.1" for
specificity; adjust the version strings in the Cargo.toml dependency entries
accordingly and run cargo update to refresh the lockfile.

@kixelated kixelated enabled auto-merge (squash) January 3, 2026 23:27
@kixelated
Copy link
Collaborator

@jhiatt-verkada Let me know if this doesn't work for you, thanks for the PR!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
rs/moq-native/src/client.rs (2)

304-311: Address TODO: Use if-let chains for cleaner syntax.

The TODO comment suggests using if-let chains (stabilized in Rust 1.64) to simplify this match expression.

🔎 Suggested refactor using if-let chains
-		// TODO if let chain
-		match self.websocket_delay {
-			Some(delay) if !WEBSOCKET_WON.lock().unwrap().contains(&key) => {
-				tokio::time::sleep(delay).await;
-				tracing::debug!(%url, delay_ms = %delay.as_millis(), "QUIC not yet connected, attempting WebSocket fallback");
-			}
-			_ => {}
-		}
+		if let Some(delay) = self.websocket_delay
+			&& !WEBSOCKET_WON.lock().unwrap().contains(&key)
+		{
+			tokio::time::sleep(delay).await;
+			tracing::debug!(%url, delay_ms = %delay.as_millis(), "QUIC not yet connected, attempting WebSocket fallback");
+		}

328-340: Consider making WebSocket config values configurable.

The WebSocket message size (64 MB) and frame size (16 MB) limits are hardcoded. Consider making these configurable through ClientWebSocket if different limits are needed in the future.

🔎 Example: Add config fields

Add to ClientWebSocket:

#[serde(skip_serializing_if = "Option::is_none")]
pub max_message_size: Option<usize>,

#[serde(skip_serializing_if = "Option::is_none")]
pub max_frame_size: Option<usize>,

Then use config.websocket.max_message_size.unwrap_or(64 << 20) in the connection code.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 79364b4 and 72e97c7.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • rs/moq-native/Cargo.toml
  • rs/moq-native/src/client.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • rs/moq-native/Cargo.toml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

In Rust crates, tests should be integrated within source files using inline test modules

Files:

  • rs/moq-native/src/client.rs
🧠 Learnings (2)
📓 Common learnings
Learnt from: Frando
Repo: moq-dev/moq PR: 794
File: rs/moq-native/src/iroh/client.rs:40-42
Timestamp: 2025-12-20T13:14:11.182Z
Learning: In rs/moq-native/src/iroh/server.rs, the Server::close method uses `&mut self` instead of `&self` (unlike Client::close) because the Server struct contains `FuturesUnordered<BoxFuture<...>>` which is Send but not Sync, and using `&self` would require Self: Sync. This is documented in the code and is an acceptable trade-off.
Learnt from: CR
Repo: moq-dev/moq PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-10T04:00:14.871Z
Learning: Core protocol implementation in the `moq` layer must be generic and not contain media-specific logic; CDN/relay does not know anything about media
📚 Learning: 2025-12-20T13:14:11.182Z
Learnt from: Frando
Repo: moq-dev/moq PR: 794
File: rs/moq-native/src/iroh/client.rs:40-42
Timestamp: 2025-12-20T13:14:11.182Z
Learning: In rs/moq-native/src/iroh/server.rs, the Server::close method uses `&mut self` instead of `&self` (unlike Client::close) because the Server struct contains `FuturesUnordered<BoxFuture<...>>` which is Send but not Sync, and using `&self` would require Self: Sync. This is documented in the code and is an acceptable trade-off.

Applied to files:

  • rs/moq-native/src/client.rs
🧬 Code graph analysis (1)
rs/moq-native/src/client.rs (1)
rs/moq-lite/src/session.rs (2)
  • new (25-29)
  • connect (35-90)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Check
🔇 Additional comments (6)
rs/moq-native/src/client.rs (6)

39-54: LGTM: Clean configuration structure.

The ClientWebSocket config with optional delay is well-designed. The use of humantime for duration parsing provides good ergonomics, and the optional nature allows flexibility in configuration.


72-82: LGTM: Configuration plumbing is correct.

The WebSocket config is properly integrated into ClientConfig and plumbed through to the Client struct, following the same pattern as the existing tls field.

Also applies to: 98-98, 167-173


175-185: LGTM: Clean two-phase connection.

The connect method properly separates transport establishment (connect_quic) from protocol handshake (moq_lite::Session::connect), providing a clean QUIC-only connection path.


187-225: LGTM: Well-implemented connection race.

The fallback logic correctly races QUIC and WebSocket connections, handling per-transport failures gracefully while ensuring at least one succeeds. The use of tokio::select! is appropriate here, and both session types correctly implement the web_transport_trait::Session trait required by moq_lite::Session::connect.


227-291: LGTM: Return type now explicit.

Making the return type of connect_quic explicit (web_transport_quinn::Session) improves code clarity without changing functionality.


313-325: LGTM: URL scheme conversion is correct.

The scheme conversion logic properly handles all expected protocols (http, https, moql, moqt, ws, wss) and provides clear error messages for unsupported schemes.

Comment on lines +12 to +13
// Track servers (hostname:port) where WebSocket won the race, so we won't give QUIC a headstart next time
static WEBSOCKET_WON: LazyLock<Mutex<HashSet<(String, u16)>>> = LazyLock::new(|| Mutex::new(HashSet::new()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Memory leak: WEBSOCKET_WON grows unbounded.

The global WEBSOCKET_WON set is never cleared and will accumulate entries for every unique server the client connects to over its lifetime. In long-running processes (such as relays or services), this constitutes a memory leak.

Consider adding a bounded cache with LRU eviction or a TTL mechanism to prevent unbounded growth.


Consider more granular tracking key.

The key (hostname, port) doesn't include the URL path. If the same server hosts multiple services on different paths with different transport characteristics, they'll all share the same WebSocket-won status. Consider whether path-level granularity is needed for your use case.

🤖 Prompt for AI Agents
In rs/moq-native/src/client.rs around lines 12-13, WEBSOCKET_WON is a global
unbounded HashSet that will grow indefinitely; replace it with a thread-safe
bounded cache (e.g., an LRU cache or a TTL-backed cache) so entries are evicted
when capacity or time limits are reached, and ensure the cache API is wrapped
behind the same Mutex or a concurrent map to preserve thread-safety; also change
the key from (String, u16) to include the request path (e.g., (String, u16,
String) or a normalized URL string) if you need per-path granularity, and add a
configurable capacity/TTL constant and a simple periodic or on-access eviction
strategy so long-running processes don’t leak memory.


// Wrap WebSocket in WebTransport compatibility layer
// Similar to what the relay does: web_transport_ws::Session::new(socket, true)
let session = web_transport_ws::Session::new(ws_stream, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for web_transport_ws::Session::new usage patterns in the codebase to verify parameter semantics

rg -nP -C3 'web_transport_ws::Session::new\(' --type rust

Repository: moq-dev/moq

Length of output: 1108


Clarify the comment about is_server parameter difference between client and relay.

The false parameter is correct for the client (it should not be a server). However, the comment on line 342 is misleading—it references the relay's usage showing web_transport_ws::Session::new(socket, true) while the client uses false. This creates confusion about whether they're truly similar. Update the comment to clarify: the relay uses true because it accepts inbound connections (acts as server), while the client uses false because it initiates outbound connections (acts as client).

🤖 Prompt for AI Agents
In rs/moq-native/src/client.rs around line 343, the inline comment about the
is_server boolean is misleading because it references the relay using
web_transport_ws::Session::new(socket, true) without clarifying roles; update
the comment to explicitly state that the relay passes true because it accepts
inbound connections (acts as a server), whereas the client passes false here
because it initiates outbound connections (acts as a client), so keep the false
parameter and revise the comment text accordingly to avoid confusion.

Comment on lines +345 to +346
tracing::warn!(%url, "using WebSocket fallback");
WEBSOCKET_WON.lock().unwrap().insert(key);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use info! or debug! log level instead of warn!.

Line 345 logs "using WebSocket fallback" at the warn! level, but this is expected behavior when QUIC fails, not a warning condition. Consider using info! or debug! instead.

🔎 Suggested fix
-		tracing::warn!(%url, "using WebSocket fallback");
+		tracing::info!(%url, "using WebSocket fallback");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tracing::warn!(%url, "using WebSocket fallback");
WEBSOCKET_WON.lock().unwrap().insert(key);
tracing::info!(%url, "using WebSocket fallback");
WEBSOCKET_WON.lock().unwrap().insert(key);
🤖 Prompt for AI Agents
In rs/moq-native/src/client.rs around lines 345 to 346, replace the
tracing::warn!(%url, "using WebSocket fallback"); call with a lower-severity log
(e.g., tracing::info!(%url, "using WebSocket fallback"); or
tracing::debug!(...)) so this expected fallback is not treated as a warning;
keep the subsequent WEBSOCKET_WON.lock().unwrap().insert(key); unchanged and
ensure the tracing macro you choose is in scope.

@kixelated kixelated merged commit 59f50c8 into moq-dev:main Jan 3, 2026
1 check passed
@moq-bot moq-bot bot mentioned this pull request Jan 3, 2026
@jhiatt-verkada jhiatt-verkada deleted the websocket-fallback-with-explicit-enum branch January 5, 2026 20:24
@jhiatt-verkada
Copy link
Contributor Author

@jhiatt-verkada Let me know if this doesn't work for you, thanks for the PR!

@kixelated I appreciate your work in getting this merged! I just did a build from main and it's working fine for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants