Skip to main content

RequireVersion

Struct RequireVersion 

Source
pub struct RequireVersion(pub Version);
Expand description

The caller’s per-request statement that this request needs a particular HTTP version, and must fail rather than go out over another one.

Put into http::Extensions on the request, and read by the transport at the moment the protocol becomes known — which is before the head is written, on every transport here that honours one. Absent, the transport picks as it always did.

§It is AllowEarlyData’s mechanism with the polarity reversed

Same shape: a mark in the request’s extensions that a transport reads and acts on before sending, Copy, defined in this crate because transports read it and do not depend on hclient. The difference is that one is a permission and this is a requirement, and that difference is why both have to be per request rather than per client — see below.

§Why a demand and not a question

Capabilities::full_duplex and its neighbours report the floor: the value that holds on the worst protocol a transport might negotiate. That is right for a static answer and cannot be otherwise — Cargo unifies features across a graph, so a library built on hclient can never know whether some other crate turned http2 on — but it leaves a caller who genuinely needs HTTP/2 with no way to act.

The two answers that do not work:

  • Per response. Response::version() already answers it, honestly, and after the fact. A caller structured for bidirectional streaming has to decide before it sends.
  • Per connection. There is no connection handle in the public API, so it means either a new seam or a query answered from a pool — and the pooled answer is racy in the way that matters: the entry can be evicted between the answer and the request that relied on it. It would be a fact about the past presented as a promise about the next request.

This is the third: the caller states the requirement, and the transport converts “the floor says no” into “this connection says yes” for one request, or fails it before committing to a shape that would deadlock.

§Why it cannot be a client-level setting

Turning an ALPN outcome into a request failure is correct for gRPC, whose RPC cannot proceed over HTTP/1.1 at all, and wrong for a browser-shaped client, which should degrade quietly. Only the caller knows which of the two it is — the same argument that put AllowEarlyData in the caller’s hands rather than in a transport’s configuration.

§Exact match, deliberately, not a minimum

RequireVersion(HTTP_2) is satisfied by HTTP/2 and by nothing else. It is tempting to read it as “at least”, and there is no ordering that makes that mean anything: a caller who needs h2 framing does not want HTTP/3 instead, and a caller who needs HTTP/1.1 — to keep an upgrade path open, say — wants strictly less than HTTP/2, not more. A “minimum” reading would satisfy the first demand with the wrong protocol and be unable to express the second at all.

§Refusal, and the two shapes it takes

A transport that always speaks one version still honours demands — hclient-h3 reports version_select: true and answers RequireVersion(HTTP_3) by proceeding and everything else with VersionNotAvailable. Reporting false there would refuse the one demand it trivially satisfies.

§The origin boundary, and why this one crosses it

AllowEarlyData comes off on a cross-origin redirect, because “replaying this is safe” is a claim about what a request does at a server and the caller judged only the first one. This mark is not that kind of claim. It is a statement about the caller’s own code — “the thing I am about to do needs this protocol” — and it is equally true at hop 1 and at hop 4. Dropping it across an origin would mean a redirect could silently deliver over HTTP/1.1 exactly the request that said it could not use HTTP/1.1, which is the failure the mark exists to prevent, arriving through the one door left open.

Tuple Fields§

§0: Version

Trait Implementations§

Source§

impl Clone for RequireVersion

Source§

fn clone(&self) -> RequireVersion

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 RequireVersion

Source§

impl Debug for RequireVersion

Source§

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

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

impl Eq for RequireVersion

Source§

impl PartialEq for RequireVersion

Source§

fn eq(&self, other: &RequireVersion) -> 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 RequireVersion

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.