mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 19:36:36 +08:00
优化
This commit is contained in:
@@ -185,7 +185,7 @@ func (c *OpenClient) doJSON(ctx context.Context, method, rawURL string, form map
|
||||
// refresh_token 刷新后重试一次。刷新失败或重试后仍失败才返回,
|
||||
// 避免长时间同步因 token 过期而整体失败。
|
||||
if isTokenCode(base.Code) {
|
||||
if access && c.tryRefreshTokenLocked() {
|
||||
if access && c.tryRefreshTokenLocked(ctx) {
|
||||
continue
|
||||
}
|
||||
if access {
|
||||
@@ -259,19 +259,43 @@ func (c *OpenClient) doAuthJSONWithUA(ctx context.Context, method, rawURL string
|
||||
}
|
||||
|
||||
// tryRefreshTokenLocked 并发安全地刷新 access_token;成功返回 true(调用方
|
||||
// 应使用内存中的新 token 重试原请求)。refresh_token 已失效时也会清空内存 token。
|
||||
func (c *OpenClient) tryRefreshTokenLocked() bool {
|
||||
// 应使用内存中的新 token 重试原请求)。
|
||||
//
|
||||
// 对"refresh_token 本身已失效/被吊销"(IsRefreshTokenDead,如 40140114/116/119/120)
|
||||
// 这类不可恢复的错误直接放弃并清空内存 token(提示需重新授权)。
|
||||
// 对其它失败(网络瞬时抖动、刷新接口可重试错误码等)做指数退避重试几次再放弃,
|
||||
// 避免同步长任务中途 token 到期时恰好撞上一个短暂的刷新失败就整体失败。
|
||||
func (c *OpenClient) tryRefreshTokenLocked(ctx context.Context) bool {
|
||||
c.tokenMu.Lock()
|
||||
defer c.tokenMu.Unlock()
|
||||
token, err := c.RefreshToken(c.RefreshTokenStr)
|
||||
if err != nil {
|
||||
for attempt := 0; attempt < refreshAttempts; attempt++ {
|
||||
token, err := c.RefreshToken(c.RefreshTokenStr)
|
||||
if err == nil {
|
||||
c.SetAuthToken(token.AccessToken, token.RefreshToken)
|
||||
return true
|
||||
}
|
||||
if IsRefreshTokenDead(err) {
|
||||
c.SetAuthToken("", "")
|
||||
return false
|
||||
}
|
||||
// 可恢复失败:退避后重试。ctx 取消时立即放弃。
|
||||
if attempt < refreshAttempts-1 {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
case <-time.After(refreshBackoff(attempt)):
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
c.SetAuthToken(token.AccessToken, token.RefreshToken)
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
// refreshAttempts 是刷新 access_token 失败时的最大尝试次数(含首次)。
|
||||
const refreshAttempts = 3
|
||||
|
||||
// refreshBackoff 返回第 attempt 次(从 0 计)刷新失败后的退避时长(指数退避)。
|
||||
func refreshBackoff(attempt int) time.Duration {
|
||||
return time.Duration(200*(1<<attempt)) * time.Millisecond // 200ms, 400ms
|
||||
}
|
||||
|
||||
// IsThrottleCode 判断是否为限流错误码。
|
||||
|
||||
@@ -429,3 +429,46 @@ func TestRemoteFileDetailRelativePath(t *testing.T) {
|
||||
t.Errorf("d3.RelativePath = %q, want %q", got, "")
|
||||
}
|
||||
}
|
||||
|
||||
// TestFsListRefreshContinue 验证 access_token 在请求中途过期(40140126)时:
|
||||
// 自动用 refresh_token 刷新得到新 token,然后对原请求重试成功(同步得以继续)。
|
||||
func TestFsListRefreshContinue(t *testing.T) {
|
||||
var filesCalls int
|
||||
var refreshCalls int
|
||||
mockAPI(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/open/refreshToken":
|
||||
refreshCalls++
|
||||
w.Write([]byte(`{"state":true,"data":{"access_token":"at2","refresh_token":"rt2","expires_in":7200}}`))
|
||||
case "/open/ufile/files":
|
||||
filesCalls++
|
||||
switch filesCalls {
|
||||
case 1:
|
||||
// 第一次用旧 access_token,返回过期错误,应触发刷新
|
||||
w.Write([]byte(`{"state":false,"code":40140126,"message":"access_token 校验失败"}`))
|
||||
default:
|
||||
// 刷新后续请求应使用新 access_token
|
||||
if got := r.Header.Get("Authorization"); got != "Bearer at2" {
|
||||
t.Errorf("retried request auth = %q, want Bearer at2", got)
|
||||
}
|
||||
w.Write([]byte(`{"state":true,"path":[],"data":[{"fid":"200","fc":"1","fn":"a.mkv","fs":123,"pc":"pickA"}]}`))
|
||||
}
|
||||
default:
|
||||
t.Errorf("unexpected path %s", r.URL.Path)
|
||||
}
|
||||
})
|
||||
c := NewOpenClient("100195125", "at1", "rt1")
|
||||
files, _, err := c.GetFsList(context.Background(), "0", 0, 100)
|
||||
if err != nil {
|
||||
t.Fatalf("expected sync to continue after refresh, got error: %v", err)
|
||||
}
|
||||
if filesCalls != 2 {
|
||||
t.Fatalf("want 2 files calls (original + retried), got %d", filesCalls)
|
||||
}
|
||||
if refreshCalls == 0 {
|
||||
t.Fatal("expected refresh_token to be used once")
|
||||
}
|
||||
if len(files) != 1 {
|
||||
t.Fatalf("want 1 file, got %d", len(files))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user