Documentation
¶
Overview ¶
Package httpclient is a fluent wrapper around net/http modelled on Laravel's Http facade.
Compared to net/http directly, the wrapper handles JSON marshalling, query strings, headers, basic/bearer auth, timeouts, and retries with exponential backoff — common boilerplate that every service caller writes by hand.
Usage:
resp, err := httpclient.New().
Timeout(5 * time.Second).
Retry(3).
BaseURL("https://api.example.com").
BearerToken(tok).
Get(ctx, "/users/42")
if err != nil { return err }
var u User
if err := resp.JSON(&u); err != nil { return err }
Errors from the underlying transport are wrapped; non-2xx responses are NOT errors (callers inspect resp.Status()).
Safety notes (caller responsibilities) ¶
SSRF: this client issues requests to whatever URL it is given and uses the stdlib http.Client default redirect policy, which follows up to 10 redirects without restriction. It does NOT validate the destination host, block private/link-local/loopback ranges, or pin redirects to an allowlist. If you pass user-controlled URLs (or talk to an upstream that can redirect you to one), guard against SSRF at the caller level — e.g. validate the host, or install a Transport whose DialContext rejects private address ranges. See Transport.
Retry idempotency: Retry retries on transport errors and on 5xx / 429 responses for every verb, including PostJSON/PutJSON/PatchJSON. The request body is buffered once and replayed on each attempt. Retrying a non-idempotent write can cause duplicate side effects if the upstream is not idempotent or does not de-duplicate (e.g. via an idempotency key). Only enable Retry for such verbs against upstreams you know are safe to retry.
Response cap: response bodies are bounded by MaxResponseBytes (DefaultMaxResponseBytes by default) so a hostile or runaway server cannot OOM the process. Bodies exceeding the cap fail with ErrResponseTooLarge.
Index ¶
- Constants
- Variables
- type Client
- func (c *Client) Backoff(d time.Duration) *Client
- func (c *Client) BaseURL(u string) *Client
- func (c *Client) BasicAuth(user, password string) *Client
- func (c *Client) BearerToken(t string) *Client
- func (c *Client) Delete(ctx context.Context, path string) (*Response, error)
- func (c *Client) Get(ctx context.Context, path string) (*Response, error)
- func (c *Client) Header(k, v string) *Client
- func (c *Client) Headers(m map[string]string) *Client
- func (c *Client) MaxResponseBytes(n int64) *Client
- func (c *Client) PatchJSON(ctx context.Context, path string, body any) (*Response, error)
- func (c *Client) PostJSON(ctx context.Context, path string, body any) (*Response, error)
- func (c *Client) PutJSON(ctx context.Context, path string, body any) (*Response, error)
- func (c *Client) Query(k, v string) *Client
- func (c *Client) Retry(n int) *Client
- func (c *Client) Timeout(d time.Duration) *Client
- func (c *Client) Transport(rt http.RoundTripper) *Client
- type Response
Constants ¶
const DefaultMaxResponseBytes int64 = 10 << 20 // 10 MiB
DefaultMaxResponseBytes caps how much of a response body is buffered into memory, guarding against a hostile or runaway server OOMing the process. Override per-client with MaxResponseBytes.
Variables ¶
var ErrResponseTooLarge = errors.New("httpclient: response body exceeds limit")
ErrResponseTooLarge is returned when a response body exceeds the configured cap (see MaxResponseBytes).
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is an immutable HTTP client configuration. Each setter returns a clone so chains compose safely:
base := httpclient.New().BaseURL("https://api.x.com").BearerToken(t)
a := base.Header("X-Trace", "1") // doesn't mutate base
func New ¶
func New() *Client
New returns a Client with safe defaults (10s timeout, no retries, stdlib transport).
func (*Client) BearerToken ¶
BearerToken sets Authorization: Bearer <token>.
func (*Client) MaxResponseBytes ¶ added in v0.20.0
MaxResponseBytes caps how many bytes of a response body are buffered into memory. A response larger than the cap fails with ErrResponseTooLarge instead of consuming unbounded memory. The default is DefaultMaxResponseBytes (10 MiB); pass a value <= 0 to disable the cap (opt-out) when you trust the upstream.
func (*Client) Query ¶
Query adds a URL query parameter (appends — call multiple times for repeated keys).
func (*Client) Retry ¶
Retry sets how many times to retry on transport errors or 5xx / 429 responses. Default 0 (no retries). Backoff grows exponentially from the value passed to Backoff().
type Response ¶
type Response struct {
// contains filtered or unexported fields
}
Response wraps an http.Response with helpers and an already-read body so callers don't need to remember to Close.