Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When WebSocket wins the race, the QUIC dial keeps going. If it lands, the Connection runs the MoQ handshake on it, swaps it in as the live session, sends an empty-URI GOAWAY on the WebSocket session, and drains it within the handover cap. The "WebSocket won" memo forgets the URL so the next dial gives QUIC its head start again. Adds Connection::transport(), reusing server::Transport as moq_tokio::Transport. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ariant Moves server::Transport to moq_tokio::Transport with no re-export, and splits WebTransport from raw QUIC on both the accept and dial sides. moq-ffi gains MoqTransport::WebTransport; the relay maps it to moq_auth::Transport::Quic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2476af001
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /// WebTransport over HTTP/3 on QUIC. | ||
| WebTransport, |
There was a problem hiding this comment.
Export WebTransport from the Go wrapper
When a WebTransport request reaches the FFI server, MoqRequest::transport() can now return this sixth value, but go/wrapper/server.go still exports constants only through TransportUnix. The ergonomic Go package therefore has no moq.TransportWebTransport symbol, forcing callers to import the raw FFI package or leaving transport handling unable to name the returned value. Add the documented wrapper constant alongside this enum addition. (Written by GPT-5.6 Sol)
AGENTS.md reference: AGENTS.md:L94-L96
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Agree. The other bindings alias the generated enum, but the Go wrapper re-exports each transport by hand and stopped at TransportUnix. Added moq.TransportWebTransport in 6bd6ee8.
(Written by Grok 4.7)
The FFI enum gained WebTransport, but the Go wrapper only re-exported constants through TransportUnix. Co-Authored-By: Grok 4.7 <noreply@x.ai>
cf3bb19
into
quest/m1/transport-upgrade/README
|
Landing the WebSocket-to-WebTransport upgrade on A WebSocket fallback session upgrades to WebTransport when that dial completes. Breaking on this unpublished quest branch: Also exported (Written by Grok 4.7) |
Completes /quest/m1/transport-upgrade/rust.md on the transport-upgrade line (#4180).
Problem
https://races QUIC against the WebSocket fallback, and the loser is dropped. When WebSocket wins (a lost Initial, a slow first dial), the session stays on TCP for its whole life, and the "WebSocket won" memo takes QUIC's head start away from every later dial to that host.Approach
race_transport_connecthands back the still-pending QUIC dial when WebSocket wins.Client::dialreturns it as anUpgradebeside the session. The attempt's existing connect timeout still bounds it.Connectionpolls the upgrade next to the live session. When the QUIC dial completes, status goes toMigratingwhile the MoQ handshake runs on QUIC. Then the QUIC session becomes live (Connected, epoch + 1). The WebSocket session getsGoaway::new().with_timeout(handover)and moves into the existingDrainingpath, which closes it at the handover cap. Live tracks splice onto QUIC at a group boundary through the origin, as in a peer GOAWAY.WEBSOCKET_WON. A failed upgrade logs at debug, restoresConnected, and the session stays on WebSocket. If the WebSocket session ends first, the pending dial is dropped and the redial races again.Shared::connectednow counts the session it replaces as ended, sosessions_started - sessions_endedstays at 1 across a migration. This also fixes the same drift that already existed on the peer-GOAWAY path.moq-tokio, since they read the memo). The QUIC path goes through an in-process UDP forwarder that holds every datagram until the test opens it, so WebSocket wins without depending on timing.websocket_upgrades_to_quic: groups 0, 1, 2 arrive in order, with group 1 written while QUIC comes up. The transport flips toWebTransport, epoch and presence advance, the memo is cleared, and the server's WebSocket session receives an empty-URI GOAWAY and closes. Stress-run 30x clean.quic_winning_opens_one_session: QUIC wins, and only one session is ever accepted.a_failed_upgrade_stays_connectedpinsMigratingand its rollback. The end-to-end test can't observeMigrating, because a moq-lite client finishes its handshake without waiting on the server.Impact
Breaking on
moq-tokio, so this line (#4180) now targetsdev.moq-tokio(breaking):server::Transportmoved to the crate root asmoq_tokio::Transport, with no re-export at the old path. It is shared by the accept and dial sides.moq-tokio(breaking): newTransport::WebTransportvariant, distinct from rawQuic(as_stris"webtransport"). The server now reports WebTransport sessions asWebTransportrather thanQuic, and the client reportshttps://dials asWebTransportandmoqt:///moql://dials asQuic.moq-tokio: newConnection::transport() -> Option<Transport>.moq-tokio: behavior change. AConnectionthat lands on WebSocket migrates to WebTransport when that dial completes, and briefly holds two sessions during the handover.Status::Migratingnow also covers this case (doc updated).moq-tokio: presence counters now count a migrated-away session as ended.moq-ffi: newMoqTransport::WebTransport(appended).MoqRequest::transport()now reports it for WebTransport sessions. The Dart bindings are regenerated.moq(Go): newTransportWebTransportconstant, so callers can name the valueRequest.Transport()now returns for WebTransport sessions.moq-relay:WebTransportmaps tomoq_auth::Transport::Quic, so the auth contract and wire are unchanged.moq-cli: callers updated to the new path.Alternatives
MigratingandConnectedback to back at the swap. Rejected: nobody could ever observe it.Migratinginstead spans the MoQ handshake on QUIC.Follow-ups
Checks:
moq-fficompiles again. The line mergedorigin/mainfor fix(ffi): name the binary config conversion so moq-ffi compiles #4157 after mergingorigin/dev. Workspace clippy and every changed-package nextest pass, andjust dart checkpasses.moq-uringtests fail withENOMEMinio_uringsetup on this host. This PR does not touchmoq-uring.Expose
Connection::transport()throughmoq-ffiand the bindings, if a consumer wants it.The qmux (WebSocket) lite session logs
WARN failed to send goaway: transport: connection closedeven though the peer received it. This is existing noise in the qmux adapter, now hit on every upgrade.Adding a
WebTransportvalue to the auth contract (moq_auth::Transport,js/auth) is left for a follow-up if an auth server needs to tell the two apart.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code