aboutsummaryrefslogtreecommitdiff
path: root/vendor/cloud.google.com/go/storage/storage.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/cloud.google.com/go/storage/storage.go')
-rw-r--r--vendor/cloud.google.com/go/storage/storage.go84
1 files changed, 68 insertions, 16 deletions
diff --git a/vendor/cloud.google.com/go/storage/storage.go b/vendor/cloud.google.com/go/storage/storage.go
index 53fc029..6b82fa3 100644
--- a/vendor/cloud.google.com/go/storage/storage.go
+++ b/vendor/cloud.google.com/go/storage/storage.go
@@ -39,6 +39,7 @@ import (
"google.golang.org/api/transport"
"cloud.google.com/go/internal/optional"
+ "cloud.google.com/go/internal/version"
"golang.org/x/net/context"
"google.golang.org/api/googleapi"
raw "google.golang.org/api/storage/v1"
@@ -65,6 +66,12 @@ const (
ScopeReadWrite = raw.DevstorageReadWriteScope
)
+var xGoogHeader = fmt.Sprintf("gl-go/%s gccl/%s", version.Go(), version.Repo)
+
+func setClientHeader(headers http.Header) {
+ headers.Set("x-goog-api-client", xGoogHeader)
+}
+
// Client is a client for interacting with Google Cloud Storage.
//
// Clients should be reused instead of created as needed.
@@ -202,7 +209,7 @@ type SignedURLOptions struct {
// If provided, the client should provide the exact value on the request
// header in order to use the signed URL.
// Optional.
- MD5 []byte
+ MD5 string
}
// SignedURL returns a URL for the specified object. Signed URLs allow
@@ -225,6 +232,12 @@ func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
if opts.Expires.IsZero() {
return "", errors.New("storage: missing required expires option")
}
+ if opts.MD5 != "" {
+ md5, err := base64.StdEncoding.DecodeString(opts.MD5)
+ if err != nil || len(md5) != 16 {
+ return "", errors.New("storage: invalid MD5 checksum")
+ }
+ }
signBytes := opts.SignBytes
if opts.PrivateKey != nil {
@@ -241,8 +254,6 @@ func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
sum[:],
)
}
- } else {
- signBytes = opts.SignBytes
}
u := &url.URL{
@@ -254,7 +265,9 @@ func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
fmt.Fprintf(buf, "%s\n", opts.MD5)
fmt.Fprintf(buf, "%s\n", opts.ContentType)
fmt.Fprintf(buf, "%d\n", opts.Expires.Unix())
- fmt.Fprintf(buf, "%s", strings.Join(opts.Headers, "\n"))
+ if len(opts.Headers) > 0 {
+ fmt.Fprintf(buf, "%s\n", strings.Join(opts.Headers, "\n"))
+ }
fmt.Fprintf(buf, "%s", u.String())
b, err := signBytes(buf.Bytes())
@@ -339,6 +352,7 @@ func (o *ObjectHandle) Attrs(ctx context.Context) (*ObjectAttrs, error) {
}
var obj *raw.Object
var err error
+ setClientHeader(call.Header())
err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
return nil, ErrObjectNotExist
@@ -412,6 +426,7 @@ func (o *ObjectHandle) Update(ctx context.Context, uattrs ObjectAttrsToUpdate) (
}
var obj *raw.Object
var err error
+ setClientHeader(call.Header())
err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
return nil, ErrObjectNotExist
@@ -452,6 +467,7 @@ func (o *ObjectHandle) Delete(ctx context.Context) error {
if err := applyConds("Delete", o.gen, o.conds, call); err != nil {
return err
}
+ setClientHeader(call.Header())
err := runWithRetry(ctx, func() error { return call.Do() })
switch e := err.(type) {
case nil:
@@ -560,15 +576,37 @@ 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,
remain: remain,
contentType: res.Header.Get("Content-Type"),
+ wantCRC: crc,
+ checkCRC: checkCRC,
}, nil
}
+func parseCRC32c(res *http.Response) (uint32, bool) {
+ const prefix = "crc32c="
+ for _, spec := range res.Header["X-Goog-Hash"] {
+ if strings.HasPrefix(spec, prefix) {
+ c, err := decodeUint32(spec[len(prefix):])
+ if err == nil {
+ return c, true
+ }
+ }
+ }
+ return 0, false
+}
+
var emptyBody = ioutil.NopCloser(strings.NewReader(""))
// NewWriter returns a storage Writer that writes to the GCS object
@@ -656,6 +694,7 @@ func (o *ObjectAttrs) toRawObject(bucket string) *raw.Object {
ContentLanguage: o.ContentLanguage,
CacheControl: o.CacheControl,
ContentDisposition: o.ContentDisposition,
+ StorageClass: o.StorageClass,
Acl: acl,
Metadata: o.Metadata,
}
@@ -717,21 +756,20 @@ type ObjectAttrs struct {
// This field is read-only.
Generation int64
- // MetaGeneration is the version of the metadata for this
+ // Metageneration is the version of the metadata for this
// object at this generation. This field is used for preconditions
// and for detecting changes in metadata. A metageneration number
// is only meaningful in the context of a particular generation
// of a particular object. This field is read-only.
- MetaGeneration int64
+ Metageneration int64
- // StorageClass is the storage class of the bucket.
+ // StorageClass is the storage class of the object.
// This value defines how objects in the bucket are stored and
// determines the SLA and the cost of storage. Typical values are
// "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD"
// and "DURABLE_REDUCED_AVAILABILITY".
// It defaults to "STANDARD", which is equivalent to "MULTI_REGIONAL"
- // or "REGIONAL" depending on the bucket's location settings. This
- // field is read-only.
+ // or "REGIONAL" depending on the bucket's location settings.
StorageClass string
// Created is the time the object was created. This field is read-only.
@@ -786,11 +824,7 @@ func newObject(o *raw.Object) *ObjectAttrs {
owner = o.Owner.Entity
}
md5, _ := base64.StdEncoding.DecodeString(o.Md5Hash)
- var crc32c uint32
- d, err := base64.StdEncoding.DecodeString(o.Crc32c)
- if err == nil && len(d) == 4 {
- crc32c = uint32(d[0])<<24 + uint32(d[1])<<16 + uint32(d[2])<<8 + uint32(d[3])
- }
+ crc32c, _ := decodeUint32(o.Crc32c)
var sha256 string
if o.CustomerEncryption != nil {
sha256 = o.CustomerEncryption.KeySha256
@@ -810,7 +844,7 @@ func newObject(o *raw.Object) *ObjectAttrs {
MediaLink: o.MediaLink,
Metadata: o.Metadata,
Generation: o.Generation,
- MetaGeneration: o.Metageneration,
+ Metageneration: o.Metageneration,
StorageClass: o.StorageClass,
CustomerKeySHA256: sha256,
Created: convertTime(o.TimeCreated),
@@ -819,6 +853,24 @@ func newObject(o *raw.Object) *ObjectAttrs {
}
}
+// Decode a uint32 encoded in Base64 in big-endian byte order.
+func decodeUint32(b64 string) (uint32, error) {
+ d, err := base64.StdEncoding.DecodeString(b64)
+ if err != nil {
+ return 0, err
+ }
+ if len(d) != 4 {
+ return 0, fmt.Errorf("storage: %q does not encode a 32-bit value", d)
+ }
+ return uint32(d[0])<<24 + uint32(d[1])<<16 + uint32(d[2])<<8 + uint32(d[3]), nil
+}
+
+// Encode a uint32 as Base64 in big-endian byte order.
+func encodeUint32(u uint32) string {
+ b := []byte{byte(u >> 24), byte(u >> 16), byte(u >> 8), byte(u)}
+ return base64.StdEncoding.EncodeToString(b)
+}
+
// Query represents a query to filter objects from a bucket.
type Query struct {
// Delimiter returns results in a directory-like fashion.