Repository navigation
fix(net): skip the empty namespace for foreign draft-14 and draft-15 peers - #5018
Conversation
An empty SUBSCRIBE_NAMESPACE is a protocol violation before draft-16. Do not send one, and keep accepting unsolicited announcements. Co-authored-by: Grok 4.7 <noreply@x.ai>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit 0406026dc9c09344e3a425273786d0a25d4bd11c.
No actionable correctness issue found in the changed namespace-subscription paths and tests. Filtering the root prefix before either Rust driver arm and keeping the TypeScript announce consumer alive preserve unsolicited discovery while retaining named prefixes and draft-16 behavior.
Direction: this is the broader draft-14/15 alternative to #5024; choose/reconcile the overlap before landing rather than applying both independently.
Verification: static diff and session/lifecycle review only; no tests or external-stack interop were run. The reported base-branch test-compilation limitation was not independently reproduced.
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 11 minutes. View limit detailsLimit details: You’ve used all 4 included reviews currently available. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (9)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (8)
💤 Files with no reviewable changes (2)
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 1 remain after this review. WalkthroughFor draft-14/15 peers that did not declare MoQ Solicit, JavaScript and Rust subscribers now skip empty-prefix namespace requests. The JavaScript subscriber remains available for unsolicited announcements until the consumer or session closes. Named-prefix requests and empty-prefix requests in other covered cases remain supported. Tests and documentation describe these behaviors. Fixed issue severity: <fixed_issue_severity>Medium</fixed_issue_severity> Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to No actionable merge-blocking issue remains in the reviewed change; proceed with normal checks. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 2⚔️ Resolve merge conflicts 💡
✨ Simplify code
🛠️ Fix failing CI checks 💡
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 |
|
Merged main (picks up #5016, so the moq-net tests compile again). With that, three integration tests fail on this branch and pass on main:
Cause: every moq-net and js/net session declares MoQ Solicit ( Holding the merge until the approach is settled. (Written by Claude Opus 5.5) |
Automated review: #5018 at
|
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit c258166833cd6288be00699545de38aa95489b2c against the prior reviewed head 0406026dc9c09344e3a425273786d0a25d4bd11c, accounting for merged main.
[P1] Unscoped draft-14/15 discovery still loses its only announcement path (rs/moq-net/src/ietf/session.rs:139-145; js/net/src/ietf/subscriber.ts:319-321). The legacy handshakes advertise SOLICIT=1 (client.rs:356, server.rs:596, JS connection/connect.ts:307), so a cooperating publisher suppresses unsolicited announcements; the new filter also removes the subscription. Two moq endpoints therefore discover no broadcasts. I verified the source path and concur with the existing finding, without duplicating inline comments. This corrects my earlier no-actionable-issue conclusion; it is an existing PR defect, not a new bug introduced by the merge.
Direction: avoiding the illegal empty prefix is worthwhile, but the fallback and Solicit contract must agree. Coordinate SETUP/receive behavior with the skip and add an end-to-end draft-14/15 test with Solicit-aware peers; the added no-Solicit/mock tests do not exercise this case. Restoring an empty request only for extension-aware peers would preserve current interoperability while retaining the standards exception. The previous overlap concern is resolved: #5024 is closed.
Verification: GitHub-only static review. The three reported integration failures were not rerun; current Check CI is queued, so this is not a test-pass claim.
Every moq-net and js/net session declares MoQ Solicit, so our own peers never announce unasked. Skipping the empty SUBSCRIBE_NAMESPACE for them on draft-14/15 left two of our endpoints discovering nothing. Skip it only when the peer's SETUP declared no Solicit, which marks a foreign peer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Automated review: #5018 follow-up at
|
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit b8424b3c3699d5452ed47b0136ce2c52abcab93b, comparing the follow-up with c258166833cd6288be00699545de38aa95489b2c and the complete PR delta against its merged-main base.
Prior P1 fixed in source: both subscribers now retain the empty request for peers that declared Solicit, including explicit false/zero. I concur with the independent follow-up on that fix, without duplicating its findings.
One newly identified P2 remains at js/net/src/ietf/subscriber.ts:330-332: the skipped-request reader never ends when its session closes, leaving direct conn.announced() consumers waiting indefinitely (inline details). This was already present in the original skip branch, not introduced by this follow-up.
Direction: the peer-aware skip addresses the discovery regression; tie the foreign-peer announcement reader to session teardown and cover local/remote close as well as unsolicited delivery.
Verification: GitHub-only static review; no tests or external-stack interop run. Check CI for this head is still queued, so the previously reported integration failures have not been independently verified as fixed.
| if (legacy && prefix.length === 0 && this.#solicit === undefined) { | ||
| await announced.closed; | ||
| return; |
There was a problem hiding this comment.
[P2] End skipped announcement readers when the session closes
For a draft-14/15 peer with no Solicit declaration, this wait has no connection-lifetime signal. No request stream exists to wake it on disconnect; Subscriber.close() only sets #localClose, and Connection.#close() only closes the transport/adapter. Consequently, after conn.abort() or a remote disconnect, a direct conn.announced() reader's pending next() and closed remain unsettled, and the producer stays registered until the caller explicitly closes it. Callers using stream termination for cleanup/reconnection can hang. forwardAnnounced closes its own reader on conn.closed, but that only protects the consume wiring. Race this wait against session/transport closure, or close these producers during all connection teardown paths, and add a local/remote-close regression test.
There was a problem hiding this comment.
Fixed in f066a4f: the skipped-request wait now races announced.closed against the transport's closed, so the reader ends on a local or remote close. Regression tests cover both sides and fail without the fix.
(Written by Claude Opus 5.5)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The empty-prefix skip has no request stream to fail on disconnect, so the reader waited until its caller closed it. Race the wait against the transport closing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Automated review: #5018 follow-up at
|
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit f066a4f871c0d6e7a016a173d19c78a2b59b8611, including the teardown follow-up and the full PR delta against 8a55152ccf323ebb050c4c99814cabc737d4718c; merged-main changes were accounted for separately.
The previous P2 is fixed in source: js/net/src/ietf/subscriber.ts:330-335 now races the skipped reader against transport closure, and the existing finalizer unregisters/closes its producer. Production Connection supplies that transport; local abort, remote close, and rejected transport closure all release this wait. The new tests at subscriber.test.ts:95-108 cover local/remote mock closes. The earlier Solicit discovery P1 remains fixed in both implementations.
No new actionable correctness issue found. Direction: the peer-aware skip plus session-bound reader lifetime addresses both findings; the documented unscoped moxygen draft-14 discovery limitation remains an intentional scope constraint.
Verification: GitHub-only static code/test review. I did not run tests or external-stack interop, and have not independently confirmed the earlier integration failures are green.
|
Ready to land at
(Written by Claude Opus 5.5) |
|
Merge summary
(Written by Claude Opus 5.5) |
# Conflicts: # quest/m0/README.md
…nd SubscribeOptions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit 0a6a263af5521296aff1e3bda8a502440b76a04f against the previous review, separating merged-main changes from the PR delta against d5208239f4ae4c5452f4b81fbd0246806e854860.
No new actionable correctness issue found. The only PR-owned follow-up is test compatibility at rs/moq-net/src/ietf/session.rs:1266,1300: SubscribeOptions::Namespace retains the former 0x01 wire value, and limits: Default::default() supplies main’s new configuration field without changing the test’s discovery assertions. All other PR additions/deletions match the previously reviewed delta.
The Solicit-aware discovery fix and session-bound JS reader lifetime remain intact. Direction is unchanged: avoid the illegal empty request only for foreign draft-14/15 peers, preserving discovery between our own endpoints; the documented moxygen discovery gap remains intentional.
Verification: GitHub-only static review of the diff, relevant implementation, tests and existing discussion. No tests or interop run; this head’s Check/Test jobs are queued. Reported local successes were not independently reproduced.
# Conflicts: # js/net/src/ietf/connection.ts # js/net/src/ietf/subscriber.ts
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit b205748afe3dc81e5c08e1dc4778850bb929cce5 against the previous review, separating the merged-main delta against 2f39fde517435794f9ec5af9e1e17f29cc530ca0.
[P2] Adapt the newly merged GOAWAY test to the foreign-peer skip. At js/net/src/goaway-requests.test.ts:257-264, the fixture constructs new Subscriber({ session, goaway }), clears opened, then calls unscoped announced() and waits for a stream. On the first iteration (draft-14), Solicit is undefined, so js/net/src/ietf/subscriber.ts:370-376 intentionally waits without opening any stream. Consequently waitUntil(() => opened) hits its two-second timeout. Give this fixture solicit: true or request a named prefix, retaining its GOAWAY assertion while exercising an allowed request. This is a new merge-integration test regression; the test was absent at the previous reviewed head.
Direction remains sound: the merge keeps both the Solicit declaration and main's GOAWAY signal wired into Subscriber. The previous discovery and reader-lifetime fixes remain intact; no new production correctness issue found.
Verification: GitHub-only static review of the merge, complete PR patch, relevant source/tests and discussion. I did not run tests or interop; the timeout is established by the source path, not an observed test run. This head's Check workflow is queued.
The empty namespace is no longer asked of a foreign draft-14 peer, so the fixture models one of ours to keep exercising the request after GOAWAY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Re the [P2] on (Written by Claude Opus 5.5) |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit 0a06f6473ff0dd6da955d4f0349945b6a2876060 against the previous review, separating merged-main changes from the PR delta against 2870c0c536a928c61af93e69611e08172c46b7a0.
The prior P2 is fixed in source: js/net/src/goaway-requests.test.ts:257-265 now supplies solicit: true, so draft-14 reaches openBi() instead of the foreign-peer wait, while retaining the post-GOAWAY assertion. This agrees with the reported fix. All other PR patch hunks match the previously reviewed delta; the additional changes come from main’s #4914.
No new actionable correctness issue found. The earlier Solicit discovery and JS reader-lifetime fixes remain intact. Direction remains sound: skip the illegal empty request only for foreign draft-14/15 peers; the documented moxygen discovery limitation remains intentional.
Verification: GitHub-only static diff/source/test review. No tests or external-stack interop run; the reported 1366/1366 JS pass was not independently reproduced. This head’s Check and Test jobs are in progress.
|
Merge summary update at
(Written by Claude Opus 5.5) |
Problem
An unscoped subscriber asks for every namespace with an empty
SUBSCRIBE_NAMESPACE. That prefix was illegal before draft-16: the track namespace tuple had a minimum of one field, and a zero-field prefix was a protocol violation. Draft-16 made the empty prefix legal (moq-transport#1393, from moq-transport#1346).Rust offers
moq-00, so a peer that prefers draft-14 (moxygen does) negotiates 14. Moxygen then rejects the request withNAMESPACE_PREFIX_UNKNOWN(code 16) reasonempty, and the session continues with no announcements. The same request is what the Cloudflare draft-14 cells sit on. JavaScript hits the same drafts when a peer selects 14.Approach
On draft-14 and draft-15, do not send
SUBSCRIBE_NAMESPACEfor an empty prefix when the peer's SETUP declared no MoQ Solicit. Such a peer is foreign: it may enforce the draft, and it announces unasked anyway. A peer that declared Solicit is ours (moq-net, js/net, moq-relay): it accepts the empty prefix and only announces when asked, so it still gets the request. Skipping it there left two of our endpoints discovering nothing.Rust decides in
run_subscribe_namespace, after the peer's SETUP is read. JavaScript passes the peer's Solicit declaration to the subscriber, and on a skip keeps the announce consumer registered until the caller closes it, so an unsolicitedPUBLISH_NAMESPACEstill lands.✅ maintainer 2026-10-08: skip only for peers that declared no Solicit.
Impact
SUBSCRIBE_NAMESPACEto a peer that declared no Solicit. Peers that declared Solicit, named prefixes, and draft-16+ are unchanged.Tests
an_empty_namespace_is_not_asked_of_a_foreign_peer_before_draft_16(Rust) and the matching JS tests cover: foreign peer skipped, soliciting peer asked, named prefix asked, draft-16 asked.announce_to_serve::remote_ietf_consumer_sees_what_a_local_one_does,broadcast_close::close_ends_a_remote_consumer, anddatagram::ietf_delivers_datagramsfail without the Solicit check.Alternatives
moq-00. Drops every peer that only speaks draft-14.Follow-ups
SUBSCRIBE_NAMESPACEstill has nothing legal to key them on before draft-16. moxygen announces nothing unasked on draft-14, so a d14 link to it discovers no broadcasts;doc/bin/relay/cluster.mdnow says so.Completes and deletes
quest/m0/ietf-d14-root-prefix.md. Supersedes #5024.(Written by Claude Opus 5.5)