Documentation
¶
Overview ¶
Package httpclient is the only way out to the network: every source and registry fetches through a Client (ADR 0002).
A Client sends requests over HTTPS to an exact list of hosts, follows a redirect only to one of them, bounds every phase of a call with its own timeout, refuses a body over a size limit and retries 429 and 5xx answers with exponential backoff, full jitter and Retry-After.
A Client never uses a proxy, even when HTTP_PROXY or HTTPS_PROXY is set: an inherited proxy would see every query and widen the egress surface without anyone choosing it. Filtering egress belongs to the network.
Index ¶
- Constants
- Variables
- type Client
- func (c *Client) Do(req *http.Request) (*http.Response, error)
- func (c *Client) Get(ctx context.Context, rawURL string) ([]byte, error)
- func (c *Client) Post(ctx context.Context, rawURL, contentType string, body []byte) error
- func (c *Client) Send(ctx context.Context, method, rawURL string, header http.Header, body []byte) ([]byte, error)
- func (c *Client) Submit(ctx context.Context, rawURL, contentType string, body []byte) ([]byte, error)
- type Config
- type StatusError
Constants ¶
const ( DefaultTimeout = 30 * time.Second DefaultDialTimeout = 5 * time.Second DefaultTLSHandshakeTimeout = 5 * time.Second DefaultResponseHeaderTimeout = 10 * time.Second DefaultMaxBodyBytes = 16 << 20 DefaultAttempts = 3 DefaultBaseBackoff = 500 * time.Millisecond DefaultMaxBackoff = 30 * time.Second )
Defaults applied by New to a zero Config field. They are choices, sized for the files and registries listed in docs/sources.md (the largest file, ARCEP MAJNUM, is about 1.1 MB).
const DefaultUserAgent = "arnakoff (+https://github.com/arnakoff/api)"
DefaultUserAgent identifies Arnakoff to upstreams and points to the project, so an operator who sees the traffic knows whom to contact.
Variables ¶
var ( // ErrURLNotAllowed means a URL, or a redirect target, is not HTTPS on an // allowed host, or carries credentials. No request was sent to it. ErrURLNotAllowed = errors.New("URL not allowed") // ErrBodyTooLarge means a response body exceeds Config.MaxBodyBytes. ErrBodyTooLarge = errors.New("response body too large") // ErrInvalidConfig means New was given a Config it cannot use. ErrInvalidConfig = errors.New("invalid HTTP client config") )
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is an outbound HTTP client restricted to allowed hosts. It is safe for concurrent use.
func (*Client) Do ¶ added in v0.50.0
Do sends req as built and returns the response, whatever its status, for a library that builds its own requests, such as an S3 SDK. It keeps the rules of Get: req must be HTTPS on an allowed host, or ErrURLNotAllowed is returned without sending anything; a redirect elsewhere is refused; Config.Timeout bounds the whole exchange, body included; reading more than Config.MaxBodyBytes of the body fails with ErrBodyTooLarge. It does not retry and leaves the User-Agent of req. The caller closes the body.
func (*Client) Get ¶
Get fetches rawURL and returns its body. It returns ErrURLNotAllowed without sending anything when rawURL is not HTTPS on an allowed host, ErrBodyTooLarge, a *StatusError for a non-2xx answer, or the error of the transport or of ctx. Its errors name the URL without its query, which may carry a credential, such as a presigned URL.
func (*Client) Post ¶ added in v0.19.0
Post sends body to rawURL with the given content type and discards the answer. It is retried like Get, so the receiver may get body twice. Its errors, those of Get otherwise, never quote rawURL: a webhook URL carries a secret.
func (*Client) Send ¶ added in v0.59.0
func (c *Client) Send(ctx context.Context, method, rawURL string, header http.Header, body []byte) ([]byte, error)
Send sends body to rawURL with method and the fields of header, such as an Authorization, and returns the answer's body. It is retried like Get, so the receiver may get the request twice: use it for an idempotent call, or through a Client of one attempt. Its errors, those of Get otherwise, never quote rawURL nor header.
func (*Client) Submit ¶ added in v0.42.0
func (c *Client) Submit(ctx context.Context, rawURL, contentType string, body []byte) ([]byte, error)
Submit sends body to rawURL with the given content type and returns the answer's body, as Post does otherwise: an upstream that must not get body twice is reached through a Client of one attempt.
type Config ¶
type Config struct {
// AllowedHosts are the exact hosts the client may reach, as in URL.Host:
// "rdap.nic.fr", or "127.0.0.1:8443" with a port. Required.
AllowedHosts []string
// UserAgent is sent with every request.
UserAgent string
// Timeout bounds a whole Get call: every attempt, every pause between
// attempts and the reading of the body.
Timeout time.Duration
// DialTimeout bounds opening a TCP connection.
DialTimeout time.Duration
// TLSHandshakeTimeout bounds the TLS handshake.
TLSHandshakeTimeout time.Duration
// ResponseHeaderTimeout bounds the wait for response headers once the
// request is sent.
ResponseHeaderTimeout time.Duration
// MaxBodyBytes is the largest response body Get accepts.
MaxBodyBytes int64
// Attempts is the number of tries for a 429 or 5xx answer, the first
// one included.
Attempts int
// BaseBackoff is the ceiling of the first pause between two attempts;
// the ceiling doubles at each retry, up to MaxBackoff.
BaseBackoff time.Duration
// MaxBackoff caps a pause. A Retry-After longer than MaxBackoff ends
// the retries rather than hammer the upstream sooner than it asked.
MaxBackoff time.Duration
// RootCAs are the trusted certificate authorities; nil means the
// system pool.
RootCAs *x509.CertPool
}
Config configures a Client. Every zero field but AllowedHosts and RootCAs takes its Default value; a negative one is invalid.
type StatusError ¶
type StatusError struct {
// StatusCode is the HTTP status of the last response.
StatusCode int
// contains filtered or unexported fields
}
StatusError is a response whose status is not 2xx, returned once retries, if any, are over. Match it with errors.As to read StatusCode, for example to read a 404 from a registry as "not found".
func (*StatusError) Error ¶
func (e *StatusError) Error() string
Error returns the status, such as "unexpected HTTP status 404 Not Found".
func (*StatusError) RetryAfter ¶ added in v0.20.0
func (e *StatusError) RetryAfter() (time.Duration, bool)
RetryAfter returns the wait the last response asked for in a valid Retry-After header, and false when it had none, so a caller can stay away from an upstream that throttles it.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package httpclienttest builds httpclient.Client values for tests against an httptest TLS server.
|
Package httpclienttest builds httpclient.Client values for tests against an httptest TLS server. |