executeMethod - instantiates a given method, and retries the request upon any error up to maxRetries attempts in a binomially delayed manner using a standard back off algorithm.
(ctx context.Context, method string, metadata requestMetadata)
| 665 | // request upon any error up to maxRetries attempts in a binomially |
| 666 | // delayed manner using a standard back off algorithm. |
| 667 | func (c *Client) executeMethod(ctx context.Context, method string, metadata requestMetadata) (res *http.Response, err error) { |
| 668 | if c.IsOffline() { |
| 669 | return nil, errors.New(c.endpointURL.String() + " is offline.") |
| 670 | } |
| 671 | |
| 672 | var retryable bool // Indicates if request can be retried. |
| 673 | var bodySeeker io.Seeker // Extracted seeker from io.Reader. |
| 674 | reqRetry := c.maxRetries // Indicates how many times we can retry the request |
| 675 | |
| 676 | if metadata.contentBody != nil { |
| 677 | // Check if body is seekable then it is retryable. |
| 678 | bodySeeker, retryable = metadata.contentBody.(io.Seeker) |
| 679 | switch bodySeeker { |
| 680 | case os.Stdin, os.Stdout, os.Stderr: |
| 681 | retryable = false |
| 682 | } |
| 683 | // Retry only when reader is seekable |
| 684 | if !retryable { |
| 685 | reqRetry = 1 |
| 686 | } |
| 687 | |
| 688 | // Figure out if the body can be closed - if yes |
| 689 | // we will definitely close it upon the function |
| 690 | // return. |
| 691 | bodyCloser, ok := metadata.contentBody.(io.Closer) |
| 692 | if ok { |
| 693 | defer bodyCloser.Close() |
| 694 | } |
| 695 | } |
| 696 | |
| 697 | if metadata.addCrc != nil && metadata.contentLength > 0 { |
| 698 | if metadata.trailer == nil { |
| 699 | metadata.trailer = make(http.Header, 1) |
| 700 | } |
| 701 | crc := metadata.addCrc.Hasher() |
| 702 | metadata.contentBody = newHashReaderWrapper(metadata.contentBody, crc, func(hash []byte) { |
| 703 | // Update trailer when done. |
| 704 | metadata.trailer.Set(metadata.addCrc.Key(), base64.StdEncoding.EncodeToString(hash)) |
| 705 | }) |
| 706 | metadata.trailer.Set(metadata.addCrc.Key(), base64.StdEncoding.EncodeToString(crc.Sum(nil))) |
| 707 | } |
| 708 | |
| 709 | for range c.newRetryTimer(ctx, reqRetry, DefaultRetryUnit, DefaultRetryCap, MaxJitter) { |
| 710 | // Retry executes the following function body if request has an |
| 711 | // error until maxRetries have been exhausted, retry attempts are |
| 712 | // performed after waiting for a given period of time in a |
| 713 | // binomial fashion. |
| 714 | if retryable { |
| 715 | // Seek back to beginning for each attempt. |
| 716 | if _, err = bodySeeker.Seek(0, 0); err != nil { |
| 717 | // If seek failed, no need to retry. |
| 718 | return nil, err |
| 719 | } |
| 720 | } |
| 721 | |
| 722 | // Instantiate a new request. |
| 723 | var req *http.Request |
| 724 | req, err = c.newRequest(ctx, method, metadata) |
no test coverage detected