Skip to main content

Crate agent_effects_http

Crate agent_effects_http 

Source
Expand description

HTTP effects for agent-effects.

HttpEffect builds an effect’s action from an HTTP request: it sends the effect’s stable idempotency key in a header, and classifies every failure so the runtime knows whether the request may have applied.

use agent_effects::{EffectKind, Runtime};
use agent_effects_http::HttpEffect;
use agent_effects_memory::MemoryStore;

#[derive(serde::Serialize)]
struct Charge { amount: u64, currency: &'static str }
#[derive(serde::Serialize, serde::Deserialize)]
struct Payment { id: String }

let client = reqwest::Client::new();
let runtime = Runtime::new(MemoryStore::new());
let outcome = runtime
    .effect("payment.charge", "order-42")
    .kind(EffectKind::IrreversibleWrite)
    .remote_idempotency(true) // the provider honours Idempotency-Key
    .run(
        HttpEffect::post(&client, "https://api.example.com/v1/charges")
            .json(&Charge { amount: 4200, currency: "eur" })
            .idempotency_key_header()
            .send_json::<Payment>(),
    )
    .await?;

The crate enables no reqwest features: TLS and the rest come from your own reqwest dependency and the reqwest::Client you pass in.

§How failures are classified

A failure is only definite when the request provably never reached the server, or the server’s answer says it did not apply. Everything in between is ambiguous, and the runtime then verifies, re-runs only if safe, or escalates.

OutcomeClass
connect, DNS or TLS failureAmbiguous; Transient (never sent) only with HttpEffect::client_follows_no_redirects
invalid request (bad URL or header)Validation (never sent)
timeout or connection lost after sendingAmbiguous
2xx with an unreadable or unparseable bodyAmbiguous (it applied, the answer was lost)
3xx (seen only when the client does not follow redirects)Ambiguous
any failing status after the client followed a redirectAmbiguous (it answers for the redirect’s target)
408, 425, 503Transient
429, or 503 with Retry-AfterRateLimited, honouring Retry-After
400, 422Validation
401Authentication
403Authorization
409, 500, 502, 504, other 5xxAmbiguous
501, other 4xxPermanent

Override per request with HttpEffect::classify_status.

A client that follows redirects (reqwest’s default) can fail to connect to a redirect’s target after the original request applied, and reqwest reports that exactly like a failure to reach the original server. So a connection failure only proves the request was never sent when the client does not follow redirects: build the client with .redirect(reqwest::redirect::Policy::none()) and say so with HttpEffect::client_follows_no_redirects.

The same goes for statuses: after a redirect, the status is the target’s, so a failing one says nothing about the original request and is ambiguous. reqwest keeps no redirect history, so a redirect is seen by the response’s URL differing from the request’s; one that leads back to the same URL (POST /orders → 303 → GET /orders) is not seen. For irreversible writes, use a client that does not follow redirects.

Structs§

HttpEffect
An HTTP request as an effect’s action. Build it, then turn it into an action with Self::send_json or Self::send.
HttpResponse
A successful response, as stored and replayed.

Enums§

StatusClass
What a response status means.

Constants§

IDEMPOTENCY_KEY
The conventional idempotency header.

Functions§

classify_error
The class of a request that failed without a response, from a client that may follow redirects: everything but an invalid request is ambiguous, a connection failure included (see the crate docs).
classify_error_without_redirects
Like classify_error, for a client that does not follow redirects: a connection failure (including a connect timeout) means nothing reached the server, so the request was never sent.
classify_status
The default meaning of status; see the crate docs.