Documentation
¶
Overview ¶
Package request is the shared HTTP transport every integration surface runs on: the API surface, the scraper, and the search providers all make their outbound calls through one Transport rather than each holding its own *http.Client. That keeps a single home for the cross-cutting concerns a remote, unattended agent must get right: bounded timeouts, retry with backoff on the right failure classes only, per-host rate limiting, and typed error classification through the fault model.
Two design choices make the transport testable and deterministic. It runs every call against a Doer interface (*http.Client satisfies it), so a test or a chaos adapter substitutes the network without touching call sites. And every delay is taken through a clock.Timing rather than time.Sleep, so a clock.Manual drives backoff and rate-limit waits with no real sleeping and a run reproduces exactly.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func StatusFault ¶
StatusFault returns the classified fault for a status code, or nil for a success (below 400). The code is "http_<status>" so it stays stable across processes and in the event log. It is exported so an API surface can classify a response it read itself with the same rule the transport uses.
Types ¶
type Doer ¶
Doer performs a single HTTP round trip. *http.Client satisfies it. The transport is defined against this interface, not a concrete client, so a test or a fault-injecting adapter can stand in for the network.
type Option ¶
type Option func(*Transport)
Option configures a Transport.
func WithBackoff ¶
WithBackoff sets the base and ceiling for exponential backoff between retries. A base of 0 disables waiting entirely (retries fire immediately), which keeps deterministic tests simple. Negative values are treated as 0.
func WithClock ¶
WithClock sets the clock backing backoff and rate-limit waits. The default is clock.System; tests pass a clock.Manual for deterministic, sleepless delays.
func WithDoer ¶
WithDoer sets the underlying HTTP doer. The default dials through netguard (anti-SSRF: public addresses only, private/loopback/metadata denied). Tests and chaos adapters use this to substitute the network, and a caller that must reach a specific private host injects its own netguard.Client with a matching policy.
func WithJitterEntropy ¶
WithJitterEntropy enables full jitter on each backoff wait, drawing from src so retries from many goroutines do not align into a thundering herd. Entropy is injected (the project reserves direct rand sources to package ids): pass crypto/rand from the composition root in production, or a deterministic reader for reproducible replay. Without this option backoff stays the exact deterministic schedule, so a run replays bit-for-bit by default.
func WithMaxAttempts ¶
WithMaxAttempts caps the total number of tries, including the first. A value below 1 is clamped to 1 (no retry).
func WithRateLimit ¶
WithRateLimit enforces a minimum interval between requests to the same host, expressed as a maximum requests-per-second. A value <= 0 disables rate limiting.
type Transport ¶
type Transport struct {
// contains filtered or unexported fields
}
Transport executes HTTP requests with bounded retries, exponential backoff, and per-host rate limiting. It is safe for concurrent use.
func New ¶
New builds a Transport. Defaults: a netguard client (public addresses only, anti-SSRF) so a freshly-built transport cannot be steered into dialing a private, loopback, or cloud-metadata address; clock.System; three attempts; 200ms base backoff capped at 10s; no jitter (deterministic schedule); no rate limit. Enable jitter with WithJitterEntropy.
func (*Transport) Backoff ¶
Backoff returns the deterministic, un-jittered wait before the given 1-based attempt: base * 2^(attempt-2) for attempt >= 2, capped at the ceiling, and zero for the first attempt (which has no preceding wait). It is exported so the schedule can be asserted directly in tests.
func (*Transport) Do ¶
Do executes req with rate limiting, retries, and backoff, and returns the final response. The returned error is always a classified *fault.Error or nil:
- A 2xx/3xx response returns the response and a nil error.
- A non-retryable status (most 4xx) returns the response and a Terminal fault, so the caller may still read the body to surface the API's error detail.
- A retryable failure (network error, 408, 429, 5xx) is retried up to the attempt ceiling; if it never succeeds the last response (if any) and a Transient fault are returned.
- A cancelled or expired context returns a Cancelled fault and is never retried.
Retries re-send the body via req.GetBody; a retryable request without GetBody is sent once and its failure returned, since the transport cannot safely replay it.