Help us shape the future of Go! Take the 2026 Go Developer Survey

httpclient

package
v0.5.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 37 Imported by: 0

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

View Source
const MaxBatchRequests = 4096

MaxBatchRequests bounds one DoAll call.

View Source
const MaxBodyBytes int64 = 64 << 20
View Source
const MaxDownloadBytes int64 = 1 << 40

MaxDownloadBytes bounds one Download independently of Config.ResponseBytes.

Variables

View Source
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 Bytes

func Bytes(data []byte) Body

Bytes returns a replayable body backed by a copy of data, recording an invalid request if data exceeds MaxBodyBytes.

func ReplayableBody

func ReplayableBody(length int64, open func(context.Context) (io.ReadCloser, error)) Body

ReplayableBody asserts each opener returns the same complete content in an independent reader. Retry safety still needs a matching operation policy.

func StreamBody

func StreamBody(length int64, open func(context.Context) (io.ReadCloser, error)) Body

StreamBody declares a non-replayable reader source with the given length, or -1 for unknown length. Each opened reader transfers ownership to the client.

func (Body) Format

func (Body) Format(state fmt.State, _ rune)

Format writes a body label without exposing its content.

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 New

func New(config Config, transport http.RoundTripper) (*Client, error)

New validates and snapshots config and creates a client. A supplied transport is borrowed and cannot be combined with restricted destinations or custom TLS settings.

func (*Client) Close

func (c *Client) Close(ctx context.Context) error

Close stops admission and waits for active operations before closing owned idle connections. A canceled wait does not stop active operations.

func (*Client) Do

func (c *Client) Do(ctx context.Context, request Request) (Response, error)

Do executes request and buffers the bounded response body. HTTP error statuses require a separate EnsureSuccess check.

func (*Client) DoAll

func (c *Client) DoAll(ctx context.Context, requests []Request, concurrency int) ([]Outcome, error)

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) Done

func (c *Client) Done() <-chan struct{}

Done returns the channel that closes when the client's operation group finishes shutting down.

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) Format

func (*Client) Format(state fmt.State, _ rune)

Format writes a client label without exposing configuration.

func (*Client) Get

func (c *Client) Get(path string) Request

Get declares a GET request to path.

func (*Client) Name

func (c *Client) Name() Name

Name returns the configured name, or an empty name for a nil client.

func (*Client) Post

func (c *Client) Post(path string) Request

Post declares a POST request to path.

func (*Client) Request

func (c *Client) Request(method, path string) Request

Request declares a request using the client's base URL, copied default headers and retry policy. Invalid methods or URLs are retained as validation errors.

func (*Client) RestrictsDestinations

func (c *Client) RestrictsDestinations() bool

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) Snapshot

func (c *Client) Snapshot() Snapshot

Snapshot reads the client's atomic request, attempt and failure counters.

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)

NewClients builds a named registry whose default aliases the selected client.

func (*Clients) Client

func (r *Clients) Client(name Name) (*Client, error)

Client looks up the explicit name and returns an error if it is unavailable.

func (*Clients) Default

func (r *Clients) Default() (*Client, error)

Default returns the selected client or an error if no default is available.

func (*Clients) DefaultName

func (r *Clients) DefaultName() Name

DefaultName returns the selected name, or an empty name for a nil registry.

func (*Clients) Names

func (r *Clients) Names() []Name

Names returns an owned snapshot of the registered names.

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
}

Config declares one named client's transport, resource bounds and request defaults.

func DefaultConfig

func DefaultConfig(name Name) Config

DefaultConfig returns the named client's default timeouts, resource bounds and retry policy.

func (Config) Format

func (Config) Format(state fmt.State, _ rune)

Format writes a configuration label without exposing settings.

func (Config) MarshalJSON

func (Config) MarshalJSON() ([]byte, error)

MarshalJSON encodes a configuration label without exposing settings.

func (Config) Validate

func (c Config) Validate() error

Validate checks the client's name, resource bounds, URL, headers, destination policy, TLS and retries.

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

Validate checks the destination mode and bounded scheme, port, host and network allowlists.

type DownloadResult

type DownloadResult struct {
	Status   int
	Headers  http.Header
	Bytes    int64
	Attempts int
}

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.

func (*Error) Attempt

func (e *Error) Attempt() int

Attempt returns the attempt number recorded for the failure.

func (*Error) Error

func (e *Error) Error() string

Error returns the failure kind, client name, attempt and status without formatting its cause.

func (*Error) Format

func (e *Error) Format(state fmt.State, _ rune)

Format writes the safe error summary without formatting its cause.

func (*Error) Is

func (e *Error) Is(target error) bool

Is matches invalid, internal and overloaded framework faults according to the failure kind.

func (*Error) Kind

func (e *Error) Kind() ErrorKind

Kind returns the outbound failure category.

func (*Error) Method

func (e *Error) Method() string

Method returns the request method recorded for the failure.

func (*Error) Name

func (e *Error) Name() Name

Name returns the client name recorded for the failure.

func (*Error) Status

func (e *Error) Status() int

