inital commit
This commit is contained in:
98
request_options.go
Normal file
98
request_options.go
Normal file
@@ -0,0 +1,98 @@
|
||||
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, "; ")
|
||||
}
|
||||
Reference in New Issue
Block a user