Skip to content

Conversation

@kixelated
Copy link
Collaborator

Summary

  • Remove loop in Capsule::decode() - now decodes single capsule instead of skipping GREASE
  • Add explicit Grease variant to Capsule enum
  • Fix GREASE detection per RFC 9297 Section 5.4 (0x29 * N + 0x17 instead of 0x1f * N + 0x21)
  • Change Capsule::read() to return Option<Self> (returns None on clean EOF)
  • Update web-transport-quinn to handle new API

Context

This changes the public API of the protocol layer to properly handle GREASE capsules and support decoding multiple capsules from a single buffer. Previously, decode() would loop internally and skip GREASE capsules, which made it impossible to decode multiple capsules from HTTP/3 DATA frames.

Breaking Changes

  • Capsule::read() now returns Option<Capsule> instead of Capsule
  • Capsule enum has new Grease variant

🤖 Generated with Claude Code

Changes:
- Remove loop in Capsule::decode() - now decodes single capsule instead of skipping GREASE
- Add explicit Grease variant to Capsule enum
- Fix GREASE detection per RFC 9297 Section 5.4 (0x29 * N + 0x17)
- Change Capsule::read() to return Option<Self> (returns None on clean EOF)
- Update web-transport-quinn to handle new API

This is foundational for properly handling HTTP/3 frames that may
contain multiple capsules or GREASE values.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 10, 2026

Warning

Rate limit exceeded

@kixelated has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 22 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

The changes introduce support for GREASE capsules and improve capsule parsing robustness across two files. In capsule.rs, a new Capsule::Grease variant is added, the is_grease logic is updated with revised thresholds, and Capsule::decode is refactored to handle GREASE capsules and store unknown capsule types instead of erroring. The Capsule::read return type is changed from Result<Self, CapsuleError> to Result<Option<Self>, CapsuleError> to handle end-of-stream conditions. In session.rs, the run_closed function is updated to handle the new Option return type, properly managing GREASE, unknown, and closed stream states without treating them as errors.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix capsule protocol handling' is concise and directly related to the main objective of the PR, which is to fix capsule protocol handling by removing the loop, adding Grease variant, and fixing GREASE detection per RFC 9297.
Description check ✅ Passed The description is well-structured and directly related to the changeset, covering the key modifications: removing the loop in decode(), adding Grease variant, fixing GREASE detection logic, changing return type, and updating dependent code.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-capsule-protocol

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

@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: 2

🤖 Fix all issues with AI agents
In `@web-transport-proto/src/capsule.rs`:
- Around line 27-34: The size check currently uses payload.remaining() which
only reflects buffered bytes and delays rejecting declared-too-large capsules;
change the logic in the block around buf.take(...) to first compare
payload.limit() (the declared length) against MAX_MESSAGE_SIZE and return
CapsuleError::MessageTooLong immediately when limit() > MAX_MESSAGE_SIZE, then
keep the existing check that if payload.remaining() < payload.limit() return
CapsuleError::UnexpectedEnd; update references to payload.limit(),
payload.remaining(), MAX_MESSAGE_SIZE, CapsuleError::MessageTooLong,
CapsuleError::UnexpectedEnd and ensure behavior in the calling read() remains
unchanged.
- Line 115: Capsule::Grease currently serializes to zero bytes which produces an
invalid wire capsule; update the encode/write path for the Grease variant (e.g.,
in Capsule::encode and any write methods that match on Self::Grease) to emit a
valid GREASE capsule header with a GREASE type byte computed as 0x29 * n + 0x17
(choose a random n per encode) followed by a zero-length payload (i.e., write
the type and a zero length prefix), or alternatively return an Err/panic from
encode/write for the Grease variant if you intend it to be decode-only; make
sure to reference and change the match arm handling Self::Grease so callers
receive either a proper GREASE capsule or a clear error.
🧹 Nitpick comments (2)
web-transport-proto/src/capsule.rs (2)

44-47: Redundant MAX_MESSAGE_SIZE check — already guaranteed by the outer guard.

After the check on line 28 ensures the full payload is ≤ MAX_MESSAGE_SIZE, the remaining bytes after consuming the 4-byte error code will always be less than MAX_MESSAGE_SIZE. This inner check is dead code.


159-160: CapsuleError::UnknownType is now dead code.

Since decode no longer returns this variant (unknown types produce Capsule::Unknown), this error variant is unreachable. Consider removing it to avoid confusion.

@kixelated kixelated enabled auto-merge (squash) February 10, 2026 01:52
@kixelated kixelated merged commit 818e1cf into main Feb 10, 2026
1 check passed
@kixelated kixelated deleted the fix-capsule-protocol branch February 10, 2026 01:58
@github-actions github-actions bot mentioned this pull request Feb 10, 2026
@github-actions github-actions bot mentioned this pull request Feb 10, 2026
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.

1 participant