func()

in registry/client/transport/http_reader.go [161:260]


func (hrs *httpReadSeeker) reader() (_ io.Reader, retErr error) {
	if hrs.err != nil {
		return nil, hrs.err
	}

	if hrs.rc != nil {
		return hrs.rc, nil
	}

	req, err := http.NewRequest(http.MethodGet, hrs.url, nil)
	if err != nil {
		return nil, err
	}

	if hrs.readerOffset > 0 {
		// If we are at different offset, issue a range request from there.
		req.Header.Add("Range", fmt.Sprintf("bytes=%d-", hrs.readerOffset))
		// TODO: get context in here
		// context.GetLogger(hrs.context).Infof("Range: %s", req.Header.Get("Range"))
	}

	req.Header.Add("Accept-Encoding", "identity")
	resp, err := hrs.client.Do(req)
	if err != nil {
		return nil, err
	}
	defer func() {
		if retErr != nil {
			_ = resp.Body.Close()
		}
	}()

	// Normally would use client.SuccessStatus, but that would be a cyclic
	// import
	if resp.StatusCode < 200 || resp.StatusCode > 399 {
		if hrs.errorHandler != nil {
			// Closing the body should be handled by the existing defer, but in
			// case a custom "errHandler" is used that doesn't return an error,
			// we close the body regardless.
			defer resp.Body.Close()
			return nil, hrs.errorHandler(resp)
		}
		return nil, fmt.Errorf("unexpected status resolving reader: %v", resp.Status)
	}

	switch {
	case hrs.readerOffset > 0:
		if resp.StatusCode != http.StatusPartialContent {
			return nil, ErrWrongCodeForByteRange
		}

		contentRange := resp.Header.Get("Content-Range")
		if contentRange == "" {
			return nil, errors.New("no Content-Range header found in HTTP 206 response")
		}

		submatches := contentRangeRegexp.FindStringSubmatch(contentRange)
		if len(submatches) < 4 {
			return nil, fmt.Errorf("could not parse Content-Range header: %s", contentRange)
		}

		startByte, err := strconv.ParseUint(submatches[1], 10, 64)
		if err != nil {
			return nil, fmt.Errorf("could not parse start of range in Content-Range header: %s", contentRange)
		}

		// nolint: gosec
		if startByte != uint64(hrs.readerOffset) {
			return nil, fmt.Errorf("received Content-Range starting at offset %d instead of requested %d", startByte, hrs.readerOffset)
		}

		endByte, err := strconv.ParseUint(submatches[2], 10, 64)
		if err != nil {
			return nil, fmt.Errorf("could not parse end of range in Content-Range header: %s", contentRange)
		}

		if submatches[3] == "*" {
			hrs.size = -1
		} else {
			size, err := strconv.ParseUint(submatches[3], 10, 64)
			if err != nil {
				return nil, fmt.Errorf("could not parse total size in Content-Range header: %s", contentRange)
			}

			if endByte+1 != size {
				return nil, fmt.Errorf("range in Content-Range stops before the end of the content: %s", contentRange)
			}

			// nolint: gosec
			hrs.size = int64(size)
		}
	case resp.StatusCode == http.StatusOK:
		hrs.size = resp.ContentLength
	default:
		hrs.size = -1
	}
	hrs.rc = resp.Body

	return hrs.rc, nil
}