pkg

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	EventPhaseStart = "start"
	EventPhaseEnd   = "end"
)

EventPhase* 表示一次刷新事件的时序阶段,用于配对 start/end 两条日志。

Variables

View Source
var (
	// ContextServerIndex uses a server configuration from the index.
	ContextServerIndex = contextKey("serverIndex")

	// ContextOperationServerIndices uses a server configuration from the index mapping.
	ContextOperationServerIndices = contextKey("serverOperationIndices")

	// ContextServerVariables overrides a server configuration variables.
	ContextServerVariables = contextKey("serverVariables")

	// ContextOperationServerVariables overrides a server configuration variables using operation specific values.
	ContextOperationServerVariables = contextKey("serverOperationVariables")
)
View Source
var (
	OnTokenRefresh = func(TokenRefreshEvent) {}
	OnAuthRetry    = func(AuthRetryEvent) {}
)

OnTokenRefresh / OnAuthRetry 是可选的事件 hook。业务侧可在启动时(例如 main 里)替换为自定义实现以接入项目日志系统。默认实现为空,SDK 不产生任何日志副作用。

两个变量只应在程序启动时设置一次,不要在运行中并发修改(SDK 内部没有锁 保护)。如果业务侧确实需要运行中切换,应自行实现同步。

View Source
var DefaultClient = &http.Client{
	Transport: SharedTransport,

	Timeout: 180 * time.Second,
	Jar:     nil,
}

DefaultClient 是基于 SharedTransport 的全局 HTTP 客户端,附带整体超时、Cookie 管理和重定向策略

View Source
var LongTimeHttpClient = &http.Client{
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	},
	Timeout: 10 * time.Minute,
	Jar:     nil,
}
View Source
var MarketplaceMap = map[string]string{
	"CA": "A2EUQ1WTGCTBG2",
	"US": "ATVPDKIKX0DER",
	"MX": "A1AM78C64UM0Y8",
	"BR": "A2Q3Y263D00KWC",
	"IE": "A28R8C7NBKEWEA",
	"ES": "A1RKKUPIHCS9HS",
	"UK": "A1F83G8C2ARO7P",
	"FR": "A13V1IB3VIYZZH",
	"BE": "AMEN7PMS3EDWL",
	"NL": "A1805IZSGTT6HS",
	"DE": "A1PA6795UKMFR9",
	"IT": "APJ6JRA9NG5V4",
	"SE": "A2NODRKZP88ZB9",
	"ZA": "AE08WJ6YKNBMC",
	"PL": "A1C3SOZRARQ6R3",
	"EG": "ARBP9OOSHTCHU",
	"TR": "A33AVAJ2PDY3EV",
	"SA": "A17E79C6D8DWNP",
	"AE": "A2VIGQ35RCS4UG",
	"IN": "A21TJRUUN4KGV",
	"SG": "A19VAU5U5O7RUS",
	"AU": "A39IBJ37TRP1C6",
	"JP": "A1VC38T7YXB528",
}
View Source
var SharedTransport = &http.Transport{

	DialContext: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).DialContext,

	MaxIdleConns:        100,
	MaxIdleConnsPerHost: 10,

	MaxConnsPerHost: 2000,

	IdleConnTimeout: 10 * time.Second,

	TLSHandshakeTimeout: 30 * time.Second,

	ExpectContinueTimeout: 30 * time.Second,

	ResponseHeaderTimeout: 30 * time.Second,

	MaxResponseHeaderBytes: 1 << 20,

	DisableKeepAlives: false,

	DisableCompression: false,

	TLSClientConfig: &tls.Config{

		InsecureSkipVerify: true,
	},

	ForceAttemptHTTP2: true,
}

SharedTransport 是一个全局可复用的 HTTP Transport,配置了连接池、超时和 TLS 等参数

Functions

func HTTPTraceEnabled

func HTTPTraceEnabled() bool

func HeadString

func HeadString(b []byte, n int) string

HeadString 返回 b 的前 n 字节作为字符串。b 短于 n 时直接全部返回。 专用于把 HTTP 响应 body 前若干字节写入诊断事件,既保留诊断价值又避免 日志膨胀 / 误把长 body 的敏感部分打出去。

func Ptr

func Ptr[T any](v T) *T

func SetHTTPTraceEnabled

func SetHTTPTraceEnabled(enabled bool)

SetHTTPTraceEnabled 控制 SDK 是否记录 HTTP 请求与响应摘要。

func TokenSuffix

func TokenSuffix(s string) string

TokenSuffix 返回字符串的最后 8 个字符,前面加 "..."。用于安全地把 refresh_token / access_token 写入日志,避免泄漏完整凭据。 对不足 8 位的字符串(异常或空值)返回固定占位符。

Types

type APIKey

type APIKey struct {
	Key    string
	Prefix string
}

