makeRequestToResolvedURL creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. streamLen, if not -1, specifies the length of the data expected on stream. makeRequest should generally be preferred. In case of an HTTP 429 status
(ctx context.Context, method string, requestURL *url.URL, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope)
| 533 | // In case of an HTTP 429 status code in the response, it may automatically retry a few times. |
| 534 | // TODO(runcom): too many arguments here, use a struct |
| 535 | func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method string, requestURL *url.URL, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) { |
| 536 | delay := backoffInitialDelay |
| 537 | attempts := 0 |
| 538 | for { |
| 539 | res, err := c.makeRequestToResolvedURLOnce(ctx, method, requestURL, headers, stream, streamLen, auth, extraScope) |
| 540 | if err != nil { |
| 541 | return nil, err |
| 542 | } |
| 543 | attempts++ |
| 544 | |
| 545 | // By default we use pre-defined scopes per operation. In |
| 546 | // certain cases, this can fail when our authentication is |
| 547 | // insufficient, then we might be getting an error back with a |
| 548 | // Www-Authenticate Header indicating an insufficient scope. |
| 549 | // |
| 550 | // Check for that and update the client challenges to retry after |
| 551 | // requesting a new token |
| 552 | // |
| 553 | // We only try this on the first attempt, to not overload an |
| 554 | // already struggling server. |
| 555 | // We also cannot retry with a body (stream != nil) as stream |
| 556 | // was already read |
| 557 | if attempts == 1 && stream == nil && auth != noAuth { |
| 558 | if retry, newScope := needsRetryWithUpdatedScope(res); retry { |
| 559 | logrus.Debug("Detected insufficient_scope error, will retry request with updated scope") |
| 560 | res.Body.Close() |
| 561 | // Note: This retry ignores extraScope. That’s, strictly speaking, incorrect, but we don’t currently |
| 562 | // expect the insufficient_scope errors to happen for those callers. If that changes, we can add support |
| 563 | // for more than one extra scope. |
| 564 | res, err = c.makeRequestToResolvedURLOnce(ctx, method, requestURL, headers, stream, streamLen, auth, newScope) |
| 565 | if err != nil { |
| 566 | return nil, err |
| 567 | } |
| 568 | extraScope = newScope |
| 569 | } |
| 570 | } |
| 571 | |
| 572 | if res.StatusCode != http.StatusTooManyRequests || // Only retry on StatusTooManyRequests, success or other failure is returned to caller immediately |
| 573 | stream != nil || // We can't retry with a body (which is not restartable in the general case) |
| 574 | attempts == backoffNumIterations { |
| 575 | return res, nil |
| 576 | } |
| 577 | // close response body before retry or context done |
| 578 | res.Body.Close() |
| 579 | |
| 580 | delay = min(parseRetryAfter(res, delay), backoffMaxDelay) |
| 581 | logrus.Debugf("Too many requests to %s: sleeping for %f seconds before next attempt", requestURL.Redacted(), delay.Seconds()) |
| 582 | select { |
| 583 | case <-ctx.Done(): |
| 584 | return nil, ctx.Err() |
| 585 | case <-time.After(delay): |
| 586 | // Nothing |
| 587 | } |
| 588 | delay *= 2 // If the registry does not specify a delay, back off exponentially. |
| 589 | } |
| 590 | } |
| 591 | |
| 592 | // makeRequestToResolvedURLOnce creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. |
no test coverage detected