Documentation
¶
Overview ¶
Package httpclient provides a centralized HTTP client factory with preset configurations.
Index ¶
- Constants
- func New(opts ...Option) *http.Client
- func NewDefault() *http.Client
- func NewForExternal(net NetworkProxyConfig, timeout time.Duration) *http.Client
- func NewLong() *http.Client
- func RedactURLError(err error) error
- func SafeDownloadCheckRedirect(maxRedirects int) func(req *http.Request, via []*http.Request) error
- func ValidateDownloadURL(raw string) error
- type NetworkProxyConfig
- type Option
- type Options
Constants ¶
const ( // DefaultTimeout is the standard timeout for most HTTP requests (30s). DefaultTimeout = 30 * time.Second // LongTimeout is for operations that may take longer (60s). LongTimeout = 60 * time.Second )
Preset timeout durations for common use cases.
Variables ¶
This section is empty.
Functions ¶
func New ¶
New creates a new HTTP client with the given options. If no timeout is specified, DefaultTimeout (30s) is used.
func NewDefault ¶
NewDefault creates a new HTTP client with the default timeout (30s).
func NewForExternal ¶
func NewForExternal(net NetworkProxyConfig, timeout time.Duration) *http.Client
NewForExternal builds a proxy-aware *http.Client with the given timeout, reading proxy settings from the supplied config. Used by every external integration (Prowlarr, SABnzbd, NZBLNK resolver, Arrs). Internal endpoints should NOT use this — call New() or use http.DefaultClient instead.
A nil net argument disables proxying.
func NewLong ¶
NewLong creates a new HTTP client with a longer timeout (60s). Suitable for operations that may take longer, such as external API calls.
func RedactURLError ¶
RedactURLError strips the query string, fragment and userinfo from the URL embedded in a *url.Error before it can reach a log or an API response.
Indexer requests carry credentials as query parameters — an `apikey` on Newznab search/caps calls, and whatever the upstream indexer embeds in a Prowlarr-issued download URL. net/http reports transport failures as a *url.Error whose message is the full request URL, so wrapping such an error unmodified publishes the credential wherever the caller logs or renders it.
The wrapped cause is preserved, so errors.Is/As against the underlying error (context.DeadlineExceeded, net.Error, ...) behave as before. Scheme, host and path are kept — they are what makes the error diagnosable.
func SafeDownloadCheckRedirect ¶
SafeDownloadCheckRedirect returns a CheckRedirect function that permits redirects up to maxRedirects (default 10) and re-validates each redirect target per hop with ValidateDownloadURL — the same literal-IP-only rule (DNS hostnames NOT resolved — see ValidateDownloadURL). It also redacts sensitive credentials (X-Api-Key, Authorization) when redirecting away from the ORIGINAL request origin (scheme + host, case-insensitive); that redaction governs request headers, not the destination allowlist.
Binding to the original origin (via[0]) rather than the immediately previous hop is load-bearing: net/http copies the initial request's headers into every redirect request before CheckRedirect runs (see makeHeadersCopier — custom headers such as X-Api-Key are copied unconditionally), so a chain like Prowlarr A -> external B -> B/path would otherwise reintroduce the key on the second hop (prev == req host) after it was stripped on the first. A hop that returns to the original origin is allowed to carry credentials again, since the key belongs to that host.
func ValidateDownloadURL ¶
ValidateDownloadURL checks that a URL supplied by a remote indexer is safe to fetch. Indexer search responses drive these requests, so a compromised or hostile indexer would otherwise be able to point AltMount at loopback, link-local or private addresses — including cloud instance-metadata endpoints — turning the download path into an SSRF primitive.
Security contract: literal-IP blocking ONLY. A literal IP that is loopback, private, link-local (unicast or multicast) or unspecified is rejected, as are "localhost" names. DNS hostnames are NOT resolved here: a DNS name that resolves to a private address still passes. This check is not a destination allowlist. Callers re-validate every redirect hop with SafeDownloadCheckRedirect, which applies this same literal-IP-only rule per hop (DNS hostnames again NOT resolved). Credential stripping on cross-host redirects is a separate concern: it governs request headers, not the destination allowlist.
Types ¶
type NetworkProxyConfig ¶
type NetworkProxyConfig interface {
GetHTTPProxy() string
GetHTTPSProxy() string
GetNoProxy() string
}
NetworkProxyConfig is the minimal interface the factory needs from the application config. Implemented by config.NetworkConfig — declared here to avoid an internal/config ↔ internal/httpclient import cycle.
type Option ¶
type Option func(*Options)
Option is a functional option for configuring HTTP clients.
func WithProxyConfig ¶
WithProxyConfig returns an Option that installs a proxy-aware *http.Transport derived from the supplied values. Pass empty strings to disable proxying.