Status returns the HTTP status recorded for the failure.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the underlying cause.

type ErrorKind

type ErrorKind string

ErrorKind identifies an outbound HTTP failure category.

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 Name

type Name string

Name identifies a configured outbound HTTP client.

func (Name) Validate

func (n Name) Validate() error

Validate checks that the client name is a valid service identifier.

type NamedClient

type NamedClient struct {
	Name  Name
	Value *Client
}

NamedClient borrows one existing instance. Selection never creates another owner.

type Outcome

type Outcome struct {
	Response Response
	Err      error
}

Outcome is one DoAll result in request order.

type Part

type Part struct {
	// contains filtered or unexported fields
}

Part is one multipart/form-data part: a field value or a file.

func Field

func Field(name, value string) Part

Field is a multipart text field.

func File

func File(name, filename, contentType string, data []byte) Part

File is a multipart file part. The filename is presentation metadata only.

func (Part) Format

func (Part) Format(state fmt.State, _ rune)

Format writes a multipart label without exposing content.

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

func (r Request) BasicAuth(user string, password secret.String) Request

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) Bearer

func (r Request) Bearer(token secret.String) Request

Bearer returns a request with a validated bearer Authorization header.

func (Request) Form

func (r Request) Form(values url.Values) Request

Form encodes values as an application/x-www-form-urlencoded replayable body.

func (Request) Format

func (Request) Format(state fmt.State, _ rune)

Format writes a request label without exposing its URL, headers or body.

func (Request) Header

func (r Request) Header(name, value string) Request

Header returns a request with a copied, validated header set containing the replacement value.

func (Request) Method

func (r Request) Method() string

Method returns the declared HTTP method.

func (Request) Multipart

func (r Request) Multipart(parts ...Part) Request

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) QueryPair

func (r Request) QueryPair(name, value string) Request

QueryPair returns a request with an additional encoded query value, retaining validation errors on the result.

func (Request) URL

func (r Request) URL() string

URL returns the request URL, or an empty string when no URL is set.

func (Request) Validate

func (r Request) Validate() error

Validate returns retained declaration errors and checks the destination policy. The zero request is invalid.

func (Request) WithBody

func (r Request) WithBody(body Body) Request

WithBody returns a request with body, retaining an error if the body is invalid or exceeds the client's request limit.

func (Request) WithRetry

func (r Request) WithRetry(policy RetryPolicy) Request

WithRetry returns a request with the validated retry policy.

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) Attempts

func (r Response) Attempts() int

Attempts returns the number of the final request attempt.

func (Response) Bytes

func (r Response) Bytes() []byte

Bytes returns a copy of the buffered body.

func (Response) EnsureSuccess

func (r Response) EnsureSuccess() error

EnsureSuccess returns a StatusFailed error unless the status is in the 2xx range.

func (Response) Format

func (r Response) Format(state fmt.State, _ rune)

Format writes the response status and body length without exposing content.

func (Response) Headers

func (r Response) Headers() http.Header

Headers returns a copy of the response headers.

func (Response) Status

func (r Response) Status() int

Status returns the HTTP status code.

func (Response) Text

func (r Response) Text() (string, error)

Text returns the body as UTF-8 text, or a DecodeFailed error for invalid UTF-8 or an uninitialized response.

type RetryMode

type RetryMode string

RetryMode selects which operations are eligible for retry.

const (
	SafeReads           RetryMode = "safe_reads"
	IdempotentOperation RetryMode = "idempotent_operation"
)

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

DefaultRetryPolicy allows up to three safe-read attempts with jittered exponential backoff.

func NoRetries

func NoRetries() RetryPolicy

NoRetries returns a policy allowing one attempt.

func (RetryPolicy) Validate

func (p RetryPolicy) Validate() error

Validate checks the retry mode, attempt and backoff bounds, and retryable status codes.

type Scheme

type Scheme string

Scheme identifies an allowed outbound URL scheme.

const (
	HTTPS Scheme = "https"
	HTTP  Scheme = "http"
)

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
}

StreamResponse exposes the response body within the client's streaming callback lifetime.

func (*StreamResponse) Attempts

func (r *StreamResponse) Attempts() int

Attempts returns the final attempt number, or zero for a nil response.

func (*StreamResponse) EnsureSuccess

func (r *StreamResponse) EnsureSuccess() error

EnsureSuccess checks for a 2xx status; a nil response is invalid.

func (*StreamResponse) Format

func (r *StreamResponse) Format(state fmt.State, _ rune)

Format writes the response status without exposing content.

func (*StreamResponse) Headers

func (r *StreamResponse) Headers() http.Header

Headers returns a copy of the headers, or nil for a nil response.

func (*StreamResponse) Read

func (r *StreamResponse) Read(p []byte) (int, error)

Read reads from the owned response body; a nil response is invalid.

func (*StreamResponse) Status

func (r *StreamResponse) Status() int

Status returns the HTTP status code, or zero for a nil response.

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.

func (TLSConfig) IsZero

func (t TLSConfig) IsZero() bool

IsZero reports whether no custom TLS settings are supplied.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL