Skip to main content

Module http

Module http 

Source
Expand description

The HTTP engine, and the credential seam consumers plug into it.

§What a consumer used to have to write

crate::transport::TapesTransport is a small trait, and that made it look cheap to implement. It was not. Every implementation had to parse the verb, join the contract-relative path onto a base in the right mode, copy the headers, attach a content type when there was a body, decide what a redirect means, split the response into status, headers, and bytes, map three different failures into the crate’s taxonomy, and then write the whole thing again for the streaming variant with a different rule about non-success statuses. Every consumer got that right in its own way, and the ways differed: one refused redirects, one did not; one surfaced a stream’s 500 as an error, one had not been asked to yet.

None of that is a consumer’s decision. The only genuine difference between the implementations was what makes a request authorised.

§The shape this module settles on

HttpEngine owns the HTTP: request building, the redirect refusal, the streaming variant, the error mapping, and the retry loop. HttpAuth is what a consumer writes instead of a transport, and it is three things:

  1. The client. Injected at construction with HttpEngine::with_client, because TLS policy is a property of the client and not of the credential: a deployment that pins a root but sends no credential should not have to implement an auth trait to say so, and one that mints a token but has no TLS opinion should not have to build a client. Omitted, the engine builds its own no-redirect client.
  2. HttpAuth::authorize — the headers this attempt carries. Called once per attempt, so a consumer that mints a fresh credential per request keeps doing exactly that, including on the retry.
  3. HttpAuth::on_unauthorized — what a rejected credential means. The hook returns a decision; the engine owns the loop. That is the difference between a retry policy that is data and one that is a reimplemented while loop in every consumer, each with its own answer to “how many times?”.

DirectHttp is the trivial instance: NoAuth, no headers, no retry. The name and the direct-http feature are unchanged, because a consumer that only wanted an unauthenticated client should not have to learn that a seam appeared underneath it.

§No auth is still the default

The tapes read API carries no authentication of its own; tenancy is settled by the deployment before a request reaches the process. A consumer that holds a credential does so because its edge demands one, which is why the credential is a hook rather than a configuration field on the engine.

§Redirects are refused, not followed

This engine speaks to exactly the server the caller configured. Both the discovery document and a cassette’s own spec are data, and data must not be able to steer a request — least of all one carrying a user-provided body or a credential — onto another host. The tapes API never redirects, so a 3xx is always either a misconfiguration or an attempt to move the client.

The defence is two-layered on purpose: the engine’s own client is built with Policy::none, and every response is checked to have come from the configured origin. The second layer is what holds when the client was injected by a consumer whose redirect policy is its own.

Structs§

HttpEngine
An HTTP transport for one tapes deployment, with the credential half left to an HttpAuth.
NoAuth
The credential-free instance: no headers, no retry.
Rejected
One rejected attempt, as the hook sees it.

Enums§

Unauthorized
What to do about a rejected credential.

Traits§

HttpAuth
What makes a request authorised, for consumers that need it to be.

Type Aliases§

DirectHttp
A direct, unauthenticated HTTP transport for one tapes server.