Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions storage/http_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1424,12 +1424,16 @@ func (r *httpReader) Read(p []byte) (int, error) {
// Read failed (likely due to connection issues), but we will try to reopen
// the pipe and continue. Send a ranged read request that takes into account
// the number of bytes we've already seen.

// Close the current body before retrying. Otherwise we leave a
// connection open and the retry might fail due to quota issues.
r.body.Close()

res, err := r.reopen(r.seen)
if err != nil {
// reopen already retries
return n, err
}
r.body.Close()
r.body = res.Body
}
return n, nil
Expand Down Expand Up @@ -1523,6 +1527,7 @@ func readerReopen(ctx context.Context, header http.Header, params *newRangeReade
if params.gen < 0 && res.Header.Get("X-Goog-Generation") != "" {
gen64, err := strconv.ParseInt(res.Header.Get("X-Goog-Generation"), 10, 64)
if err != nil {
res.Body.Close()
return err
}
params.gen = gen64
Expand All @@ -1536,8 +1541,12 @@ func readerReopen(ctx context.Context, header http.Header, params *newRangeReade
}
}

func parseReadResponse(res *http.Response, params *newRangeReaderParams, reopen func(int64) (*http.Response, error)) (*Reader, error) {
var err error
func parseReadResponse(res *http.Response, params *newRangeReaderParams, reopen func(int64) (*http.Response, error)) (r *Reader, err error) {
defer func() {
if err != nil {
res.Body.Close()
}
}()
var (
size int64 // total size of object, even if a range was requested.
checkCRC bool
Expand All @@ -1547,20 +1556,23 @@ func parseReadResponse(res *http.Response, params *newRangeReaderParams, reopen
if res.StatusCode == http.StatusPartialContent {
cr := strings.TrimSpace(res.Header.Get("Content-Range"))
if !strings.HasPrefix(cr, "bytes ") || !strings.Contains(cr, "/") {
return nil, fmt.Errorf("storage: invalid Content-Range %q", cr)
err = fmt.Errorf("storage: invalid Content-Range %q", cr)
return nil, err
}
// Content range is formatted <first byte>-<last byte>/<total size>. We take
// the total size.
size, err = strconv.ParseInt(cr[strings.LastIndex(cr, "/")+1:], 10, 64)
if err != nil {
return nil, fmt.Errorf("storage: invalid Content-Range %q", cr)
err = fmt.Errorf("storage: invalid Content-Range %q", cr)
return nil, err
Comment on lines +1566 to +1567
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error from strconv.ParseInt is discarded here. Per repository rules, helper functions that parse strings should include error handling for parsing failures. For consistency and better error context, consider wrapping the error here as well.

Suggested change
err = fmt.Errorf("storage: invalid Content-Range %q", cr)
return nil, err
err = fmt.Errorf("storage: invalid Content-Range %q: %w", cr, err)
return nil, err
References
  1. When implementing helper functions that parse strings, always include error handling for parsing failures.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix this?

}

dashIndex := strings.Index(cr, "-")
if dashIndex >= 0 {
startOffset, err = strconv.ParseInt(cr[len("bytes="):dashIndex], 10, 64)
if err != nil {
return nil, fmt.Errorf("storage: invalid Content-Range %q: %w", cr, err)
err = fmt.Errorf("storage: invalid Content-Range %q: %w", cr, err)
return nil, err
}
}
} else {
Expand Down
Loading