Documentation
¶
Overview ¶
Package httpx is the one outbound HTTP policy this app has. Every request that leaves the box - a router being asked for a new address, a page being crawled, a debrid API being polled - is made by a client built here, so that a proxy, a user agent, a redirect rule or a connection ceiling is one edit instead of fifteen scattered http.Client literals.
Clients come from New rather than from one client the package exports. A package-level client is a dependency nothing declares: a test cannot give one subsystem a stub without every other subsystem silently receiving the same stub, and nothing in the wiring shows which of them share a connection pool. Sharing is still fine here, it just has to be written down at the call site where it can be seen.
This is not the download path. Downloaded bytes are metered through internal/netproxy and must not carry a whole-request deadline: a transfer that runs for an hour is not a stuck request. Clients from here are for the short control-plane calls that happen around a download.
Index ¶
Constants ¶
const ( // DefaultTimeout bounds a whole request, body included. Everything this // package is pointed at answers in a few kilobytes, so a minute is already a // server that has effectively refused. DefaultTimeout = 60 * time.Second // DefaultDialTimeout bounds reaching the host at all. A dropped packet to a // dead LAN address otherwise sits in the kernel's SYN retry schedule for // well over a minute, which the user reads as the app being broken. DefaultDialTimeout = 15 * time.Second // DefaultTLSHandshakeTimeout bounds the handshake separately, because a // middlebox that completes the TCP connection and then eats the ClientHello // is indistinguishable from a slow server without it. DefaultTLSHandshakeTimeout = 10 * time.Second // DefaultResponseHeaderTimeout is the one that catches the nastiest case: a // host that accepts everything and answers nothing. Without it such a peer // is only noticed when the overall Timeout expires, and a caller that opted // out of that ceiling would wait forever. DefaultResponseHeaderTimeout = 30 * time.Second // DefaultMaxRedirects bounds the hop chain. An unbounded chain is the // cheapest way to send a client in circles; no legitimate endpoint needs // anywhere near this many. DefaultMaxRedirects = 10 )
The ceilings. None of these is tuning: each one exists so that a specific way of hanging terminates on its own instead of pinning the goroutine that started it for the life of the process.
const NoTimeout = time.Duration(-1)
NoTimeout switches the whole-request ceiling off for a caller that streams a response rather than reading a small one. The transport ceilings still apply, so a peer that accepts the connection and then stops talking is still not immortal.
Variables ¶
This section is empty.
Functions ¶
func New ¶
New builds a client with the whole policy applied: the transport below, the redirect rule, the user agent and the request ceiling.
func NewTransport ¶
NewTransport builds just the transport, for the callers that need to hand one to a library instead of a whole client. It carries the ceilings and the pool; the user agent and the redirect rule live on the client, because a transport never sees a redirect.
Types ¶
type Options ¶
type Options struct {
// UserAgent overrides the app's own identification. Empty means UserAgent();
// a caller only sets this when a host refuses anything that does not look
// like a browser.
UserAgent string
// Timeout bounds the whole request. Zero means DefaultTimeout, NoTimeout
// removes the ceiling for a streaming caller.
Timeout time.Duration
DialTimeout time.Duration // zero means DefaultDialTimeout
TLSHandshakeTimeout time.Duration // zero means DefaultTLSHandshakeTimeout
ResponseHeaderTimeout time.Duration // zero means DefaultResponseHeaderTimeout
// MaxRedirects bounds the hop chain. Zero means DefaultMaxRedirects;
// negative follows none at all and hands the caller the 3xx itself, which is
// what a resolver wants when the redirect target *is* the answer.
MaxRedirects int
// Proxy resolves the proxy for a request. Nil means the environment, which
// is what a container operator expects HTTP_PROXY to do; NoProxy pins a
// client to direct connections.
Proxy func(*http.Request) (*url.URL, error)
// Jar keeps cookies for callers that need a session. Nil means cookies are
// not kept at all, so nothing leaks between two unrelated hosts.
Jar http.CookieJar
}
Options tunes one client. The zero value is the policy, so a caller with no opinion of its own writes New(Options{}) and gets every ceiling above.