1// Copyright 2014 Google Inc. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package storage 16 17import ( 18 "bytes" 19 "crypto" 20 "crypto/rand" 21 "crypto/rsa" 22 "crypto/sha256" 23 "crypto/x509" 24 "encoding/base64" 25 "encoding/pem" 26 "errors" 27 "fmt" 28 "io" 29 "io/ioutil" 30 "net/http" 31 "net/url" 32 "reflect" 33 "strconv" 34 "strings" 35 "time" 36 "unicode/utf8" 37 38 "google.golang.org/api/option" 39 htransport "google.golang.org/api/transport/http" 40 41 "cloud.google.com/go/internal/optional" 42 "cloud.google.com/go/internal/version" 43 "golang.org/x/net/context" 44 "google.golang.org/api/googleapi" 45 raw "google.golang.org/api/storage/v1" 46) 47 48var ( 49 ErrBucketNotExist = errors.New("storage: bucket doesn't exist") 50 ErrObjectNotExist = errors.New("storage: object doesn't exist") 51) 52 53const userAgent = "gcloud-golang-storage/20151204" 54 55const ( 56 // ScopeFullControl grants permissions to manage your 57 // data and permissions in Google Cloud Storage. 58 ScopeFullControl = raw.DevstorageFullControlScope 59 60 // ScopeReadOnly grants permissions to 61 // view your data in Google Cloud Storage. 62 ScopeReadOnly = raw.DevstorageReadOnlyScope 63 64 // ScopeReadWrite grants permissions to manage your 65 // data in Google Cloud Storage. 66 ScopeReadWrite = raw.DevstorageReadWriteScope 67) 68 69var xGoogHeader = fmt.Sprintf("gl-go/%s gccl/%s", version.Go(), version.Repo) 70 71func setClientHeader(headers http.Header) { 72 headers.Set("x-goog-api-client", xGoogHeader) 73} 74 75// Client is a client for interacting with Google Cloud Storage. 76// 77// Clients should be reused instead of created as needed. 78// The methods of Client are safe for concurrent use by multiple goroutines. 79type Client struct { 80 hc *http.Client 81 raw *raw.Service 82} 83 84// NewClient creates a new Google Cloud Storage client. 85// The default scope is ScopeFullControl. To use a different scope, like ScopeReadOnly, use option.WithScopes. 86func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) { 87 o := []option.ClientOption{ 88 option.WithScopes(ScopeFullControl), 89 option.WithUserAgent(userAgent), 90 } 91 opts = append(o, opts...) 92 hc, ep, err := htransport.NewClient(ctx, opts...) 93 if err != nil { 94 return nil, fmt.Errorf("dialing: %v", err) 95 } 96 rawService, err := raw.New(hc) 97 if err != nil { 98 return nil, fmt.Errorf("storage client: %v", err) 99 } 100 if ep != "" { 101 rawService.BasePath = ep 102 } 103 return &Client{ 104 hc: hc, 105 raw: rawService, 106 }, nil 107} 108 109// Close closes the Client. 110// 111// Close need not be called at program exit. 112func (c *Client) Close() error { 113 c.hc = nil 114 return nil 115} 116 117// SignedURLOptions allows you to restrict the access to the signed URL. 118type SignedURLOptions struct { 119 // GoogleAccessID represents the authorizer of the signed URL generation. 120 // It is typically the Google service account client email address from 121 // the Google Developers Console in the form of "xxx@developer.gserviceaccount.com". 122 // Required. 123 GoogleAccessID string 124 125 // PrivateKey is the Google service account private key. It is obtainable 126 // from the Google Developers Console. 127 // At https://console.developers.google.com/project/<your-project-id>/apiui/credential, 128 // create a service account client ID or reuse one of your existing service account 129 // credentials. Click on the "Generate new P12 key" to generate and download 130 // a new private key. Once you download the P12 file, use the following command 131 // to convert it into a PEM file. 132 // 133 // $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes 134 // 135 // Provide the contents of the PEM file as a byte slice. 136 // Exactly one of PrivateKey or SignBytes must be non-nil. 137 PrivateKey []byte 138 139 // SignBytes is a function for implementing custom signing. 140 // If your application is running on Google App Engine, you can use appengine's internal signing function: 141 // ctx := appengine.NewContext(request) 142 // acc, _ := appengine.ServiceAccount(ctx) 143 // url, err := SignedURL("bucket", "object", &SignedURLOptions{ 144 // GoogleAccessID: acc, 145 // SignBytes: func(b []byte) ([]byte, error) { 146 // _, signedBytes, err := appengine.SignBytes(ctx, b) 147 // return signedBytes, err 148 // }, 149 // // etc. 150 // }) 151 // 152 // Exactly one of PrivateKey or SignBytes must be non-nil. 153 SignBytes func([]byte) ([]byte, error) 154 155 // Method is the HTTP method to be used with the signed URL. 156 // Signed URLs can be used with GET, HEAD, PUT, and DELETE requests. 157 // Required. 158 Method string 159 160 // Expires is the expiration time on the signed URL. It must be 161 // a datetime in the future. 162 // Required. 163 Expires time.Time 164 165 // ContentType is the content type header the client must provide 166 // to use the generated signed URL. 167 // Optional. 168 ContentType string 169 170 // Headers is a list of extention headers the client must provide 171 // in order to use the generated signed URL. 172 // Optional. 173 Headers []string 174 175 // MD5 is the base64 encoded MD5 checksum of the file. 176 // If provided, the client should provide the exact value on the request 177 // header in order to use the signed URL. 178 // Optional. 179 MD5 string 180} 181 182// SignedURL returns a URL for the specified object. Signed URLs allow 183// the users access to a restricted resource for a limited time without having a 184// Google account or signing in. For more information about the signed 185// URLs, see https://cloud.google.com/storage/docs/accesscontrol#Signed-URLs. 186func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) { 187 if opts == nil { 188 return "", errors.New("storage: missing required SignedURLOptions") 189 } 190 if opts.GoogleAccessID == "" { 191 return "", errors.New("storage: missing required GoogleAccessID") 192 } 193 if (opts.PrivateKey == nil) == (opts.SignBytes == nil) { 194 return "", errors.New("storage: exactly one of PrivateKey or SignedBytes must be set") 195 } 196 if opts.Method == "" { 197 return "", errors.New("storage: missing required method option") 198 } 199 if opts.Expires.IsZero() { 200 return "", errors.New("storage: missing required expires option") 201 } 202 if opts.MD5 != "" { 203 md5, err := base64.StdEncoding.DecodeString(opts.MD5) 204 if err != nil || len(md5) != 16 { 205 return "", errors.New("storage: invalid MD5 checksum") 206 } 207 } 208 209 signBytes := opts.SignBytes 210 if opts.PrivateKey != nil { 211 key, err := parseKey(opts.PrivateKey) 212 if err != nil { 213 return "", err 214 } 215 signBytes = func(b []byte) ([]byte, error) { 216 sum := sha256.Sum256(b) 217 return rsa.SignPKCS1v15( 218 rand.Reader, 219 key, 220 crypto.SHA256, 221 sum[:], 222 ) 223 } 224 } 225 226 u := &url.URL{ 227 Path: fmt.Sprintf("/%s/%s", bucket, name), 228 } 229 230 buf := &bytes.Buffer{} 231 fmt.Fprintf(buf, "%s\n", opts.Method) 232 fmt.Fprintf(buf, "%s\n", opts.MD5) 233 fmt.Fprintf(buf, "%s\n", opts.ContentType) 234 fmt.Fprintf(buf, "%d\n", opts.Expires.Unix()) 235 if len(opts.Headers) > 0 { 236 fmt.Fprintf(buf, "%s\n", strings.Join(opts.Headers, "\n")) 237 } 238 fmt.Fprintf(buf, "%s", u.String()) 239 240 b, err := signBytes(buf.Bytes()) 241 if err != nil { 242 return "", err 243 } 244 encoded := base64.StdEncoding.EncodeToString(b) 245 u.Scheme = "https" 246 u.Host = "storage.googleapis.com" 247 q := u.Query() 248 q.Set("GoogleAccessId", opts.GoogleAccessID) 249 q.Set("Expires", fmt.Sprintf("%d", opts.Expires.Unix())) 250 q.Set("Signature", string(encoded)) 251 u.RawQuery = q.Encode() 252 return u.String(), nil 253} 254 255// ObjectHandle provides operations on an object in a Google Cloud Storage bucket. 256// Use BucketHandle.Object to get a handle. 257type ObjectHandle struct { 258 c *Client 259 bucket string 260 object string 261 acl ACLHandle 262 gen int64 // a negative value indicates latest 263 conds *Conditions 264 encryptionKey []byte // AES-256 key 265 userProject string // for requester-pays buckets 266} 267 268// ACL provides access to the object's access control list. 269// This controls who can read and write this object. 270// This call does not perform any network operations. 271func (o *ObjectHandle) ACL() *ACLHandle { 272 return &o.acl 273} 274 275// Generation returns a new ObjectHandle that operates on a specific generation 276// of the object. 277// By default, the handle operates on the latest generation. Not 278// all operations work when given a specific generation; check the API 279// endpoints at https://cloud.google.com/storage/docs/json_api/ for details. 280func (o *ObjectHandle) Generation(gen int64) *ObjectHandle { 281 o2 := *o 282 o2.gen = gen 283 return &o2 284} 285 286// If returns a new ObjectHandle that applies a set of preconditions. 287// Preconditions already set on the ObjectHandle are ignored. 288// Operations on the new handle will only occur if the preconditions are 289// satisfied. See https://cloud.google.com/storage/docs/generations-preconditions 290// for more details. 291func (o *ObjectHandle) If(conds Conditions) *ObjectHandle { 292 o2 := *o 293 o2.conds = &conds 294 return &o2 295} 296 297// Key returns a new ObjectHandle that uses the supplied encryption 298// key to encrypt and decrypt the object's contents. 299// 300// Encryption key must be a 32-byte AES-256 key. 301// See https://cloud.google.com/storage/docs/encryption for details. 302func (o *ObjectHandle) Key(encryptionKey []byte) *ObjectHandle { 303 o2 := *o 304 o2.encryptionKey = encryptionKey 305 return &o2 306} 307 308// Attrs returns meta information about the object. 309// ErrObjectNotExist will be returned if the object is not found. 310func (o *ObjectHandle) Attrs(ctx context.Context) (*ObjectAttrs, error) { 311 if err := o.validate(); err != nil { 312 return nil, err 313 } 314 call := o.c.raw.Objects.Get(o.bucket, o.object).Projection("full").Context(ctx) 315 if err := applyConds("Attrs", o.gen, o.conds, call); err != nil { 316 return nil, err 317 } 318 if o.userProject != "" { 319 call.UserProject(o.userProject) 320 } 321 if err := setEncryptionHeaders(call.Header(), o.encryptionKey, false); err != nil { 322 return nil, err 323 } 324 var obj *raw.Object 325 var err error 326 setClientHeader(call.Header()) 327 err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err }) 328 if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound { 329 return nil, ErrObjectNotExist 330 } 331 if err != nil { 332 return nil, err 333 } 334 return newObject(obj), nil 335} 336 337// Update updates an object with the provided attributes. 338// All zero-value attributes are ignored. 339// ErrObjectNotExist will be returned if the object is not found. 340func (o *ObjectHandle) Update(ctx context.Context, uattrs ObjectAttrsToUpdate) (*ObjectAttrs, error) { 341 if err := o.validate(); err != nil { 342 return nil, err 343 } 344 var attrs ObjectAttrs 345 // Lists of fields to send, and set to null, in the JSON. 346 var forceSendFields, nullFields []string 347 if uattrs.ContentType != nil { 348 attrs.ContentType = optional.ToString(uattrs.ContentType) 349 forceSendFields = append(forceSendFields, "ContentType") 350 } 351 if uattrs.ContentLanguage != nil { 352 attrs.ContentLanguage = optional.ToString(uattrs.ContentLanguage) 353 // For ContentLanguage It's an error to send the empty string. 354 // Instead we send a null. 355 if attrs.ContentLanguage == "" { 356 nullFields = append(nullFields, "ContentLanguage") 357 } else { 358 forceSendFields = append(forceSendFields, "ContentLanguage") 359 } 360 } 361 if uattrs.ContentEncoding != nil { 362 attrs.ContentEncoding = optional.ToString(uattrs.ContentEncoding) 363 forceSendFields = append(forceSendFields, "ContentEncoding") 364 } 365 if uattrs.ContentDisposition != nil { 366 attrs.ContentDisposition = optional.ToString(uattrs.ContentDisposition) 367 forceSendFields = append(forceSendFields, "ContentDisposition") 368 } 369 if uattrs.CacheControl != nil { 370 attrs.CacheControl = optional.ToString(uattrs.CacheControl) 371 forceSendFields = append(forceSendFields, "CacheControl") 372 } 373 if uattrs.Metadata != nil { 374 attrs.Metadata = uattrs.Metadata 375 if len(attrs.Metadata) == 0 { 376 // Sending the empty map is a no-op. We send null instead. 377 nullFields = append(nullFields, "Metadata") 378 } else { 379 forceSendFields = append(forceSendFields, "Metadata") 380 } 381 } 382 if uattrs.ACL != nil { 383 attrs.ACL = uattrs.ACL 384 // It's an error to attempt to delete the ACL, so 385 // we don't append to nullFields here. 386 forceSendFields = append(forceSendFields, "Acl") 387 } 388 rawObj := attrs.toRawObject(o.bucket) 389 rawObj.ForceSendFields = forceSendFields 390 rawObj.NullFields = nullFields 391 call := o.c.raw.Objects.Patch(o.bucket, o.object, rawObj).Projection("full").Context(ctx) 392 if err := applyConds("Update", o.gen, o.conds, call); err != nil { 393 return nil, err 394 } 395 if o.userProject != "" { 396 call.UserProject(o.userProject) 397 } 398 if err := setEncryptionHeaders(call.Header(), o.encryptionKey, false); err != nil { 399 return nil, err 400 } 401 var obj *raw.Object 402 var err error 403 setClientHeader(call.Header()) 404 err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err }) 405 if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound { 406 return nil, ErrObjectNotExist 407 } 408 if err != nil { 409 return nil, err 410 } 411 return newObject(obj), nil 412} 413 414// ObjectAttrsToUpdate is used to update the attributes of an object. 415// Only fields set to non-nil values will be updated. 416// Set a field to its zero value to delete it. 417// 418// For example, to change ContentType and delete ContentEncoding and 419// Metadata, use 420// ObjectAttrsToUpdate{ 421// ContentType: "text/html", 422// ContentEncoding: "", 423// Metadata: map[string]string{}, 424// } 425type ObjectAttrsToUpdate struct { 426 ContentType optional.String 427 ContentLanguage optional.String 428 ContentEncoding optional.String 429 ContentDisposition optional.String 430 CacheControl optional.String 431 Metadata map[string]string // set to map[string]string{} to delete 432 ACL []ACLRule 433} 434 435// Delete deletes the single specified object. 436func (o *ObjectHandle) Delete(ctx context.Context) error { 437 if err := o.validate(); err != nil { 438 return err 439 } 440 call := o.c.raw.Objects.Delete(o.bucket, o.object).Context(ctx) 441 if err := applyConds("Delete", o.gen, o.conds, call); err != nil { 442 return err 443 } 444 if o.userProject != "" { 445 call.UserProject(o.userProject) 446 } 447 // Encryption doesn't apply to Delete. 448 setClientHeader(call.Header()) 449 err := runWithRetry(ctx, func() error { return call.Do() }) 450 switch e := err.(type) { 451 case nil: 452 return nil 453 case *googleapi.Error: 454 if e.Code == http.StatusNotFound { 455 return ErrObjectNotExist 456 } 457 } 458 return err 459} 460 461// NewReader creates a new Reader to read the contents of the 462// object. 463// ErrObjectNotExist will be returned if the object is not found. 464// 465// The caller must call Close on the returned Reader when done reading. 466func (o *ObjectHandle) NewReader(ctx context.Context) (*Reader, error) { 467 return o.NewRangeReader(ctx, 0, -1) 468} 469 470// NewRangeReader reads part of an object, reading at most length bytes 471// starting at the given offset. If length is negative, the object is read 472// until the end. 473func (o *ObjectHandle) NewRangeReader(ctx context.Context, offset, length int64) (*Reader, error) { 474 if err := o.validate(); err != nil { 475 return nil, err 476 } 477 if offset < 0 { 478 return nil, fmt.Errorf("storage: invalid offset %d < 0", offset) 479 } 480 if o.conds != nil { 481 if err := o.conds.validate("NewRangeReader"); err != nil { 482 return nil, err 483 } 484 } 485 u := &url.URL{ 486 Scheme: "https", 487 Host: "storage.googleapis.com", 488 Path: fmt.Sprintf("/%s/%s", o.bucket, o.object), 489 RawQuery: conditionsQuery(o.gen, o.conds), 490 } 491 verb := "GET" 492 if length == 0 { 493 verb = "HEAD" 494 } 495 req, err := http.NewRequest(verb, u.String(), nil) 496 if err != nil { 497 return nil, err 498 } 499 req = withContext(req, ctx) 500 if length < 0 && offset > 0 { 501 req.Header.Set("Range", fmt.Sprintf("bytes=%d-", offset)) 502 } else if length > 0 { 503 req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+length-1)) 504 } 505 if o.userProject != "" { 506 req.Header.Set("X-Goog-User-Project", o.userProject) 507 } 508 if err := setEncryptionHeaders(req.Header, o.encryptionKey, false); err != nil { 509 return nil, err 510 } 511 var res *http.Response 512 err = runWithRetry(ctx, func() error { 513 res, err = o.c.hc.Do(req) 514 if err != nil { 515 return err 516 } 517 if res.StatusCode == http.StatusNotFound { 518 res.Body.Close() 519 return ErrObjectNotExist 520 } 521 if res.StatusCode < 200 || res.StatusCode > 299 { 522 body, _ := ioutil.ReadAll(res.Body) 523 res.Body.Close() 524 return &googleapi.Error{ 525 Code: res.StatusCode, 526 Header: res.Header, 527 Body: string(body), 528 } 529 } 530 if offset > 0 && length != 0 && res.StatusCode != http.StatusPartialContent { 531 res.Body.Close() 532 return errors.New("storage: partial request not satisfied") 533 } 534 return nil 535 }) 536 if err != nil { 537 return nil, err 538 } 539 540 var size int64 // total size of object, even if a range was requested. 541 if res.StatusCode == http.StatusPartialContent { 542 cr := strings.TrimSpace(res.Header.Get("Content-Range")) 543 if !strings.HasPrefix(cr, "bytes ") || !strings.Contains(cr, "/") { 544 return nil, fmt.Errorf("storage: invalid Content-Range %q", cr) 545 } 546 size, err = strconv.ParseInt(cr[strings.LastIndex(cr, "/")+1:], 10, 64) 547 if err != nil { 548 return nil, fmt.Errorf("storage: invalid Content-Range %q", cr) 549 } 550 } else { 551 size = res.ContentLength 552 } 553 554 remain := res.ContentLength 555 body := res.Body 556 if length == 0 { 557 remain = 0 558 body.Close() 559 body = emptyBody 560 } 561 var ( 562 checkCRC bool 563 crc uint32 564 ) 565 // Even if there is a CRC header, we can't compute the hash on partial data. 566 if remain == size { 567 crc, checkCRC = parseCRC32c(res) 568 } 569 return &Reader{ 570 body: body, 571 size: size, 572 remain: remain, 573 contentType: res.Header.Get("Content-Type"), 574 cacheControl: res.Header.Get("Cache-Control"), 575 wantCRC: crc, 576 checkCRC: checkCRC, 577 }, nil 578} 579 580func parseCRC32c(res *http.Response) (uint32, bool) { 581 const prefix = "crc32c=" 582 for _, spec := range res.Header["X-Goog-Hash"] { 583 if strings.HasPrefix(spec, prefix) { 584 c, err := decodeUint32(spec[len(prefix):]) 585 if err == nil { 586 return c, true 587 } 588 } 589 } 590 return 0, false 591} 592 593var emptyBody = ioutil.NopCloser(strings.NewReader("")) 594 595// NewWriter returns a storage Writer that writes to the GCS object 596// associated with this ObjectHandle. 597// 598// A new object will be created unless an object with this name already exists. 599// Otherwise any previous object with the same name will be replaced. 600// The object will not be available (and any previous object will remain) 601// until Close has been called. 602// 603// Attributes can be set on the object by modifying the returned Writer's 604// ObjectAttrs field before the first call to Write. If no ContentType 605// attribute is specified, the content type will be automatically sniffed 606// using net/http.DetectContentType. 607// 608// It is the caller's responsibility to call Close when writing is done. 609func (o *ObjectHandle) NewWriter(ctx context.Context) *Writer { 610 return &Writer{ 611 ctx: ctx, 612 o: o, 613 donec: make(chan struct{}), 614 ObjectAttrs: ObjectAttrs{Name: o.object}, 615 ChunkSize: googleapi.DefaultUploadChunkSize, 616 } 617} 618 619func (o *ObjectHandle) validate() error { 620 if o.bucket == "" { 621 return errors.New("storage: bucket name is empty") 622 } 623 if o.object == "" { 624 return errors.New("storage: object name is empty") 625 } 626 if !utf8.ValidString(o.object) { 627 return fmt.Errorf("storage: object name %q is not valid UTF-8", o.object) 628 } 629 return nil 630} 631 632// parseKey converts the binary contents of a private key file 633// to an *rsa.PrivateKey. It detects whether the private key is in a 634// PEM container or not. If so, it extracts the the private key 635// from PEM container before conversion. It only supports PEM 636// containers with no passphrase. 637func parseKey(key []byte) (*rsa.PrivateKey, error) { 638 if block, _ := pem.Decode(key); block != nil { 639 key = block.Bytes 640 } 641 parsedKey, err := x509.ParsePKCS8PrivateKey(key) 642 if err != nil { 643 parsedKey, err = x509.ParsePKCS1PrivateKey(key) 644 if err != nil { 645 return nil, err 646 } 647 } 648 parsed, ok := parsedKey.(*rsa.PrivateKey) 649 if !ok { 650 return nil, errors.New("oauth2: private key is invalid") 651 } 652 return parsed, nil 653} 654 655func toRawObjectACL(oldACL []ACLRule) []*raw.ObjectAccessControl { 656 var acl []*raw.ObjectAccessControl 657 if len(oldACL) > 0 { 658 acl = make([]*raw.ObjectAccessControl, len(oldACL)) 659 for i, rule := range oldACL { 660 acl[i] = &raw.ObjectAccessControl{ 661 Entity: string(rule.Entity), 662 Role: string(rule.Role), 663 } 664 } 665 } 666 return acl 667} 668 669// toRawObject copies the editable attributes from o to the raw library's Object type. 670func (o *ObjectAttrs) toRawObject(bucket string) *raw.Object { 671 acl := toRawObjectACL(o.ACL) 672 return &raw.Object{ 673 Bucket: bucket, 674 Name: o.Name, 675 ContentType: o.ContentType, 676 ContentEncoding: o.ContentEncoding, 677 ContentLanguage: o.ContentLanguage, 678 CacheControl: o.CacheControl, 679 ContentDisposition: o.ContentDisposition, 680 StorageClass: o.StorageClass, 681 Acl: acl, 682 Metadata: o.Metadata, 683 } 684} 685 686// ObjectAttrs represents the metadata for a Google Cloud Storage (GCS) object. 687type ObjectAttrs struct { 688 // Bucket is the name of the bucket containing this GCS object. 689 // This field is read-only. 690 Bucket string 691 692 // Name is the name of the object within the bucket. 693 // This field is read-only. 694 Name string 695 696 // ContentType is the MIME type of the object's content. 697 ContentType string 698 699 // ContentLanguage is the content language of the object's content. 700 ContentLanguage string 701 702 // CacheControl is the Cache-Control header to be sent in the response 703 // headers when serving the object data. 704 CacheControl string 705 706 // ACL is the list of access control rules for the object. 707 ACL []ACLRule 708 709 // Owner is the owner of the object. This field is read-only. 710 // 711 // If non-zero, it is in the form of "user-<userId>". 712 Owner string 713 714 // Size is the length of the object's content. This field is read-only. 715 Size int64 716 717 // ContentEncoding is the encoding of the object's content. 718 ContentEncoding string 719 720 // ContentDisposition is the optional Content-Disposition header of the object 721 // sent in the response headers. 722 ContentDisposition string 723 724 // MD5 is the MD5 hash of the object's content. This field is read-only, 725 // except when used from a Writer. If set on a Writer, the uploaded 726 // data is rejected if its MD5 hash does not match this field. 727 MD5 []byte 728 729 // CRC32C is the CRC32 checksum of the object's content using 730 // the Castagnoli93 polynomial. This field is read-only, except when 731 // used from a Writer. If set on a Writer and Writer.SendCRC32C 732 // is true, the uploaded data is rejected if its CRC32c hash does not 733 // match this field. 734 CRC32C uint32 735 736 // MediaLink is an URL to the object's content. This field is read-only. 737 MediaLink string 738 739 // Metadata represents user-provided metadata, in key/value pairs. 740 // It can be nil if no metadata is provided. 741 Metadata map[string]string 742 743 // Generation is the generation number of the object's content. 744 // This field is read-only. 745 Generation int64 746 747 // Metageneration is the version of the metadata for this 748 // object at this generation. This field is used for preconditions 749 // and for detecting changes in metadata. A metageneration number 750 // is only meaningful in the context of a particular generation 751 // of a particular object. This field is read-only. 752 Metageneration int64 753 754 // StorageClass is the storage class of the object. 755 // This value defines how objects in the bucket are stored and 756 // determines the SLA and the cost of storage. Typical values are 757 // "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD" 758 // and "DURABLE_REDUCED_AVAILABILITY". 759 // It defaults to "STANDARD", which is equivalent to "MULTI_REGIONAL" 760 // or "REGIONAL" depending on the bucket's location settings. 761 StorageClass string 762 763 // Created is the time the object was created. This field is read-only. 764 Created time.Time 765 766 // Deleted is the time the object was deleted. 767 // If not deleted, it is the zero value. This field is read-only. 768 Deleted time.Time 769 770 // Updated is the creation or modification time of the object. 771 // For buckets with versioning enabled, changing an object's 772 // metadata does not change this property. This field is read-only. 773 Updated time.Time 774 775 // CustomerKeySHA256 is the base64-encoded SHA-256 hash of the 776 // customer-supplied encryption key for the object. It is empty if there is 777 // no customer-supplied encryption key. 778 // See // https://cloud.google.com/storage/docs/encryption for more about 779 // encryption in Google Cloud Storage. 780 CustomerKeySHA256 string 781 782 // Prefix is set only for ObjectAttrs which represent synthetic "directory 783 // entries" when iterating over buckets using Query.Delimiter. See 784 // ObjectIterator.Next. When set, no other fields in ObjectAttrs will be 785 // populated. 786 Prefix string 787} 788 789// convertTime converts a time in RFC3339 format to time.Time. 790// If any error occurs in parsing, the zero-value time.Time is silently returned. 791func convertTime(t string) time.Time { 792 var r time.Time 793 if t != "" { 794 r, _ = time.Parse(time.RFC3339, t) 795 } 796 return r 797} 798 799func newObject(o *raw.Object) *ObjectAttrs { 800 if o == nil { 801 return nil 802 } 803 acl := make([]ACLRule, len(o.Acl)) 804 for i, rule := range o.Acl { 805 acl[i] = ACLRule{ 806 Entity: ACLEntity(rule.Entity), 807 Role: ACLRole(rule.Role), 808 } 809 } 810 owner := "" 811 if o.Owner != nil { 812 owner = o.Owner.Entity 813 } 814 md5, _ := base64.StdEncoding.DecodeString(o.Md5Hash) 815 crc32c, _ := decodeUint32(o.Crc32c) 816 var sha256 string 817 if o.CustomerEncryption != nil { 818 sha256 = o.CustomerEncryption.KeySha256 819 } 820 return &ObjectAttrs{ 821 Bucket: o.Bucket, 822 Name: o.Name, 823 ContentType: o.ContentType, 824 ContentLanguage: o.ContentLanguage, 825 CacheControl: o.CacheControl, 826 ACL: acl, 827 Owner: owner, 828 ContentEncoding: o.ContentEncoding, 829 ContentDisposition: o.ContentDisposition, 830 Size: int64(o.Size), 831 MD5: md5, 832 CRC32C: crc32c, 833 MediaLink: o.MediaLink, 834 Metadata: o.Metadata, 835 Generation: o.Generation, 836 Metageneration: o.Metageneration, 837 StorageClass: o.StorageClass, 838 CustomerKeySHA256: sha256, 839 Created: convertTime(o.TimeCreated), 840 Deleted: convertTime(o.TimeDeleted), 841 Updated: convertTime(o.Updated), 842 } 843} 844 845// Decode a uint32 encoded in Base64 in big-endian byte order. 846func decodeUint32(b64 string) (uint32, error) { 847 d, err := base64.StdEncoding.DecodeString(b64) 848 if err != nil { 849 return 0, err 850 } 851 if len(d) != 4 { 852 return 0, fmt.Errorf("storage: %q does not encode a 32-bit value", d) 853 } 854 return uint32(d[0])<<24 + uint32(d[1])<<16 + uint32(d[2])<<8 + uint32(d[3]), nil 855} 856 857// Encode a uint32 as Base64 in big-endian byte order. 858func encodeUint32(u uint32) string { 859 b := []byte{byte(u >> 24), byte(u >> 16), byte(u >> 8), byte(u)} 860 return base64.StdEncoding.EncodeToString(b) 861} 862 863// Query represents a query to filter objects from a bucket. 864type Query struct { 865 // Delimiter returns results in a directory-like fashion. 866 // Results will contain only objects whose names, aside from the 867 // prefix, do not contain delimiter. Objects whose names, 868 // aside from the prefix, contain delimiter will have their name, 869 // truncated after the delimiter, returned in prefixes. 870 // Duplicate prefixes are omitted. 871 // Optional. 872 Delimiter string 873 874 // Prefix is the prefix filter to query objects 875 // whose names begin with this prefix. 876 // Optional. 877 Prefix string 878 879 // Versions indicates whether multiple versions of the same 880 // object will be included in the results. 881 Versions bool 882} 883 884// contentTyper implements ContentTyper to enable an 885// io.ReadCloser to specify its MIME type. 886type contentTyper struct { 887 io.Reader 888 t string 889} 890 891func (c *contentTyper) ContentType() string { 892 return c.t 893} 894 895// Conditions constrain methods to act on specific generations of 896// objects. 897// 898// The zero value is an empty set of constraints. Not all conditions or 899// combinations of conditions are applicable to all methods. 900// See https://cloud.google.com/storage/docs/generations-preconditions 901// for details on how these operate. 902type Conditions struct { 903 // Generation constraints. 904 // At most one of the following can be set to a non-zero value. 905 906 // GenerationMatch specifies that the object must have the given generation 907 // for the operation to occur. 908 // If GenerationMatch is zero, it has no effect. 909 // Use DoesNotExist to specify that the object does not exist in the bucket. 910 GenerationMatch int64 911 912 // GenerationNotMatch specifies that the object must not have the given 913 // generation for the operation to occur. 914 // If GenerationNotMatch is zero, it has no effect. 915 GenerationNotMatch int64 916 917 // DoesNotExist specifies that the object must not exist in the bucket for 918 // the operation to occur. 919 // If DoesNotExist is false, it has no effect. 920 DoesNotExist bool 921 922 // Metadata generation constraints. 923 // At most one of the following can be set to a non-zero value. 924 925 // MetagenerationMatch specifies that the object must have the given 926 // metageneration for the operation to occur. 927 // If MetagenerationMatch is zero, it has no effect. 928 MetagenerationMatch int64 929 930 // MetagenerationNotMatch specifies that the object must not have the given 931 // metageneration for the operation to occur. 932 // If MetagenerationNotMatch is zero, it has no effect. 933 MetagenerationNotMatch int64 934} 935 936func (c *Conditions) validate(method string) error { 937 if *c == (Conditions{}) { 938 return fmt.Errorf("storage: %s: empty conditions", method) 939 } 940 if !c.isGenerationValid() { 941 return fmt.Errorf("storage: %s: multiple conditions specified for generation", method) 942 } 943 if !c.isMetagenerationValid() { 944 return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method) 945 } 946 return nil 947} 948 949func (c *Conditions) isGenerationValid() bool { 950 n := 0 951 if c.GenerationMatch != 0 { 952 n++ 953 } 954 if c.GenerationNotMatch != 0 { 955 n++ 956 } 957 if c.DoesNotExist { 958 n++ 959 } 960 return n <= 1 961} 962 963func (c *Conditions) isMetagenerationValid() bool { 964 return c.MetagenerationMatch == 0 || c.MetagenerationNotMatch == 0 965} 966 967// applyConds modifies the provided call using the conditions in conds. 968// call is something that quacks like a *raw.WhateverCall. 969func applyConds(method string, gen int64, conds *Conditions, call interface{}) error { 970 cval := reflect.ValueOf(call) 971 if gen >= 0 { 972 if !setConditionField(cval, "Generation", gen) { 973 return fmt.Errorf("storage: %s: generation not supported", method) 974 } 975 } 976 if conds == nil { 977 return nil 978 } 979 if err := conds.validate(method); err != nil { 980 return err 981 } 982 switch { 983 case conds.GenerationMatch != 0: 984 if !setConditionField(cval, "IfGenerationMatch", conds.GenerationMatch) { 985 return fmt.Errorf("storage: %s: ifGenerationMatch not supported", method) 986 } 987 case conds.GenerationNotMatch != 0: 988 if !setConditionField(cval, "IfGenerationNotMatch", conds.GenerationNotMatch) { 989 return fmt.Errorf("storage: %s: ifGenerationNotMatch not supported", method) 990 } 991 case conds.DoesNotExist: 992 if !setConditionField(cval, "IfGenerationMatch", int64(0)) { 993 return fmt.Errorf("storage: %s: DoesNotExist not supported", method) 994 } 995 } 996 switch { 997 case conds.MetagenerationMatch != 0: 998 if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) { 999 return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method) 1000 } 1001 case conds.MetagenerationNotMatch != 0: 1002 if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) { 1003 return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method) 1004 } 1005 } 1006 return nil 1007} 1008 1009func applySourceConds(gen int64, conds *Conditions, call *raw.ObjectsRewriteCall) error { 1010 if gen >= 0 { 1011 call.SourceGeneration(gen) 1012 } 1013 if conds == nil { 1014 return nil 1015 } 1016 if err := conds.validate("CopyTo source"); err != nil { 1017 return err 1018 } 1019 switch { 1020 case conds.GenerationMatch != 0: 1021 call.IfSourceGenerationMatch(conds.GenerationMatch) 1022 case conds.GenerationNotMatch != 0: 1023 call.IfSourceGenerationNotMatch(conds.GenerationNotMatch) 1024 case conds.DoesNotExist: 1025 call.IfSourceGenerationMatch(0) 1026 } 1027 switch { 1028 case conds.MetagenerationMatch != 0: 1029 call.IfSourceMetagenerationMatch(conds.MetagenerationMatch) 1030 case conds.MetagenerationNotMatch != 0: 1031 call.IfSourceMetagenerationNotMatch(conds.MetagenerationNotMatch) 1032 } 1033 return nil 1034} 1035 1036// setConditionField sets a field on a *raw.WhateverCall. 1037// We can't use anonymous interfaces because the return type is 1038// different, since the field setters are builders. 1039func setConditionField(call reflect.Value, name string, value interface{}) bool { 1040 m := call.MethodByName(name) 1041 if !m.IsValid() { 1042 return false 1043 } 1044 m.Call([]reflect.Value{reflect.ValueOf(value)}) 1045 return true 1046} 1047 1048// conditionsQuery returns the generation and conditions as a URL query 1049// string suitable for URL.RawQuery. It assumes that the conditions 1050// have been validated. 1051func conditionsQuery(gen int64, conds *Conditions) string { 1052 // URL escapes are elided because integer strings are URL-safe. 1053 var buf []byte 1054 1055 appendParam := func(s string, n int64) { 1056 if len(buf) > 0 { 1057 buf = append(buf, '&') 1058 } 1059 buf = append(buf, s...) 1060 buf = strconv.AppendInt(buf, n, 10) 1061 } 1062 1063 if gen >= 0 { 1064 appendParam("generation=", gen) 1065 } 1066 if conds == nil { 1067 return string(buf) 1068 } 1069 switch { 1070 case conds.GenerationMatch != 0: 1071 appendParam("ifGenerationMatch=", conds.GenerationMatch) 1072 case conds.GenerationNotMatch != 0: 1073 appendParam("ifGenerationNotMatch=", conds.GenerationNotMatch) 1074 case conds.DoesNotExist: 1075 appendParam("ifGenerationMatch=", 0) 1076 } 1077 switch { 1078 case conds.MetagenerationMatch != 0: 1079 appendParam("ifMetagenerationMatch=", conds.MetagenerationMatch) 1080 case conds.MetagenerationNotMatch != 0: 1081 appendParam("ifMetagenerationNotMatch=", conds.MetagenerationNotMatch) 1082 } 1083 return string(buf) 1084} 1085 1086// composeSourceObj wraps a *raw.ComposeRequestSourceObjects, but adds the methods 1087// that modifyCall searches for by name. 1088type composeSourceObj struct { 1089 src *raw.ComposeRequestSourceObjects 1090} 1091 1092func (c composeSourceObj) Generation(gen int64) { 1093 c.src.Generation = gen 1094} 1095 1096func (c composeSourceObj) IfGenerationMatch(gen int64) { 1097 // It's safe to overwrite ObjectPreconditions, since its only field is 1098 // IfGenerationMatch. 1099 c.src.ObjectPreconditions = &raw.ComposeRequestSourceObjectsObjectPreconditions{ 1100 IfGenerationMatch: gen, 1101 } 1102} 1103 1104func setEncryptionHeaders(headers http.Header, key []byte, copySource bool) error { 1105 if key == nil { 1106 return nil 1107 } 1108 // TODO(jbd): Ask the API team to return a more user-friendly error 1109 // and avoid doing this check at the client level. 1110 if len(key) != 32 { 1111 return errors.New("storage: not a 32-byte AES-256 key") 1112 } 1113 var cs string 1114 if copySource { 1115 cs = "copy-source-" 1116 } 1117 headers.Set("x-goog-"+cs+"encryption-algorithm", "AES256") 1118 headers.Set("x-goog-"+cs+"encryption-key", base64.StdEncoding.EncodeToString(key)) 1119 keyHash := sha256.Sum256(key) 1120 headers.Set("x-goog-"+cs+"encryption-key-sha256", base64.StdEncoding.EncodeToString(keyHash[:])) 1121 return nil 1122} 1123 1124// TODO(jbd): Add storage.objects.watch. 1125