Documentation
¶
Overview ¶
Package sourcehttp is the HTTP toolkit a pull connector's API client is built over: the failure taxonomy, status and rate-limit mapping, conditional requests, and a logging transport. Provider vocabulary and source semantics (caching, poll cadence, cooldowns) stay with the caller.
Index ¶
- Constants
- Variables
- func Conditional(v Validators) httpclient.Middleware
- func New(cfg Config, mws ...httpclient.Middleware) *httpclient.Client
- func NewTransport(name string, log zerolog.Logger, next http.RoundTripper) http.RoundTripper
- func NotModified(resp *http.Response) bool
- type Config
- type Errors
- type ForbiddenClassifier
- type RateLimitError
- type Validators
Constants ¶
const DefaultTimeout = 30 * time.Second
Variables ¶
var ( ErrRateLimited = errors.New("source: rate limited") ErrUnreachable = errors.New("source: unreachable") )
The taxonomy the app acts on: unauthorized drives re-auth, rate limited and unreachable drive fetch cooldowns. A connector classifies into these three and gets both behaviours without the app knowing its name.
Functions ¶
func Conditional ¶
func Conditional(v Validators) httpclient.Middleware
Conditional makes one request conditional on v.
func New ¶
func New(cfg Config, mws ...httpclient.Middleware) *httpclient.Client
New builds the client a source's API calls go through. Logging is installed as a transport rather than middleware so it observes the request that reaches the wire, after redirects and regardless of caller middleware.
otelhttp wraps the logging transport, so the span covers everything the log line describes. Its semconv metrics are the whole of this package's request instrumentation; a hand-written counter would duplicate them under a name no dashboard knows.
func NewTransport ¶
func NewTransport(name string, log zerolog.Logger, next http.RoundTripper) http.RoundTripper
func NotModified ¶
NotModified must be checked before Errors.Status, which classifies a 304 as a failure like any other non-2xx.
Types ¶
type Config ¶
type Config struct {
// Name identifies the provider ("github") in log lines and error messages.
Name string
BaseURL string
Logger zerolog.Logger
// Token is resolved per request, so rotation needs no rebuild. nil is
// anonymous.
Token func() string
Timeout time.Duration
// Transport is the base the logging transport wraps; nil uses
// http.DefaultTransport.
Transport http.RoundTripper
}
type Errors ¶
type Errors struct {
Name string
Forbidden ForbiddenClassifier
}
Errors maps one provider's responses onto the taxonomy.
func (Errors) Status ¶
Status maps a response onto the taxonomy, returning nil for 2xx. It reads and closes nothing on success; on failure it consumes up to bodyPeek bytes for the message.
func (Errors) Unreachable ¶
Unreachable wraps a transport failure.
type ForbiddenClassifier ¶
ForbiddenClassifier reports whether a 403 is a rate limit rather than a permission failure. Only providers that overload the status need one.
type RateLimitError ¶
type RateLimitError struct {
// ResetAt is zero when the server sent neither Retry-After nor
// X-RateLimit-Reset.
ResetAt time.Time
}
RateLimitError carries the server's retry time. It unwraps to ErrRateLimited, so errors.Is keeps working for callers that cannot act on the deadline.
func RateLimit ¶
func RateLimit(h http.Header) *RateLimitError
RateLimit extracts the server's wait hint. Retry-After wins when present: it describes an active secondary-limit penalty, where X-RateLimit-Reset is the primary limit's epoch reset.
func (*RateLimitError) Error ¶
func (e *RateLimitError) Error() string
func (*RateLimitError) Unwrap ¶
func (e *RateLimitError) Unwrap() error
type Validators ¶
Validators are the cache validators carried between a response and the next conditional request for the same resource. An authenticated 304 is free of rate-limit cost on most APIs, so polling an unchanged resource costs nothing.
func ReadValidators ¶
func ReadValidators(h http.Header) Validators
func (Validators) Apply ¶
func (v Validators) Apply(h http.Header)
Apply makes a request conditional. Both headers are sent when both validators are held; RFC 9110 has the server prefer If-None-Match.
func (Validators) Empty ¶
func (v Validators) Empty() bool