hairpin.bend relies on unsafe/foreign
raw source on the hub · import bend-kit-hairpin@0.3.0.0/hairpin.bend as Hairpin
Hairpin: an HTTP client with a base URL, default headers, a pool, cookies, mTLS, redirects, and proven retries. Source: https://github.com/paymog/bend-kit/tree/main/hairpin
7 imports
import Base import bend-kit-http@0.32.0.0/http.bend as Http import bend-kit-time@0.1.2.1/time.bend as Time import 0x1f2d80f53f971b16c6de6a65cb1918ae/url.bend as Url import 0x584fc27920487ceab242392391418d7f/json.bend as Json import 0x49814d83de8f70993a43e1002be29ecd/bytes.bend as Bytes import ./retry.bend as Retry
Types
type Opts source · line 12 · raw
Data
base: "" for none. headers: lowercase names. ms: the step timeout. policy: attempts, backoff, and deadline of each request.
Opts@base:String -> @headers:Map<&2, List<&2, String>> -> @ms:U32 -> @mode:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Mode -> @policy:0x5f997a9351e7133e43ba9611e734f8a6/retry.Policy -> @cert:Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Cert> -> Opts
type Client source · line 16 · raw
Type
breaker: the circuit breaker of every request on this client.
Client@opts:Opts -> @pool:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Pool -> @jar:Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar> -> @breaker:0x5f997a9351e7133e43ba9611e734f8a6/retry.Breaker -> Client
type Err source · line 20 · raw
Data
ErrHttp: the last attempt failed. ErrDenied: no attempt started, for why.
ErrHttp@error:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err -> Err
ErrDenied@why:0x5f997a9351e7133e43ba9611e734f8a6/retry.Deny -> Err
type Step source · line 175 · raw
Type
The live state of one request between attempts. last: the result of the last attempt, None before the first.
Step@pool:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Pool -> @jar:Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar> -> @breaker:0x5f997a9351e7133e43ba9611e734f8a6/retry.Breaker -> @budget:0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget -> @body:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @last:Maybe<&1, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>> -> @plan:0x5f997a9351e7133e43ba9611e734f8a6/retry.Plan -> Step
Definitions
def policy source · line 25 · raw
0x5f997a9351e7133e43ba9611e734f8a6/retry.Policy
One attempt, the first included; backoff from 500 ms, doubled to at most 30 s; no deadline.
def new.with source · line 30 · raw
@+cap:U32 -> Client
A pool of up to cap idle sockets per origin, no base, no default headers, 30 s steps, follow redirects, no retries, and no circuit breaker.
def new source · line 33 · raw
Client
def base source · line 36 · raw
@c:Client -> @url:String -> Client
def header source · line 41 · raw
@c:Client -> @k:String -> @v:String -> Client
A default header. It replaces an earlier default of the same name; a request header of that name replaces it.
def timeout source · line 45 · raw
@c:Client -> @ms:U32 -> Client
def redirect source · line 49 · raw
@c:Client -> @mode:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Mode -> Client
def retry source · line 54 · raw
@c:Client -> @n:U32 -> Client
Up to n retries after the first attempt: n + 1 attempts in all.
def deadline source · line 59 · raw
@c:Client -> @ms:U32 -> Client
No attempt of a request starts ms or more after the request starts.
def circuit source · line 64 · raw
@c:Client -> @circ:0x5f997a9351e7133e43ba9611e734f8a6/retry.Circuit -> Client
A circuit breaker in front of every request, starting closed.
def breaker.of source · line 69 · raw
@c:Client -> Pair(Client, 0x5f997a9351e7133e43ba9611e734f8a6/retry.Breaker)
The breaker, for inspection.
def cert source · line 74 · raw
@c:Client -> @chain:String -> @key:String -> Client
Paths to a PEM client chain and its private key, presented to every HTTPS origin.
def jar source · line 79 · raw
@c:Client -> @jar:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar -> Client
Every request stores and sends cookies through jar. Its clock moves to Time.now() before each try.
def jar.of source · line 84 · raw
@c:Client -> Pair(Client, Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar>)
The jar, if the client has one.
def close source · line 88 · raw
@c:Client -> IO(Unit)
def ms.of source · line 92 · raw
@r:Pair(U32, Pair(U32, U32)) -> Nat
def now.ms source · line 97 · raw
IO(Nat)
Milliseconds on the monotonic clock that Retry budgets and breakers use.
def target.pick source · line 102 · raw
@none:Bool -> @b:String -> @ref:String -> Maybe<&2, String>
def target source · line 110 · raw
@+b:String -> @ref:String -> Maybe<&2, String>
ref against the base (RFC 3986 §5.2); with no base, ref as given. None when the base is not absolute.
def target.abs source · line 113 · raw
@m:Maybe<&2, String> -> Maybe<&2, 0x1f2d80f53f971b16c6de6a65cb1918ae/url.Abs>
def merge source · line 122 · raw
@defaults:Map<&2, List<&2, String>> -> @headers:Map<&2, List<&2, String>> -> Map<&2, List<&2, String>>
The defaults under the request headers, names lowercased; a request header replaces a default of its name. Host, Connection, Content-Length, and Transfer-Encoding are the client's own, so both sides drop them.
def jar.now source · line 125 · raw
@j:Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar> -> IO(Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar>)
def once.final source · line 134 · raw
@+enc:String -> @x:Pair(0x6a946b742ab6f76f0ba5b90b14da52ad/http.Pool, Pair(Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>, Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar>)) -> IO(Pair(0x6a946b742ab6f76f0ba5b90b14da52ad/http.Pool, Pair(Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar>, Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>)))
def once source · line 142 · raw
@+ms:U32 -> @+mode:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Mode -> @+cert:Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Cert> -> @+enc:String -> @p:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Pool -> @j:Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar> -> @+method:String -> @+u:Maybe<&2, 0x1f2d80f53f971b16c6de6a65cb1918ae/url.Abs> -> @+headers:Map<&2, List<&2, String>> -> @body:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> IO(Pair(0x6a946b742ab6f76f0ba5b90b14da52ad/http.Pool, Pair(Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar>, Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>)))
One try: the redirect loop on the pool, with the jar and the certificate, then the body decoded.
def hint.ms source · line 151 · raw
@m:Maybe<&2, U32> -> Maybe<&2, Nat>
def outcome source · line 160 · raw
@+idem:Bool -> @m:Maybe<&2, String> -> @now:0xe00bbf72c3ff9dc6d7b077524522b3a8/time.Instant -> 0x5f997a9351e7133e43ba9611e734f8a6/retry.Outcome
m is Http.retry.judge's verdict for an idempotent request: Some{Retry-After} when another try may help. A non-idempotent request with that verdict is Unsafe: the breaker counts it, but it never repeats.
def lift source · line 167 · raw
@r:Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res> -> Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>
def clock source · line 180 · raw
@need:Bool -> IO(Nat)
The clock, read only when a decision depends on it; 0 otherwise, which no such decision reads.
def breaker.on source · line 187 · raw
@br:0x5f997a9351e7133e43ba9611e734f8a6/retry.Breaker -> Bool
def transient source · line 194 · raw
@o:0x5f997a9351e7133e43ba9611e734f8a6/retry.Outcome -> Bool
def some source · line 203 · raw
@m:Maybe<&2, Nat> -> Bool
def deadline.some source · line 210 · raw
@b:0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget -> Bool
def outcome.io source · line 215 · raw
@+idem:Bool -> @m:Maybe<&2, String> -> IO(0x5f997a9351e7133e43ba9611e734f8a6/retry.Outcome)
The wall clock dates a Retry-After, so it is read only for a transient failure.
def jitter.io source · line 225 · raw
@need:Bool -> IO(Nat)
Jitter for the backoff, drawn only when a retry may follow.
def settle.at source · line 234 · raw
@+now:Nat -> @+rnd:Nat -> @+p:0x5f997a9351e7133e43ba9611e734f8a6/retry.Policy -> @+n:Nat -> @+o:0x5f997a9351e7133e43ba9611e734f8a6/retry.Outcome -> @br:0x5f997a9351e7133e43ba9611e734f8a6/retry.Breaker -> @+b:0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget -> @body:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @pool:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Pool -> @jar:Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar> -> @r:Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res> -> Step
def settle.outcome source · line 239 · raw
@+p:0x5f997a9351e7133e43ba9611e734f8a6/retry.Policy -> @+n:Nat -> @+o:0x5f997a9351e7133e43ba9611e734f8a6/retry.Outcome -> @+br:0x5f997a9351e7133e43ba9611e734f8a6/retry.Breaker -> @b:0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget -> @body:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @pool:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Pool -> @jar:Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar> -> @r:Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res> -> IO(Step)
The attempt has ended: tell the breaker, and plan the next step.
def settle.judged source · line 246 · raw
@+p:0x5f997a9351e7133e43ba9611e734f8a6/retry.Policy -> @+idem:Bool -> @+n:Nat -> @br:0x5f997a9351e7133e43ba9611e734f8a6/retry.Breaker -> @b:0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget -> @body:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @pool:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Pool -> @jar:Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar> -> @j:Pair(Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>, Maybe<&2, String>) -> IO(Step)
def judge source · line 254 · raw
@r:Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res> -> Pair(Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>, Maybe<&2, String>)
The default judge: Http.retry.judge, as if the request were idempotent; outcome then applies idem.
def denied source · line 273 · raw
@why:0x5f997a9351e7133e43ba9611e734f8a6/retry.Deny -> @last:Maybe<&1, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>> -> Maybe<&1, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>>
def pause source · line 301 · raw
@ms:Nat -> IO(Unit)
def result.of source · line 326 · raw
@m:Maybe<&1, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>> -> Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>
def request.done source · line 333 · raw
@+o:Opts -> @s:Step -> Pair(Client, Pair(0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>))
def attempts source · line 337 · raw
@p:0x5f997a9351e7133e43ba9611e734f8a6/retry.Policy -> Nat
def budget source · line 342 · raw
@c:Client -> IO(Pair(Client, 0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget))
The budget of one plain request: the policy's attempts, and its deadline from now. The clock is read only for a deadline.
def request.in source · line 367 · raw
@c:Client -> @b:0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget -> @+method:String -> @url:String -> @headers:Map<&2, List<&2, String>> -> @body:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> IO(Pair(Client, Pair(0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>)))
request.as with judge, the idempotency of method, and every coding Http decodes.
def request.drop source · line 373 · raw
@x:Pair(Client, Pair(0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>)) -> Pair(Client, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>)
def request.budget source · line 377 · raw
@+method:String -> @url:String -> @headers:Map<&2, List<&2, String>> -> @body:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @x:Pair(Client, 0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget) -> IO(Pair(Client, Pair(0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>)))
def request source · line 387 · raw
@c:Client -> @+method:String -> @url:String -> @headers:Map<&2, List<&2, String>> -> @body:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> IO(Pair(Client, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>))
url resolves against the base; headers go over the defaults. Redirects follow the client's mode, at most 20. Each request gets a fresh budget from budget(c). Before each attempt the deadline, the budget, and the breaker must admit it. GET, HEAD, OPTIONS, TRACE, PUT, and DELETE try again after a connect error, a timeout, or 408, 429, 500, 502, 503, or 504, as Http.retry.judge says, on the same pool. When the attempts run out, the last result comes back; when none started, ErrDenied says why.
def get source · line 394 · raw
@c:Client -> @url:String -> IO(Pair(Client, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>))
def post.json source · line 398 · raw
@c:Client -> @url:String -> @v:0x584fc27920487ceab242392391418d7f/json.Val -> IO(Pair(Client, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>))
POST v as compact JSON, with content-type: application/json.
Templates
template settle source · line 257 · raw
@-judge:(@_:Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res> -> Pair(Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>, Maybe<&2, String>)) -> @+p:0x5f997a9351e7133e43ba9611e734f8a6/retry.Policy -> @+idem:Bool -> @+n:Nat -> @br:0x5f997a9351e7133e43ba9611e734f8a6/retry.Breaker -> @b:0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget -> @body:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @x:Pair(0x6a946b742ab6f76f0ba5b90b14da52ad/http.Pool, Pair(Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar>, Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>)) -> IO(Step)
template attempt.run source · line 263 · raw
@-judge:(@_:Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res> -> Pair(Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>, Maybe<&2, String>)) -> @+o:Opts -> @+enc:String -> @+idem:Bool -> @+method:String -> @+u:Maybe<&2, 0x1f2d80f53f971b16c6de6a65cb1918ae/url.Abs> -> @+headers:Map<&2, List<&2, String>> -> @+n:Nat -> @pool:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Pool -> @jar:Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar> -> @br:0x5f997a9351e7133e43ba9611e734f8a6/retry.Breaker -> @b:0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget -> @r:Pair(0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes, 0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes) -> IO(Step)
template attempt.gate source · line 280 · raw
@-judge:(@_:Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res> -> Pair(Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>, Maybe<&2, String>)) -> @x:Pair(0x5f997a9351e7133e43ba9611e734f8a6/retry.Breaker, Pair(0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget, 0x5f997a9351e7133e43ba9611e734f8a6/retry.Gate)) -> @+o:Opts -> @+enc:String -> @+idem:Bool -> @+method:String -> @+u:Maybe<&2, 0x1f2d80f53f971b16c6de6a65cb1918ae/url.Abs> -> @+headers:Map<&2, List<&2, String>> -> @+n:Nat -> @pool:0x6a946b742ab6f76f0ba5b90b14da52ad/http.Pool -> @jar:Maybe<&2, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Jar> -> @body:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> @last:Maybe<&1, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>> -> IO(Step)
template attempt source · line 293 · raw
@-judge:(@_:Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res> -> Pair(Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>, Maybe<&2, String>)) -> @+o:Opts -> @+enc:String -> @+idem:Bool -> @+method:String -> @+u:Maybe<&2, 0x1f2d80f53f971b16c6de6a65cb1918ae/url.Abs> -> @+headers:Map<&2, List<&2, String>> -> @+n:Nat -> @s:Step -> IO(Step)
Ask the gate; when it admits, make attempt n (0 first) of this layer. With no deadline and no breaker, admission does not read the clock.
template go source · line 309 · raw
@-judge:(@_:Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res> -> Pair(Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>, Maybe<&2, String>)) -> @left:Nat -> @+n:Nat -> @+o:Opts -> @+enc:String -> @+idem:Bool -> @+method:String -> @+u:Maybe<&2, 0x1f2d80f53f971b16c6de6a65cb1918ae/url.Abs> -> @+headers:Map<&2, List<&2, String>> -> @s:Step -> IO(Step)
left: attempts this layer may still make. n: attempts made so far. Each turn follows the plan of the last step.
template request.as source · line 355 · raw
@-judge:(@_:Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res> -> Pair(Result<&1, &1, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>, Maybe<&2, String>)) -> @c:Client -> @b:0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget -> @+idem:Bool -> @+enc:String -> @+method:String -> @url:String -> @headers:Map<&2, List<&2, String>> -> @body:0x49814d83de8f70993a43e1002be29ecd/bytes.Bytes -> IO(Pair(Client, Pair(0x5f997a9351e7133e43ba9611e734f8a6/retry.Budget, Result<&1, &1, Err, 0x6a946b742ab6f76f0ba5b90b14da52ad/http.Res>)))
One request inside a caller's retry layer, with every choice explicit. Every attempt takes from b, the caller's shared budget, and obeys its deadline; the client's own policy caps the attempts of this call. The budget left comes back beside the result: pass it to the next call, so nested layers never reset or multiply it. The client's own deadline does not apply here; b's does. judge: Some{Retry-After} when another attempt may help. idem: the request is safe to repeat, so a judged failure is retried; else the breaker counts it and it never repeats. enc: the Accept-Encoding codings.