aboutsummaryrefslogtreecommitdiff
path: root/vendor/cloud.google.com/go/storage/reader.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/cloud.google.com/go/storage/reader.go')
-rw-r--r--vendor/cloud.google.com/go/storage/reader.go48
1 files changed, 36 insertions, 12 deletions
diff --git a/vendor/cloud.google.com/go/storage/reader.go b/vendor/cloud.google.com/go/storage/reader.go
index c0f26bf..1fdc197 100644
--- a/vendor/cloud.google.com/go/storage/reader.go
+++ b/vendor/cloud.google.com/go/storage/reader.go
@@ -25,6 +25,7 @@ import (
"reflect"
"strconv"
"strings"
+ "time"
"cloud.google.com/go/internal/trace"
"golang.org/x/net/context"
@@ -130,7 +131,11 @@ func (o *ObjectHandle) NewRangeReader(ctx context.Context, offset, length int64)
if err != nil {
return nil, err
}
- var size int64 // total size of object, even if a range was requested.
+ var (
+ size int64 // total size of object, even if a range was requested.
+ checkCRC bool
+ crc uint32
+ )
if res.StatusCode == http.StatusPartialContent {
cr := strings.TrimSpace(res.Header.Get("Content-Range"))
if !strings.HasPrefix(cr, "bytes ") || !strings.Contains(cr, "/") {
@@ -143,6 +148,18 @@ func (o *ObjectHandle) NewRangeReader(ctx context.Context, offset, length int64)
}
} else {
size = res.ContentLength
+ // Check the CRC iff all of the following hold:
+ // - We asked for content (length != 0).
+ // - We got all the content (status != PartialContent).
+ // - The server sent a CRC header.
+ // - The Go http stack did not uncompress the file.
+ // - We were not served compressed data that was uncompressed on download.
+ // The problem with the last two cases is that the CRC will not match -- GCS
+ // computes it on the compressed contents, but we compute it on the
+ // uncompressed contents.
+ if length != 0 && !goHTTPUncompressed(res) && !uncompressedByServer(res) {
+ crc, checkCRC = parseCRC32c(res)
+ }
}
remain := res.ContentLength
@@ -152,14 +169,6 @@ func (o *ObjectHandle) NewRangeReader(ctx context.Context, offset, length int64)
body.Close()
body = emptyBody
}
- var (
- checkCRC bool
- crc uint32
- )
- // Even if there is a CRC header, we can't compute the hash on partial data.
- if remain == size {
- crc, checkCRC = parseCRC32c(res)
- }
return &Reader{
body: body,
size: size,
@@ -167,12 +176,20 @@ func (o *ObjectHandle) NewRangeReader(ctx context.Context, offset, length int64)
contentType: res.Header.Get("Content-Type"),
contentEncoding: res.Header.Get("Content-Encoding"),
cacheControl: res.Header.Get("Cache-Control"),
+ lastModified: res.Header.Get("Last-Modified"),
wantCRC: crc,
checkCRC: checkCRC,
reopen: reopen,
}, nil
}
+func uncompressedByServer(res *http.Response) bool {
+ // If the data is stored as gzip but is not encoded as gzip, then it
+ // was uncompressed by the server.
+ return res.Header.Get("X-Goog-Stored-Content-Encoding") == "gzip" &&
+ res.Header.Get("Content-Encoding") != "gzip"
+}
+
func parseCRC32c(res *http.Response) (uint32, bool) {
const prefix = "crc32c="
for _, spec := range res.Header["X-Goog-Hash"] {
@@ -200,10 +217,10 @@ type Reader struct {
contentType string
contentEncoding string
cacheControl string
+ lastModified string
checkCRC bool // should we check the CRC?
wantCRC uint32 // the CRC32c value the server sent in the header
gotCRC uint32 // running crc
- checkedCRC bool // did we check the CRC? (For tests.)
reopen func(seen int64) (*http.Response, error)
}
@@ -222,8 +239,7 @@ func (r *Reader) Read(p []byte) (int, error) {
// Check CRC here. It would be natural to check it in Close, but
// everybody defers Close on the assumption that it doesn't return
// anything worth looking at.
- if r.remain == 0 { // Only check if we have Content-Length.
- r.checkedCRC = true
+ if err == io.EOF {
if r.gotCRC != r.wantCRC {
return n, fmt.Errorf("storage: bad CRC on read: got %d, want %d",
r.gotCRC, r.wantCRC)
@@ -288,3 +304,11 @@ func (r *Reader) ContentEncoding() string {
func (r *Reader) CacheControl() string {
return r.cacheControl
}
+
+// LastModified returns the value of the Last-Modified header.
+func (r *Reader) LastModified() (time.Time, error) {
+ if r.lastModified == "" {
+ return time.Time{}, errors.New("storage: no Last-Modified header")
+ }
+ return http.ParseTime(r.lastModified)
+}