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:
- 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. 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.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 reimplementedwhileloop 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§
- Http
Engine - 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§
- Http
Auth - What makes a request authorised, for consumers that need it to be.
Type Aliases§
- Direct
Http - A direct, unauthenticated HTTP transport for one tapes server.