Skip to content

quinn: expose accepted_0rtt() on Connection#2613

Open
HuginSecurity wants to merge 1 commit into
quinn-rs:mainfrom
HuginSecurity:hugin-accepted-0rtt
Open

quinn: expose accepted_0rtt() on Connection#2613
HuginSecurity wants to merge 1 commit into
quinn-rs:mainfrom
HuginSecurity:hugin-accepted-0rtt

Conversation

@HuginSecurity
Copy link
Copy Markdown

Summary

The high-level quinn::Connection wrapper omits accepted_0rtt() even though quinn_proto::Connection already exposes it publicly. Server-side consumers that accept 0-RTT early data need this signal to safely gate replay-sensitive operations (e.g. non-idempotent HTTP methods on an HTTP/3 MITM listener) without falling back to time-window heuristics.

This adds a one-line delegation. No behavioral change to other code paths.

Motivation

Building an HTTP/3 MITM interception proxy (Hugin), I needed to decide per-request whether to reject non-idempotent methods (POST/PUT/PATCH/DELETE) that arrived as 0-RTT early data — replayable and therefore unsafe for anything with side effects. The only public signal available was a time-since-handshake heuristic ("if the stream arrived within N ms of handshake start, assume it's 0-RTT"), which is imprecise under network jitter and adversarial timing.

The exact signal already exists in quinn_proto::Connection::accepted_0rtt(). Exposing it on the high-level Connection closes the gap with no new API surface on the proto side.

Test plan

  • Local build + test of a downstream project using the accessor
  • Any upstream tests reviewers would like me to add (happy to extend)

Notes

Targeted at main off tag quinn-0.11.9. The change is a single public method; semver-compatible as a minor-version addition.

The high-level Connection wrapper in quinn 0.11.x omits accepted_0rtt()
even though quinn_proto::Connection already has it public. Server-side
consumers that accept 0-RTT early data need this signal to safely gate
replay-sensitive operations (e.g. non-idempotent HTTP methods on an H3
MITM listener) without relying on time-window heuristics.

One-line delegation, no behavioral change to other code paths.
@Ralith
Copy link
Copy Markdown
Collaborator

Ralith commented Apr 18, 2026

The given motivation is confused. 0-RTT application data is only processed prior to the handshake by explicit application action: if you don't call Connecting::into_0rtt, then there is no way for you to receive any application data until the handshake has succeeded, at which point there is no replay hazard.

If your TLS configuration allows 0-RTT, then it is always unconditionally "accepted" (in the formal sense exposed by this accessor) regardless of whether you choose to process it before handshake completion, and regardless of whether any particular stream I/O occurred before or after the handshake. The peer which typically cares about this is the client, which needs to know if 0-RTT data it sent was discarded. That information is already exposed in the ZeroRttAccepted future.

I think what you actually want to know is whether an individual stream read completed prior to the handshake. That information is already available in the completion timing of the ZeroRttAccepted future.

@flub
Copy link
Copy Markdown
Contributor

flub commented Apr 20, 2026

I think you are after https://docs.rs/quinn/latest/quinn/struct.RecvStream.html#method.is_0rtt if you need to decide whether to accept non-idempotent operations.

@Ralith Ralith mentioned this pull request Apr 21, 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.

3 participants