Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

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