Skip to main content

AllowEarlyData

Struct AllowEarlyData 

Source
pub struct AllowEarlyData;
Expand description

The caller’s per-request statement that this request may go into TLS 1.3 early data (0-RTT).

Put into http::Extensions on the request. Absent, the request waits for the handshake to complete, and there is no configuration in which a request the caller did not mark ends up in early data. Present against a transport reporting EarlyDataSupport::None, it is a typed UnsupportedCapability rather than a silent no-op.

§What marking a request asserts, and what it does not

It is an assertion that replaying this request is SAFE — not that replaying it is POSSIBLE. Those are different questions, and only the caller can answer the first one.

RequestBody::retry_kind answers the second: Free, ViaFactory, Impossiblecan I send these bytes again. A transport needs that answer, because a rejected 0-RTT request has to be replayed after the handshake and a RetryKind::Impossible body cannot be. So RetryKind is a correctness precondition here, and it is checked as one.

It is emphatically not a safety condition, and reading it as one is the mistake to avoid. POST /transfer with a fully buffered body is RetryKind::Free — trivially replayable, and precisely the request that must never enter early data, because an attacker can replay it too. quinn says the same in one line: “this enables transmission of 0-RTT data, which is vulnerable to replay attacks, and should therefore never invoke non-idempotent operations”.

The notion that would answer the safety question — method safety and idempotency — deliberately does not exist in this codebase, and its absence is written down where the one v0.2 retry lives. RFC 8470 §2 puts the default on the conservative side (“clients MAY send requests with safe HTTP methods … and MUST NOT send unsafe methods (or methods whose safety is not known) in early data”) and, in the same sentence, says why a method table cannot be the whole answer: “absent other information”. GET is not safe on plenty of real APIs, and only the caller knows which. Hence this extension: a caller-visible decision, with a method check beneath it, rather than a table hidden in a transport.

§The third failure path

A request placed in early data can fail in three places, not one: no usable key material (nothing was risked, fall back silently), the server rejecting the 0-RTT keys (replay on the same connection once the handshake finishes — the transport’s job, invisible to the caller), and HTTP 425 Too Early (RFC 8470 §5.2), which arrives a full round trip later and must be retried not in early data. The third is a status-code branch in the client, not in a transport.

A retry built for a 425 must remove this extension from the request it replays. RFC 8470 requires it, and it is not a formality: on hclient-h3 this mark is part of the connection pool’s key, so a replay that kept it would ask for the early-data connection and — if that one has been evicted or closed since — would open a fresh one and go out in early data again, to the server that just refused to risk it. See hclient_h3::early.

§The other boundary: an origin

The mark does not cross one, and hclient’s redirect stage drops it on the same condition that drops Cookie and Authorization — the host or scheme changed.

The asymmetry is the point and the two halves are easy to conflate. This is a claim about what a request does at a server, so a caller who marked a request for origin A never judged origin B, and carrying it across would act on a judgement nobody made. A method change is the opposite case and the mark stays: a 303 rewriting POST to GET leaves a request strictly less consequential than the one already vouched for.

Trait Implementations§

Source§

impl Clone for AllowEarlyData

Source§

fn clone(&self) -> AllowEarlyData

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for AllowEarlyData

Source§

impl Debug for AllowEarlyData

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for AllowEarlyData

Source§

impl PartialEq for AllowEarlyData

Source§

fn eq(&self, other: &AllowEarlyData) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for AllowEarlyData

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.