You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

178 lines
5.6 KiB

  1. // Copyright 2014 Google LLC
  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. package storage
  15. import (
  16. "context"
  17. "crypto/sha256"
  18. "encoding/base64"
  19. "net/http"
  20. "strings"
  21. "testing"
  22. "cloud.google.com/go/internal/testutil"
  23. "google.golang.org/api/googleapi"
  24. "google.golang.org/api/option"
  25. )
  26. var testEncryptionKey = []byte("secret-key-that-is-32-bytes-long")
  27. func TestErrorOnObjectsInsertCall(t *testing.T) {
  28. t.Parallel()
  29. ctx := context.Background()
  30. const contents = "hello world"
  31. doWrite := func(mt *mockTransport) *Writer {
  32. client := mockClient(t, mt)
  33. wc := client.Bucket("bucketname").Object("filename1").NewWriter(ctx)
  34. wc.ContentType = "text/plain"
  35. // We can't check that the Write fails, since it depends on the write to the
  36. // underling mockTransport failing which is racy.
  37. wc.Write([]byte(contents))
  38. return wc
  39. }
  40. wc := doWrite(&mockTransport{})
  41. // Close must always return an error though since it waits for the transport to
  42. // have closed.
  43. if err := wc.Close(); err == nil {
  44. t.Errorf("expected error on close, got nil")
  45. }
  46. // Retry on 5xx
  47. mt := &mockTransport{}
  48. mt.addResult(&http.Response{StatusCode: 503, Body: bodyReader("")}, nil)
  49. mt.addResult(&http.Response{StatusCode: 200, Body: bodyReader("{}")}, nil)
  50. wc = doWrite(mt)
  51. if err := wc.Close(); err != nil {
  52. t.Errorf("got %v, want nil", err)
  53. }
  54. got := string(mt.gotBody)
  55. if !strings.Contains(got, contents) {
  56. t.Errorf("got body %q, which does not contain %q", got, contents)
  57. }
  58. }
  59. func TestEncryption(t *testing.T) {
  60. t.Parallel()
  61. ctx := context.Background()
  62. mt := &mockTransport{}
  63. mt.addResult(&http.Response{StatusCode: 200, Body: bodyReader("{}")}, nil)
  64. client := mockClient(t, mt)
  65. obj := client.Bucket("bucketname").Object("filename1")
  66. wc := obj.Key(testEncryptionKey).NewWriter(ctx)
  67. if _, err := wc.Write([]byte("hello world")); err != nil {
  68. t.Fatal(err)
  69. }
  70. if err := wc.Close(); err != nil {
  71. t.Fatal(err)
  72. }
  73. if got, want := mt.gotReq.Header.Get("x-goog-encryption-algorithm"), "AES256"; got != want {
  74. t.Errorf("algorithm: got %q, want %q", got, want)
  75. }
  76. gotKey, err := base64.StdEncoding.DecodeString(mt.gotReq.Header.Get("x-goog-encryption-key"))
  77. if err != nil {
  78. t.Fatalf("decoding key: %v", err)
  79. }
  80. if !testutil.Equal(gotKey, testEncryptionKey) {
  81. t.Errorf("key: got %v, want %v", gotKey, testEncryptionKey)
  82. }
  83. wantHash := sha256.Sum256(testEncryptionKey)
  84. gotHash, err := base64.StdEncoding.DecodeString(mt.gotReq.Header.Get("x-goog-encryption-key-sha256"))
  85. if err != nil {
  86. t.Fatalf("decoding hash: %v", err)
  87. }
  88. if !testutil.Equal(gotHash, wantHash[:]) { // wantHash is an array
  89. t.Errorf("hash: got\n%v, want\n%v", gotHash, wantHash)
  90. }
  91. // Using a customer-supplied encryption key and a KMS key together is an error.
  92. checkKMSError := func(msg string, err error) {
  93. if err == nil {
  94. t.Errorf("%s: got nil, want error", msg)
  95. } else if !strings.Contains(err.Error(), "KMS") {
  96. t.Errorf(`%s: got %q, want it to contain "KMS"`, msg, err)
  97. }
  98. }
  99. wc = obj.Key(testEncryptionKey).NewWriter(ctx)
  100. wc.KMSKeyName = "key"
  101. _, err = wc.Write([]byte{})
  102. checkKMSError("Write", err)
  103. checkKMSError("Close", wc.Close())
  104. }
  105. // This test demonstrates the data race on Writer.err that can happen when the
  106. // Writer's context is cancelled. To see the race, comment out the w.mu.Lock/Unlock
  107. // lines in writer.go and run this test with -race.
  108. func TestRaceOnCancel(t *testing.T) {
  109. client := mockClient(t, &mockTransport{})
  110. cctx, cancel := context.WithCancel(context.Background())
  111. w := client.Bucket("b").Object("o").NewWriter(cctx)
  112. w.ChunkSize = googleapi.MinUploadChunkSize
  113. buf := make([]byte, w.ChunkSize)
  114. // This Write starts the goroutine in Writer.open. That reads the first chunk in its entirety
  115. // before sending the request (see google.golang.org/api/gensupport.PrepareUpload),
  116. // so to exhibit the race we must provide ChunkSize bytes. The goroutine then makes the RPC (L137).
  117. w.Write(buf)
  118. // Canceling the context causes the call to return context.Canceled, which makes the open goroutine
  119. // write to w.err (L151).
  120. cancel()
  121. // This call to Write concurrently reads w.err (L169).
  122. w.Write([]byte(nil))
  123. }
  124. func TestCancelDoesNotLeak(t *testing.T) {
  125. ctx, cancel := context.WithCancel(context.Background())
  126. const contents = "hello world"
  127. mt := mockTransport{}
  128. client, err := NewClient(ctx, option.WithHTTPClient(&http.Client{Transport: &mt}))
  129. if err != nil {
  130. t.Fatal(err)
  131. }
  132. wc := client.Bucket("bucketname").Object("filename1").NewWriter(ctx)
  133. wc.ContentType = "text/plain"
  134. // We can't check that the Write fails, since it depends on the write to the
  135. // underling mockTransport failing which is racy.
  136. wc.Write([]byte(contents))
  137. cancel()
  138. }
  139. func TestCloseDoesNotLeak(t *testing.T) {
  140. ctx := context.Background()
  141. const contents = "hello world"
  142. mt := mockTransport{}
  143. client, err := NewClient(ctx, option.WithHTTPClient(&http.Client{Transport: &mt}))
  144. if err != nil {
  145. t.Fatal(err)
  146. }
  147. wc := client.Bucket("bucketname").Object("filename1").NewWriter(ctx)
  148. wc.ContentType = "text/plain"
  149. // We can't check that the Write fails, since it depends on the write to the
  150. // underling mockTransport failing which is racy.
  151. wc.Write([]byte(contents))
  152. wc.Close()
  153. }