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
}