Documentation
¶
Overview ¶
Package httpclient provides named, bounded outbound HTTP clients over reusable net/http transports. Clients own operation lifetimes; custom transports are borrowed.
Index ¶
- Constants
- Variables
- func DecodeJSON[T any](ctx context.Context, response Response, descriptor contract.JSON[T]) (T, error)
- func Module(name foundation.ProviderID, key foundation.Key[*Client], config Config, ...) foundation.Module
- type Body
- type Client
- func (c *Client) Close(ctx context.Context) error
- func (c *Client) Do(ctx context.Context, request Request) (Response, error)
- func (c *Client) DoAll(ctx context.Context, requests []Request, concurrency int) ([]Outcome, error)
- func (c *Client) Done() <-chan struct{}
- func (c *Client) Download(ctx context.Context, request Request, destination io.Writer, maximum int64) (DownloadResult, error)
- func (*Client) Format(state fmt.State, _ rune)
- func (c *Client) Get(path string) Request
- func (c *Client) Name() Name
- func (c *Client) Post(path string) Request
- func (c *Client) Request(method, path string) Request
- func (c *Client) RestrictsDestinations() bool
- func (c *Client) Snapshot() Snapshot
- func (c *Client) Stream(ctx context.Context, request Request, ...) error
- type Clients
- type Config
- type DestinationMode
- type DestinationPolicy
- type DownloadResult
- type Error
- type ErrorKind
- type Name
- type NamedClient
- type Outcome
- type Part
- type Request
- func (r Request) BasicAuth(user string, password secret.String) Request
- func (r Request) Bearer(token secret.String) Request
- func (r Request) Form(values url.Values) Request
- func (Request) Format(state fmt.State, _ rune)
- func (r Request) Header(name, value string) Request
- func (r Request) Method() string
- func (r Request) Multipart(parts ...Part) Request
- func (r Request) QueryPair(name, value string) Request
- func (r Request) URL() string
- func (r Request) Validate() error
- func (r Request) WithBody(body Body) Request
- func (r Request) WithRetry(policy RetryPolicy) Request
- type Response
- type RetryMode
- type RetryPolicy
- type Scheme
- type Snapshot
- type StreamResponse
- type TLSConfig
Constants ¶
const MaxBatchRequests = 4096
MaxBatchRequests bounds one DoAll call.
const MaxBodyBytes int64 = 64 << 20
const MaxDownloadBytes int64 = 1 << 40
MaxDownloadBytes bounds one Download independently of Config.ResponseBytes.
Variables ¶
var DestinationDenied = safeerror.Sentinel(fault.Invalid, "outbound destination is not permitted")
Functions ¶
func DecodeJSON ¶
func DecodeJSON[T any](ctx context.Context, response Response, descriptor contract.JSON[T]) (T, error)
DecodeJSON validates the complete received DTO against its existing generated contract. Status handling is explicit through Response.EnsureSuccess.
func Module ¶
func Module(name foundation.ProviderID, key foundation.Key[*Client], config Config, requires []foundation.ProviderID, transport func(foundation.Resolver) (http.RoundTripper, error)) foundation.Module
Module binds one named upstream to its typed service key. requires identifies providers owning a borrowed transport so all operations drain before teardown.
Types ¶
type Body ¶
type Body struct {
// contains filtered or unexported fields
}
Body is an immutable body source. Zero means no body. Stream openers transfer ownership of a new reader per call and must support concurrent Read/Close.
func ReplayableBody ¶
ReplayableBody asserts each opener returns the same complete content in an independent reader. Retry safety still needs a matching operation policy.
func StreamBody ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client owns native connection pooling and bounded operation lifetimes. A supplied RoundTripper is borrowed and must honor request cancellation, close bodies and avoid implementing an additional retry/redirect policy.
func (*Client) DoAll ¶
DoAll runs Do for each request with at most concurrency in flight and returns outcomes in request order. Each request still uses the client's own queued admission, deadlines and retry policy; one failure does not cancel the others. concurrency must not exceed Config.Concurrency.
func (*Client) Download ¶
func (c *Client) Download(ctx context.Context, request Request, destination io.Writer, maximum int64) (DownloadResult, error)
Download streams one successful (2xx) response body into destination under its own byte limit, for files larger than Config.ResponseBytes. Other statuses fail as StatusFailed without writing. The destination is borrowed and receives bytes as they arrive: a failed or interrupted download can leave a partial write, so write to a temporary file and publish it only on success. Retries follow the request policy until the body starts.
func (*Client) RestrictsDestinations ¶
RestrictsDestinations reports whether the client enforces a restricted destination policy on every connection. Callers that fetch untrusted URLs (for example attachment imports) require it.
func (*Client) Stream ¶
func (c *Client) Stream(ctx context.Context, request Request, consume func(context.Context, *StreamResponse) error) error
Stream lends one response body to consume. It always closes the stream, and retains admission until consume and actual body closure finish. Retained readers reject use after return. Once consume starts, the request is never retried.
type Clients ¶
type Clients struct {
// contains filtered or unexported fields
}
Clients is immutable; default and named selection alias the same instance.
func NewClients ¶
func NewClients(selected Name, entries ...NamedClient) (*Clients, error)
func (*Clients) DefaultName ¶
type Config ¶
type Config struct {
// Destination optionally enforces direct, address-checked outbound networking.
Destination DestinationPolicy
Name Name
BaseURL string
Headers http.Header `config:",json,secret"`
ConnectTimeout time.Duration
AttemptTimeout time.Duration
Timeout time.Duration
Concurrency int
RequestBytes int64
ResponseBytes int64
HeaderBytes int
Retry RetryPolicy
// PropagateTrace opts this named destination into bounded W3C correlation.
// When enabled, framework context replaces manual trace headers on the
// owned request copy. Disabled clients do not add propagation headers.
PropagateTrace bool
// TLS customizes certificate verification and client certificates for the
// owned transport. A supplied custom transport cannot combine with it.
TLS TLSConfig
}
func DefaultConfig ¶
func (Config) MarshalJSON ¶
type DestinationMode ¶
type DestinationMode string
DestinationMode selects ordinary networking or an enforced direct-connect policy.
const ( UnrestrictedDestinations DestinationMode = "" RestrictedDestinations DestinationMode = "restricted" )
type DestinationPolicy ¶
type DestinationPolicy struct {
Mode DestinationMode
Schemes []Scheme `config:",json"`
Ports []uint16 `config:",json"`
Hosts []string `config:",json"`
Networks []netip.Prefix `config:",json"`
}
DestinationPolicy constrains the URL and actual connected address. Empty Hosts allows any DNS name; empty Networks allows public Internet addresses only. Nonempty Networks is an exclusive allowlist, including explicitly allowed private networks. Restricted clients bypass environment proxies and reject custom transports, because either can resolve/connect outside this policy.
func PublicDestinations ¶
func PublicDestinations() DestinationPolicy
PublicDestinations permits HTTPS on port 443 to public Internet addresses. Restrict Hosts for known services; explicitly add other schemes/ports if needed.
func (DestinationPolicy) Validate ¶
func (p DestinationPolicy) Validate() error
type DownloadResult ¶
DownloadResult describes a completed download.
type Error ¶
type Error struct {
// contains filtered or unexported fields
}
Error exposes stable operation metadata, never URL, header, query, body or underlying transport text. The cause is available only through explicit inspection.
type ErrorKind ¶
type ErrorKind string
const ( InvalidRequest ErrorKind = "invalid_request" TransportFailed ErrorKind = "transport_failed" BodyFailed ErrorKind = "body_failed" StatusFailed ErrorKind = "status_failed" DecodeFailed ErrorKind = "decode_failed" CallbackFailed ErrorKind = "callback_failed" // Overloaded reports that the client's queued operation admission was // exhausted; no request was sent. It matches fault.Overloaded. Overloaded ErrorKind = "overloaded" )
type NamedClient ¶
NamedClient borrows one existing instance. Selection never creates another owner.
type Part ¶
type Part struct {
// contains filtered or unexported fields
}
Part is one multipart/form-data part: a field value or a file.
type Request ¶
type Request struct {
// contains filtered or unexported fields
}
Request is an immutable client-bound request. Builders retain errors until Do or Stream, so invalid requests never invoke body factories or transports.
func JSON ¶
func JSON[T any](ctx context.Context, request Request, descriptor contract.JSON[T], payload T) (Request, error)
JSON reuses the request DTO's generated codec and supplies Content-Type. It retains the concrete payload type and encodes once before any network attempt.
func (Request) BasicAuth ¶
BasicAuth sets RFC 7617 Basic credentials. The user ID must not contain a colon or control characters; the password is revealed only into the header.
func (Request) Multipart ¶
Multipart encodes parts once as a replayable multipart/form-data body within the client's request byte limit and sets its Content-Type with the boundary.
func (Request) WithRetry ¶
func (r Request) WithRetry(policy RetryPolicy) Request
type Response ¶
type Response struct {
// contains filtered or unexported fields
}
Response owns a complete, bounded body and copied response headers. Accessors exposing bytes/headers are explicit and return independent snapshots.
func (Response) EnsureSuccess ¶
type RetryPolicy ¶
type RetryPolicy struct {
Mode RetryMode
Attempts int
InitialBackoff, MaxBackoff time.Duration
Jitter bool
Statuses []int `config:",json"`
}
RetryPolicy always counts total attempts, including the first. Mutation retries require an explicit IdempotentOperation policy and a replayable request body. Jitter draws each backoff uniformly from zero to its exponential cap ("full jitter") so synchronized clients spread out. Statuses replaces the default retryable statuses (408, 429, 500, 502, 503, 504) when nonempty. A Retry-After response header on a retryable status sets the wait; a value beyond MaxBackoff ends retrying and returns that response.
func DefaultRetryPolicy ¶
func DefaultRetryPolicy() RetryPolicy
func NoRetries ¶
func NoRetries() RetryPolicy
func (RetryPolicy) Validate ¶
func (p RetryPolicy) Validate() error
type Snapshot ¶
type Snapshot struct{ Requests, Attempts, Failures uint64 }
Snapshot contains only bounded operation counts; no request values are retained.
type StreamResponse ¶
type StreamResponse struct {
// contains filtered or unexported fields
}
func (*StreamResponse) Attempts ¶
func (r *StreamResponse) Attempts() int
func (*StreamResponse) EnsureSuccess ¶
func (r *StreamResponse) EnsureSuccess() error
func (*StreamResponse) Headers ¶
func (r *StreamResponse) Headers() http.Header
func (*StreamResponse) Status ¶
func (r *StreamResponse) Status() int
type TLSConfig ¶
type TLSConfig struct {
CertificateAuthorities string
AppendSystemRoots bool
Certificate secret.String
PrivateKey secret.String
ServerName string
}
TLSConfig customizes the owned transport's TLS. CertificateAuthorities (PEM) replaces the system roots unless AppendSystemRoots is set. Certificate and PrivateKey (PEM) present a client certificate and must be set together. ServerName overrides verification of the URL host name.