APIKey provides API key based authentication to a request passed via context using ContextAPIKey

type AccessTokenCache

type AccessTokenCache struct {
	sync.Map
}

func (*AccessTokenCache) Get

func (s *AccessTokenCache) Get(key any) string

func (*AccessTokenCache) Invalidate

func (s *AccessTokenCache) Invalidate(key any)

Invalidate 移除给定 refresh_token 对应的缓存条目。 当下游接口明确告知"access token 已过期"时(缓存的 ExpiresAt 与 Amazon 服务端判定不一致的偶发场景——时钟偏差/节点缓存),调用方应显式 Invalidate, 让下一次 Get 返回空,触发 singleflight 重新刷新。

注意:名字不叫 Delete 是为了避免和嵌入的 sync.Map.Delete 方法产生歧义/误用。

func (*AccessTokenCache) InvalidateIfMatch

func (s *AccessTokenCache) InvalidateIfMatch(key any, failedToken string)

InvalidateIfMatch 仅当缓存中当前的 AccessToken 仍等于 failedToken 时才删除, 专用于多 goroutine 并发重试的场景:

t0  A 用 T1 请求 -> 401
t1  A Invalidate,singleflight 刷出 T2,写入 cache
t2  B 用 T1 请求 -> 401 (B 是在 A 刷新前发出去的,返回慢了)
t3  B 若无脑 Invalidate,会把 A 刚写的 T2 删掉,触发多余的一次 LwA 刷新

改用 InvalidateIfMatch(key, T1) 后:t3 时 cache 里已是 T2,failedToken=T1 不匹配,不会误删;B 紧跟着的 acquireAccessToken 从 cache 直接命中 T2 复用。

对 Amazon 配额压力不大,但能让"token 自然过期那一刻"的刷新次数收敛到 1 次。

DEBUG 日志区分三种结果,便于排查"并发竞态是否真的发生":

  • hit:成功删除了失败 token
  • skipped-newer-token:缓存里已经是别的 goroutine 刷出来的新 token, 本方法保护了新 token 不被误删(就是避免冗余刷新的关键)
  • miss:key 不存在或值类型异常

func (*AccessTokenCache) Put

func (s *AccessTokenCache) Put(key any, value CacheItem) string

type AuthRetryEvent

type AuthRetryEvent struct {
	Service            string
	RefreshTokenSuffix string
	Method             string
	URL                string

	// FirstStatus / FirstBodyHead 首次响应的状态码与 body 前 256 字节,
	// 便于诊断 shouldRetryAuthExpired 匹配了哪条规则。
	FirstStatus   int
	FirstBodyHead string

	// FailedAccessTokenSuffix 首次请求用的 token 后 8 位。
	FailedAccessTokenSuffix string

	// 以下字段表示"重试本身的结果":
	//   - RetrySkipped 非空:未执行重试,原因如 "body-not-cloneable";
	//     此时 RetryStatus/RetryErr 均为零值。
	//   - RetrySkipped 空且 RetryErr 非 nil:网络错误,重试请求未拿到响应。
	//   - RetrySkipped 空且 RetryStatus != 0:重试已经拿到 HTTP 响应。
	RetrySkipped string
	RetryStatus  int
	RetryErr     error
}

AuthRetryEvent 描述一次 SDK 层的 401/403 兜底重试。

触发条件:shouldRetryAuthExpired 命中(SDK 判定是 access_token 被 Amazon 拒绝的瞬时错误),SDK 将清缓存、重刷 token、重发一次原请求。

type BasicAuth

type BasicAuth struct {
	UserName string `json:"userName,omitempty"`
	Password string `json:"password,omitempty"`
}

BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth

type CacheItem

type CacheItem struct {
	AccessToken            string
	AccessTokenExpiredTime time.Time
}

type Configuration

type Configuration struct {
	Host             string            `json:"host,omitempty"`
	Scheme           string            `json:"scheme,omitempty"`
	DefaultHeader    map[string]string `json:"defaultHeader,omitempty"`
	UserAgent        string            `json:"userAgent,omitempty"`
	Debug            bool              `json:"debug,omitempty"`
	Servers          ServerConfigurations
	OperationServers map[string]ServerConfigurations
	HTTPClient       *http.Client
}

Configuration stores the configuration of the API client

func NewConfiguration

func NewConfiguration(iAuth IAuth) *Configuration

NewConfiguration returns a new Configuration object

func (*Configuration) AddDefaultHeader

func (c *Configuration) AddDefaultHeader(key string, value string)

AddDefaultHeader adds a new HTTP header to the default header in the request

func (*Configuration) ServerURL

func (c *Configuration) ServerURL(index int, variables map[string]string) (string, error)

ServerURL returns URL based on server settings

func (*Configuration) ServerURLWithContext

