refreshTokenWithRetry exchanges the refresh token for a new access token, retrying with exponential backoff on transient failures. Permanent failures (as classified by isFailedRefresh) and the no-op case where no refresh token is set bypass the retry loop so a doomed refresh is not repeatedly attemp
(ctx context.Context, existingToken *oauth2.Token)
| 393 | // refresh token is set bypass the retry loop so a doomed refresh is not |
| 394 | // repeatedly attempted. |
| 395 | func (c *Config) refreshTokenWithRetry(ctx context.Context, existingToken *oauth2.Token) (*oauth2.Token, error) { |
| 396 | // Without a refresh token the oauth2 library short-circuits with |
| 397 | // "token expired and refresh token is not set". No retry can recover |
| 398 | // from that, so make a single attempt and return. |
| 399 | if existingToken.RefreshToken == "" { |
| 400 | return c.TokenSource(ctx, existingToken).Token() |
| 401 | } |
| 402 | |
| 403 | initial := c.RefreshRetryInitialBackoff |
| 404 | if initial <= 0 { |
| 405 | initial = defaultRefreshRetryInitialBackoff |
| 406 | } |
| 407 | maximum := c.RefreshRetryMaxBackoff |
| 408 | if maximum <= 0 { |
| 409 | maximum = defaultRefreshRetryMaxBackoff |
| 410 | } |
| 411 | total := c.RefreshRetryTimeout |
| 412 | if total <= 0 { |
| 413 | total = defaultRefreshRetryTimeout |
| 414 | } |
| 415 | |
| 416 | retryCtx, retryCancel := context.WithTimeout(ctx, total) |
| 417 | defer retryCancel() |
| 418 | backoff := retry.New(initial, maximum) |
| 419 | |
| 420 | var ( |
| 421 | token *oauth2.Token |
| 422 | err error |
| 423 | ) |
| 424 | for { |
| 425 | token, err = c.TokenSource(ctx, existingToken).Token() |
| 426 | if err == nil || isFailedRefresh(existingToken, err) { |
| 427 | return token, err |
| 428 | } |
| 429 | // Bail out before waiting if the retry budget is already gone. |
| 430 | // retry.Wait selects between time.After(delay) and ctx.Done(); when |
| 431 | // delay is zero and the context is already canceled the two cases |
| 432 | // race nondeterministically, which would cause an unwanted extra |
| 433 | // refresh attempt with a near-zero budget (notably in tests). |
| 434 | if retryCtx.Err() != nil { |
| 435 | return token, err |
| 436 | } |
| 437 | if !backoff.Wait(retryCtx) { |
| 438 | return token, err |
| 439 | } |
| 440 | } |
| 441 | } |
| 442 | |
| 443 | // ValidateToken checks if the Git token provided is valid. |
| 444 | // The user is optionally returned if the provider supports it. |
no test coverage detected