| 683 | var errContentRangeIgnored = errors.New("content range requests ignored") |
| 684 | |
| 685 | func withOffsetCheck(offset, parallelism int64) doChecks { |
| 686 | return func(r *request, resp *http.Response) error { |
| 687 | if parallelism <= 1 && offset == 0 { |
| 688 | return nil |
| 689 | } |
| 690 | if resp.StatusCode == http.StatusPartialContent { |
| 691 | return nil |
| 692 | } |
| 693 | if cr := resp.Header.Get("Content-Range"); cr != "" { |
| 694 | if !strings.HasPrefix(cr, fmt.Sprintf("bytes %d-", offset)) { |
| 695 | return fmt.Errorf("unhandled content range in response: %v", cr) |
| 696 | } |
| 697 | return nil |
| 698 | } |
| 699 | |
| 700 | // Discard up to offset |
| 701 | // Could use buffer pool here but this case should be rare |
| 702 | n, err := io.Copy(io.Discard, io.LimitReader(resp.Body, offset)) |
| 703 | if err != nil { |
| 704 | return fmt.Errorf("failed to discard to offset: %w", err) |
| 705 | } |
| 706 | if n != offset { |
| 707 | return errors.New("unable to discard to offset") |
| 708 | } |
| 709 | |
| 710 | // content range ignored, we can't do concurrent fetches here. |
| 711 | // return an error to be caught |
| 712 | return errContentRangeIgnored |
| 713 | } |
| 714 | } |
| 715 | |
| 716 | const maxAttempts = 5 |
| 717 | |