chunked_reader_v4_test.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. package s3api
  2. import (
  3. "bytes"
  4. "encoding/base64"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "strings"
  9. "sync"
  10. "testing"
  11. "hash/crc32"
  12. "github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
  13. "github.com/stretchr/testify/assert"
  14. )
  15. const (
  16. defaultTimestamp = "20130524T000000Z"
  17. defaultBucketName = "examplebucket"
  18. defaultAccessKeyId = "AKIAIOSFODNN7EXAMPLE"
  19. defaultSecretAccessKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
  20. defaultRegion = "us-east-1"
  21. )
  22. func generatestreamingAws4HmacSha256Payload() string {
  23. // This test will implement the following scenario:
  24. // https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html#example-signature-calculations-streaming
  25. chunk1 := "10000;chunk-signature=ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648\r\n" +
  26. strings.Repeat("a", 65536) + "\r\n"
  27. chunk2 := "400;chunk-signature=0055627c9e194cb4542bae2aa5492e3c1575bbb81b612b7d234b86a503ef5497\r\n" +
  28. strings.Repeat("a", 1024) + "\r\n"
  29. chunk3 := "0;chunk-signature=b6c6ea8a5354eaf15b3cb7646744f4275b71ea724fed81ceb9323e279d449df9\r\n" +
  30. "\r\n" // The last chunk is empty
  31. payload := chunk1 + chunk2 + chunk3
  32. return payload
  33. }
  34. func NewRequeststreamingAws4HmacSha256Payload() (*http.Request, error) {
  35. // This test will implement the following scenario:
  36. // https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html#example-signature-calculations-streaming
  37. payload := generatestreamingAws4HmacSha256Payload()
  38. req, err := http.NewRequest("PUT", "http://s3.amazonaws.com/examplebucket/chunkObject.txt", bytes.NewReader([]byte(payload)))
  39. if err != nil {
  40. return nil, err
  41. }
  42. req.Header.Set("Host", "s3.amazonaws.com")
  43. req.Header.Set("x-amz-date", defaultTimestamp)
  44. req.Header.Set("x-amz-storage-class", "REDUCED_REDUNDANCY")
  45. req.Header.Set("Authorization", "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=content-encoding;content-length;host;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length;x-amz-storage-class,Signature=4f232c4386841ef735655705268965c44a0e4690baa4adea153f7db9fa80a0a9")
  46. req.Header.Set("x-amz-content-sha256", "STREAMING-AWS4-HMAC-SHA256-PAYLOAD")
  47. req.Header.Set("Content-Encoding", "aws-chunked")
  48. req.Header.Set("x-amz-decoded-content-length", "66560")
  49. req.Header.Set("Content-Length", "66824")
  50. return req, nil
  51. }
  52. func TestNewSignV4ChunkedReaderstreamingAws4HmacSha256Payload(t *testing.T) {
  53. // This test will implement the following scenario:
  54. // https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html#example-signature-calculations-streaming
  55. req, err := NewRequeststreamingAws4HmacSha256Payload()
  56. if err != nil {
  57. t.Fatalf("Failed to create request: %v", err)
  58. }
  59. iam := setupIam()
  60. // The expected payload a long string of 'a's
  61. expectedPayload := strings.Repeat("a", 66560)
  62. runWithRequest(iam, req, t, expectedPayload)
  63. }
  64. func generateStreamingUnsignedPayloadTrailerPayload(includeFinalCRLF bool) string {
  65. // This test will implement the following scenario:
  66. // https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html
  67. chunk1 := "2000\r\n" + strings.Repeat("a", 8192) + "\r\n"
  68. chunk2 := "2000\r\n" + strings.Repeat("a", 8192) + "\r\n"
  69. chunk3 := "400\r\n" + strings.Repeat("a", 1024) + "\r\n"
  70. chunk4 := "0\r\n" /* the last chunk is empty */
  71. if includeFinalCRLF {
  72. // Some clients omit the final CRLF, so we need to test that case as well
  73. chunk4 += "\r\n"
  74. }
  75. data := strings.Repeat("a", 17408)
  76. writer := crc32.NewIEEE()
  77. _, err := writer.Write([]byte(data))
  78. if err != nil {
  79. fmt.Println("Error:", err)
  80. }
  81. checksum := writer.Sum(nil)
  82. base64EncodedChecksum := base64.StdEncoding.EncodeToString(checksum)
  83. trailer := "x-amz-checksum-crc32:" + base64EncodedChecksum + "\n\r\n\r\n\r\n"
  84. payload := chunk1 + chunk2 + chunk3 + chunk4 + trailer
  85. return payload
  86. }
  87. func NewRequestStreamingUnsignedPayloadTrailer(includeFinalCRLF bool) (*http.Request, error) {
  88. // This test will implement the following scenario:
  89. // https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html
  90. payload := generateStreamingUnsignedPayloadTrailerPayload(includeFinalCRLF)
  91. req, err := http.NewRequest("PUT", "http://amzn-s3-demo-bucket/Key+", bytes.NewReader([]byte(payload)))
  92. if err != nil {
  93. return nil, err
  94. }
  95. req.Header.Set("Host", "amzn-s3-demo-bucket")
  96. req.Header.Set("x-amz-date", defaultTimestamp)
  97. req.Header.Set("Content-Encoding", "aws-chunked")
  98. req.Header.Set("x-amz-decoded-content-length", "17408")
  99. req.Header.Set("x-amz-content-sha256", "STREAMING-UNSIGNED-PAYLOAD-TRAILER")
  100. req.Header.Set("x-amz-trailer", "x-amz-checksum-crc32")
  101. return req, nil
  102. }
  103. func TestNewSignV4ChunkedReaderStreamingUnsignedPayloadTrailer(t *testing.T) {
  104. // This test will implement the following scenario:
  105. // https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html
  106. iam := setupIam()
  107. req, err := NewRequestStreamingUnsignedPayloadTrailer(true)
  108. if err != nil {
  109. t.Fatalf("Failed to create request: %v", err)
  110. }
  111. // The expected payload a long string of 'a's
  112. expectedPayload := strings.Repeat("a", 17408)
  113. runWithRequest(iam, req, t, expectedPayload)
  114. req, err = NewRequestStreamingUnsignedPayloadTrailer(false)
  115. if err != nil {
  116. t.Fatalf("Failed to create request: %v", err)
  117. }
  118. runWithRequest(iam, req, t, expectedPayload)
  119. }
  120. func runWithRequest(iam IdentityAccessManagement, req *http.Request, t *testing.T, expectedPayload string) {
  121. reader, errCode := iam.newChunkedReader(req)
  122. assert.NotNil(t, reader)
  123. assert.Equal(t, s3err.ErrNone, errCode)
  124. data, err := io.ReadAll(reader)
  125. if err != nil {
  126. t.Fatalf("Failed to read data: %v", err)
  127. }
  128. assert.Equal(t, expectedPayload, string(data))
  129. }
  130. func setupIam() IdentityAccessManagement {
  131. // Create an IdentityAccessManagement instance
  132. // Add default access keys and secrets
  133. iam := IdentityAccessManagement{
  134. identities: []*Identity{},
  135. accessKeyIdent: map[string]*Identity{},
  136. accounts: map[string]*Account{},
  137. emailAccount: map[string]*Account{},
  138. hashes: map[string]*sync.Pool{},
  139. hashCounters: map[string]*int32{},
  140. identityAnonymous: nil,
  141. domain: "",
  142. isAuthEnabled: false,
  143. }
  144. iam.identities = append(iam.identities, &Identity{
  145. Name: "default",
  146. Credentials: []*Credential{
  147. {
  148. AccessKey: defaultAccessKeyId,
  149. SecretKey: defaultSecretAccessKey,
  150. },
  151. },
  152. Actions: []Action{
  153. "Read",
  154. "Write",
  155. "List",
  156. },
  157. })
  158. iam.accessKeyIdent[defaultAccessKeyId] = iam.identities[0]
  159. return iam
  160. }