99 lines
2.8 KiB
Go
99 lines
2.8 KiB
Go
package fa
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
)
|
|
|
|
// reqOverrideKey scopes the per-request override on a context. The
|
|
// transport reads it; nothing outside this package can synthesize one.
|
|
type reqOverrideKey struct{}
|
|
|
|
// requestOverride is the resolved diff between the client's default
|
|
// config and the options passed to a single call. The transport applies
|
|
// it on every outbound request whose context carries one.
|
|
//
|
|
// Only request-level fields appear here. Client-only fields (rate limiter,
|
|
// http.Client, retries, parser flags) are deliberately absent: passing the
|
|
// corresponding Option to a call is a silent no-op, which is documented on
|
|
// each Option.
|
|
type requestOverride struct {
|
|
cookies Cookies
|
|
cf CFCookies
|
|
sfw SFWMode
|
|
userAgent string
|
|
}
|
|
|
|
// applyRequestOptions resolves per-call options on top of the client's
|
|
// config and, if any request-level field actually changed, returns a
|
|
// context carrying a *requestOverride for the transport to read. When no
|
|
// options are passed or none of them touch request-level fields, the
|
|
// original context is returned unchanged so the hot path stays
|
|
// allocation-free.
|
|
func (c *Client) applyRequestOptions(ctx context.Context, opts []Option) context.Context {
|
|
if len(opts) == 0 {
|
|
return ctx
|
|
}
|
|
cfg := c.cfg
|
|
for _, o := range opts {
|
|
if o != nil {
|
|
o(&cfg)
|
|
}
|
|
}
|
|
if cfg.cookies == c.cfg.cookies &&
|
|
cfg.cf == c.cfg.cf &&
|
|
cfg.sfw == c.cfg.sfw &&
|
|
cfg.userAgent == c.cfg.userAgent {
|
|
return ctx
|
|
}
|
|
return context.WithValue(ctx, reqOverrideKey{}, &requestOverride{
|
|
cookies: cfg.cookies,
|
|
cf: cfg.cf,
|
|
sfw: cfg.sfw,
|
|
userAgent: cfg.userAgent,
|
|
})
|
|
}
|
|
|
|
// requestOverrideFrom extracts the override the transport should apply
|
|
// to this request, or nil if none was attached.
|
|
func requestOverrideFrom(ctx context.Context) *requestOverride {
|
|
if ctx == nil {
|
|
return nil
|
|
}
|
|
ov, _ := ctx.Value(reqOverrideKey{}).(*requestOverride)
|
|
return ov
|
|
}
|
|
|
|
// touchesCookies reports whether this override should replace the Cookie
|
|
// header. A UA-only override leaves the header (and thus the jar's
|
|
// cookies) alone.
|
|
func (o *requestOverride) touchesCookies() bool {
|
|
return o.cookies.A != "" ||
|
|
o.cookies.B != "" ||
|
|
o.cf.Clearance != "" ||
|
|
o.sfw != SFWAuto
|
|
}
|
|
|
|
// cookieHeader renders the override's cookies into a single Cookie
|
|
// header value. The transport writes this verbatim, replacing whatever
|
|
// the shared cookie jar produced from c.http's jar.
|
|
func (o *requestOverride) cookieHeader() string {
|
|
var parts []string
|
|
if o.cookies.A != "" {
|
|
parts = append(parts, "a="+o.cookies.A)
|
|
}
|
|
if o.cookies.B != "" {
|
|
parts = append(parts, "b="+o.cookies.B)
|
|
}
|
|
if o.cf.Clearance != "" {
|
|
parts = append(parts, "cf_clearance="+o.cf.Clearance)
|
|
}
|
|
switch o.sfw {
|
|
case SFWOn:
|
|
parts = append(parts, "sfw=1")
|
|
case SFWOff:
|
|
parts = append(parts, "sfw=0")
|
|
}
|
|
return strings.Join(parts, "; ")
|
|
}
|