fix(leonardo): get-session 返回 null 时重试 3 次再判 cookie 失效
预热成功后的 200 null 之前第一次就 break 并直接 ErrAuth;同样的响应也可能是某个出口 IP 被挑战页静默降级,改成最多 3 次(第 2、3 次走代理换 IP)都拿不到 accessToken 才算死号。
This commit is contained in:
@@ -40,6 +40,12 @@ const (
|
|||||||
// attempt is retried, falling back to the proxy for a different exit IP.
|
// attempt is retried, falling back to the proxy for a different exit IP.
|
||||||
getSessionAttempts = 10
|
getSessionAttempts = 10
|
||||||
getSessionRetryDelay = 2 * time.Second
|
getSessionRetryDelay = 2 * time.Second
|
||||||
|
// A warmed 200 null usually means the session is gone server-side, but the
|
||||||
|
// same answer also comes back when the checkpoint silently serves a session-
|
||||||
|
// less page to an exit IP — so retry it a few times (later attempts through
|
||||||
|
// the proxy) before calling the account dead. Fewer attempts than the 429
|
||||||
|
// budget: each one costs two requests and a truly dead cookie never recovers.
|
||||||
|
getSessionNullAttempts = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -229,6 +235,9 @@ func (c *Client) GetSession(ctx context.Context, cookie string) (*Session, error
|
|||||||
}
|
}
|
||||||
status, body, setCookies, err = c.fetchSession(ctx, client, send)
|
status, body, setCookies, err = c.fetchSession(ctx, client, send)
|
||||||
if err == nil && warmed && status != 429 && status != 403 {
|
if err == nil && warmed && status != 429 && status != 403 {
|
||||||
|
if status == 200 && sessionAccessToken(body) == "" && attempt < getSessionNullAttempts-1 {
|
||||||
|
continue // retry a null session on another exit IP before giving up
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,6 +358,20 @@ func (c *Client) fetchSession(ctx context.Context, client tlsclient.HttpClient,
|
|||||||
return resp.StatusCode, body, resp.Header["Set-Cookie"], nil
|
return resp.StatusCode, body, resp.Header["Set-Cookie"], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sessionAccessToken pulls the bearer out of a get-session body; "" means the
|
||||||
|
// answer carried none (a null session, or a session without a token).
|
||||||
|
func sessionAccessToken(body []byte) string {
|
||||||
|
var raw struct {
|
||||||
|
Session struct {
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
} `json:"session"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &raw); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(raw.Session.AccessToken)
|
||||||
|
}
|
||||||
|
|
||||||
// sessionHeader is the auth endpoints' request shape, copied from a real
|
// sessionHeader is the auth endpoints' request shape, copied from a real
|
||||||
// browser's call (HAR): a same-origin GET carries NO origin header and DOES
|
// browser's call (HAR): a same-origin GET carries NO origin header and DOES
|
||||||
// carry the ua client hints + priority — sending origin while omitting the
|
// carry the ua client hints + priority — sending origin while omitting the
|
||||||
|
|||||||
Reference in New Issue
Block a user