Windows support: dual-stack IPv4/IPv6 sockets, setup.bat, and just dev#1732
Conversation
Quinn and the relay's axum HTTP/HTTPS listeners bind a single `[::]` socket and rely on the OS to route both address families. Linux does, but Windows defaults IPV6_V6ONLY to on, so an IPv6 socket silently drops all IPv4 traffic. A relay on `[::]` is then unreachable over 127.0.0.1, and a client on `[::]` cannot dial an IPv4 server (the existing pick_addr IPv4-mapping only works on a dual-stack socket). Clear IPV6_V6ONLY before binding via socket2. A new bind_udp helper backs the quinn and noq client/server sockets; a new public bind_tcp helper backs the relay and CLI HTTP/HTTPS listeners via axum_server::from_tcp. Verified end-to-end over IPv4 (127.0.0.1) on Windows: HTTP cert fetch and a QUIC publish/subscribe round-trip both succeed (relay logs the client as ::ffff:127.0.0.1). Both were connection-refused before. Public API: adds moq_native::bind_tcp (additive). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a winget-based setup.bat and a Windows setup page (registered in the sidebar and linked from the setup index). The script installs the Rust + JS toolchain, the MSVC C++ Build Tools, the ffmpeg CLI used by the demo, and gh/uv. Guard the demo free-port picker so it falls back to the start port when lsof is unavailable (Git Bash on Windows). With ffmpeg present and run from Git Bash, `just dev` now works end-to-end on Windows: relay, ffmpeg publish, and the web UI at localhost:5173. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
WalkthroughThis PR adds dual-stack IPv4/IPv6 socket binding utilities ( 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches✨ Simplify code
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.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
setup.bat (1)
1-73:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse CRLF line endings for batch reliability on Windows.
This
.batfile is currently LF-only; convert it to CRLF to avoid cmd parsing edge cases on Windows.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@setup.bat` around lines 1 - 73, The setup.bat file is currently using LF (Unix-style) line endings, which can cause parsing issues on Windows when the script is executed. Convert all line endings throughout the entire setup.bat file from LF to CRLF (Windows-style carriage return + line feed). This can be done using a text editor's line ending conversion feature (most modern editors like VS Code have this functionality) or by running a command-line tool designed for this purpose. Ensure the entire file, from the opening `@echo` off to the closing endlocal, uses CRLF line endings consistently.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@rs/moq-native/src/util.rs`:
- Around line 118-125: The bind_udp_ipv6_is_dual_stack test currently assumes
unconditional IPv6 and only_v6 socket option support, which creates CI flakiness
on platforms without these capabilities. Since bind_udp is intentionally
best-effort, modify the test to gracefully handle cases where IPv6 binding or
reading the only_v6 socket option is not supported. Use conditional logic (such
as skip or early return) to handle failures in the bind_udp call at line 122 or
the only_v6call at line 124, rather than panicking on unsupported platforms.
---
Outside diff comments:
In `@setup.bat`:
- Around line 1-73: The setup.bat file is currently using LF (Unix-style) line
endings, which can cause parsing issues on Windows when the script is executed.
Convert all line endings throughout the entire setup.bat file from LF to CRLF
(Windows-style carriage return + line feed). This can be done using a text
editor's line ending conversion feature (most modern editors like VS Code have
this functionality) or by running a command-line tool designed for this purpose.
Ensure the entire file, from the opening `@echo` off to the closing endlocal, uses
CRLF line endings consistently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2bf90321-3a82-43fc-ba06-b6c686f6eabd
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (12)
demo/justfiledoc/.vitepress/config.tsdoc/setup/index.mddoc/setup/windows.mdrs/moq-cli/src/web.rsrs/moq-native/Cargo.tomlrs/moq-native/src/lib.rsrs/moq-native/src/noq.rsrs/moq-native/src/quinn.rsrs/moq-native/src/util.rsrs/moq-relay/src/web.rssetup.bat
| #[test] | ||
| fn bind_udp_ipv6_is_dual_stack() { | ||
| // An IPv6 wildcard bind should come back dual-stack so IPv4 traffic | ||
| // reaches it. socket2 lets us read the option back to confirm. | ||
| let socket = bind_udp("[::]:0".parse().unwrap()).unwrap(); | ||
| let socket = socket2::Socket::from(socket); | ||
| assert!(!socket.only_v6().unwrap(), "IPv6 socket should be dual-stack"); | ||
| } |
There was a problem hiding this comment.
Harden the IPv6 dual-stack test for platform capability variance.
bind_udp is intentionally best-effort, but Line [122] and Line [124] currently require unconditional IPv6 + only_v6 support, which can fail on valid environments and create CI flakiness.
Suggested adjustment
#[test]
fn bind_udp_ipv6_is_dual_stack() {
- let socket = bind_udp("[::]:0".parse().unwrap()).unwrap();
+ let socket = match bind_udp("[::]:0".parse().unwrap()) {
+ Ok(socket) => socket,
+ Err(err)
+ if matches!(
+ err.kind(),
+ std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::Unsupported
+ ) =>
+ {
+ return; // host/runner doesn't support this capability
+ }
+ Err(err) => panic!("unexpected bind failure: {err}"),
+ };
let socket = socket2::Socket::from(socket);
- assert!(!socket.only_v6().unwrap(), "IPv6 socket should be dual-stack");
+ if let Ok(only_v6) = socket.only_v6() {
+ assert!(!only_v6, "IPv6 socket should be dual-stack");
+ }
}📝 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.
| #[test] | |
| fn bind_udp_ipv6_is_dual_stack() { | |
| // An IPv6 wildcard bind should come back dual-stack so IPv4 traffic | |
| // reaches it. socket2 lets us read the option back to confirm. | |
| let socket = bind_udp("[::]:0".parse().unwrap()).unwrap(); | |
| let socket = socket2::Socket::from(socket); | |
| assert!(!socket.only_v6().unwrap(), "IPv6 socket should be dual-stack"); | |
| } | |
| #[test] | |
| fn bind_udp_ipv6_is_dual_stack() { | |
| // An IPv6 wildcard bind should come back dual-stack so IPv4 traffic | |
| // reaches it. socket2 lets us read the option back to confirm. | |
| let socket = match bind_udp("[::]:0".parse().unwrap()) { | |
| Ok(socket) => socket, | |
| Err(err) | |
| if matches!( | |
| err.kind(), | |
| std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::Unsupported | |
| ) => | |
| { | |
| return; // host/runner doesn't support this capability | |
| } | |
| Err(err) => panic!("unexpected bind failure: {err}"), | |
| }; | |
| let socket = socket2::Socket::from(socket); | |
| if let Ok(only_v6) = socket.only_v6() { | |
| assert!(!only_v6, "IPv6 socket should be dual-stack"); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@rs/moq-native/src/util.rs` around lines 118 - 125, The
bind_udp_ipv6_is_dual_stack test currently assumes unconditional IPv6 and
only_v6 socket option support, which creates CI flakiness on platforms without
these capabilities. Since bind_udp is intentionally best-effort, modify the test
to gracefully handle cases where IPv6 binding or reading the only_v6 socket
option is not supported. Use conditional logic (such as skip or early return) to
handle failures in the bind_udp call at line 122 or the only_v6call at line 124,
rather than panicking on unsupported platforms.
Move the dual-stack socket helpers out of the internal `util` module into a new `pub mod bind`, so callers read `moq_native::bind::tcp` / `bind::udp` and the crate-internal sockets use `crate::bind::udp`. `udp` is now public alongside `tcp`. Also drops the public-to-private doc links the flat `bind_tcp` re-export carried. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR #1732 added an autolink to http://localhost:4443/certificate.sha256 in setup/windows.md, which VitePress can't reach at build time, failing the doc build (and every PR's `just ci`). Generalize the existing localhost exemption from a single :5173 literal to a regex covering any localhost URL. Folded in here so this PR is green on its own; supersedes #1734. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Two related changes that get MoQ building, running, and developing on Windows.
1. Dual-stack IPv6 sockets (the bug)
Quinn and the relay's axum HTTP/HTTPS listeners bind a single
[::]socket and rely on the OS to route both address families. Linux does, but Windows defaultsIPV6_V6ONLYto on, so an IPv6 socket silently drops all IPv4 traffic. The result on Windows:[::]is unreachable over127.0.0.1(both QUIC/UDP and the cert/WebSocket HTTP listener), and[::]cannot dial an IPv4 server (the existingpick_addrIPv4-mapping only works on a dual-stack socket).Fix: clear
IPV6_V6ONLYbefore binding, viasocket2. The helpers live in a newpub mod bind:bind::udpbacks the quinn and noq client/server sockets (crate::bind::udp).bind::tcpbacks the relay and CLI HTTP/HTTPS listeners throughaxum_server::from_tcp.2. Windows onboarding
setup.batat the repo root: awinget-based installer for the Rust + JS toolchain, the MSVC C++ Build Tools, theffmpegCLI (demo media), andgh/uv. Idempotent.doc/setup/windows.md(registered in the sidebar, linked from the setup index).lsofbehindcommand -vand falls back to the start port when it is missing (Git Bash ships nolsof). This unblocksjust dev.Why
Out of the box on Windows:
cargo buildworked only afterrustup update(deps need a recent stable), the relay/clients couldn't talk over IPv4, andjust devfailed onlsof+ the missing POSIX shell.Verification (Windows 11, MSVC toolchain)
moq-native+moq-relaytests pass (the 16auth::testsfailures are pre-existing and reproduce on the base tree: a Windows temp-path-as-URL fixture issue, unrelated to this change).cargo docwith-D warningsis clean onmoq-native.127.0.0.1): HTTP cert fetch and a QUIC publish/subscribe clock round-trip both succeed; the relay logs the client as::ffff:127.0.0.1. Both were connection-refused before.just devfrom a Git Bash terminal runs the relay, publishes Big Buck Bunny via ffmpeg, and serves the web UI atlocalhost:5173(playback confirmed).Reviewer notes
moq_native::bindmodule withbind::tcpandbind::udp(additive, non-breaking). Targetsmainper the branch rules (bug fix + additive).cargo clippyon this machine flags ~20 pre-existingresult_large_errlints inquinn.rs/tls.rs; they come from clippy 1.96 being newer than the Nix-pinned toolchain, not from this change. None are in the new code.setup.batwas run on a real machine; thejust devchain was smoke-tested end to end.(Written by Claude)