func (c *Configuration) ServerURLWithContext(ctx context.Context, endpoint string) (string, error)

ServerURLWithContext returns a new server URL given an endpoint

type IAuth

type IAuth interface {
	GetLwaTokenEndpoint() string

	GetDataEndpoint() string

	GetLwaCodeEndpoint() string

	GetLwaURL() string

	GetAccessTokenFromEndpoint() error

	ParseToken(resp *http.Response) error

	GetRefreshToken(code string) error

	BuildClient() *http.Client
}

type ISO8601Time

type ISO8601Time time.Time

func (*ISO8601Time) MarshalJSON

func (t *ISO8601Time) MarshalJSON() ([]byte, error)

func (*ISO8601Time) Scan

func (t *ISO8601Time) Scan(v any) error

func (ISO8601Time) String

func (t ISO8601Time) String() string

func (*ISO8601Time) UnmarshalJSON

func (t *ISO8601Time) UnmarshalJSON(data []byte) error

func (ISO8601Time) Value

func (t ISO8601Time) Value() (driver.Value, error)

type Logger

type Logger interface {
	Debug(msg string, fields ...any)
	Info(msg string, fields ...any)
	Warn(msg string, fields ...any)
	Error(msg string, fields ...any)
}

Logger 是 SDK 内部使用的日志抽象。

SDK 刻意不直接依赖 slog/logrus/zap 等具体库,避免把调用方绑死在某个 logger 生态上。业务侧可以在启动时提供一个实现,把 SDK 内部的细粒度诊断日志 (网络错误、RDT 透传、cache 失效行为等)统一路由到自己的日志系统。

职责划分:

  • OnTokenRefresh / OnAuthRetry 是**事件 hook**,强类型结构体,适合做 指标统计 / 告警路由等。
  • Logger 是**诊断日志**,松散 kv,用来记录事件 hook 未覆盖的边角情况。

两者正交,业务侧各自按需注册。

方法签名参考 slog.Logger:fields 是变长 any,每两个一组 (key, value); 奇数个或 key 非字符串时,Adapter 实现应自行决定如何处理(通常是丢弃或 塞到 "?key" 里)。

var DefaultLogger Logger = noopLogger{}

DefaultLogger 是 SDK 包级日志入口,默认 no-op。业务侧可在进程启动时替换:

pkg.DefaultLogger = myAdapter{...}

只应在程序启动阶段设置一次,运行期并发写没有锁保护。

type ServerConfiguration

type ServerConfiguration struct {
	URL         string
	Description string
	Variables   map[string]ServerVariable
}

ServerConfiguration stores the information about a server

type ServerConfigurations

type ServerConfigurations []ServerConfiguration

ServerConfigurations stores multiple ServerConfiguration items

func (ServerConfigurations) URL

func (sc ServerConfigurations) URL(index int, variables map[string]string) (string, error)

URL formats template on a index using given variables

type ServerVariable

type ServerVariable struct {
	Description  string
	DefaultValue string
	EnumValues   []string
}

ServerVariable stores the information about a server variable

type TokenCache

type TokenCache interface {
	Get(key any) string
	Put(key any, value CacheItem) string
}

type TokenRefreshEvent

type TokenRefreshEvent struct {
	// Service 触发方,固定取值:"advertising" / "selling_partner"。
	Service string

	// RefreshTokenSuffix 已脱敏的 refresh_token 最后 8 位,用于关联同一 seller
	// 的多次事件,安全地写入日志。
	RefreshTokenSuffix string

	// Reason 触发本次刷新的上游原因:
	//   "cache-miss"  首次或 token 自然临近过期
	//   "401-retry"   前一次业务请求被 Amazon 判为无效 token,强制刷新重试
	Reason string

	// Phase 当前处于刷新的哪个阶段:EventPhaseStart / EventPhaseEnd。
	Phase string

	// Success 是否成功;为 false 时 Err 必定非 nil。
	Success bool
	// Duration 从发起 HTTP 到收到响应的耗时。
	Duration time.Duration
	// AccessTokenSuffix 新 access_token 最后 8 位(仅成功时)。
	AccessTokenSuffix string
	// ExpiresAt 新 token 的预期过期时刻(仅成功时)。
	ExpiresAt time.Time
	// Err 失败原因(仅失败时)。
	Err error
}

TokenRefreshEvent 描述一次"真正发生的"LwA access_token 换取过程。

发送时机:singleflight 回调进入 HTTP 请求阶段时就算一次事件;cache 命中、 singleflight 双检直接复用别人结果的场景不发(避免淹没日志)。

一次刷新按时序会收到两个事件:Phase=EventPhaseStart / EventPhaseEnd。 末尾事件通过 Success 字段区分成功失败,便于外部 logger 做 start+duration 对齐。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL